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