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