/* canonusr.c - user canonicalization support * Rob Siemborski */ /* * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Carnegie Mellon University * Center for Technology Transfer and Enterprise Creation * 4615 Forbes Avenue * Suite 302 * Pittsburgh, PA 15213 * (412) 268-7393, fax: (412) 268-7395 * innovation@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "saslint.h" typedef struct canonuser_plug_list { struct canonuser_plug_list *next; char name[PATH_MAX]; const sasl_canonuser_plug_t *plug; } canonuser_plug_list_t; static canonuser_plug_list_t *canonuser_head = NULL; /* default behavior: * eliminate leading & trailing whitespace, * null-terminate, and get into the outparams * (handled by INTERNAL plugin) */ /* a zero ulen or alen indicates that it is strlen(value) */ int _sasl_canon_user(sasl_conn_t *conn, const char *user, unsigned ulen, unsigned flags, sasl_out_params_t *oparams) { canonuser_plug_list_t *ptr; sasl_server_conn_t *sconn = NULL; sasl_client_conn_t *cconn = NULL; sasl_canon_user_t *cuser_cb; sasl_getopt_t *getopt; void *context; int result; const char *plugin_name = NULL; char *user_buf; unsigned *lenp; if(!conn) return SASL_BADPARAM; if(!user || !oparams) return SASL_BADPARAM; if(flags & SASL_CU_AUTHID) { user_buf = conn->authid_buf; lenp = &(oparams->alen); } else if (flags & SASL_CU_AUTHZID) { user_buf = conn->user_buf; lenp = &(oparams->ulen); } else { return SASL_BADPARAM; } if (conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn; else if (conn->type == SASL_CONN_CLIENT) cconn = (sasl_client_conn_t *)conn; else return SASL_FAIL; if(!ulen) ulen = (unsigned int)strlen(user); /* check to see if we have a callback to make*/ result = _sasl_getcallback(conn, SASL_CB_CANON_USER, (sasl_callback_ft *)&cuser_cb, &context); if(result == SASL_OK && cuser_cb) { result = cuser_cb(conn, context, user, ulen, flags, (sconn ? sconn->user_realm : NULL), user_buf, CANON_BUF_SIZE, lenp); if (result != SASL_OK) return result; /* Point the input copy at the stored buffer */ user = user_buf; ulen = *lenp; } /* which plugin are we supposed to use? */ result = _sasl_getcallback(conn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context); if (result == SASL_OK && getopt) { getopt(context, NULL, "canon_user_plugin", &plugin_name, NULL); } if (!plugin_name) { /* Use Default */ plugin_name = "INTERNAL"; } for (ptr = canonuser_head; ptr; ptr = ptr->next) { /* A match is if we match the internal name of the plugin, or if * we match the filename (old-style) */ if ((ptr->plug->name && !strcmp(plugin_name, ptr->plug->name)) || !strcmp(plugin_name, ptr->name)) break; } /* We clearly don't have this one! */ if (!ptr) { sasl_seterror(conn, 0, "desired canon_user plugin %s not found", plugin_name); return SASL_NOMECH; } if (sconn) { /* we're a server */ result = ptr->plug->canon_user_server(ptr->plug->glob_context, sconn->sparams, user, ulen, flags, user_buf, CANON_BUF_SIZE, lenp); } else { /* we're a client */ result = ptr->plug->canon_user_client(ptr->plug->glob_context, cconn->cparams, user, ulen, flags, user_buf, CANON_BUF_SIZE, lenp); } if (result != SASL_OK) return result; if ((flags & SASL_CU_AUTHID) && (flags & SASL_CU_AUTHZID)) { /* We did both, so we need to copy the result into * the buffer for the authzid from the buffer for the authid */ memcpy(conn->user_buf, conn->authid_buf, CANON_BUF_SIZE); oparams->ulen = oparams->alen; } /* Set the appropriate oparams (lengths have already been set by lenp) */ if (flags & SASL_CU_AUTHID) { oparams->authid = conn->authid_buf; } if (flags & SASL_CU_AUTHZID) { oparams->user = conn->user_buf; } RETURN(conn, result); } /* Lookup all properties for authentication and/or authorization identity. */ static int _sasl_auxprop_lookup_user_props (sasl_conn_t *conn, unsigned flags, sasl_out_params_t *oparams) { sasl_server_conn_t *sconn = NULL; int result = SASL_OK; if (!conn) return SASL_BADPARAM; if (!oparams) return SASL_BADPARAM; #ifndef macintosh if (conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn; /* do auxprop lookups (server only) */ if (sconn) { int authz_result; unsigned auxprop_lookup_flags = flags & SASL_CU_ASIS_MASK; if (flags & SASL_CU_OVERRIDE) { auxprop_lookup_flags |= SASL_AUXPROP_OVERRIDE; } if (flags & SASL_CU_AUTHID) { result = _sasl_auxprop_lookup(sconn->sparams, auxprop_lookup_flags, oparams->authid, oparams->alen); } else { result = SASL_CONTINUE; } if (flags & SASL_CU_AUTHZID) { authz_result = _sasl_auxprop_lookup(sconn->sparams, auxprop_lookup_flags | SASL_AUXPROP_AUTHZID, oparams->user, oparams->ulen); if (result == SASL_CONTINUE) { /* Only SASL_CU_AUTHZID was requested. The authz_result value is authoritative. */ result = authz_result; } else if (result == SASL_OK && authz_result != SASL_NOUSER) { /* Use the authz_result value, unless "result" already contains an error */ result = authz_result; } } if ((flags & SASL_CU_EXTERNALLY_VERIFIED) && (result == SASL_NOUSER || result == SASL_NOMECH)) { /* The called has explicitly told us that the authentication identity was already verified or will be verified independently. So a failure to retrieve any associated properties is not an error. For example the caller is using Kerberos to verify user, but the LDAPDB/SASLDB auxprop plugin doesn't contain any auxprops for the user. Another case is PLAIN/LOGIN not using auxprop to verify user passwords. */ result = SASL_OK; } } #endif RETURN(conn, result); } /* default behavior: * Eliminate leading & trailing whitespace, * null-terminate, and get into the outparams * (handled by INTERNAL plugin). * * Server only: Also does auxprop lookups once username * is canonicalized. */ int _sasl_canon_user_lookup (sasl_conn_t *conn, const char *user, unsigned ulen, unsigned flags, sasl_out_params_t *oparams) { int result; result = _sasl_canon_user (conn, user, ulen, flags, oparams); if (result == SASL_OK) { result = _sasl_auxprop_lookup_user_props (conn, flags, oparams); } RETURN(conn, result); } void _sasl_canonuser_free() { canonuser_plug_list_t *ptr, *ptr_next; for(ptr = canonuser_head; ptr; ptr = ptr_next) { ptr_next = ptr->next; if(ptr->plug->canon_user_free) ptr->plug->canon_user_free(ptr->plug->glob_context, sasl_global_utils); sasl_FREE(ptr); } canonuser_head = NULL; } int sasl_canonuser_add_plugin(const char *plugname, sasl_canonuser_init_t *canonuserfunc) { int result, out_version; canonuser_plug_list_t *new_item; sasl_canonuser_plug_t *plug; if(!plugname || strlen(plugname) > (PATH_MAX - 1)) { sasl_seterror(NULL, 0, "bad plugname passed to sasl_canonuser_add_plugin\n"); return SASL_BADPARAM; } result = canonuserfunc(sasl_global_utils, SASL_CANONUSER_PLUG_VERSION, &out_version, &plug, plugname); if(result != SASL_OK) { _sasl_log(NULL, SASL_LOG_ERR, "%s_canonuser_plug_init() failed in sasl_canonuser_add_plugin(): %z\n", plugname, result); return result; } if(!plug->canon_user_server && !plug->canon_user_client) { /* We need at least one of these implemented */ _sasl_log(NULL, SASL_LOG_ERR, "canonuser plugin '%s' without either client or server side", plugname); return SASL_BADPROT; } new_item = sasl_ALLOC(sizeof(canonuser_plug_list_t)); if(!new_item) return SASL_NOMEM; strncpy(new_item->name, plugname, PATH_MAX - 1); new_item->name[strlen(plugname)] = '\0'; new_item->plug = plug; new_item->next = canonuser_head; canonuser_head = new_item; return SASL_OK; } #ifdef MIN #undef MIN #endif #define MIN(a,b) (((a) < (b))? (a):(b)) static int _canonuser_internal(const sasl_utils_t *utils, const char *user, unsigned ulen, unsigned flags __attribute__((unused)), char *out_user, unsigned out_umax, unsigned *out_ulen) { unsigned i; char *in_buf, *userin; const char *begin_u; unsigned u_apprealm = 0; sasl_server_conn_t *sconn = NULL; if(!utils || !user) return SASL_BADPARAM; in_buf = sasl_ALLOC((ulen + 2) * sizeof(char)); if(!in_buf) return SASL_NOMEM; userin = in_buf; memcpy(userin, user, ulen); userin[ulen] = '\0'; /* Strip User ID */ for(i=0;isspace((int)userin[i]) && i0) ulen -= i; for(;ulen > 0 && isspace((int)begin_u[ulen-1]); ulen--); if(begin_u == &(userin[ulen])) { sasl_FREE(in_buf); utils->seterror(utils->conn, 0, "All-whitespace username."); return SASL_FAIL; } if(utils->conn && utils->conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)utils->conn; /* Need to append realm if necessary (see sasl.h) */ if(sconn && sconn->user_realm && !strchr(user, '@')) { u_apprealm = (unsigned) strlen(sconn->user_realm) + 1; } /* Now Copy */ memcpy(out_user, begin_u, MIN(ulen, out_umax)); if(sconn && u_apprealm) { if(ulen >= out_umax) return SASL_BUFOVER; out_user[ulen] = '@'; memcpy(&(out_user[ulen+1]), sconn->user_realm, MIN(u_apprealm-1, out_umax-ulen-1)); } out_user[MIN(ulen + u_apprealm,out_umax)] = '\0'; if(ulen + u_apprealm > out_umax) return SASL_BUFOVER; if(out_ulen) *out_ulen = MIN(ulen + u_apprealm,out_umax); sasl_FREE(in_buf); return SASL_OK; } static int _cu_internal_server(void *glob_context __attribute__((unused)), sasl_server_params_t *sparams, const char *user, unsigned ulen, unsigned flags, char *out_user, unsigned out_umax, unsigned *out_ulen) { return _canonuser_internal(sparams->utils, user, ulen, flags, out_user, out_umax, out_ulen); } static int _cu_internal_client(void *glob_context __attribute__((unused)), sasl_client_params_t *cparams, const char *user, unsigned ulen, unsigned flags, char *out_user, unsigned out_umax, unsigned *out_ulen) { return _canonuser_internal(cparams->utils, user, ulen, flags, out_user, out_umax, out_ulen); } static sasl_canonuser_plug_t canonuser_internal_plugin = { 0, /* features */ 0, /* spare */ NULL, /* glob_context */ "INTERNAL", /* name */ NULL, /* canon_user_free */ _cu_internal_server, _cu_internal_client, NULL, NULL, NULL }; int internal_canonuser_init(const sasl_utils_t *utils __attribute__((unused)), int max_version, int *out_version, sasl_canonuser_plug_t **plug, const char *plugname __attribute__((unused))) { if(!out_version || !plug) return SASL_BADPARAM; if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS; *out_version = SASL_CANONUSER_PLUG_VERSION; *plug = &canonuser_internal_plugin; return SASL_OK; }