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