1/* db_gdbm.c--SASL gdbm interface 2 * Rob Siemborski 3 * Rob Earhart 4 * $Id: db_gdbm.c,v 1.5 2005/01/10 19:01:34 snsimon 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#include <gdbm.h> 48#include <sys/stat.h> 49#include <stdlib.h> 50#include <assert.h> 51#include "sasldb.h" 52 53static int db_ok = 0; 54 55int _sasldb_getdata(const sasl_utils_t *utils, 56 sasl_conn_t *conn, 57 const char *authid, 58 const char *realm, 59 const char *propName, 60 char *out, const size_t max_out, size_t *out_len) 61{ 62 int result = SASL_OK; 63 char *key; 64 size_t key_len; 65 GDBM_FILE db; 66 datum gkey, gvalue; 67 void *cntxt; 68 sasl_getopt_t *getopt; 69 const char *path = SASL_DB_PATH; 70 71 if (!utils) return SASL_BADPARAM; 72 if (!authid || !propName || !realm || !out || !max_out) { 73 utils->seterror(conn, 0, 74 "Bad parameter in db_gdbm.c: _sasldb_getdata"); 75 return SASL_BADPARAM; 76 } 77 78 if (!db_ok) { 79 utils->seterror(conn, 0, 80 "Database not checked"); 81 return SASL_FAIL; 82 } 83 84 result = _sasldb_alloc_key(utils, authid, realm, propName, 85 &key, &key_len); 86 if (result != SASL_OK) { 87 utils->seterror(conn, 0, 88 "Could not allocate key in _sasldb_getdata"); 89 return result; 90 } 91 92 if (utils->getcallback(conn, SASL_CB_GETOPT, 93 &getopt, &cntxt) == SASL_OK) { 94 const char *p; 95 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK 96 && p != NULL && *p != 0) { 97 path = p; 98 } 99 } 100 db = gdbm_open((char *)path, 0, GDBM_READER, S_IRUSR | S_IWUSR, NULL); 101 if (! db) { 102 utils->seterror(cntxt, 0, "Could not open %s: gdbm_errno=%d", 103 path, gdbm_errno); 104 result = SASL_FAIL; 105 goto cleanup; 106 } 107 gkey.dptr = key; 108 gkey.dsize = key_len; 109 gvalue = gdbm_fetch(db, gkey); 110 gdbm_close(db); 111 if (! gvalue.dptr) { 112 if (gdbm_errno == GDBM_ITEM_NOT_FOUND) { 113 utils->seterror(conn, SASL_NOLOG, 114 "user: %s@%s property: %s not found in %s", 115 authid, realm, propName, path); 116 result = SASL_NOUSER; 117 } else { 118 utils->seterror(conn, 0, 119 "Couldn't fetch entry from %s: gdbm_errno=%d", 120 path, gdbm_errno); 121 result = SASL_FAIL; 122 } 123 goto cleanup; 124 } 125 126 if((size_t)gvalue.dsize > max_out + 1) { 127 utils->seterror(cntxt, 0, "buffer overflow"); 128 return SASL_BUFOVER; 129 } 130 131 if(out_len) *out_len = gvalue.dsize; 132 memcpy(out, gvalue.dptr, gvalue.dsize); 133 out[gvalue.dsize] = '\0'; 134 135 /* Note: not sasl_FREE! This is memory allocated by gdbm, 136 * which is using libc malloc/free. */ 137 free(gvalue.dptr); 138 139 cleanup: 140 utils->free(key); 141 142 return result; 143} 144 145int _sasldb_putdata(const sasl_utils_t *utils, 146 sasl_conn_t *conn, 147 const char *authid, 148 const char *realm, 149 const char *propName, 150 const char *data, size_t data_len) 151{ 152 int result = SASL_OK; 153 char *key; 154 size_t key_len; 155 GDBM_FILE db; 156 datum gkey; 157 void *cntxt; 158 sasl_getopt_t *getopt; 159 const char *path = SASL_DB_PATH; 160 161 if (!utils) return SASL_BADPARAM; 162 163 if (!authid || !realm || !propName) { 164 utils->seterror(conn, 0, 165 "Bad parameter in db_gdbm.c: _sasldb_putdata"); 166 return SASL_BADPARAM; 167 } 168 169 result = _sasldb_alloc_key(utils, authid, realm, propName, 170 &key, &key_len); 171 if (result != SASL_OK) { 172 utils->seterror(conn, 0, 173 "Could not allocate key in _sasldb_putdata"); 174 return result; 175 } 176 177 if (utils->getcallback(conn, SASL_CB_GETOPT, 178 &getopt, &cntxt) == SASL_OK) { 179 const char *p; 180 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK 181 && p != NULL && *p != 0) { 182 path = p; 183 } 184 } 185 db = gdbm_open((char *)path, 0, GDBM_WRCREAT, S_IRUSR | S_IWUSR, NULL); 186 if (! db) { 187 utils->log(conn, SASL_LOG_ERR, 188 "SASL error opening password file. " 189 "Do you have write permissions?\n"); 190 utils->seterror(conn, 0, "Could not open %s for write: gdbm_errno=%d", 191 path, gdbm_errno); 192 result = SASL_FAIL; 193 goto cleanup; 194 } 195 gkey.dptr = key; 196 gkey.dsize = key_len; 197 if (data) { 198 datum gvalue; 199 gvalue.dptr = (char *)data; 200 if(!data_len) data_len = strlen(data); 201 gvalue.dsize = data_len; 202 if (gdbm_store(db, gkey, gvalue, GDBM_REPLACE)) { 203 utils->seterror(conn, 0, 204 "Couldn't replace entry in %s: gdbm_errno=%d", 205 path, gdbm_errno); 206 result = SASL_FAIL; 207 } 208 } else { 209 if (gdbm_delete(db, gkey)) { 210 utils->seterror(conn, 0, 211 "Couldn't delete entry in %s: gdbm_errno=%d", 212 path, gdbm_errno); 213 result = SASL_NOUSER; 214 } 215 } 216 gdbm_close(db); 217 218 cleanup: 219 utils->free(key); 220 221 return result; 222} 223 224int _sasl_check_db(const sasl_utils_t *utils, 225 sasl_conn_t *conn) 226{ 227 const char *path = SASL_DB_PATH; 228 int ret; 229 void *cntxt; 230 sasl_getopt_t *getopt; 231 sasl_verifyfile_t *vf; 232 233 if(!utils) return SASL_BADPARAM; 234 235 if (utils->getcallback(conn, SASL_CB_GETOPT, 236 &getopt, &cntxt) == SASL_OK) { 237 const char *p; 238 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK 239 && p != NULL && *p != 0) { 240 path = p; 241 } 242 } 243 244 ret = utils->getcallback(NULL, SASL_CB_VERIFYFILE, 245 &vf, &cntxt); 246 if(ret != SASL_OK) { 247 utils->seterror(conn, 0, 248 "No verifyfile callback"); 249 return ret; 250 } 251 252 ret = vf(cntxt, path, SASL_VRFY_PASSWD); 253 if (ret == SASL_OK) { 254 db_ok = 1; 255 } 256 257 if (ret == SASL_OK || ret == SASL_CONTINUE) { 258 return SASL_OK; 259 } else { 260 utils->seterror(conn, 0, 261 "Verifyfile failed"); 262 return ret; 263 } 264} 265 266typedef struct gdbm_handle 267{ 268 GDBM_FILE db; 269 datum dkey; 270 int first; 271} handle_t; 272 273sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils, 274 sasl_conn_t *conn) 275{ 276 const char *path = SASL_DB_PATH; 277 sasl_getopt_t *getopt; 278 void *cntxt; 279 GDBM_FILE db; 280 handle_t *handle; 281 282 if(!utils || !conn) return NULL; 283 284 if(!db_ok) { 285 utils->seterror(conn, 0, "Database not OK in _sasldb_getkeyhandle"); 286 return NULL; 287 } 288 289 if (utils->getcallback(conn, SASL_CB_GETOPT, 290 &getopt, &cntxt) == SASL_OK) { 291 const char *p; 292 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK 293 && p != NULL && *p != 0) { 294 path = p; 295 } 296 } 297 298 db = gdbm_open((char *)path, 0, GDBM_READER, S_IRUSR | S_IWUSR, NULL); 299 300 if(!db) { 301 utils->seterror(conn, 0, "Could not open %s: gdbm_errno=%d", 302 path, gdbm_errno); 303 return NULL; 304 } 305 306 handle = utils->malloc(sizeof(handle_t)); 307 if(!handle) { 308 utils->seterror(conn, 0, "no memory in _sasldb_getkeyhandle"); 309 gdbm_close(db); 310 return NULL; 311 } 312 313 handle->db = db; 314 handle->first = 1; 315 316 return (sasldb_handle)handle; 317} 318 319int _sasldb_getnextkey(const sasl_utils_t *utils __attribute__((unused)), 320 sasldb_handle handle, char *out, 321 const size_t max_out, size_t *out_len) 322{ 323 handle_t *dbh = (handle_t *)handle; 324 datum nextkey; 325 326 if(!utils || !handle || !out || !max_out) 327 return SASL_BADPARAM; 328 329 if(dbh->first) { 330 dbh->dkey = gdbm_firstkey(dbh->db); 331 dbh->first = 0; 332 } else { 333 nextkey = gdbm_nextkey(dbh->db, dbh->dkey); 334 dbh->dkey = nextkey; 335 } 336 337 if(dbh->dkey.dptr == NULL) 338 return SASL_OK; 339 340 if((unsigned)dbh->dkey.dsize > max_out) 341 return SASL_BUFOVER; 342 343 memcpy(out, dbh->dkey.dptr, dbh->dkey.dsize); 344 if(out_len) *out_len = dbh->dkey.dsize; 345 346 return SASL_CONTINUE; 347} 348 349int _sasldb_releasekeyhandle(const sasl_utils_t *utils, 350 sasldb_handle handle) 351{ 352 handle_t *dbh = (handle_t *)handle; 353 354 if(!utils || !dbh) return SASL_BADPARAM; 355 356 if(dbh->db) gdbm_close(dbh->db); 357 358 utils->free(dbh); 359 360 return SASL_OK; 361} 362 363