]> granicus.if.org Git - apache/blob - support/htdbm.c
allow htdbm to work with multiple DBM types by using a new option
[apache] / support / htdbm.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * htdbm.c: simple program for manipulating DBM
61  * password databases for the Apache HTTP server
62  *
63  * Contributed by Mladen Turk <mturk@mappingsoft.com>
64  * 12 Oct 2001
65  */
66
67 #include "apr.h"
68 #include "apr_lib.h"
69 #include "apr_strings.h"
70 #include "apr_file_io.h"
71 #include "apr_file_info.h"
72 #include "apr_pools.h"
73 #include "apr_signal.h"
74 #include "apr_md5.h"
75 #include "apr_sha1.h"
76 #include "apr_dbm.h"
77
78 #if APR_HAVE_STDLIB_H
79 #include <stdlib.h>
80 #endif
81 #if APR_HAVE_STRING_H
82 #include <string.h>
83 #endif
84 #if APR_HAVE_STRINGS_H
85 #include <strings.h>
86 #endif
87
88 #if APR_CHARSET_EBCDIC
89 #include "apr_xlate.h"
90 #endif /*APR_CHARSET_EBCDIC*/
91
92 #if APR_HAVE_CRYPT_H
93 #include <crypt.h>
94 #endif
95
96
97 #if !APR_CHARSET_EBCDIC
98 #define LF 10
99 #define CR 13
100 #else /*APR_CHARSET_EBCDIC*/
101 #define LF '\n'
102 #define CR '\r'
103 #endif /*APR_CHARSET_EBCDIC*/
104
105 #define MAX_STRING_LEN 256
106 #define ALG_PLAIN 0
107 #define ALG_APMD5 1
108 #define ALG_APSHA 2
109  
110 #if APR_HAVE_CRYPT_H
111 #define ALG_CRYPT 3
112 #endif
113
114
115 #define ERR_FILEPERM    1
116 #define ERR_SYNTAX      2
117 #define ERR_PWMISMATCH  3
118 #define ERR_INTERRUPTED 4
119 #define ERR_OVERFLOW    5
120 #define ERR_BADUSER     6
121 #define ERR_EMPTY       7
122
123
124 typedef struct htdbm_t htdbm_t;
125
126 struct htdbm_t {
127     apr_dbm_t               *dbm;
128     apr_pool_t              *pool;
129 #if APR_CHARSET_EBCDIC
130     apr_xlate_t             *to_ascii;
131 #endif
132     char                    *filename;
133     char                    *username;
134     char                    *userpass;
135     char                    *comment;
136     char                    *type;
137     int                     create;
138     int                     rdonly;
139     int                     alg;
140 };
141
142
143 #define HTDBM_MAKE   0
144 #define HTDBM_DELETE 1
145 #define HTDBM_VERIFY 2
146 #define HTDBM_LIST   3
147 #define HTDBM_NOFILE 4
148 #define HTDBM_STDIN  5
149
150 static void htdbm_terminate(htdbm_t *htdbm) 
151 {
152     
153     if (htdbm->dbm)
154         apr_dbm_close(htdbm->dbm);
155     htdbm->dbm = NULL;
156 }
157
158 static htdbm_t *h;
159   
160 static void htdbm_interrupted(void) 
161 {
162     htdbm_terminate(h);
163     fprintf(stderr, "htdbm Interrupted !\n");
164     exit(ERR_INTERRUPTED);
165 }
166
167 static apr_status_t htdbm_init(apr_pool_t **pool, htdbm_t **hdbm) 
168 {
169
170 #if APR_CHARSET_EBCDIC
171     apr_status_t rv;
172 #endif
173
174     apr_initialize();
175     atexit(apr_terminate);
176     apr_pool_create( pool, NULL);
177     apr_signal(SIGINT, (void (*)(int)) htdbm_interrupted);
178
179     (*hdbm) = (htdbm_t *)apr_pcalloc(*pool, sizeof(htdbm_t));
180     (*hdbm)->pool = *pool;
181
182 #if APR_CHARSET_EBCDIC
183     rv = apr_xlate_open(to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, (*hdbm)->pool);
184     if (rv) {
185         fprintf(stderr, "apr_xlate_open(to ASCII)->%d\n", rv);
186         return APR_EGENERAL;
187     }
188     rv = apr_SHA1InitEBCDIC((*hdbm)->to_ascii);
189     if (rv) {
190         fprintf(stderr, "apr_SHA1InitEBCDIC()->%d\n", rv);
191         return APR_EGENERAL;
192     }
193     rv = apr_MD5InitEBCDIC((*hdbm)->to_ascii);
194     if (rv) {
195         fprintf(stderr, "apr_MD5InitEBCDIC()->%d\n", rv);
196         return APR_EGENERAL;
197     }
198 #endif /*APR_CHARSET_EBCDIC*/
199
200     /* Set MD5 as default */
201     (*hdbm)->alg = ALG_APMD5;
202     (*hdbm)->type = "default";
203     return APR_SUCCESS;
204 }
205
206 static apr_status_t htdbm_open(htdbm_t *htdbm) 
207 {
208     if (htdbm->create)
209         return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename, APR_DBM_RWCREATE, 
210                             APR_OS_DEFAULT, htdbm->pool);
211     else
212         return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename, 
213                             htdbm->rdonly ? APR_DBM_READONLY : APR_DBM_READWRITE, 
214                             APR_OS_DEFAULT, htdbm->pool);
215 }
216
217 static char * ap_getword(apr_pool_t *atrans, char **line, char stop)
218 {
219     char *pos = strrchr(*line, stop);
220     char *res;
221
222     if (!pos) {
223         res = apr_pstrdup(atrans, *line);
224         *line += strlen(*line);
225         return res;
226     }
227
228     res = apr_pstrndup(atrans, *line, pos - *line);
229
230     while (*pos == stop)
231         ++pos;
232     *line = pos;
233     return res;
234 }
235
236 static apr_status_t htdbm_save(htdbm_t *htdbm, int *changed) 
237 {
238     apr_datum_t key, val;
239
240     if (!htdbm->username)
241         return APR_SUCCESS;
242
243     key.dptr = htdbm->username;
244     key.dsize = strlen(htdbm->username);
245     if (apr_dbm_exists(htdbm->dbm, key))
246         *changed = 1;
247
248     val.dsize = strlen(htdbm->userpass);
249     if (!htdbm->comment)
250         val.dptr  = htdbm->userpass;
251     else {
252         val.dptr = apr_pstrcat(htdbm->pool, htdbm->userpass, ";",
253                                htdbm->comment, NULL);
254         val.dsize += (strlen(htdbm->comment) + 1);
255     }
256     return apr_dbm_store(htdbm->dbm, key, val);
257 }
258
259 static apr_status_t htdbm_del(htdbm_t *htdbm) 
260 {
261     apr_datum_t key;
262
263     key.dptr = htdbm->username;
264     key.dsize = strlen(htdbm->username);
265     if (!apr_dbm_exists(htdbm->dbm, key))
266         return APR_ENOENT;
267
268     return apr_dbm_delete(htdbm->dbm, key);
269 }
270
271 static apr_status_t htdbm_verify(htdbm_t *htdbm) 
272 {
273     apr_datum_t key, val;
274     char pwd[MAX_STRING_LEN] = {0};
275     char *rec, *cmnt;
276
277     key.dptr = htdbm->username;
278     key.dsize = strlen(htdbm->username);
279     if (!apr_dbm_exists(htdbm->dbm, key))
280         return APR_ENOENT;    
281     if (apr_dbm_fetch(htdbm->dbm, key, &val) != APR_SUCCESS)
282         return APR_ENOENT;
283     rec = apr_pstrndup(htdbm->pool, val.dptr, val.dsize);
284     cmnt = strchr(rec, ';');
285     if (cmnt)
286         strncpy(pwd, rec, cmnt - rec);
287     else
288         strcpy(pwd, rec);
289     return apr_password_validate(htdbm->userpass, pwd);
290 }
291
292 static apr_status_t htdbm_list(htdbm_t *htdbm) 
293 {
294     apr_status_t rv;
295     apr_datum_t key, val;
296     char *rec, *cmnt;
297     char kb[MAX_STRING_LEN];
298     int i = 0;
299
300     rv = apr_dbm_firstkey(htdbm->dbm, &key);
301     if (rv != APR_SUCCESS) {
302         fprintf(stderr, "Empty database -- %s\n", htdbm->filename); 
303         return APR_ENOENT;
304     }
305     rec = apr_pcalloc(htdbm->pool, HUGE_STRING_LEN);
306
307     fprintf(stderr, "Dumping records from database -- %s\n", htdbm->filename); 
308     fprintf(stderr, "    %-32sComment\n", "Username");    
309     while (key.dptr != NULL) {
310         rv = apr_dbm_fetch(htdbm->dbm, key, &val);
311         if (rv != APR_SUCCESS) {
312             fprintf(stderr, "Failed getting data from %s\n", htdbm->filename);
313             return APR_EGENERAL;
314         }
315         strncpy(kb, key.dptr, key.dsize);
316         kb[key.dsize] = '\0';
317         fprintf(stderr, "    %-32s", kb);
318         strncpy(rec, val.dptr, val.dsize);
319         rec[val.dsize] = '\0';
320         cmnt = strchr(rec, ';');
321         if (cmnt)
322             fprintf(stderr, cmnt + 1);
323         fprintf(stderr, "\n");
324         rv = apr_dbm_nextkey(htdbm->dbm, &key);
325         if (rv != APR_SUCCESS)
326             fprintf(stderr, "Failed getting NextKey\n");
327         ++i;
328     }
329
330     fprintf(stderr, "Total #records : %d\n", i);
331     return APR_SUCCESS;
332 }
333
334 static void to64(char *s, unsigned long v, int n)
335 {
336     static unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */
337     "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
338
339     while (--n >= 0) {
340         *s++ = itoa64[v&0x3f];
341         v >>= 6;
342     }
343 }
344
345 static apr_status_t htdbm_make(htdbm_t *htdbm) 
346 {
347     char cpw[MAX_STRING_LEN];
348     char salt[9];
349
350     switch (htdbm->alg) {
351         case ALG_APSHA:
352             /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
353             apr_sha1_base64(htdbm->userpass,strlen(htdbm->userpass),cpw);
354         break;
355
356         case ALG_APMD5: 
357             (void) srand((int) time((time_t *) NULL));
358             to64(&salt[0], rand(), 8);
359             salt[8] = '\0';
360             apr_md5_encode((const char *)htdbm->userpass, (const char *)salt,
361                             cpw, sizeof(cpw));
362         break;
363         case ALG_PLAIN:
364             /* XXX this len limitation is not in sync with any HTTPd len. */
365             apr_cpystrn(cpw,htdbm->userpass,sizeof(cpw));
366         break;
367 #if APR_HAVE_CRYPT_H
368         case ALG_CRYPT:
369             (void) srand((int) time((time_t *) NULL));
370             to64(&salt[0], rand(), 8);
371             salt[8] = '\0';
372             apr_cpystrn(cpw, (char *)crypt(htdbm->userpass, salt), sizeof(cpw) - 1);
373             fprintf(stderr, "CRYPT is now depriciated, use MD5 instead !\n");
374 #endif
375         default:
376         break;
377     }
378     htdbm->userpass = apr_pstrdup(htdbm->pool, cpw);
379     return APR_SUCCESS;
380 }
381
382 static apr_status_t htdbm_valid_username(htdbm_t *htdbm)
383 {
384     if (!htdbm->username || (strlen(htdbm->username) > 64) || (strlen(htdbm->username) < 1)) {
385         fprintf(stderr, "Invalid username length\n");
386         return APR_EINVAL;
387     }
388     if (strchr(htdbm->username, ':')) {
389         fprintf(stderr, "Username contains invalid characters\n");
390         return APR_EINVAL;
391     }
392     return APR_SUCCESS;
393 }
394
395 static void htdbm_usage(void)
396 {
397
398 #if APR_HAVE_CRYPT_H
399 #define CRYPT_OPTION "d"
400 #else
401 #define CRYPT_OPTION ""
402 #endif
403     fprintf(stderr, "htdbm -- program for manipulating DBM password databases.\n\n");
404     fprintf(stderr, "Usage: htdbm    [-cm"CRYPT_OPTION"pstvx] [-TDBTYPE] database username\n");
405     fprintf(stderr, "                -b[cm"CRYPT_OPTION"ptsv] [-TDBTYPE] database username password\n");
406     fprintf(stderr, "                -n[m"CRYPT_OPTION"pst]   username\n");
407     fprintf(stderr, "                -nb[m"CRYPT_OPTION"pst]  username password\n");
408     fprintf(stderr, "                -v[m"CRYPT_OPTION"ps]    [-TDBTYPE] database username\n");
409     fprintf(stderr, "                -vb[m"CRYPT_OPTION"ps]   [-TDBTYPE] database username password\n");
410     fprintf(stderr, "                -x[m"CRYPT_OPTION"ps]    [-TDBTYPE] database username\n");
411     fprintf(stderr, "                -l                       [-TDBTYPE] database\n");
412     fprintf(stderr, "Options:\n");
413     fprintf(stderr, "   -b   Use the password from the command line rather"
414                     "than prompting for it.\n");
415     fprintf(stderr, "   -c   Create a new database.\n");
416     fprintf(stderr, "   -n   Don't update database; display results on stdout.\n");
417     fprintf(stderr, "   -m   Force MD5 encryption of the password (default).\n");
418 #if APR_HAVE_CRYPT_H
419     fprintf(stderr, "   -d   Force CRYPT encryption of the password (now depriciated).\n");
420 #endif
421     fprintf(stderr, "   -p   Do not encrypt the password (plaintext).\n");
422     fprintf(stderr, "   -s   Force SHA encryption of the password.\n");
423     fprintf(stderr, "   -T   DBM Type (SDBM|GDBM|DB|default).\n");
424     fprintf(stderr, "   -l   Display usernames from database on stdout.\n");
425     fprintf(stderr, "   -t   The last param is username comment.\n");
426     fprintf(stderr, "   -v   Verify the username/password.\n");
427     fprintf(stderr, "   -x   Remove the username record from database.\n");
428     exit(ERR_SYNTAX);
429
430 }
431
432
433 int main(int argc, const char *argv[])
434 {
435     apr_pool_t *pool;
436     apr_status_t rv;
437     apr_size_t l;
438     char pwi[MAX_STRING_LEN];
439     char pwc[MAX_STRING_LEN];
440     char errbuf[MAX_STRING_LEN];
441     const char *arg;
442     int  need_file = 1;
443     int  need_user = 1;
444     int  need_pwd  = 1;
445     int  need_cmnt = 0;
446     int  pwd_supplied = 0;
447     int  changed;
448     int  cmd = HTDBM_MAKE;
449     int  i;
450     int args_left = 2;
451
452     if ((rv = htdbm_init(&pool, &h)) != APR_SUCCESS) {
453         fprintf(stderr, "Unable to initialize htdbm terminating!\n");
454         apr_strerror(rv, errbuf, sizeof(errbuf));
455         exit(1);
456     }
457     /*
458      * Preliminary check to make sure they provided at least
459      * three arguments, we'll do better argument checking as 
460      * we parse the command line.
461      */
462     if (argc < 3)
463        htdbm_usage();
464     /*
465      * Go through the argument list and pick out any options.  They
466      * have to precede any other arguments.
467      */
468     for (i = 1; i < argc; i++) {
469         arg = argv[i];
470         if (*arg != '-')
471             break;
472         
473         while (*++arg != '\0') {
474             switch (*arg) {
475             case 'b':
476                 pwd_supplied = 1;
477                 need_pwd = 0;
478                 args_left++;
479                 break;
480             case 'c':
481                 h->create = 1;
482                 break;
483             case 'n':
484                 need_file = 0;
485                 cmd = HTDBM_NOFILE;
486                     args_left--;
487                 break;
488             case 'l':
489                 need_pwd = 0;
490                 need_user = 0;
491                 cmd = HTDBM_LIST;
492                 h->rdonly = 1;
493                 args_left--;
494                 break;
495             case 't':
496                 need_cmnt = 1;
497                 args_left++;
498                 break;
499             case 'T':
500                 h->type = apr_pstrdup(h->pool, ++arg);
501                 while (*arg !='\0')
502                     *++arg;
503                 *--arg; /* so incrementing this in the loop with find a null */
504                 break;
505             case 'v':
506                 h->rdonly = 1;
507                 cmd = HTDBM_VERIFY;
508                 break;
509             case 'x':
510                 need_pwd = 0;
511                 cmd = HTDBM_DELETE;
512                 break;
513             case 'm':
514                 h->alg = ALG_APMD5;
515                 break;
516             case 'p':
517                 h->alg = ALG_PLAIN;
518                 break;
519             case 's':
520                 h->alg = ALG_APSHA;
521                 break;
522 #if APR_HAVE_CRYPT_H
523             case 'd':
524                 h->alg = ALG_CRYPT;
525                 break;
526 #endif
527             default:
528                 htdbm_usage();
529                 break;
530             }
531         }
532     }
533     /*
534      * Make sure we still have exactly the right number of arguments left
535      * (the filename, the username, and possibly the password if -b was
536      * specified).
537      */
538     if ((argc - i) != args_left)
539         htdbm_usage();
540
541     if (!need_file)
542         i--;
543     else {
544         h->filename = apr_pstrdup(h->pool, argv[i]);
545             if ((rv = htdbm_open(h)) != APR_SUCCESS) {
546             fprintf(stderr, "Error opening database %s\n", argv[i]);
547             apr_strerror(rv, errbuf, sizeof(errbuf));
548             fprintf(stderr,"%s\n",errbuf);
549             exit(ERR_FILEPERM);
550         }
551     }
552     if (need_user) {
553         h->username = apr_pstrdup(pool, argv[i+1]);
554         if (htdbm_valid_username(h) != APR_SUCCESS)
555             exit(ERR_BADUSER);
556     }
557     if (pwd_supplied)
558         h->userpass = apr_pstrdup(pool, argv[i+2]);
559
560     if (need_pwd) {
561         l = sizeof(pwc);
562         if (apr_password_get("Enter password        : ", pwi, &l) != APR_SUCCESS) {
563             fprintf(stderr, "Password too long\n");
564             exit(ERR_OVERFLOW);
565         }
566         l = sizeof(pwc);
567         if (apr_password_get("Re-type password      : ", pwc, &l) != APR_SUCCESS) {
568             fprintf(stderr, "Password too long\n");
569             exit(ERR_OVERFLOW);
570         }
571         if (strcmp(pwi, pwc) != 0) {
572             fprintf(stderr, "Password verification error\n");
573             exit(ERR_PWMISMATCH);
574         }
575             
576         h->userpass = apr_pstrdup(pool,  pwi);
577     }
578     if (need_cmnt && pwd_supplied)
579         h->comment = apr_pstrdup(pool, argv[i+3]);
580     else if (need_cmnt)
581         h->comment = apr_pstrdup(pool, argv[i+2]);
582
583     switch (cmd) {
584         case HTDBM_VERIFY:
585             if ((rv = htdbm_verify(h)) != APR_SUCCESS) {
586                 if(rv == APR_ENOENT) {
587                     fprintf(stderr, "The user '%s' cold not be found in database\n", h->username);
588                     exit(ERR_BADUSER);
589                 }
590                 else {
591                     fprintf(stderr, "Password mismatch for user '%s'\n", h->username);
592                     exit(ERR_PWMISMATCH);
593                 }
594             }
595             else
596                 fprintf(stderr, "Password validated for user '%s'\n", h->username);
597             break;
598         case HTDBM_DELETE:
599             if (htdbm_del(h) != APR_SUCCESS) {
600                 fprintf(stderr, "Cannot find user '%s' in database\n", h->username);
601                 exit(ERR_BADUSER);
602             }
603             h->username = NULL;
604             changed = 1;
605             break;
606         case HTDBM_LIST:
607             htdbm_list(h);
608             break;
609         default:
610             htdbm_make(h);
611             break;
612
613     }    
614     if (need_file && !h->rdonly) {
615         if ((rv = htdbm_save(h, &changed)) != APR_SUCCESS) {
616             apr_strerror(rv, errbuf, sizeof(errbuf));
617             exit(ERR_FILEPERM);
618         }
619         fprintf(stdout, "Database %s %s.\n", h->filename, 
620                 h->create ? "created" : (changed ? "modified" : "updated"));
621     }
622     if (cmd == HTDBM_NOFILE)
623         fprintf(stderr, "%s:%s\n", h->username, h->userpass);
624     htdbm_terminate(h);
625     apr_terminate();
626     
627     return 0; /* Supress compiler warning. */
628 }