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