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