1/* SASL server API implementation 2 * Rob Siemborski 3 * Tim Martin 4 * $Id: sasldb.c,v 1.17 2009/03/10 14:37:03 mel Exp $ 5 */ 6/* 7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The name "Carnegie Mellon University" must not be used to 22 * endorse or promote products derived from this software without 23 * prior written permission. For permission or any other legal 24 * details, please contact 25 * Office of Technology Transfer 26 * Carnegie Mellon University 27 * 5000 Forbes Avenue 28 * Pittsburgh, PA 15213-3890 29 * (412) 268-4387, fax: (412) 268-7395 30 * tech-transfer@andrew.cmu.edu 31 * 32 * 4. Redistributions of any form whatsoever must retain the following 33 * acknowledgment: 34 * "This product includes software developed by Computing Services 35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 36 * 37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 44 */ 45 46#include <config.h> 47 48/* sasldb stuff */ 49 50#include <stdio.h> 51 52#include "sasl.h" 53#include "saslutil.h" 54#include "saslplug.h" 55#include "../sasldb/sasldb.h" 56 57#include "plugin_common.h" 58 59static int sasldb_auxprop_lookup(void *glob_context __attribute__((unused)), 60 sasl_server_params_t *sparams, 61 unsigned flags, 62 const char *user, 63 unsigned ulen) 64{ 65 char *userid = NULL; 66 char *realm = NULL; 67 const char *user_realm = NULL; 68 int ret; 69 const struct propval *to_fetch, *cur; 70 char value[8192]; 71 size_t value_len; 72 char *user_buf; 73 int verify_against_hashed_password; 74 int saw_user_password = 0; 75 76 if (!sparams || !user) return SASL_BADPARAM; 77 78 user_buf = sparams->utils->malloc(ulen + 1); 79 if(!user_buf) { 80 ret = SASL_NOMEM; 81 goto done; 82 } 83 84 memcpy(user_buf, user, ulen); 85 user_buf[ulen] = '\0'; 86 87 if(sparams->user_realm) { 88 user_realm = sparams->user_realm; 89 } else { 90 user_realm = sparams->serverFQDN; 91 } 92 93 ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm, 94 sparams->serverFQDN, user_buf); 95 if(ret != SASL_OK) goto done; 96 97 to_fetch = sparams->utils->prop_get(sparams->propctx); 98 if (!to_fetch) { 99 ret = SASL_NOMEM; 100 goto done; 101 } 102 103 verify_against_hashed_password = flags & SASL_AUXPROP_VERIFY_AGAINST_HASH; 104 105 /* Use a fake value to signal that we have no property to lookup */ 106 ret = SASL_CONTINUE; 107 for(cur = to_fetch; cur->name; cur++) { 108 int cur_ret; 109 const char *realname = cur->name; 110 111 /* Only look up properties that apply to this lookup! */ 112 if(cur->name[0] == '*' && (flags & SASL_AUXPROP_AUTHZID)) continue; 113 if(!(flags & SASL_AUXPROP_AUTHZID)) { 114 if(cur->name[0] != '*') continue; 115 else realname = cur->name + 1; 116 } 117 118 /* If it's there already, we want to see if it needs to be 119 * overridden. userPassword is a special case, because it's value 120 is always present if SASL_AUXPROP_VERIFY_AGAINST_HASH is specified. 121 When SASL_AUXPROP_VERIFY_AGAINST_HASH is set, we just clear userPassword. */ 122 if (cur->values && !(flags & SASL_AUXPROP_OVERRIDE) && 123 (verify_against_hashed_password == 0 || 124 strcasecmp(realname, SASL_AUX_PASSWORD_PROP) != 0)) { 125 continue; 126 } else if (cur->values) { 127 sparams->utils->prop_erase(sparams->propctx, cur->name); 128 } 129 130 if (strcasecmp(realname, SASL_AUX_PASSWORD_PROP) == 0) { 131 saw_user_password = 1; 132 } 133 134 cur_ret = _sasldb_getdata(sparams->utils, 135 sparams->utils->conn, userid, realm, 136 realname, value, sizeof(value), &value_len); 137 138 /* Assumption: cur_ret is never SASL_CONTINUE */ 139 140 /* If this is the first property we've tried to fetch ==> 141 always set the global error code. 142 If we had SASL_NOUSER ==> any other error code overrides it 143 (including SASL_NOUSER). */ 144 if (ret == SASL_CONTINUE || ret == SASL_NOUSER) { 145 ret = cur_ret; 146 } else if (ret == SASL_OK) { 147 /* Any error code other than SASL_NOUSER overrides SASL_OK. 148 (And SASL_OK overrides SASL_OK as well) */ 149 if (cur_ret != SASL_NOUSER) { 150 ret = cur_ret; 151 } 152 } 153 /* Any other global error code is left as is */ 154 155 if (cur_ret != SASL_OK) { 156 if (cur_ret != SASL_NOUSER) { 157 /* No point in continuing if we hit any serious error */ 158 break; 159 } 160 /* We didn't find it, leave it as not found */ 161 continue; 162 } 163 164 sparams->utils->prop_set(sparams->propctx, cur->name, 165 value, (unsigned) value_len); 166 } 167 168 /* [Keep in sync with LDAPDB, SQL] 169 If ret is SASL_CONTINUE, it means that no properties were requested 170 (or maybe some were requested, but they already have values and 171 SASL_AUXPROP_OVERRIDE flag is not set). 172 Always return SASL_OK in this case. */ 173 if (ret == SASL_CONTINUE) { 174 ret = SASL_OK; 175 } 176 177 if (flags & SASL_AUXPROP_AUTHZID) { 178 /* This is a lie, but the caller can't handle 179 when we return SASL_NOUSER for authorization identity lookup. */ 180 if (ret == SASL_NOUSER) { 181 ret = SASL_OK; 182 } 183 } else { 184 if (ret == SASL_NOUSER && saw_user_password == 0) { 185 /* Verify user existence by checking presence of 186 the userPassword attribute */ 187 ret = _sasldb_getdata(sparams->utils, 188 sparams->utils->conn, 189 userid, 190 realm, 191 SASL_AUX_PASSWORD_PROP, 192 value, 193 sizeof(value), 194 &value_len); 195 } 196 } 197 198 done: 199 if (userid) sparams->utils->free(userid); 200 if (realm) sparams->utils->free(realm); 201 if (user_buf) sparams->utils->free(user_buf); 202 203 return ret; 204} 205 206static int sasldb_auxprop_store(void *glob_context __attribute__((unused)), 207 sasl_server_params_t *sparams, 208 struct propctx *ctx, 209 const char *user, 210 unsigned ulen) 211{ 212 char *userid = NULL; 213 char *realm = NULL; 214 const char *user_realm = NULL; 215 int ret = SASL_FAIL; 216 const struct propval *to_store, *cur; 217 char *user_buf; 218 219 /* just checking if we are enabled */ 220 if(!ctx) return SASL_OK; 221 222 if(!sparams || !user) return SASL_BADPARAM; 223 224 user_buf = sparams->utils->malloc(ulen + 1); 225 if(!user_buf) { 226 ret = SASL_NOMEM; 227 goto done; 228 } 229 230 memcpy(user_buf, user, ulen); 231 user_buf[ulen] = '\0'; 232 233 if(sparams->user_realm) { 234 user_realm = sparams->user_realm; 235 } else { 236 user_realm = sparams->serverFQDN; 237 } 238 239 ret = _plug_parseuser(sparams->utils, &userid, &realm, user_realm, 240 sparams->serverFQDN, user_buf); 241 if(ret != SASL_OK) goto done; 242 243 to_store = sparams->utils->prop_get(ctx); 244 if(!to_store) { 245 ret = SASL_BADPARAM; 246 goto done; 247 } 248 249 ret = SASL_OK; 250 for (cur = to_store; cur->name; cur++) { 251 char * value = (char *)((cur->values && cur->values[0]) ? cur->values[0] : NULL); /* APPLE: cast */ 252 253 if (cur->name[0] == '*') { 254 continue; 255 } 256 257 /* WARN: We only support one value right now. */ 258 ret = _sasldb_putdata(sparams->utils, 259 sparams->utils->conn, 260 userid, 261 realm, 262 cur->name, 263 value, 264 value ? strlen(value) : 0); 265 266 if (value == NULL && ret == SASL_NOUSER) { 267 /* Deleting something which is not there is not an error */ 268 ret = SASL_OK; 269 } 270 271 if (ret != SASL_OK) { 272 /* We've already failed, no point in continuing */ 273 break; 274 } 275 } 276 277 done: 278 if (userid) sparams->utils->free(userid); 279 if (realm) sparams->utils->free(realm); 280 if (user_buf) sparams->utils->free(user_buf); 281 282 return ret; 283} 284 285static sasl_auxprop_plug_t sasldb_auxprop_plugin = { 286 0, /* Features */ 287 0, /* spare */ 288 NULL, /* glob_context */ 289 sasldb_auxprop_free, /* auxprop_free */ 290 sasldb_auxprop_lookup, /* auxprop_lookup */ 291 "sasldb", /* name */ 292 sasldb_auxprop_store /* auxprop_store */ 293}; 294 295int sasldb_auxprop_plug_init(const sasl_utils_t *utils, 296 int max_version, 297 int *out_version, 298 sasl_auxprop_plug_t **plug, 299 const char *plugname __attribute__((unused))) 300{ 301 if(!out_version || !plug) return SASL_BADPARAM; 302 303 /* Do we have database support? */ 304 /* Note that we can use a NULL sasl_conn_t because our 305 * sasl_utils_t is "blessed" with the global callbacks */ 306 if(_sasl_check_db(utils, NULL) != SASL_OK) 307 return SASL_NOMECH; 308 309 /* Check if libsasl API is older than ours. If it is, fail */ 310 if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS; 311 312 *out_version = SASL_AUXPROP_PLUG_VERSION; 313 314 *plug = &sasldb_auxprop_plugin; 315 316 return SASL_OK; 317} 318