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