1 /* SASL server API implementation
2  * Rob Siemborski
3  * Tim Martin
4  */
5 /*
6  * Copyright (c) 1998-2016 Carnegie Mellon University.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The name "Carnegie Mellon University" must not be used to
21  *    endorse or promote products derived from this software without
22  *    prior written permission. For permission or any other legal
23  *    details, please contact
24  *      Carnegie Mellon University
25  *      Center for Technology Transfer and Enterprise Creation
26  *      4615 Forbes Avenue
27  *      Suite 302
28  *      Pittsburgh, PA  15213
29  *      (412) 268-7393, fax: (412) 268-7395
30  *      innovation@andrew.cmu.edu
31  *
32  * 4. Redistributions of any form whatsoever must retain the following
33  *    acknowledgment:
34  *    "This product includes software developed by Computing Services
35  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
36  *
37  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
38  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
40  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
42  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
43  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44  */
45 
46 #include <config.h>
47 
48 /* sasldb stuff */
49 
50 #include <stdio.h>
51 
52 #include "sasl.h"
53 #include "saslutil.h"
54 #include "saslplug.h"
55 #include "../sasldb/sasldb.h"
56 
57 #include "plugin_common.h"
58 
sasldb_auxprop_lookup(void * glob_context,sasl_server_params_t * sparams,unsigned flags,const char * user,unsigned ulen)59 static int sasldb_auxprop_lookup(void *glob_context __attribute__((unused)),
60 				  sasl_server_params_t *sparams,
61 				  unsigned flags,
62 				  const char *user,
63 				  unsigned ulen)
64 {
65     char *userid = NULL;
66     char *realm = NULL;
67     const char *user_realm = NULL;
68     int ret;
69     const struct propval *to_fetch, *cur;
70     char value[8192];
71     size_t value_len;
72     char *user_buf;
73     int verify_against_hashed_password;
74     int saw_user_password = 0;
75 
76     if (!sparams || !user) return SASL_BADPARAM;
77 
78     user_buf = sparams->utils->malloc(ulen + 1);
79     if(!user_buf) {
80 	ret = SASL_NOMEM;
81 	goto done;
82     }
83 
84     memcpy(user_buf, user, ulen);
85     user_buf[ulen] = '\0';
86 
87     if(sparams->user_realm) {
88 	user_realm = sparams->user_realm;
89     } else {
90 	user_realm = sparams->serverFQDN;
91     }
92 
93     ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm,
94 			  sparams->serverFQDN, user_buf);
95     if(ret != SASL_OK) goto done;
96 
97     to_fetch = sparams->utils->prop_get(sparams->propctx);
98     if (!to_fetch) {
99 	ret = SASL_NOMEM;
100 	goto done;
101     }
102 
103     verify_against_hashed_password = flags & SASL_AUXPROP_VERIFY_AGAINST_HASH;
104 
105     /* Use a fake value to signal that we have no property to lookup */
106     ret = SASL_CONTINUE;
107     for(cur = to_fetch; cur->name; cur++) {
108 	int cur_ret;
109 	const char *realname = cur->name;
110 
111 	/* Only look up properties that apply to this lookup! */
112 	if(cur->name[0] == '*' && (flags & SASL_AUXPROP_AUTHZID)) continue;
113 	if(!(flags & SASL_AUXPROP_AUTHZID)) {
114 	    if(cur->name[0] != '*') continue;
115 	    else realname = cur->name + 1;
116 	}
117 
118 	/* If it's there already, we want to see if it needs to be
119 	 * overridden. userPassword is a special case, because it's value
120 	   is always present if SASL_AUXPROP_VERIFY_AGAINST_HASH is specified.
121 	   When SASL_AUXPROP_VERIFY_AGAINST_HASH is set, we just clear userPassword. */
122 	if (cur->values && !(flags & SASL_AUXPROP_OVERRIDE) &&
123 	    (verify_against_hashed_password == 0 ||
124 	     strcasecmp(realname, SASL_AUX_PASSWORD_PROP) != 0)) {
125 	    continue;
126 	} else if (cur->values) {
127 	    sparams->utils->prop_erase(sparams->propctx, cur->name);
128 	}
129 
130 	if (strcasecmp(realname, SASL_AUX_PASSWORD_PROP) == 0) {
131 	    saw_user_password = 1;
132 	}
133 
134 	cur_ret = _sasldb_getdata(sparams->utils,
135 			      sparams->utils->conn, userid, realm,
136 			      realname, value, sizeof(value), &value_len);
137 
138 	/* Assumption: cur_ret is never SASL_CONTINUE */
139 
140 	/* If this is the first property we've tried to fetch ==>
141 	   always set the global error code.
142 	   If we had SASL_NOUSER ==> any other error code overrides it
143 	   (including SASL_NOUSER). */
144 	if (ret == SASL_CONTINUE || ret == SASL_NOUSER) {
145 	    ret = cur_ret;
146 	} else if (ret == SASL_OK) {
147 	    /* Any error code other than SASL_NOUSER overrides SASL_OK.
148 	       (And SASL_OK overrides SASL_OK as well) */
149 	    if (cur_ret != SASL_NOUSER) {
150 		ret = cur_ret;
151 	    }
152 	}
153 	/* Any other global error code is left as is */
154 
155 	if (cur_ret != SASL_OK) {
156 	    if (cur_ret != SASL_NOUSER) {
157 		/* No point in continuing if we hit any serious error */
158 		break;
159 	    }
160 	    /* We didn't find it, leave it as not found */
161 	    continue;
162 	}
163 
164 	sparams->utils->prop_set(sparams->propctx, cur->name,
165 				 value, (unsigned) value_len);
166     }
167 
168     /* [Keep in sync with LDAPDB, SQL]
169        If ret is SASL_CONTINUE, it means that no properties were requested
170        (or maybe some were requested, but they already have values and
171        SASL_AUXPROP_OVERRIDE flag is not set).
172        Always return SASL_OK in this case. */
173     if (ret == SASL_CONTINUE) {
174         ret = SASL_OK;
175     }
176 
177     if (flags & SASL_AUXPROP_AUTHZID) {
178 	/* This is a lie, but the caller can't handle
179 	   when we return SASL_NOUSER for authorization identity lookup. */
180 	if (ret == SASL_NOUSER) {
181 	    ret = SASL_OK;
182 	}
183     } else {
184 	if (ret == SASL_NOUSER && saw_user_password == 0) {
185 	    /* Verify user existence by checking presence of
186 	       the userPassword attribute */
187 	    ret = _sasldb_getdata(sparams->utils,
188 				  sparams->utils->conn,
189 				  userid,
190 				  realm,
191 				  SASL_AUX_PASSWORD_PROP,
192 				  value,
193 				  sizeof(value),
194 				  &value_len);
195 	}
196     }
197 
198  done:
199     if (userid) sparams->utils->free(userid);
200     if (realm)  sparams->utils->free(realm);
201     if (user_buf) sparams->utils->free(user_buf);
202 
203     return ret;
204 }
205 
sasldb_auxprop_store(void * glob_context,sasl_server_params_t * sparams,struct propctx * ctx,const char * user,unsigned ulen)206 static int sasldb_auxprop_store(void *glob_context __attribute__((unused)),
207 				sasl_server_params_t *sparams,
208 				struct propctx *ctx,
209 				const char *user,
210 				unsigned ulen)
211 {
212     char *userid = NULL;
213     char *realm = NULL;
214     const char *user_realm = NULL;
215     int ret = SASL_FAIL;
216     const struct propval *to_store, *cur;
217     char *user_buf;
218 
219     /* just checking if we are enabled */
220     if(!ctx) return SASL_OK;
221 
222     if(!sparams || !user) return SASL_BADPARAM;
223 
224     user_buf = sparams->utils->malloc(ulen + 1);
225     if(!user_buf) {
226 	ret = SASL_NOMEM;
227 	goto done;
228     }
229 
230     memcpy(user_buf, user, ulen);
231     user_buf[ulen] = '\0';
232 
233     if(sparams->user_realm) {
234 	user_realm = sparams->user_realm;
235     } else {
236 	user_realm = sparams->serverFQDN;
237     }
238 
239     ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm,
240 			  sparams->serverFQDN, user_buf);
241     if(ret != SASL_OK) goto done;
242 
243     to_store = sparams->utils->prop_get(ctx);
244     if(!to_store) {
245 	ret = SASL_BADPARAM;
246 	goto done;
247     }
248 
249     ret = SASL_OK;
250     for (cur = to_store; cur->name; cur++) {
251 	const char *value = (cur->values && cur->values[0]) ? cur->values[0] : NULL;
252 
253 	if (cur->name[0] == '*') {
254 	    continue;
255 	}
256 
257 	/* WARN: We only support one value right now. */
258 	ret = _sasldb_putdata(sparams->utils,
259 			      sparams->utils->conn,
260 			      userid,
261 			      realm,
262 			      cur->name,
263 			      value,
264 			      value ? strlen(value) : 0);
265 
266 	if (value == NULL && ret == SASL_NOUSER) {
267 	    /* Deleting something which is not there is not an error */
268 	    ret = SASL_OK;
269 	}
270 
271 	if (ret != SASL_OK) {
272 	    /* We've already failed, no point in continuing */
273 	    break;
274 	}
275     }
276 
277  done:
278     if (userid) sparams->utils->free(userid);
279     if (realm)  sparams->utils->free(realm);
280     if (user_buf) sparams->utils->free(user_buf);
281 
282     return ret;
283 }
284 
285 static sasl_auxprop_plug_t sasldb_auxprop_plugin = {
286     0,           		/* Features */
287     0,           		/* spare */
288     NULL,        		/* glob_context */
289     sasldb_auxprop_free,        /* auxprop_free */
290     sasldb_auxprop_lookup,	/* auxprop_lookup */
291     "sasldb",			/* name */
292     sasldb_auxprop_store	/* auxprop_store */
293 };
294 
sasldb_auxprop_plug_init(const sasl_utils_t * utils,int max_version,int * out_version,sasl_auxprop_plug_t ** plug,const char * plugname)295 int sasldb_auxprop_plug_init(const sasl_utils_t *utils,
296                              int max_version,
297                              int *out_version,
298                              sasl_auxprop_plug_t **plug,
299                              const char *plugname __attribute__((unused)))
300 {
301     if(!out_version || !plug) return SASL_BADPARAM;
302 
303     /* Do we have database support? */
304     /* Note that we can use a NULL sasl_conn_t because our
305      * sasl_utils_t is "blessed" with the global callbacks */
306     if(_sasl_check_db(utils, NULL) != SASL_OK)
307 	return SASL_NOMECH;
308 
309     /* Check if libsasl API is older than ours. If it is, fail */
310     if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
311 
312     *out_version = SASL_AUXPROP_PLUG_VERSION;
313 
314     *plug = &sasldb_auxprop_plugin;
315 
316     return SASL_OK;
317 }
318