1 /* db_gdbm.c--SASL gdbm 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 <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
_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)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 (sasl_callback_ft *)&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 int fetch_errno = gdbm_errno;
111
112 gdbm_close(db);
113 if (! gvalue.dptr) {
114 if (fetch_errno == GDBM_ITEM_NOT_FOUND) {
115 utils->seterror(conn, SASL_NOLOG,
116 "user: %s@%s property: %s not found in %s",
117 authid, realm, propName, path);
118 result = SASL_NOUSER;
119 } else {
120 utils->seterror(conn, 0,
121 "Couldn't fetch entry from %s: gdbm_errno=%d",
122 path, gdbm_errno);
123 result = SASL_FAIL;
124 }
125 goto cleanup;
126 }
127
128 if((size_t)gvalue.dsize > max_out + 1) {
129 utils->seterror(cntxt, 0, "buffer overflow");
130 return SASL_BUFOVER;
131 }
132
133 if(out_len) *out_len = gvalue.dsize;
134 memcpy(out, gvalue.dptr, gvalue.dsize);
135 out[gvalue.dsize] = '\0';
136
137 /* Note: not sasl_FREE! This is memory allocated by gdbm,
138 * which is using libc malloc/free. */
139 free(gvalue.dptr);
140
141 cleanup:
142 utils->free(key);
143
144 return result;
145 }
146
_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)147 int _sasldb_putdata(const sasl_utils_t *utils,
148 sasl_conn_t *conn,
149 const char *authid,
150 const char *realm,
151 const char *propName,
152 const char *data, size_t data_len)
153 {
154 int result = SASL_OK;
155 char *key;
156 size_t key_len;
157 GDBM_FILE db;
158 datum gkey;
159 void *cntxt;
160 sasl_getopt_t *getopt;
161 const char *path = SASL_DB_PATH;
162
163 if (!utils) return SASL_BADPARAM;
164
165 if (!authid || !realm || !propName) {
166 utils->seterror(conn, 0,
167 "Bad parameter in db_gdbm.c: _sasldb_putdata");
168 return SASL_BADPARAM;
169 }
170
171 result = _sasldb_alloc_key(utils, authid, realm, propName,
172 &key, &key_len);
173 if (result != SASL_OK) {
174 utils->seterror(conn, 0,
175 "Could not allocate key in _sasldb_putdata");
176 return result;
177 }
178
179 if (utils->getcallback(conn, SASL_CB_GETOPT,
180 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) {
181 const char *p;
182 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
183 && p != NULL && *p != 0) {
184 path = p;
185 }
186 }
187 db = gdbm_open((char *)path, 0, GDBM_WRCREAT, S_IRUSR | S_IWUSR, NULL);
188 if (! db) {
189 utils->log(conn, SASL_LOG_ERR,
190 "SASL error opening password file. "
191 "Do you have write permissions?\n");
192 utils->seterror(conn, 0, "Could not open %s for write: gdbm_errno=%d",
193 path, gdbm_errno);
194 result = SASL_FAIL;
195 goto cleanup;
196 }
197 gkey.dptr = key;
198 gkey.dsize = key_len;
199 if (data) {
200 datum gvalue;
201 gvalue.dptr = (char *)data;
202 if(!data_len) data_len = strlen(data);
203 gvalue.dsize = data_len;
204 if (gdbm_store(db, gkey, gvalue, GDBM_REPLACE)) {
205 utils->seterror(conn, 0,
206 "Couldn't replace entry in %s: gdbm_errno=%d",
207 path, gdbm_errno);
208 result = SASL_FAIL;
209 }
210 } else {
211 if (gdbm_delete(db, gkey)) {
212 utils->seterror(conn, 0,
213 "Couldn't delete entry in %s: gdbm_errno=%d",
214 path, gdbm_errno);
215 result = SASL_NOUSER;
216 }
217 }
218 gdbm_close(db);
219
220 cleanup:
221 utils->free(key);
222
223 return result;
224 }
225
_sasl_check_db(const sasl_utils_t * utils,sasl_conn_t * conn)226 LIBSASL_API int _sasl_check_db(const sasl_utils_t *utils,
227 sasl_conn_t *conn)
228 {
229 const char *path = SASL_DB_PATH;
230 int ret;
231 void *cntxt;
232 sasl_getopt_t *getopt;
233 sasl_verifyfile_t *vf;
234
235 if(!utils) return SASL_BADPARAM;
236
237 if (utils->getcallback(conn, SASL_CB_GETOPT,
238 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) {
239 const char *p;
240 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
241 && p != NULL && *p != 0) {
242 path = p;
243 }
244 }
245
246 ret = utils->getcallback(NULL, SASL_CB_VERIFYFILE,
247 (sasl_callback_ft *)&vf, &cntxt);
248 if(ret != SASL_OK) {
249 utils->seterror(conn, 0,
250 "No verifyfile callback");
251 return ret;
252 }
253
254 ret = vf(cntxt, path, SASL_VRFY_PASSWD);
255 if (ret == SASL_OK) {
256 db_ok = 1;
257 }
258
259 if (ret == SASL_OK || ret == SASL_CONTINUE) {
260 return SASL_OK;
261 } else {
262 utils->seterror(conn, 0,
263 "Verifyfile failed");
264 return ret;
265 }
266 }
267
268 typedef struct gdbm_handle
269 {
270 GDBM_FILE db;
271 datum dkey;
272 int first;
273 } handle_t;
274
_sasldb_getkeyhandle(const sasl_utils_t * utils,sasl_conn_t * conn)275 LIBSASL_API sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils,
276 sasl_conn_t *conn)
277 {
278 const char *path = SASL_DB_PATH;
279 sasl_getopt_t *getopt;
280 void *cntxt;
281 GDBM_FILE db;
282 handle_t *handle;
283
284 if(!utils || !conn) return NULL;
285
286 if(!db_ok) {
287 utils->seterror(conn, 0, "Database not OK in _sasldb_getkeyhandle");
288 return NULL;
289 }
290
291 if (utils->getcallback(conn, SASL_CB_GETOPT,
292 (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) {
293 const char *p;
294 if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
295 && p != NULL && *p != 0) {
296 path = p;
297 }
298 }
299
300 db = gdbm_open((char *)path, 0, GDBM_READER, S_IRUSR | S_IWUSR, NULL);
301
302 if(!db) {
303 utils->seterror(conn, 0, "Could not open %s: gdbm_errno=%d",
304 path, gdbm_errno);
305 return NULL;
306 }
307
308 handle = utils->malloc(sizeof(handle_t));
309 if(!handle) {
310 utils->seterror(conn, 0, "no memory in _sasldb_getkeyhandle");
311 gdbm_close(db);
312 return NULL;
313 }
314
315 handle->db = db;
316 handle->first = 1;
317
318 return (sasldb_handle)handle;
319 }
320
_sasldb_getnextkey(const sasl_utils_t * utils,sasldb_handle handle,char * out,const size_t max_out,size_t * out_len)321 LIBSASL_API int _sasldb_getnextkey(const sasl_utils_t *utils __attribute__((unused)),
322 sasldb_handle handle, char *out,
323 const size_t max_out, size_t *out_len)
324 {
325 handle_t *dbh = (handle_t *)handle;
326 datum nextkey;
327
328 if(!utils || !handle || !out || !max_out)
329 return SASL_BADPARAM;
330
331 if(dbh->first) {
332 dbh->dkey = gdbm_firstkey(dbh->db);
333 dbh->first = 0;
334 } else {
335 nextkey = gdbm_nextkey(dbh->db, dbh->dkey);
336 dbh->dkey = nextkey;
337 }
338
339 if(dbh->dkey.dptr == NULL)
340 return SASL_OK;
341
342 if((unsigned)dbh->dkey.dsize > max_out)
343 return SASL_BUFOVER;
344
345 memcpy(out, dbh->dkey.dptr, dbh->dkey.dsize);
346 if(out_len) *out_len = dbh->dkey.dsize;
347
348 return SASL_CONTINUE;
349 }
350
_sasldb_releasekeyhandle(const sasl_utils_t * utils,sasldb_handle handle)351 LIBSASL_API int _sasldb_releasekeyhandle(const sasl_utils_t *utils,
352 sasldb_handle handle)
353 {
354 handle_t *dbh = (handle_t *)handle;
355
356 if(!utils || !dbh) return SASL_BADPARAM;
357
358 if(dbh->db) gdbm_close(dbh->db);
359
360 utils->free(dbh);
361
362 return SASL_OK;
363 }
364
365