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