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
60typedef 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_
72static 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) */
82int _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_
236void _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
254void _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_
271int 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
278int _sasl_canonuser_add_plugin(void *ctx,
279                               const char *plugname,
280                               sasl_canonuser_init_t *canonuserfunc)
281#else
282int 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
378static 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
454static 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
466static 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
478static 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
491int 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