1/* db_ndbm.c--SASL ndbm interface
2 * Rob Siemborski
3 * Rob Earhart
4 * $Id: db_ndbm.c,v 1.6 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 <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
55static int db_ok = 0;
56
57/* This provides a version of _sasl_db_getsecret and
58 * _sasl_db_putsecret which work with ndbm. */
59int _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                        (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) { /* APPLE: cast */
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
140int _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			 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) { /* APPLE: cast */
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
224int _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			   (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) { /* APPLE: cast */
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			     (sasl_callback_ft *)&vf, &cntxt); /* APPLE: cast */
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
290typedef struct ndbm_handle
291{
292    DBM *db;
293    datum dkey;
294    int first;
295} handle_t;
296
297sasldb_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			   (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) { /* APPLE: cast */
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
342int _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
372int _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}
385