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