1 /* db_ndbm.c--SASL ndbm interface
2 * Rob Siemborski
3 * Rob Earhart
4 */
5 /*
6 * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The name "Carnegie Mellon University" must not be used to
21 * endorse or promote products derived from this software without
22 * prior written permission. For permission or any other legal
23 * details, please contact
24 * Carnegie Mellon University
25 * Center for Technology Transfer and Enterprise Creation
26 * 4615 Forbes Avenue
27 * Suite 302
28 * Pittsburgh, PA 15213
29 * (412) 268-7393, fax: (412) 268-7395
30 * innovation@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 <errno.h>
54 #include "sasldb.h"
55
56 static int db_ok = 0;
57
58 /* This provides a version of _sasl_db_getsecret and
59 * _sasl_db_putsecret which work with ndbm. */
_sasldb_getdata(const sasl_utils_t * utils,sasl_conn_t * conn,const char * authid,const char * realm,const char * propName,char * out,const size_t max_out,size_t * out_len)60 int _sasldb_getdata(const sasl_utils_t *utils,
61 sasl_conn_t *conn,
62 const char *authid,
63 const char *realm,
64 const char *propName,
65 char *out, const size_t max_out, size_t *out_len)
66 {
67 int result = SASL_OK;
68 char *key;
69 size_t key_len;
70 DBM *db;
71 datum dkey, dvalue;
72 void *cntxt;
73 sasl_getopt_t *getopt;
74 const char *path = SASL_DB_PATH;
75
76 if (!utils) return SASL_BADPARAM;
77 if (!authid || !propName || !realm || !out || !max_out) {
78 utils->seterror(conn, 0,
79 "Bad parameter in db_ndbm.c: _sasldb_getdata");
80 return SASL_BADPARAM;
81 }
82 if (!db_ok) {
83 utils->seterror(conn, 0, "Database not checked");
84 return SASL_FAIL;
85 }
86
87 result = _sasldb_alloc_key(utils, authid, realm, propName,
88 &key, &key_len);
89 if (result != SASL_OK) {
90 utils->seterror(conn, 0,
91 "Could not allocate key in _sasldb_getdata");
92 return result;
93 }
94
95 if (utils->getcallback(conn, SASL_CB_GETOPT,
96 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) {
97 const char *p;
98 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
99 && p != NULL && *p != 0) {
100 path = p;
101 }
102 }
103 db = dbm_open(path, O_RDONLY, S_IRUSR | S_IWUSR);
104 if (! db) {
105 utils->seterror(cntxt, 0, "Could not open db `%s': %s",
106 path, strerror(errno));
107 result = SASL_FAIL;
108 goto cleanup;
109 }
110 dkey.dptr = key;
111 dkey.dsize = key_len;
112 dvalue = dbm_fetch(db, dkey);
113 if (! dvalue.dptr) {
114 utils->seterror(cntxt, SASL_NOLOG,
115 "user: %s@%s property: %s not found in sasldb %s",
116 authid, realm, propName, path);
117 result = SASL_NOUSER;
118 goto cleanup;
119 }
120
121 if((size_t)dvalue.dsize > max_out + 1) {
122 utils->seterror(cntxt, 0, "buffer overflow");
123 return SASL_BUFOVER;
124 }
125
126 if(out_len) *out_len = dvalue.dsize;
127 memcpy(out, dvalue.dptr, dvalue.dsize);
128 out[dvalue.dsize] = '\0';
129
130 #if NDBM_FREE
131 /* Note: not sasl_FREE! This is memory allocated by ndbm,
132 * which is using libc malloc/free. */
133 free(dvalue.dptr);
134 #endif
135
136 cleanup:
137 utils->free(key);
138 if(db)
139 dbm_close(db);
140
141 return result;
142 }
143
_sasldb_putdata(const sasl_utils_t * utils,sasl_conn_t * conn,const char * authid,const char * realm,const char * propName,const char * data,size_t data_len)144 int _sasldb_putdata(const sasl_utils_t *utils,
145 sasl_conn_t *conn,
146 const char *authid,
147 const char *realm,
148 const char *propName,
149 const char *data, size_t data_len)
150 {
151 int result = SASL_OK;
152 char *key;
153 size_t key_len;
154 DBM *db;
155 datum dkey;
156 void *cntxt;
157 sasl_getopt_t *getopt;
158 const char *path = SASL_DB_PATH;
159
160 if (!utils) return SASL_BADPARAM;
161
162 if (!authid || !realm || !propName) {
163 utils->seterror(conn, 0,
164 "Bad parameter in db_ndbm.c: _sasldb_putdata");
165 return SASL_BADPARAM;
166 }
167
168 result = _sasldb_alloc_key(utils, authid, realm, propName,
169 &key, &key_len);
170 if (result != SASL_OK) {
171 utils->seterror(conn, 0,
172 "Could not allocate key in _sasldb_putdata");
173 return result;
174 }
175
176 if (utils->getcallback(conn, SASL_CB_GETOPT,
177 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) {
178 const char *p;
179 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
180 && p != NULL && *p != 0) {
181 path = p;
182 }
183 }
184
185 db = dbm_open(path,
186 O_RDWR | O_CREAT,
187 S_IRUSR | S_IWUSR);
188 if (! db) {
189 utils->seterror(conn, 0, "Could not open db `%s' for writing: %s",
190 path, strerror(errno));
191 utils->log(conn, SASL_LOG_ERR,
192 "SASL error opening password file. "
193 "Do you have write permissions?\n");
194 result = SASL_FAIL;
195 goto cleanup;
196 }
197 dkey.dptr = key;
198 dkey.dsize = key_len;
199 if (data) {
200 datum dvalue;
201 dvalue.dptr = (void *)data;
202 if(!data_len) data_len = strlen(data);
203 dvalue.dsize = data_len;
204 if (dbm_store(db, dkey, dvalue, DBM_REPLACE)) {
205 utils->seterror(conn, 0,
206 "Couldn't update record for %s@%s property %s "
207 "in db %s: %s", authid, realm, propName, path,
208 strerror(errno));
209 result = SASL_FAIL;
210 }
211 } else {
212 if (dbm_delete(db, dkey)) {
213 utils->seterror(conn, 0,
214 "Couldn't delete record for %s@%s property %s "
215 "in db %s: %s", authid, realm, propName, path,
216 strerror(errno));
217 result = SASL_NOUSER;
218 }
219 }
220 dbm_close(db);
221
222 cleanup:
223 utils->free(key);
224
225 return result;
226 }
227
228 #ifdef DBM_SUFFIX
229 #define SUFLEN (strlen(DBM_SUFFIX) + 1)
230 #else
231 #define SUFLEN 5
232 #endif
233
_sasl_check_db(const sasl_utils_t * utils,sasl_conn_t * conn)234 int _sasl_check_db(const sasl_utils_t *utils,
235 sasl_conn_t *conn)
236 {
237 const char *path = SASL_DB_PATH;
238 void *cntxt;
239 sasl_getopt_t *getopt;
240 sasl_verifyfile_t *vf;
241 int ret = SASL_OK;
242 char *db;
243
244 if(!utils) return SASL_BADPARAM;
245
246 if (utils->getcallback(conn, SASL_CB_GETOPT,
247 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) {
248 const char *p;
249 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
250 && p != NULL && *p != 0) {
251 path = p;
252 }
253 }
254
255 db = utils->malloc(strlen(path) + SUFLEN);
256
257 if (db == NULL) {
258 ret = SASL_NOMEM;
259 }
260
261 ret = utils->getcallback(NULL, SASL_CB_VERIFYFILE,
262 (sasl_callback_ft *)&vf, &cntxt);
263 if(ret != SASL_OK) {
264 utils->seterror(conn, 0,
265 "No verifyfile callback");
266 return ret;
267 }
268
269 #ifdef DBM_SUFFIX
270 if (ret == SASL_OK) {
271 sprintf(db, "%s%s", path, DBM_SUFFIX);
272 ret = vf(cntxt, db, SASL_VRFY_PASSWD);
273 }
274 #else
275 if (ret == SASL_OK) {
276 sprintf(db, "%s.dir", path);
277 ret = vf(cntxt, db, SASL_VRFY_PASSWD);
278 }
279 if (ret == SASL_OK) {
280 sprintf(db, "%s.pag", path);
281 ret = vf(cntxt, db, SASL_VRFY_PASSWD);
282 }
283 #endif
284 if (db) {
285 utils->free(db);
286 }
287 if (ret == SASL_OK) {
288 db_ok = 1;
289 }
290
291 if (ret == SASL_OK || ret == SASL_CONTINUE) {
292 return SASL_OK;
293 } else {
294 utils->seterror(conn, 0,
295 "Verifyfile failed");
296 return ret;
297 }
298 }
299
300 typedef struct ndbm_handle
301 {
302 DBM *db;
303 datum dkey;
304 int first;
305 } handle_t;
306
_sasldb_getkeyhandle(const sasl_utils_t * utils,sasl_conn_t * conn)307 sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils,
308 sasl_conn_t *conn)
309 {
310 const char *path = SASL_DB_PATH;
311 sasl_getopt_t *getopt;
312 void *cntxt;
313 DBM *db;
314 handle_t *handle;
315
316 if(!utils || !conn) return NULL;
317
318 if(!db_ok) {
319 utils->seterror(conn, 0, "Database not OK in _sasldb_getkeyhandle");
320 return NULL;
321 }
322
323 if (utils->getcallback(conn, SASL_CB_GETOPT,
324 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) {
325 const char *p;
326 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
327 && p != NULL && *p != 0) {
328 path = p;
329 }
330 }
331
332 db = dbm_open(path, O_RDONLY, S_IRUSR | S_IWUSR);
333
334 if(!db) {
335 utils->seterror(conn, 0, "Could not open db `%s': %s",
336 path, strerror(errno));
337 return NULL;
338 }
339
340 handle = utils->malloc(sizeof(handle_t));
341 if(!handle) {
342 utils->seterror(conn, 0, "no memory in _sasldb_getkeyhandle");
343 dbm_close(db);
344 return NULL;
345 }
346
347 handle->db = db;
348 handle->first = 1;
349
350 return (sasldb_handle)handle;
351 }
352
_sasldb_getnextkey(const sasl_utils_t * utils,sasldb_handle handle,char * out,const size_t max_out,size_t * out_len)353 int _sasldb_getnextkey(const sasl_utils_t *utils __attribute__((unused)),
354 sasldb_handle handle, char *out,
355 const size_t max_out, size_t *out_len)
356 {
357 handle_t *dbh = (handle_t *)handle;
358 datum nextkey;
359
360 if(!utils || !handle || !out || !max_out)
361 return SASL_BADPARAM;
362
363 if(dbh->first) {
364 dbh->dkey = dbm_firstkey(dbh->db);
365 dbh->first = 0;
366 } else {
367 nextkey = dbm_nextkey(dbh->db);
368 dbh->dkey = nextkey;
369 }
370
371 if(dbh->dkey.dptr == NULL)
372 return SASL_OK;
373
374 if((unsigned)dbh->dkey.dsize > max_out)
375 return SASL_BUFOVER;
376
377 memcpy(out, dbh->dkey.dptr, dbh->dkey.dsize);
378 if(out_len) *out_len = dbh->dkey.dsize;
379
380 return SASL_CONTINUE;
381 }
382
_sasldb_releasekeyhandle(const sasl_utils_t * utils,sasldb_handle handle)383 int _sasldb_releasekeyhandle(const sasl_utils_t *utils,
384 sasldb_handle handle)
385 {
386 handle_t *dbh = (handle_t *)handle;
387
388 if(!utils || !dbh) return SASL_BADPARAM;
389
390 if(dbh->db) dbm_close(dbh->db);
391
392 utils->free(dbh);
393
394 return SASL_OK;
395 }
396