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