1/* canonusr.c - user canonicalization support
2 * Rob Siemborski
3 * $Id: canonusr.c,v 1.3 2004/07/07 22:48:35 snsimon Exp $
4 */
5/*
6 * Copyright (c) 1998-2003 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 *      Office of Technology Transfer
25 *      Carnegie Mellon University
26 *      5000 Forbes Avenue
27 *      Pittsburgh, PA  15213-3890
28 *      (412) 268-4387, fax: (412) 268-7395
29 *      tech-transfer@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
54typedef 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
61static 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 *
67 *                   (handled by INTERNAL plugin) */
68/* Also does auxprop lookups once username is canonicalized */
69/* a zero ulen or alen indicates that it is strlen(value) */
70int _sasl_canon_user(sasl_conn_t *conn,
71                     const char *user, unsigned ulen,
72                     unsigned flags,
73                     sasl_out_params_t *oparams)
74{
75    canonuser_plug_list_t *ptr;
76    sasl_server_conn_t *sconn = NULL;
77    sasl_client_conn_t *cconn = NULL;
78    sasl_canon_user_t *cuser_cb;
79    sasl_getopt_t *getopt;
80    void *context;
81    int result;
82    const char *plugin_name = NULL;
83    char *user_buf;
84    unsigned *lenp;
85
86    if(!conn) return SASL_BADPARAM;
87    if(!user || !oparams) return SASL_BADPARAM;
88
89    if(flags & SASL_CU_AUTHID) {
90	user_buf = conn->authid_buf;
91	lenp = &(oparams->alen);
92    } else if (flags & SASL_CU_AUTHZID) {
93	user_buf = conn->user_buf;
94	lenp = &(oparams->ulen);
95    } else {
96	return SASL_BADPARAM;
97    }
98
99    if(conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn;
100    else if(conn->type == SASL_CONN_CLIENT) 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, SASL_CB_CANON_USER,
107			       &cuser_cb, &context);
108    if(result == SASL_OK && cuser_cb) {
109	result = cuser_cb(conn, context,
110			user, ulen,
111			flags, (conn->type == SASL_CONN_SERVER ?
112				((sasl_server_conn_t *)conn)->user_realm :
113				NULL),
114			user_buf, CANON_BUF_SIZE, lenp);
115
116
117	if (result != SASL_OK) return result;
118
119	/* Point the input copy at the stored buffer */
120	user = user_buf;
121	ulen = *lenp;
122    }
123
124    /* which plugin are we supposed to use? */
125    result = _sasl_getcallback(conn, SASL_CB_GETOPT,
126			       &getopt, &context);
127    if(result == SASL_OK && getopt) {
128	getopt(context, NULL, "canon_user_plugin", &plugin_name, NULL);
129    }
130
131    if(!plugin_name) {
132	/* Use Defualt */
133	plugin_name = "INTERNAL";
134    }
135
136    for(ptr = canonuser_head; ptr; ptr = ptr->next) {
137	/* A match is if we match the internal name of the plugin, or if
138	 * we match the filename (old-style) */
139	if((ptr->plug->name && !strcmp(plugin_name, ptr->plug->name))
140	   || !strcmp(plugin_name, ptr->name)) break;
141    }
142
143    /* We clearly don't have this one! */
144    if(!ptr) {
145	sasl_seterror(conn, 0, "desired canon_user plugin %s not found",
146		      plugin_name);
147	return SASL_NOMECH;
148    }
149
150    if(sconn) {
151	/* we're a server */
152	result = ptr->plug->canon_user_server(ptr->plug->glob_context,
153					      sconn->sparams,
154					      user, ulen,
155					      flags,
156					      user_buf,
157					      CANON_BUF_SIZE, lenp);
158    } else {
159	/* we're a client */
160	result = ptr->plug->canon_user_client(ptr->plug->glob_context,
161					      cconn->cparams,
162					      user, ulen,
163					      flags,
164					      user_buf,
165					      CANON_BUF_SIZE, lenp);
166    }
167
168    if(result != SASL_OK) return result;
169
170    if((flags & SASL_CU_AUTHID) && (flags & SASL_CU_AUTHZID)) {
171	/* We did both, so we need to copy the result into
172	 * the buffer for the authzid from the buffer for the authid */
173	memcpy(conn->user_buf, conn->authid_buf, CANON_BUF_SIZE);
174	oparams->ulen = oparams->alen;
175    }
176
177    /* Set the appropriate oparams (lengths have already been set by lenp) */
178    if(flags & SASL_CU_AUTHID) {
179	oparams->authid = conn->authid_buf;
180    }
181
182    if (flags & SASL_CU_AUTHZID) {
183	oparams->user = conn->user_buf;
184    }
185
186#ifndef macintosh
187    /* do auxprop lookups (server only) */
188    if(sconn) {
189	if(flags & SASL_CU_AUTHID) {
190	    _sasl_auxprop_lookup(sconn->sparams, 0,
191				 oparams->authid, oparams->alen);
192	}
193	if(flags & SASL_CU_AUTHZID) {
194	    _sasl_auxprop_lookup(sconn->sparams, SASL_AUXPROP_AUTHZID,
195				 oparams->user, oparams->ulen);
196	}
197    }
198#endif
199
200
201    RETURN(conn, SASL_OK);
202}
203
204void _sasl_canonuser_free()
205{
206    canonuser_plug_list_t *ptr, *ptr_next;
207
208    for(ptr = canonuser_head; ptr; ptr = ptr_next) {
209	ptr_next = ptr->next;
210	if(ptr->plug->canon_user_free)
211	    ptr->plug->canon_user_free(ptr->plug->glob_context,
212				       sasl_global_utils);
213	sasl_FREE(ptr);
214    }
215
216    canonuser_head = NULL;
217}
218
219int sasl_canonuser_add_plugin(const char *plugname,
220			      sasl_canonuser_init_t *canonuserfunc)
221{
222    int result, out_version;
223    canonuser_plug_list_t *new_item;
224    sasl_canonuser_plug_t *plug;
225
226    if(!plugname || strlen(plugname) > (PATH_MAX - 1)) {
227	sasl_seterror(NULL, 0,
228		      "bad plugname passed to sasl_canonuser_add_plugin\n");
229	return SASL_BADPARAM;
230    }
231
232    result = canonuserfunc(sasl_global_utils, SASL_CANONUSER_PLUG_VERSION,
233			   &out_version, &plug, plugname);
234
235    if(result != SASL_OK) {
236	_sasl_log(NULL, SASL_LOG_ERR, "canonuserfunc error %i\n",result);
237	return result;
238    }
239
240    if(!plug->canon_user_server && !plug->canon_user_client) {
241	/* We need at least one of these implemented */
242	_sasl_log(NULL, SASL_LOG_ERR,
243		  "canonuser plugin without either client or server side");
244	return SASL_BADPROT;
245    }
246
247    new_item = sasl_ALLOC(sizeof(canonuser_plug_list_t));
248    if(!new_item) return SASL_NOMEM;
249
250    strncpy(new_item->name, plugname, PATH_MAX);
251
252    new_item->plug = plug;
253    new_item->next = canonuser_head;
254    canonuser_head = new_item;
255
256    return SASL_OK;
257}
258
259#ifdef MIN
260#undef MIN
261#endif
262#define MIN(a,b) (((a) < (b))? (a):(b))
263
264static int _canonuser_internal(const sasl_utils_t *utils,
265			       const char *user, unsigned ulen,
266			       unsigned flags __attribute__((unused)),
267			       char *out_user,
268			       unsigned out_umax, unsigned *out_ulen)
269{
270    unsigned i;
271    char *in_buf, *userin;
272    const char *begin_u;
273    size_t u_apprealm = 0;
274    sasl_server_conn_t *sconn = NULL;
275
276    if(!utils || !user) return SASL_BADPARAM;
277
278    in_buf = sasl_ALLOC((ulen + 2) * sizeof(char));
279    if(!in_buf) return SASL_NOMEM;
280
281    userin = in_buf;
282
283    memcpy(userin, user, ulen);
284    userin[ulen] = '\0';
285
286    /* Strip User ID */
287    for(i=0;isspace((int)userin[i]) && i<ulen;i++);
288    begin_u = &(userin[i]);
289    if(i>0) ulen -= i;
290
291    for(;ulen > 0 && isspace((int)begin_u[ulen-1]); ulen--);
292    if(begin_u == &(userin[ulen])) {
293	sasl_FREE(in_buf);
294	utils->seterror(utils->conn, 0, "All-whitespace username.");
295	return SASL_FAIL;
296    }
297
298    if(utils->conn && utils->conn->type == SASL_CONN_SERVER)
299	sconn = (sasl_server_conn_t *)utils->conn;
300
301    /* Need to append realm if necessary (see sasl.h) */
302    if(sconn && sconn->user_realm && !strchr(user, '@')) {
303	u_apprealm = strlen(sconn->user_realm) + 1;
304    }
305
306    /* Now Copy */
307    memcpy(out_user, begin_u, MIN(ulen, out_umax));
308    if(sconn && u_apprealm) {
309	if(ulen >= out_umax) return SASL_BUFOVER;
310	out_user[ulen] = '@';
311	memcpy(&(out_user[ulen+1]), sconn->user_realm,
312	       MIN(u_apprealm-1, out_umax-ulen-1));
313    }
314    out_user[MIN(ulen + u_apprealm,out_umax)] = '\0';
315
316    if(ulen + u_apprealm > out_umax) return SASL_BUFOVER;
317
318    if(out_ulen) *out_ulen = MIN(ulen + u_apprealm,out_umax);
319
320    sasl_FREE(in_buf);
321    return SASL_OK;
322}
323
324static int _cu_internal_server(void *glob_context __attribute__((unused)),
325			       sasl_server_params_t *sparams,
326			       const char *user, unsigned ulen,
327			       unsigned flags,
328			       char *out_user,
329			       unsigned out_umax, unsigned *out_ulen)
330{
331    return _canonuser_internal(sparams->utils,
332			       user, ulen,
333			       flags, out_user, out_umax, out_ulen);
334}
335
336static int _cu_internal_client(void *glob_context __attribute__((unused)),
337			       sasl_client_params_t *cparams,
338			       const char *user, unsigned ulen,
339			       unsigned flags,
340			       char *out_user,
341			       unsigned out_umax, unsigned *out_ulen)
342{
343    return _canonuser_internal(cparams->utils,
344			       user, ulen,
345			       flags, out_user, out_umax, out_ulen);
346}
347
348static sasl_canonuser_plug_t canonuser_internal_plugin = {
349        0, /* features */
350	0, /* spare */
351	NULL, /* glob_context */
352	"INTERNAL", /* name */
353	NULL, /* canon_user_free */
354	_cu_internal_server,
355	_cu_internal_client,
356	NULL,
357	NULL,
358	NULL
359};
360
361int internal_canonuser_init(const sasl_utils_t *utils __attribute__((unused)),
362                            int max_version,
363                            int *out_version,
364                            sasl_canonuser_plug_t **plug,
365                            const char *plugname __attribute__((unused)))
366{
367    if(!out_version || !plug) return SASL_BADPARAM;
368
369    if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS;
370
371    *out_version = SASL_CANONUSER_PLUG_VERSION;
372
373    *plug = &canonuser_internal_plugin;
374
375    return SASL_OK;
376}
377