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