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