GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / sasldb / db_berkeley.c
1 /* db_berkeley.c--SASL berkeley db interface
2  * Rob Siemborski
3  * Tim Martin
4  * $Id: db_berkeley.c,v 1.8 2006/04/03 10:58:19 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 #include <db.h>
49
50 #include <sys/stat.h>
51 #include <stdlib.h>
52 #include <assert.h>
53 #include "sasldb.h"
54
55 static int db_ok = 0;
56 #if defined(KEEP_DB_OPEN)
57 static DB * g_db = NULL;
58 #endif
59
60 /*
61  * Open the database
62  */
63 static int berkeleydb_open(const sasl_utils_t *utils,
64                            sasl_conn_t *conn,
65                            int rdwr, DB **mbdb)
66 {
67     const char *path = SASL_DB_PATH;
68     int ret;
69     int flags;
70     void *cntxt;
71     sasl_getopt_t *getopt;
72
73 #if defined(KEEP_DB_OPEN)
74     if (g_db) {
75         *mbdb = g_db;
76         return SASL_OK;
77     }
78 #endif
79
80     if (utils->getcallback(conn, SASL_CB_GETOPT,
81                           &getopt, &cntxt) == SASL_OK) {
82         const char *p;
83         if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK 
84             && p != NULL && *p != 0) {
85             path = p;
86         }
87     }
88
89     if (rdwr) flags = DB_CREATE;
90     else flags = DB_RDONLY;
91 #if defined(KEEP_DB_OPEN)
92 #if defined(DB_THREAD)
93     flags |= DB_THREAD;
94 #endif
95 #endif
96
97 #if DB_VERSION_MAJOR < 3
98     ret = db_open(path, DB_HASH, flags, 0660, NULL, NULL, mbdb);
99 #else /* DB_VERSION_MAJOR < 3 */
100     ret = db_create(mbdb, NULL, 0);
101     if (ret == 0 && *mbdb != NULL)
102     {
103 #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1
104         ret = (*mbdb)->open(*mbdb, NULL, path, NULL, DB_HASH, flags, 0660);
105 #else
106         ret = (*mbdb)->open(*mbdb, path, NULL, DB_HASH, flags, 0660);
107 #endif
108         if (ret != 0)
109         {
110             (void) (*mbdb)->close(*mbdb, 0);
111             *mbdb = NULL;
112         }
113     }
114 #endif /* DB_VERSION_MAJOR < 3 */
115
116     if (ret != 0) {
117         utils->log(conn, SASL_LOG_ERR,
118                    "unable to open Berkeley db %s: %s",
119                    path, db_strerror(ret));
120         utils->seterror(conn, SASL_NOLOG, "Unable to open DB");
121         return SASL_FAIL;
122     }
123
124 #if defined(KEEP_DB_OPEN)
125     /* Save the DB handle for later use */
126     g_db = *mbdb;
127 #endif
128     return SASL_OK;
129 }
130
131 /*
132  * Close the database
133  */
134 static void berkeleydb_close(const sasl_utils_t *utils, DB *mbdb)
135 {
136     int ret;
137
138 #if defined(KEEP_DB_OPEN)
139     /* Prevent other threads from reusing the same handle */
140     /* msdb == g_db */    
141     g_db = NULL;
142 #endif
143
144     ret = mbdb->close(mbdb, 0);
145     if (ret!=0) {
146         /* error closing! */
147         utils->log(NULL, SASL_LOG_ERR,
148                    "error closing sasldb: %s",
149                    db_strerror(ret));
150     }
151 }
152
153
154 /*
155  * Retrieve the secret from the database. 
156  * 
157  * Return SASL_NOUSER if the entry doesn't exist,
158  * SASL_OK on success.
159  *
160  */
161 int _sasldb_getdata(const sasl_utils_t *utils,
162                     sasl_conn_t *context,
163                     const char *auth_identity,
164                     const char *realm,
165                     const char *propName,
166                     char *out, const size_t max_out, size_t *out_len)
167 {
168   int result = SASL_OK;
169   char *key;
170   size_t key_len;
171   DBT dbkey, data;
172   DB *mbdb = NULL;
173
174   if(!utils) return SASL_BADPARAM;
175
176   /* check parameters */
177   if (!auth_identity || !realm || !propName || !out || !max_out) {
178       utils->seterror(context, 0,
179                       "Bad parameter in db_berkeley.c: _sasldb_getdata");
180       return SASL_BADPARAM;
181   }
182
183   if (!db_ok) {
184       utils->seterror(context, 0,
185                       "Database not checked");
186       return SASL_FAIL;
187   }
188
189   /* allocate a key */
190   result = _sasldb_alloc_key(utils, auth_identity, realm, propName,
191                              &key, &key_len);
192   if (result != SASL_OK) {
193       utils->seterror(context, 0,
194                       "Could not allocate key in _sasldb_getdata");
195       return result;
196   }
197
198   /* zero out */
199   memset(&dbkey, 0, sizeof(dbkey));
200   memset(&data, 0, sizeof(data));
201
202   /* open the db */
203   result = berkeleydb_open(utils, context, 0, &mbdb);
204   if (result != SASL_OK) goto cleanup;
205
206   /* create the key to search for */
207   dbkey.data = key;
208   dbkey.size = (u_int32_t) key_len;
209   dbkey.flags = DB_DBT_USERMEM;
210   data.flags = DB_DBT_MALLOC;
211
212   /* ask berkeley db for the entry */
213   result = mbdb->get(mbdb, NULL, &dbkey, &data, 0);
214
215   switch (result) {
216   case 0:
217     /* success */
218     break;
219
220   case DB_NOTFOUND:
221     result = SASL_NOUSER;
222     utils->seterror(context, SASL_NOLOG,
223                     "user: %s@%s property: %s not found in sasldb",
224                     auth_identity,realm,propName);
225     goto cleanup;
226     break;
227   default:
228     utils->seterror(context, 0,
229                     "error fetching from sasldb: %s",
230                     db_strerror(result));
231     result = SASL_FAIL;
232     goto cleanup;
233     break;
234   }
235
236   if(data.size > max_out + 1)
237       return SASL_BUFOVER;
238
239   if(out_len) *out_len = data.size;
240   memcpy(out, data.data, data.size);
241   out[data.size] = '\0';
242   
243  cleanup:
244
245 #if !defined(KEEP_DB_OPEN)
246   if (mbdb != NULL) berkeleydb_close(utils, mbdb);
247 #endif
248
249   utils->free(key);
250   utils->free(data.data);
251
252   return result;
253 }
254
255 /*
256  * Put or delete an entry
257  * 
258  *
259  */
260
261 int _sasldb_putdata(const sasl_utils_t *utils,
262                     sasl_conn_t *context,
263                     const char *authid,
264                     const char *realm,
265                     const char *propName,
266                     const char *data_in, size_t data_len)
267 {
268   int result = SASL_OK;
269   char *key;
270   size_t key_len;
271   DBT dbkey;
272   DB *mbdb = NULL;
273
274   if (!utils) return SASL_BADPARAM;
275
276   if (!authid || !realm || !propName) {
277       utils->seterror(context, 0,
278                       "Bad parameter in db_berkeley.c: _sasldb_putdata");
279       return SASL_BADPARAM;
280   }
281   
282   if (!db_ok) {
283       utils->seterror(context, 0,
284                       "Database not checked");   
285       return SASL_FAIL;
286   }
287
288   result = _sasldb_alloc_key(utils, authid, realm, propName,
289                              &key, &key_len);
290   if (result != SASL_OK) {
291        utils->seterror(context, 0,
292                       "Could not allocate key in _sasldb_putdata");     
293        return result;
294   }
295
296   /* open the db */
297   result=berkeleydb_open(utils, context, 1, &mbdb);
298   if (result!=SASL_OK) goto cleanup;
299
300   /* create the db key */
301   memset(&dbkey, 0, sizeof(dbkey));
302   dbkey.data = key;
303   dbkey.size = (u_int32_t) key_len;
304
305   if (data_in) {   /* putting secret */
306     DBT data;
307
308     memset(&data, 0, sizeof(data));    
309
310     data.data = (char *)data_in;
311     if(!data_len) data_len = strlen(data_in);
312     data.size = (u_int32_t) data_len;
313
314     result = mbdb->put(mbdb, NULL, &dbkey, &data, 0);
315
316     if (result != 0)
317     {
318       utils->log(NULL, SASL_LOG_ERR,
319                  "error updating sasldb: %s", db_strerror(result));
320       utils->seterror(context, SASL_NOLOG,
321                       "Couldn't update db");
322       result = SASL_FAIL;
323       goto cleanup;
324     }
325   } else {        /* removing secret */
326     result=mbdb->del(mbdb, NULL, &dbkey, 0);
327
328     if (result != 0)
329     {
330       utils->log(NULL, SASL_LOG_ERR,
331                  "error deleting entry from sasldb: %s", db_strerror(result));
332       utils->seterror(context, SASL_NOLOG,
333                       "Couldn't update db");
334       if (result == DB_NOTFOUND)
335           result = SASL_NOUSER;
336       else        
337           result = SASL_FAIL;
338       goto cleanup;
339     }
340   }
341
342  cleanup:
343
344 #if !defined(KEEP_DB_OPEN)
345   if (mbdb != NULL) berkeleydb_close(utils, mbdb);
346 #endif
347
348   utils->free(key);
349
350   return result;
351 }
352
353 int _sasl_check_db(const sasl_utils_t *utils,
354                    sasl_conn_t *conn)
355 {
356     const char *path = SASL_DB_PATH;
357     int ret;
358     void *cntxt;
359     sasl_getopt_t *getopt;
360     sasl_verifyfile_t *vf;
361
362     if (!utils) return SASL_BADPARAM;
363
364     if (utils->getcallback(conn, SASL_CB_GETOPT,
365                           &getopt, &cntxt) == SASL_OK) {
366         const char *p;
367         if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK 
368             && p != NULL && *p != 0) {
369             path = p;
370         }
371     }
372
373     ret = utils->getcallback(conn, SASL_CB_VERIFYFILE,
374                              &vf, &cntxt);
375     if (ret != SASL_OK) {
376         utils->seterror(conn, 0, "verifyfile failed");
377         return ret;
378     }
379
380     ret = vf(cntxt, path, SASL_VRFY_PASSWD);
381
382     if (ret == SASL_OK) {
383         db_ok = 1;
384     }
385
386     if (ret == SASL_OK || ret == SASL_CONTINUE) {
387         return SASL_OK;
388     } else {
389         return ret;
390     }
391 }
392
393 #if defined(KEEP_DB_OPEN)
394 void sasldb_auxprop_free (void *glob_context,
395                           const sasl_utils_t *utils)
396 {
397     if (g_db != NULL) berkeleydb_close(utils, g_db);
398 }
399 #endif
400
401 typedef struct berkeleydb_handle 
402 {
403     DB *mbdb;
404     DBC *cursor;
405 } berkleyhandle_t;
406
407 sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils,
408                                    sasl_conn_t *conn) 
409 {
410     int ret;
411     DB *mbdb;
412     berkleyhandle_t *handle;
413     
414     if(!utils || !conn) return NULL;
415
416     if(!db_ok) {
417         utils->seterror(conn, 0, "Database not OK in _sasldb_getkeyhandle");
418         return NULL;
419     }
420
421     ret = berkeleydb_open(utils, conn, 0, &mbdb);
422
423     if (ret != SASL_OK) {
424         return NULL;
425     }
426
427     handle = utils->malloc(sizeof(berkleyhandle_t));
428     if(!handle) {
429 #if !defined(KEEP_DB_OPEN)
430         (void)mbdb->close(mbdb, 0);
431 #endif
432         utils->seterror(conn, 0, "Memory error in _sasldb_gethandle");
433         return NULL;
434     }
435     
436     handle->mbdb = mbdb;
437     handle->cursor = NULL;
438
439     return (sasldb_handle)handle;
440 }
441
442 int _sasldb_getnextkey(const sasl_utils_t *utils __attribute__((unused)),
443                        sasldb_handle handle, char *out,
444                        const size_t max_out, size_t *out_len) 
445 {
446     DB *mbdb;
447     int result;
448     berkleyhandle_t *dbh = (berkleyhandle_t *)handle;
449     DBT key, data;
450
451     if(!utils || !handle || !out || !max_out)
452         return SASL_BADPARAM;
453
454     mbdb = dbh->mbdb;
455
456     memset(&key,0, sizeof(key));
457     memset(&data,0,sizeof(data));
458
459     if(!dbh->cursor) {
460         /* make cursor */
461 #if DB_VERSION_MAJOR < 3
462 #if DB_VERSION_MINOR < 6
463         result = mbdb->cursor(mbdb, NULL,&dbh->cursor); 
464 #else
465         result = mbdb->cursor(mbdb, NULL,&dbh->cursor, 0); 
466 #endif /* DB_VERSION_MINOR < 7 */
467 #else /* DB_VERSION_MAJOR < 3 */
468         result = mbdb->cursor(mbdb, NULL,&dbh->cursor, 0); 
469 #endif /* DB_VERSION_MAJOR < 3 */
470
471         if (result!=0) {
472             return SASL_FAIL;
473         }
474
475         /* loop thru */
476         result = dbh->cursor->c_get(dbh->cursor, &key, &data,
477                                     DB_FIRST);
478     } else {
479         result = dbh->cursor->c_get(dbh->cursor, &key, &data,
480                                     DB_NEXT);
481     }
482
483     if (result == DB_NOTFOUND) return SASL_OK;
484
485     if (result != 0) {
486         return SASL_FAIL;
487     }
488     
489     if (key.size > max_out) {
490         return SASL_BUFOVER;
491     }
492     
493     memcpy(out, key.data, key.size);
494     if (out_len) *out_len = key.size;
495
496     return SASL_CONTINUE;
497 }
498
499
500 int _sasldb_releasekeyhandle(const sasl_utils_t *utils,
501                              sasldb_handle handle) 
502 {
503     berkleyhandle_t *dbh = (berkleyhandle_t *)handle;
504     int ret = 0;
505     
506     if (!utils || !dbh) return SASL_BADPARAM;
507
508     if (dbh->cursor) {
509         dbh->cursor->c_close(dbh->cursor);
510     }
511
512 #if !defined(KEEP_DB_OPEN)
513     /* This is almost the same berkeleydb_close(), except that
514        berkeleydb_close logs a message on error and does not return
515        any error */
516     if (dbh->mbdb) {
517           ret = dbh->mbdb->close(dbh->mbdb, 0);
518     }
519 #endif
520     
521     utils->free(dbh);
522     
523     if (ret) {
524         return SASL_FAIL;
525     } else {
526         return SASL_OK;
527     }
528 }