]> granicus.if.org Git - apache/blob - support/htdbm.c
one step closer to happiness on ebcdic boxes
[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 #include <time.h>
88
89 #if APR_CHARSET_EBCDIC
90 #include "apr_xlate.h"
91 #endif /*APR_CHARSET_EBCDIC*/
92
93 #if APR_HAVE_CRYPT_H
94 #include <crypt.h>
95 #endif
96
97
98 #if !APR_CHARSET_EBCDIC
99 #define LF 10
100 #define CR 13
101 #else /*APR_CHARSET_EBCDIC*/
102 #define LF '\n'
103 #define CR '\r'
104 #endif /*APR_CHARSET_EBCDIC*/
105
106 #define MAX_STRING_LEN 256
107 #define ALG_PLAIN 0
108 #define ALG_APMD5 1
109 #define ALG_APSHA 2
110  
111 #if APR_HAVE_CRYPT_H
112 #define ALG_CRYPT 3
113 #endif
114
115
116 #define ERR_FILEPERM    1
117 #define ERR_SYNTAX      2
118 #define ERR_PWMISMATCH  3
119 #define ERR_INTERRUPTED 4
120 #define ERR_OVERFLOW    5
121 #define ERR_BADUSER     6
122 #define ERR_EMPTY       7
123
124
125 typedef struct htdbm_t htdbm_t;
126
127 struct htdbm_t {
128     apr_dbm_t               *dbm;
129     apr_pool_t              *pool;
130 #if APR_CHARSET_EBCDIC
131     apr_xlate_t             *to_ascii;
132 #endif
133     char                    *filename;
134     char                    *username;
135     char                    *userpass;
136     char                    *comment;
137     char                    *type;
138     int                     create;
139     int                     rdonly;
140     int                     alg;
141 };
142
143
144 #define HTDBM_MAKE   0
145 #define HTDBM_DELETE 1
146 #define HTDBM_VERIFY 2
147 #define HTDBM_LIST   3
148 #define HTDBM_NOFILE 4
149 #define HTDBM_STDIN  5
150
151 static void htdbm_terminate(htdbm_t *htdbm) 
152 {
153     
154     if (htdbm->dbm)
155         apr_dbm_close(htdbm->dbm);
156     htdbm->dbm = NULL;
157 }
158
159 static htdbm_t *h;
160   
161 static void htdbm_interrupted(void) 
162 {
163     htdbm_terminate(h);
164     fprintf(stderr, "htdbm Interrupted !\n");
165     exit(ERR_INTERRUPTED);
166 }
167
168 static apr_status_t htdbm_init(apr_pool_t **pool, htdbm_t **hdbm) 
169 {
170
171 #if APR_CHARSET_EBCDIC
172     apr_status_t rv;
173 #endif
174
175     apr_initialize();
176     atexit(apr_terminate);
177     apr_pool_create( pool, NULL);
178     apr_signal(SIGINT, (void (*)(int)) htdbm_interrupted);
179
180     (*hdbm) = (htdbm_t *)apr_pcalloc(*pool, sizeof(htdbm_t));
181     (*hdbm)->pool = *pool;
182
183 #if APR_CHARSET_EBCDIC
184     rv = apr_xlate_open(&((*hdbm)->to_ascii), "ISO8859-1", APR_DEFAULT_CHARSET, (*hdbm)->pool);
185     if (rv) {
186         fprintf(stderr, "apr_xlate_open(to ASCII)->%d\n", rv);
187         return APR_EGENERAL;
188     }
189     rv = apr_SHA1InitEBCDIC((*hdbm)->to_ascii);
190     if (rv) {
191         fprintf(stderr, "apr_SHA1InitEBCDIC()->%d\n", rv);
192         return APR_EGENERAL;
193     }
194     rv = apr_MD5InitEBCDIC((*hdbm)->to_ascii);
195     if (rv) {
196         fprintf(stderr, "apr_MD5InitEBCDIC()->%d\n", rv);
197         return APR_EGENERAL;
198     }
199 #endif /*APR_CHARSET_EBCDIC*/
200
201     /* Set MD5 as default */
202     (*hdbm)->alg = ALG_APMD5;
203     (*hdbm)->type = "default";
204     return APR_SUCCESS;
205 }
206
207 static apr_status_t htdbm_open(htdbm_t *htdbm) 
208 {
209     if (htdbm->create)
210         return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename, APR_DBM_RWCREATE, 
211                             APR_OS_DEFAULT, htdbm->pool);
212     else
213         return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename, 
214                             htdbm->rdonly ? APR_DBM_READONLY : APR_DBM_READWRITE, 
215                             APR_OS_DEFAULT, htdbm->pool);
216 }
217
218 static apr_status_t htdbm_save(htdbm_t *htdbm, int *changed) 
219 {
220     apr_datum_t key, val;
221
222     if (!htdbm->username)
223         return APR_SUCCESS;
224
225     key.dptr = htdbm->username;
226     key.dsize = strlen(htdbm->username);
227     if (apr_dbm_exists(htdbm->dbm, key))
228         *changed = 1;
229
230     val.dsize = strlen(htdbm->userpass);
231     if (!htdbm->comment)
232         val.dptr  = htdbm->userpass;
233     else {
234         val.dptr = apr_pstrcat(htdbm->pool, htdbm->userpass, ";",
235                                htdbm->comment, NULL);
236         val.dsize += (strlen(htdbm->comment) + 1);
237     }
238     return apr_dbm_store(htdbm->dbm, key, val);
239 }
240
241 static apr_status_t htdbm_del(htdbm_t *htdbm) 
242 {
243     apr_datum_t key;
244
245     key.dptr = htdbm->username;
246     key.dsize = strlen(htdbm->username);
247     if (!apr_dbm_exists(htdbm->dbm, key))
248         return APR_ENOENT;
249
250     return apr_dbm_delete(htdbm->dbm, key);
251 }
252
253 static apr_status_t htdbm_verify(htdbm_t *htdbm) 
254 {
255     apr_datum_t key, val;
256     char pwd[MAX_STRING_LEN] = {0};
257     char *rec, *cmnt;
258
259     key.dptr = htdbm->username;
260     key.dsize = strlen(htdbm->username);
261     if (!apr_dbm_exists(htdbm->dbm, key))
262         return APR_ENOENT;    
263     if (apr_dbm_fetch(htdbm->dbm, key, &val) != APR_SUCCESS)
264         return APR_ENOENT;
265     rec = apr_pstrndup(htdbm->pool, val.dptr, val.dsize);
266     cmnt = strchr(rec, ';');
267     if (cmnt)
268         strncpy(pwd, rec, cmnt - rec);
269     else
270         strcpy(pwd, rec);
271     return apr_password_validate(htdbm->userpass, pwd);
272 }
273
274 static apr_status_t htdbm_list(htdbm_t *htdbm) 
275 {
276     apr_status_t rv;
277     apr_datum_t key, val;
278     char *rec, *cmnt;
279     char kb[MAX_STRING_LEN];
280     int i = 0;
281
282     rv = apr_dbm_firstkey(htdbm->dbm, &key);
283     if (rv != APR_SUCCESS) {
284         fprintf(stderr, "Empty database -- %s\n", htdbm->filename); 
285         return APR_ENOENT;
286     }
287     rec = apr_pcalloc(htdbm->pool, HUGE_STRING_LEN);
288
289     fprintf(stderr, "Dumping records from database -- %s\n", htdbm->filename); 
290     fprintf(stderr, "    %-32sComment\n", "Username");    
291     while (key.dptr != NULL) {
292         rv = apr_dbm_fetch(htdbm->dbm, key, &val);
293         if (rv != APR_SUCCESS) {
294             fprintf(stderr, "Failed getting data from %s\n", htdbm->filename);
295             return APR_EGENERAL;
296         }
297         strncpy(kb, key.dptr, key.dsize);
298         kb[key.dsize] = '\0';
299         fprintf(stderr, "    %-32s", kb);
300         strncpy(rec, val.dptr, val.dsize);
301         rec[val.dsize] = '\0';
302         cmnt = strchr(rec, ';');
303         if (cmnt)
304             fprintf(stderr, cmnt + 1);
305         fprintf(stderr, "\n");
306         rv = apr_dbm_nextkey(htdbm->dbm, &key);
307         if (rv != APR_SUCCESS)
308             fprintf(stderr, "Failed getting NextKey\n");
309         ++i;
310     }
311
312     fprintf(stderr, "Total #records : %d\n", i);
313     return APR_SUCCESS;
314 }
315
316 static void to64(char *s, unsigned long v, int n)
317 {
318     static unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */
319     "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
320
321     while (--n >= 0) {
322         *s++ = itoa64[v&0x3f];
323         v >>= 6;
324     }
325 }
326
327 static apr_status_t htdbm_make(htdbm_t *htdbm) 
328 {
329     char cpw[MAX_STRING_LEN];
330     char salt[9];
331
332     switch (htdbm->alg) {
333         case ALG_APSHA:
334             /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
335             apr_sha1_base64(htdbm->userpass,strlen(htdbm->userpass),cpw);
336         break;
337
338         case ALG_APMD5: 
339             (void) srand((int) time((time_t *) NULL));
340             to64(&salt[0], rand(), 8);
341             salt[8] = '\0';
342             apr_md5_encode((const char *)htdbm->userpass, (const char *)salt,
343                             cpw, sizeof(cpw));
344         break;
345         case ALG_PLAIN:
346             /* XXX this len limitation is not in sync with any HTTPd len. */
347             apr_cpystrn(cpw,htdbm->userpass,sizeof(cpw));
348         break;
349 #if APR_HAVE_CRYPT_H
350         case ALG_CRYPT:
351             (void) srand((int) time((time_t *) NULL));
352             to64(&salt[0], rand(), 8);
353             salt[8] = '\0';
354             apr_cpystrn(cpw, (char *)crypt(htdbm->userpass, salt), sizeof(cpw) - 1);
355             fprintf(stderr, "CRYPT is now deprecated, use MD5 instead!\n");
356 #endif
357         default:
358         break;
359     }
360     htdbm->userpass = apr_pstrdup(htdbm->pool, cpw);
361     return APR_SUCCESS;
362 }
363
364 static apr_status_t htdbm_valid_username(htdbm_t *htdbm)
365 {
366     if (!htdbm->username || (strlen(htdbm->username) > 64) || (strlen(htdbm->username) < 1)) {
367         fprintf(stderr, "Invalid username length\n");
368         return APR_EINVAL;
369     }
370     if (strchr(htdbm->username, ':')) {
371         fprintf(stderr, "Username contains invalid characters\n");
372         return APR_EINVAL;
373     }
374     return APR_SUCCESS;
375 }
376
377 static void htdbm_usage(void)
378 {
379
380 #if APR_HAVE_CRYPT_H
381 #define CRYPT_OPTION "d"
382 #else
383 #define CRYPT_OPTION ""
384 #endif
385     fprintf(stderr, "htdbm -- program for manipulating DBM password databases.\n\n");
386     fprintf(stderr, "Usage: htdbm    [-cm"CRYPT_OPTION"pstvx] [-TDBTYPE] database username\n");
387     fprintf(stderr, "                -b[cm"CRYPT_OPTION"ptsv] [-TDBTYPE] database username password\n");
388     fprintf(stderr, "                -n[m"CRYPT_OPTION"pst]   username\n");
389     fprintf(stderr, "                -nb[m"CRYPT_OPTION"pst]  username password\n");
390     fprintf(stderr, "                -v[m"CRYPT_OPTION"ps]    [-TDBTYPE] database username\n");
391     fprintf(stderr, "                -vb[m"CRYPT_OPTION"ps]   [-TDBTYPE] database username password\n");
392     fprintf(stderr, "                -x[m"CRYPT_OPTION"ps]    [-TDBTYPE] database username\n");
393     fprintf(stderr, "                -l                       [-TDBTYPE] database\n");
394     fprintf(stderr, "Options:\n");
395     fprintf(stderr, "   -b   Use the password from the command line rather "
396                     "than prompting for it.\n");
397     fprintf(stderr, "   -c   Create a new database.\n");
398     fprintf(stderr, "   -n   Don't update database; display results on stdout.\n");
399     fprintf(stderr, "   -m   Force MD5 encryption of the password (default).\n");
400 #if APR_HAVE_CRYPT_H
401     fprintf(stderr, "   -d   Force CRYPT encryption of the password (now deprecated).\n");
402 #endif
403     fprintf(stderr, "   -p   Do not encrypt the password (plaintext).\n");
404     fprintf(stderr, "   -s   Force SHA encryption of the password.\n");
405     fprintf(stderr, "   -T   DBM Type (SDBM|GDBM|DB|default).\n");
406     fprintf(stderr, "   -l   Display usernames from database on stdout.\n");
407     fprintf(stderr, "   -t   The last param is username comment.\n");
408     fprintf(stderr, "   -v   Verify the username/password.\n");
409     fprintf(stderr, "   -x   Remove the username record from database.\n");
410     exit(ERR_SYNTAX);
411
412 }
413
414
415 int main(int argc, const char *argv[])
416 {
417     apr_pool_t *pool;
418     apr_status_t rv;
419     apr_size_t l;
420     char pwi[MAX_STRING_LEN];
421     char pwc[MAX_STRING_LEN];
422     char errbuf[MAX_STRING_LEN];
423     const char *arg;
424     int  need_file = 1;
425     int  need_user = 1;
426     int  need_pwd  = 1;
427     int  need_cmnt = 0;
428     int  pwd_supplied = 0;
429     int  changed;
430     int  cmd = HTDBM_MAKE;
431     int  i;
432     int args_left = 2;
433
434     if ((rv = htdbm_init(&pool, &h)) != APR_SUCCESS) {
435         fprintf(stderr, "Unable to initialize htdbm terminating!\n");
436         apr_strerror(rv, errbuf, sizeof(errbuf));
437         exit(1);
438     }
439     /*
440      * Preliminary check to make sure they provided at least
441      * three arguments, we'll do better argument checking as 
442      * we parse the command line.
443      */
444     if (argc < 3)
445        htdbm_usage();
446     /*
447      * Go through the argument list and pick out any options.  They
448      * have to precede any other arguments.
449      */
450     for (i = 1; i < argc; i++) {
451         arg = argv[i];
452         if (*arg != '-')
453             break;
454         
455         while (*++arg != '\0') {
456             switch (*arg) {
457             case 'b':
458                 pwd_supplied = 1;
459                 need_pwd = 0;
460                 args_left++;
461                 break;
462             case 'c':
463                 h->create = 1;
464                 break;
465             case 'n':
466                 need_file = 0;
467                 cmd = HTDBM_NOFILE;
468                     args_left--;
469                 break;
470             case 'l':
471                 need_pwd = 0;
472                 need_user = 0;
473                 cmd = HTDBM_LIST;
474                 h->rdonly = 1;
475                 args_left--;
476                 break;
477             case 't':
478                 need_cmnt = 1;
479                 args_left++;
480                 break;
481             case 'T':
482                 h->type = apr_pstrdup(h->pool, ++arg);
483                 while (*arg != '\0')
484                     ++arg;
485                 --arg; /* so incrementing this in the loop with find a null */
486                 break;
487             case 'v':
488                 h->rdonly = 1;
489                 cmd = HTDBM_VERIFY;
490                 break;
491             case 'x':
492                 need_pwd = 0;
493                 cmd = HTDBM_DELETE;
494                 break;
495             case 'm':
496                 h->alg = ALG_APMD5;
497                 break;
498             case 'p':
499                 h->alg = ALG_PLAIN;
500                 break;
501             case 's':
502                 h->alg = ALG_APSHA;
503                 break;
504 #if APR_HAVE_CRYPT_H
505             case 'd':
506                 h->alg = ALG_CRYPT;
507                 break;
508 #endif
509             default:
510                 htdbm_usage();
511                 break;
512             }
513         }
514     }
515     /*
516      * Make sure we still have exactly the right number of arguments left
517      * (the filename, the username, and possibly the password if -b was
518      * specified).
519      */
520     if ((argc - i) != args_left)
521         htdbm_usage();
522
523     if (!need_file)
524         i--;
525     else {
526         h->filename = apr_pstrdup(h->pool, argv[i]);
527             if ((rv = htdbm_open(h)) != APR_SUCCESS) {
528             fprintf(stderr, "Error opening database %s\n", argv[i]);
529             apr_strerror(rv, errbuf, sizeof(errbuf));
530             fprintf(stderr,"%s\n",errbuf);
531             exit(ERR_FILEPERM);
532         }
533     }
534     if (need_user) {
535         h->username = apr_pstrdup(pool, argv[i+1]);
536         if (htdbm_valid_username(h) != APR_SUCCESS)
537             exit(ERR_BADUSER);
538     }
539     if (pwd_supplied)
540         h->userpass = apr_pstrdup(pool, argv[i+2]);
541
542     if (need_pwd) {
543         l = sizeof(pwc);
544         if (apr_password_get("Enter password        : ", pwi, &l) != APR_SUCCESS) {
545             fprintf(stderr, "Password too long\n");
546             exit(ERR_OVERFLOW);
547         }
548         l = sizeof(pwc);
549         if (apr_password_get("Re-type password      : ", pwc, &l) != APR_SUCCESS) {
550             fprintf(stderr, "Password too long\n");
551             exit(ERR_OVERFLOW);
552         }
553         if (strcmp(pwi, pwc) != 0) {
554             fprintf(stderr, "Password verification error\n");
555             exit(ERR_PWMISMATCH);
556         }
557             
558         h->userpass = apr_pstrdup(pool,  pwi);
559     }
560     if (need_cmnt && pwd_supplied)
561         h->comment = apr_pstrdup(pool, argv[i+3]);
562     else if (need_cmnt)
563         h->comment = apr_pstrdup(pool, argv[i+2]);
564
565     switch (cmd) {
566         case HTDBM_VERIFY:
567             if ((rv = htdbm_verify(h)) != APR_SUCCESS) {
568                 if(rv == APR_ENOENT) {
569                     fprintf(stderr, "The user '%s' could not be found in database\n", h->username);
570                     exit(ERR_BADUSER);
571                 }
572                 else {
573                     fprintf(stderr, "Password mismatch for user '%s'\n", h->username);
574                     exit(ERR_PWMISMATCH);
575                 }
576             }
577             else
578                 fprintf(stderr, "Password validated for user '%s'\n", h->username);
579             break;
580         case HTDBM_DELETE:
581             if (htdbm_del(h) != APR_SUCCESS) {
582                 fprintf(stderr, "Cannot find user '%s' in database\n", h->username);
583                 exit(ERR_BADUSER);
584             }
585             h->username = NULL;
586             changed = 1;
587             break;
588         case HTDBM_LIST:
589             htdbm_list(h);
590             break;
591         default:
592             htdbm_make(h);
593             break;
594
595     }    
596     if (need_file && !h->rdonly) {
597         if ((rv = htdbm_save(h, &changed)) != APR_SUCCESS) {
598             apr_strerror(rv, errbuf, sizeof(errbuf));
599             exit(ERR_FILEPERM);
600         }
601         fprintf(stdout, "Database %s %s.\n", h->filename, 
602                 h->create ? "created" : (changed ? "modified" : "updated"));
603     }
604     if (cmd == HTDBM_NOFILE)
605         fprintf(stderr, "%s:%s\n", h->username, h->userpass);
606     htdbm_terminate(h);
607     apr_terminate();
608     
609     return 0; /* Suppress compiler warning. */
610 }