1 /* canonusr.c - user canonicalization support
2  * Rob Siemborski
3  */
4 /*
5  * Copyright (c) 1998-2016 Carnegie Mellon University.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The name "Carnegie Mellon University" must not be used to
20  *    endorse or promote products derived from this software without
21  *    prior written permission. For permission or any other legal
22  *    details, please contact
23  *      Carnegie Mellon University
24  *      Center for Technology Transfer and Enterprise Creation
25  *      4615 Forbes Avenue
26  *      Suite 302
27  *      Pittsburgh, PA  15213
28  *      (412) 268-7393, fax: (412) 268-7395
29  *      innovation@andrew.cmu.edu
30  *
31  * 4. Redistributions of any form whatsoever must retain the following
32  *    acknowledgment:
33  *    "This product includes software developed by Computing Services
34  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35  *
36  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43  */
44 
45 #include <config.h>
46 #include <sasl.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <prop.h>
50 #include <stdio.h>
51 
52 #include "saslint.h"
53 
54 typedef struct canonuser_plug_list
55 {
56     struct canonuser_plug_list *next;
57     char name[PATH_MAX];
58     const sasl_canonuser_plug_t *plug;
59 } canonuser_plug_list_t;
60 
61 static canonuser_plug_list_t *canonuser_head = NULL;
62 
63 /* default behavior:
64  *                   eliminate leading & trailing whitespace,
65  *                   null-terminate, and get into the outparams
66  *                   (handled by INTERNAL plugin) */
67 /* a zero ulen or alen indicates that it is strlen(value) */
_sasl_canon_user(sasl_conn_t * conn,const char * user,unsigned ulen,unsigned flags,sasl_out_params_t * oparams)68 int _sasl_canon_user(sasl_conn_t *conn,
69                      const char *user, unsigned ulen,
70                      unsigned flags,
71                      sasl_out_params_t *oparams)
72 {
73     canonuser_plug_list_t *ptr;
74     sasl_server_conn_t *sconn = NULL;
75     sasl_client_conn_t *cconn = NULL;
76     sasl_canon_user_t *cuser_cb;
77     sasl_getopt_t *getopt;
78     void *context;
79     int result;
80     const char *plugin_name = NULL;
81     char *user_buf;
82     unsigned *lenp;
83 
84     if(!conn) return SASL_BADPARAM;
85     if(!user || !oparams) return SASL_BADPARAM;
86 
87     if(flags & SASL_CU_AUTHID) {
88 	user_buf = conn->authid_buf;
89 	lenp = &(oparams->alen);
90     } else if (flags & SASL_CU_AUTHZID) {
91 	user_buf = conn->user_buf;
92 	lenp = &(oparams->ulen);
93     } else {
94 	return SASL_BADPARAM;
95     }
96 
97     if (conn->type == SASL_CONN_SERVER)
98       sconn = (sasl_server_conn_t *)conn;
99     else if (conn->type == SASL_CONN_CLIENT)
100       cconn = (sasl_client_conn_t *)conn;
101     else return SASL_FAIL;
102 
103     if(!ulen) ulen = (unsigned int)strlen(user);
104 
105     /* check to see if we have a callback to make*/
106     result = _sasl_getcallback(conn,
107 			       SASL_CB_CANON_USER,
108 			       (sasl_callback_ft *)&cuser_cb,
109 			       &context);
110     if(result == SASL_OK && cuser_cb) {
111 	result = cuser_cb(conn,
112 			  context,
113 			  user,
114 			  ulen,
115 			  flags,
116 			  (sconn ?
117 				sconn->user_realm :
118 				NULL),
119 			  user_buf,
120 			  CANON_BUF_SIZE,
121 			  lenp);
122 
123 
124 	if (result != SASL_OK) return result;
125 
126 	/* Point the input copy at the stored buffer */
127 	user = user_buf;
128 	ulen = *lenp;
129     }
130 
131     /* which plugin are we supposed to use? */
132     result = _sasl_getcallback(conn,
133 			       SASL_CB_GETOPT,
134 			       (sasl_callback_ft *)&getopt,
135 			       &context);
136     if (result == SASL_OK && getopt) {
137 	getopt(context, NULL, "canon_user_plugin", &plugin_name, NULL);
138     }
139 
140     if (!plugin_name) {
141 	/* Use Default */
142 	plugin_name = "INTERNAL";
143     }
144 
145     for (ptr = canonuser_head; ptr; ptr = ptr->next) {
146 	/* A match is if we match the internal name of the plugin, or if
147 	 * we match the filename (old-style) */
148 	if ((ptr->plug->name && !strcmp(plugin_name, ptr->plug->name))
149 	   || !strcmp(plugin_name, ptr->name)) break;
150     }
151 
152     /* We clearly don't have this one! */
153     if (!ptr) {
154 	sasl_seterror(conn, 0, "desired canon_user plugin %s not found",
155 		      plugin_name);
156 	return SASL_NOMECH;
157     }
158 
159     if (sconn) {
160 	/* we're a server */
161 	result = ptr->plug->canon_user_server(ptr->plug->glob_context,
162 					      sconn->sparams,
163 					      user, ulen,
164 					      flags,
165 					      user_buf,
166 					      CANON_BUF_SIZE, lenp);
167     } else {
168 	/* we're a client */
169 	result = ptr->plug->canon_user_client(ptr->plug->glob_context,
170 					      cconn->cparams,
171 					      user, ulen,
172 					      flags,
173 					      user_buf,
174 					      CANON_BUF_SIZE, lenp);
175     }
176 
177     if (result != SASL_OK) return result;
178 
179     if ((flags & SASL_CU_AUTHID) && (flags & SASL_CU_AUTHZID)) {
180 	/* We did both, so we need to copy the result into
181 	 * the buffer for the authzid from the buffer for the authid */
182 	memcpy(conn->user_buf, conn->authid_buf, CANON_BUF_SIZE);
183 	oparams->ulen = oparams->alen;
184     }
185 
186     /* Set the appropriate oparams (lengths have already been set by lenp) */
187     if (flags & SASL_CU_AUTHID) {
188 	oparams->authid = conn->authid_buf;
189     }
190 
191     if (flags & SASL_CU_AUTHZID) {
192 	oparams->user = conn->user_buf;
193     }
194 
195     RETURN(conn, result);
196 }
197 
198 /* Lookup all properties for authentication and/or authorization identity. */
_sasl_auxprop_lookup_user_props(sasl_conn_t * conn,unsigned flags,sasl_out_params_t * oparams)199 static int _sasl_auxprop_lookup_user_props (sasl_conn_t *conn,
200 					    unsigned flags,
201 					    sasl_out_params_t *oparams)
202 {
203     sasl_server_conn_t *sconn = NULL;
204     int result = SASL_OK;
205 
206     if (!conn) return SASL_BADPARAM;
207     if (!oparams) return SASL_BADPARAM;
208 
209 #ifndef macintosh
210     if (conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn;
211 
212     /* do auxprop lookups (server only) */
213     if (sconn) {
214 	int authz_result;
215 	unsigned auxprop_lookup_flags = flags & SASL_CU_ASIS_MASK;
216 
217 	if (flags & SASL_CU_OVERRIDE) {
218 	    auxprop_lookup_flags |= SASL_AUXPROP_OVERRIDE;
219 	}
220 
221 	if (flags & SASL_CU_AUTHID) {
222 	    result = _sasl_auxprop_lookup(sconn->sparams,
223 					  auxprop_lookup_flags,
224 					  oparams->authid,
225 					  oparams->alen);
226 	} else {
227 	    result = SASL_CONTINUE;
228 	}
229 	if (flags & SASL_CU_AUTHZID) {
230 	    authz_result = _sasl_auxprop_lookup(sconn->sparams,
231 						auxprop_lookup_flags | SASL_AUXPROP_AUTHZID,
232 						oparams->user,
233 						oparams->ulen);
234 
235 	    if (result == SASL_CONTINUE) {
236 		/* Only SASL_CU_AUTHZID was requested.
237 		   The authz_result value is authoritative. */
238 		result = authz_result;
239 	    } else if (result == SASL_OK && authz_result != SASL_NOUSER) {
240 		/* Use the authz_result value, unless "result"
241 		   already contains an error */
242 		result = authz_result;
243 	    }
244 	}
245 
246 	if ((flags & SASL_CU_EXTERNALLY_VERIFIED) && (result == SASL_NOUSER || result == SASL_NOMECH)) {
247 	    /* The called has explicitly told us that the authentication identity
248 	       was already verified or will be verified independently.
249 	       So a failure to retrieve any associated properties
250 	       is not an error. For example the caller is using Kerberos to verify user,
251 	       but the LDAPDB/SASLDB auxprop plugin doesn't contain any auxprops for
252 	       the user.
253 	       Another case is PLAIN/LOGIN not using auxprop to verify user passwords. */
254 	    result = SASL_OK;
255 	}
256     }
257 #endif
258 
259     RETURN(conn, result);
260 }
261 
262 /* default behavior:
263  *                   Eliminate leading & trailing whitespace,
264  *                   null-terminate, and get into the outparams
265  *                   (handled by INTERNAL plugin).
266  *
267  *                   Server only: Also does auxprop lookups once username
268  *                   is canonicalized. */
_sasl_canon_user_lookup(sasl_conn_t * conn,const char * user,unsigned ulen,unsigned flags,sasl_out_params_t * oparams)269 int _sasl_canon_user_lookup (sasl_conn_t *conn,
270 			     const char *user,
271 			     unsigned ulen,
272 			     unsigned flags,
273 			     sasl_out_params_t *oparams)
274 {
275     int result;
276 
277     result = _sasl_canon_user (conn,
278 			       user,
279 			       ulen,
280 			       flags,
281 			       oparams);
282     if (result == SASL_OK) {
283 	result = _sasl_auxprop_lookup_user_props (conn,
284 						  flags,
285 						  oparams);
286     }
287 
288     RETURN(conn, result);
289 }
290 
_sasl_canonuser_free()291 void _sasl_canonuser_free()
292 {
293     canonuser_plug_list_t *ptr, *ptr_next;
294 
295     for(ptr = canonuser_head; ptr; ptr = ptr_next) {
296 	ptr_next = ptr->next;
297 	if(ptr->plug->canon_user_free)
298 	    ptr->plug->canon_user_free(ptr->plug->glob_context,
299 				       sasl_global_utils);
300 	sasl_FREE(ptr);
301     }
302 
303     canonuser_head = NULL;
304 }
305 
sasl_canonuser_add_plugin(const char * plugname,sasl_canonuser_init_t * canonuserfunc)306 int sasl_canonuser_add_plugin(const char *plugname,
307 			      sasl_canonuser_init_t *canonuserfunc)
308 {
309     int result, out_version;
310     canonuser_plug_list_t *new_item;
311     sasl_canonuser_plug_t *plug;
312 
313     if(!plugname || strlen(plugname) > (PATH_MAX - 1)) {
314 	sasl_seterror(NULL, 0,
315 		      "bad plugname passed to sasl_canonuser_add_plugin\n");
316 	return SASL_BADPARAM;
317     }
318 
319     result = canonuserfunc(sasl_global_utils, SASL_CANONUSER_PLUG_VERSION,
320 			   &out_version, &plug, plugname);
321 
322     if(result != SASL_OK) {
323 	_sasl_log(NULL, SASL_LOG_ERR, "%s_canonuser_plug_init() failed in sasl_canonuser_add_plugin(): %z\n",
324 		  plugname, result);
325 	return result;
326     }
327 
328     if(!plug->canon_user_server && !plug->canon_user_client) {
329 	/* We need at least one of these implemented */
330 	_sasl_log(NULL, SASL_LOG_ERR,
331 		  "canonuser plugin '%s' without either client or server side", plugname);
332 	return SASL_BADPROT;
333     }
334 
335     new_item = sasl_ALLOC(sizeof(canonuser_plug_list_t));
336     if(!new_item) return SASL_NOMEM;
337 
338     strncpy(new_item->name, plugname, PATH_MAX - 1);
339     new_item->name[strlen(plugname)] = '\0';
340 
341     new_item->plug = plug;
342     new_item->next = canonuser_head;
343     canonuser_head = new_item;
344 
345     return SASL_OK;
346 }
347 
348 #ifdef MIN
349 #undef MIN
350 #endif
351 #define MIN(a,b) (((a) < (b))? (a):(b))
352 
_canonuser_internal(const sasl_utils_t * utils,const char * user,unsigned ulen,unsigned flags,char * out_user,unsigned out_umax,unsigned * out_ulen)353 static int _canonuser_internal(const sasl_utils_t *utils,
354 			       const char *user, unsigned ulen,
355 			       unsigned flags __attribute__((unused)),
356 			       char *out_user,
357 			       unsigned out_umax, unsigned *out_ulen)
358 {
359     unsigned i;
360     char *in_buf, *userin;
361     const char *begin_u;
362     unsigned u_apprealm = 0;
363     sasl_server_conn_t *sconn = NULL;
364 
365     if(!utils || !user) return SASL_BADPARAM;
366 
367     in_buf = sasl_ALLOC((ulen + 2) * sizeof(char));
368     if(!in_buf) return SASL_NOMEM;
369 
370     userin = in_buf;
371 
372     memcpy(userin, user, ulen);
373     userin[ulen] = '\0';
374 
375     /* Strip User ID */
376     for(i=0;isspace((int)userin[i]) && i<ulen;i++);
377     begin_u = &(userin[i]);
378     if(i>0) ulen -= i;
379 
380     for(;ulen > 0 && isspace((int)begin_u[ulen-1]); ulen--);
381     if(begin_u == &(userin[ulen])) {
382 	sasl_FREE(in_buf);
383 	utils->seterror(utils->conn, 0, "All-whitespace username.");
384 	return SASL_FAIL;
385     }
386 
387     if(utils->conn && utils->conn->type == SASL_CONN_SERVER)
388 	sconn = (sasl_server_conn_t *)utils->conn;
389 
390     /* Need to append realm if necessary (see sasl.h) */
391     if(sconn && sconn->user_realm && !strchr(user, '@')) {
392 	u_apprealm = (unsigned) strlen(sconn->user_realm) + 1;
393     }
394 
395     /* Now Copy */
396     memcpy(out_user, begin_u, MIN(ulen, out_umax));
397     if(sconn && u_apprealm) {
398 	if(ulen >= out_umax) return SASL_BUFOVER;
399 	out_user[ulen] = '@';
400 	memcpy(&(out_user[ulen+1]), sconn->user_realm,
401 	       MIN(u_apprealm-1, out_umax-ulen-1));
402     }
403     out_user[MIN(ulen + u_apprealm,out_umax)] = '\0';
404 
405     if(ulen + u_apprealm > out_umax) return SASL_BUFOVER;
406 
407     if(out_ulen) *out_ulen = MIN(ulen + u_apprealm,out_umax);
408 
409     sasl_FREE(in_buf);
410     return SASL_OK;
411 }
412 
_cu_internal_server(void * glob_context,sasl_server_params_t * sparams,const char * user,unsigned ulen,unsigned flags,char * out_user,unsigned out_umax,unsigned * out_ulen)413 static int _cu_internal_server(void *glob_context __attribute__((unused)),
414 			       sasl_server_params_t *sparams,
415 			       const char *user, unsigned ulen,
416 			       unsigned flags,
417 			       char *out_user,
418 			       unsigned out_umax, unsigned *out_ulen)
419 {
420     return _canonuser_internal(sparams->utils,
421 			       user, ulen,
422 			       flags, out_user, out_umax, out_ulen);
423 }
424 
_cu_internal_client(void * glob_context,sasl_client_params_t * cparams,const char * user,unsigned ulen,unsigned flags,char * out_user,unsigned out_umax,unsigned * out_ulen)425 static int _cu_internal_client(void *glob_context __attribute__((unused)),
426 			       sasl_client_params_t *cparams,
427 			       const char *user, unsigned ulen,
428 			       unsigned flags,
429 			       char *out_user,
430 			       unsigned out_umax, unsigned *out_ulen)
431 {
432     return _canonuser_internal(cparams->utils,
433 			       user, ulen,
434 			       flags, out_user, out_umax, out_ulen);
435 }
436 
437 static sasl_canonuser_plug_t canonuser_internal_plugin = {
438         0, /* features */
439 	0, /* spare */
440 	NULL, /* glob_context */
441 	"INTERNAL", /* name */
442 	NULL, /* canon_user_free */
443 	_cu_internal_server,
444 	_cu_internal_client,
445 	NULL,
446 	NULL,
447 	NULL
448 };
449 
internal_canonuser_init(const sasl_utils_t * utils,int max_version,int * out_version,sasl_canonuser_plug_t ** plug,const char * plugname)450 int internal_canonuser_init(const sasl_utils_t *utils __attribute__((unused)),
451                             int max_version,
452                             int *out_version,
453                             sasl_canonuser_plug_t **plug,
454                             const char *plugname __attribute__((unused)))
455 {
456     if(!out_version || !plug) return SASL_BADPARAM;
457 
458     if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS;
459 
460     *out_version = SASL_CANONUSER_PLUG_VERSION;
461 
462     *plug = &canonuser_internal_plugin;
463 
464     return SASL_OK;
465 }
466