GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / sasldb / db_gdbm.c
1 /* db_gdbm.c--SASL gdbm interface
2  * Rob Siemborski
3  * Rob Earhart
4  * $Id: db_gdbm.c,v 1.4 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 <gdbm.h>
48 #include <sys/stat.h>
49 #include <stdlib.h>
50 #include <assert.h>
51 #include "sasldb.h"
52
53 static int db_ok = 0;
54
55 int _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
145 int _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
224 int _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
266 typedef struct gdbm_handle 
267 {
268     GDBM_FILE db;
269     datum dkey;
270     int first;
271 } handle_t;
272
273 sasldb_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
319 int _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
349 int _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