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