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