1/* dbconverter-2.c -- convert libsasl v1 sasldb's to SASLv2 format
2 * $Id: dbconverter-2.c,v 1.8 2003/02/13 19:56:17 rjs3 Exp $
3 * Rob Siemborski
4 * based on SASLv1 sasldblistusers
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
48#include <stdio.h>
49#include <stdlib.h>
50
51#include <sasl.h>
52#include <saslplug.h>
53#include "../sasldb/sasldb.h"
54
55/* Cheating to make the utils work out right */
56extern const sasl_utils_t *sasl_global_utils;
57sasl_conn_t *globalconn;
58
59typedef void *listcb_t(const char *, const char *, const char *,
60		       const char *, unsigned);
61
62void listusers_cb(const char *authid, const char *realm,
63		  const char *mechanism, const char *secret,
64		  unsigned seclen)
65{
66    char newPropBuffer[8192];
67
68    if (!authid || !mechanism || !realm) {
69	fprintf(stderr,"userlist callback has bad param");
70	return;
71    }
72
73    /* the entries that just say the mechanism exists */
74    if (strlen(authid)==0) return;
75
76    printf("Converting: %s@%s (%s)...",authid,realm,mechanism);
77
78    /* Maybe we have a plaintext password? */
79    if(!strcmp(mechanism,"PLAIN-APOP")) {
80	sprintf(newPropBuffer, "userPassword");
81	/* Skip salt + NULL */
82	secret = secret + 17;
83	seclen -= 17;
84    } else {
85	sprintf(newPropBuffer, "cmusaslsecret%s", mechanism);
86    }
87
88    _sasldb_putdata(sasl_global_utils, globalconn,
89		    authid, realm, newPropBuffer,
90		    secret, seclen);
91
92    printf("ok\n");
93}
94
95/*
96 * List all users in database
97 */
98
99#if defined(SASL_GDBM)
100
101#include <gdbm.h>
102#include <fcntl.h>
103#include <sys/stat.h>
104
105int listusers(const char *path, listcb_t *cb)
106{
107    GDBM_FILE indb;
108    datum dkey, nextkey, dvalue;
109
110    indb = gdbm_open((char *)path, 0, GDBM_READER, S_IRUSR | S_IWUSR, NULL);
111
112    if (!indb) {
113	fprintf(stderr, "can't open %s\n", path);
114	return 1;
115    }
116
117    memset(&dkey, 0, sizeof(datum));
118
119    dkey = gdbm_firstkey(indb);
120
121    while (dkey.dptr != NULL) {
122	char *authid = dkey.dptr;
123	char *realm  = dkey.dptr+strlen(authid)+1;
124	char *tmp    = realm + strlen(realm)+1;
125	char mech[1024];
126	int len = dkey.dsize - (tmp - ((char *)dkey.dptr));
127
128	if (len >= (int) sizeof mech) {
129	    fprintf(stderr, "malformed database entry\n");
130	    break;
131	}
132	memcpy(mech, tmp, len);
133	mech[dkey.dsize - (tmp - dkey.dptr)] = '\0';
134
135	dvalue = gdbm_fetch(indb, dkey);
136
137	if (*authid && dvalue.dptr) {
138	    /* don't check return values */
139	    cb(authid,realm,mech,dvalue.dptr,dvalue.dsize);
140	}
141
142	nextkey=gdbm_nextkey(indb, dkey);
143	dkey=nextkey;
144    }
145
146    gdbm_close(indb);
147    return 0;
148}
149
150#elif defined(SASL_NDBM)
151
152#include <ndbm.h>
153#include <fcntl.h>
154#include <sys/stat.h>
155
156int listusers(const char *path, listcb_t *cb)
157{
158    DBM *indb;
159    datum dkey, nextkey, dvalue;
160
161    indb = dbm_open(path, O_RDONLY, S_IRUSR | S_IWUSR);
162
163    if (!indb) {
164	fprintf(stderr, "can't open %s\n", path);
165	return 1;
166    }
167
168    dkey = dbm_firstkey(indb);
169
170    while (dkey.dptr != NULL) {
171	char *authid = dkey.dptr;
172	char *realm  = dkey.dptr+strlen(authid)+1;
173	char *tmp    = realm + strlen(realm)+1;
174	char mech[1024];
175	int len = dkey.dsize - (tmp - ((char *)dkey.dptr));
176
177	if (len >= (int) sizeof mech) {
178	    fprintf(stderr, "malformed database entry\n");
179	    break;
180	}
181	memcpy(mech, tmp, len);
182	mech[dkey.dsize - (tmp - ((char *)dkey.dptr))] = '\0';
183
184	dvalue = dbm_fetch(indb, dkey);
185
186	if (*authid && dvalue.dptr) {
187	    /* don't check return values */
188	    cb(authid,realm,mech,dvalue.dptr,dvalue.dsize);
189	}
190
191	nextkey=dbm_nextkey(indb);
192	dkey=nextkey;
193    }
194
195    dbm_close(indb);
196    return 0;
197}
198
199#elif defined(SASL_BERKELEYDB)
200
201#include <db.h>
202
203#define DB_VERSION_FULL ((DB_VERSION_MAJOR << 24) | (DB_VERSION_MINOR << 16) | DB_VERSION_PATCH)
204/*
205 * Open the database
206 *
207 */
208static int berkeleydb_open(const char *path,DB **mbdb)
209{
210    int ret;
211
212#if DB_VERSION_FULL < 0x03000000
213    ret = db_open(path, DB_HASH, DB_CREATE, 0664, NULL, NULL, mbdb);
214#else /* DB_VERSION_FULL < 0x03000000 */
215    ret = db_create(mbdb, NULL, 0);
216    if (ret == 0 && *mbdb != NULL)
217    {
218#if DB_VERSION_FULL >= 0x04010000
219	ret = (*mbdb)->open(*mbdb, NULL, path, NULL, DB_HASH, DB_CREATE, 0664);
220#else
221	ret = (*mbdb)->open(*mbdb, path, NULL, DB_HASH, DB_CREATE, 0664);
222#endif
223	if (ret != 0)
224	{
225	    (void) (*mbdb)->close(*mbdb, 0);
226	    *mbdb = NULL;
227	}
228    }
229#endif /* DB_VERSION_FULL < 0x03000000 */
230
231    if (ret != 0) {
232	fprintf(stderr,"Error opening password file %s\n", path);
233	return SASL_FAIL;
234    }
235
236    return SASL_OK;
237}
238
239/*
240 * Close the database
241 *
242 */
243
244static void berkeleydb_close(DB *mbdb)
245{
246    int ret;
247
248    ret = mbdb->close(mbdb, 0);
249    if (ret!=0) {
250	fprintf(stderr,"error closing sasldb: %s",
251		db_strerror(ret));
252    }
253}
254
255int listusers(const char *path, listcb_t *cb)
256{
257    int result;
258    DB *mbdb = NULL;
259    DBC *cursor;
260    DBT key, data;
261
262    /* open the db */
263    result=berkeleydb_open(path, &mbdb);
264    if (result!=SASL_OK) goto cleanup;
265
266    /* make cursor */
267#if DB_VERSION_FULL < 0x03060000
268    result = mbdb->cursor(mbdb, NULL,&cursor);
269#else
270    result = mbdb->cursor(mbdb, NULL,&cursor, 0);
271#endif /* DB_VERSION_FULL < 0x03060000 */
272
273    if (result!=0) {
274	fprintf(stderr,"Making cursor failure: %s\n",db_strerror(result));
275      result = SASL_FAIL;
276      goto cleanup;
277    }
278
279    memset(&key,0, sizeof(key));
280    memset(&data,0,sizeof(data));
281
282    /* loop thru */
283    result = cursor->c_get(cursor, &key, &data,
284			   DB_FIRST);
285
286    while (result != DB_NOTFOUND)
287    {
288	char *authid;
289	char *realm;
290	char *tmp;
291	unsigned int len;
292	char mech[1024];
293	int numnulls = 0;
294	unsigned int lup;
295
296	/* make sure there are exactly 2 null's */
297	for (lup=0;lup<key.size;lup++)
298	    if (((char *)key.data)[lup]=='\0')
299		numnulls++;
300
301	if (numnulls != 2) {
302	    fprintf(stderr,"warning: probable database corruption\n");
303	    result = cursor->c_get(cursor, &key, &data, DB_NEXT);
304	    continue;
305	}
306
307	authid = key.data;
308	realm  = authid + strlen(authid)+1;
309	tmp    = realm + strlen(realm)+1;
310	len = key.size - (tmp - authid);
311
312	/* make sure we have enough space of mech */
313	if (len >=sizeof(mech)) {
314	    fprintf(stderr,"warning: absurdly long mech name\n");
315	    result = cursor->c_get(cursor, &key, &data, DB_NEXT);
316	    continue;
317	}
318
319	memcpy(mech, tmp, key.size - (tmp - ((char *)key.data)));
320	mech[key.size - (tmp - ((char *)key.data))] = '\0';
321
322	if (*authid) {
323	    /* don't check return values */
324	    cb(authid,realm,mech,data.data,data.size);
325	}
326
327	result = cursor->c_get(cursor, &key, &data, DB_NEXT);
328    }
329
330    if (result != DB_NOTFOUND) {
331	fprintf(stderr,"failure: %s\n",db_strerror(result));
332	result = SASL_FAIL;
333	goto cleanup;
334    }
335
336    result = cursor->c_close(cursor);
337    if (result!=0) result = SASL_FAIL;
338
339    result = SASL_OK;
340
341 cleanup:
342
343    if (mbdb != NULL) berkeleydb_close(mbdb);
344    return result;
345}
346
347#else
348
349/* ARGSUSED */
350
351int listusers(const char *path __attribute__((unused)),
352	      listcb_t *cb __attribute__((unused)))
353{
354    fprintf(stderr,"Unsupported DB format");
355    exit(1);
356}
357
358#endif
359
360char *db_new=SASL_DB_PATH;
361
362int good_getopt(void *context __attribute__((unused)),
363		const char *plugin_name __attribute__((unused)),
364		const char *option,
365		const char **result,
366		unsigned *len)
367{
368    if (db_new && !strcmp(option, "sasldb_path")) {
369	*result = db_new;
370	if (len)
371	    *len = strlen(db_new);
372	return SASL_OK;
373    }
374
375    return SASL_FAIL;
376}
377
378static struct sasl_callback goodsasl_cb[] = {
379    { SASL_CB_GETOPT, &good_getopt, NULL },
380    { SASL_CB_LIST_END, NULL, NULL }
381};
382
383int main(int argc, char **argv)
384{
385    const char *db="/etc/sasldb";
386    int result;
387
388    if (argc > 1) {
389	db = argv[1];
390	if(argc > 2) {
391	    db_new = argv[2];
392	}
393    }
394
395    result = sasl_server_init(goodsasl_cb, "dbconverter");
396    if (result != SASL_OK) {
397	printf("couldn't init saslv2\n");
398	return 1;
399    }
400
401    result = sasl_server_new("sasldb",
402			     "localhost",
403			     NULL,
404			     NULL,
405			     NULL,
406			     NULL,
407			     0,
408			     &globalconn);
409    if (result != SASL_OK) {
410	printf("couldn't create globalconn\n");
411	return 1;
412    }
413
414    if(_sasl_check_db(sasl_global_utils,globalconn) != SASL_OK) {
415	printf("target DB %s is not OK\n", db_new);
416	return 1;
417    }
418
419    printf("\nThis program will take the sasldb file specified on the\n"
420           "command line and convert it to a new sasldb file in the default\n"
421           "location (usually /etc/sasldb). It is STRONGLY RECOMMENDED that you\n"
422           "backup sasldb before allowing this program to run\n\n"
423	   "We are going to convert %s and our output will be in %s\n\n"
424           "Press return to continue\n", db, db_new);
425
426    getchar();
427
428    listusers(db, (listcb_t *) &listusers_cb);
429
430    sasl_dispose(&globalconn);
431    sasl_done();
432
433    exit(0);
434}
435