1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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
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.
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.
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.
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
47 * ====================================================================
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/>.
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.
60 * htdbm.c: simple program for manipulating DBM
61 * password databases for the Apache HTTP server
63 * Contributed by Mladen Turk <mturk@mappingsoft.com>
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"
84 #if APR_HAVE_STRINGS_H
88 #if APR_CHARSET_EBCDIC
89 #include "apr_xlate.h"
90 #endif /*APR_CHARSET_EBCDIC*/
97 #if !APR_CHARSET_EBCDIC
100 #else /*APR_CHARSET_EBCDIC*/
103 #endif /*APR_CHARSET_EBCDIC*/
105 #define MAX_STRING_LEN 256
115 #define ERR_FILEPERM 1
117 #define ERR_PWMISMATCH 3
118 #define ERR_INTERRUPTED 4
119 #define ERR_OVERFLOW 5
120 #define ERR_BADUSER 6
124 typedef struct htdbm_t htdbm_t;
129 #if APR_CHARSET_EBCDIC
130 apr_xlate_t *to_ascii;
143 #define HTDBM_DELETE 1
144 #define HTDBM_VERIFY 2
146 #define HTDBM_NOFILE 4
147 #define HTDBM_STDIN 5
149 static void htdbm_terminate(htdbm_t *htdbm)
153 apr_dbm_close(htdbm->dbm);
159 static void htdbm_interrupted(void)
162 fprintf(stderr, "htdbm Interrupted !\n");
163 exit(ERR_INTERRUPTED);
166 static apr_status_t htdbm_init(apr_pool_t **pool, htdbm_t **hdbm)
169 #if APR_CHARSET_EBCDIC
174 atexit(apr_terminate);
175 apr_pool_create( pool, NULL);
176 apr_signal(SIGINT, (void (*)(int)) htdbm_interrupted);
178 (*hdbm) = (htdbm_t *)apr_pcalloc(*pool, sizeof(htdbm_t));
179 (*hdbm)->pool = *pool;
181 #if APR_CHARSET_EBCDIC
182 rv = apr_xlate_open(to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, (*hdbm)->pool);
184 fprintf(stderr, "apr_xlate_open(to ASCII)->%d\n", rv);
187 rv = apr_SHA1InitEBCDIC((*hdbm)->to_ascii);
189 fprintf(stderr, "apr_SHA1InitEBCDIC()->%d\n", rv);
192 rv = apr_MD5InitEBCDIC((*hdbm)->to_ascii);
194 fprintf(stderr, "apr_MD5InitEBCDIC()->%d\n", rv);
197 #endif /*APR_CHARSET_EBCDIC*/
199 /* Set MD5 as default */
200 (*hdbm)->alg = ALG_APMD5;
204 static apr_status_t) htdbm_open(htdbm_t *htdbm)
207 return apr_dbm_open(&htdbm->dbm, htdbm->filename, APR_DBM_RWCREATE,
208 APR_OS_DEFAULT, htdbm->pool);
210 return apr_dbm_open(&htdbm->dbm, htdbm->filename,
211 htdbm->rdonly ? APR_DBM_READONLY : APR_DBM_READWRITE,
212 APR_OS_DEFAULT, htdbm->pool);
215 static char * ap_getword(apr_pool_t *atrans, char **line, char stop)
217 char *pos = strrchr(*line, stop);
221 res = apr_pstrdup(atrans, *line);
222 *line += strlen(*line);
226 res = apr_pstrndup(atrans, *line, pos - *line);
234 static apr_status_t htdbm_save(htdbm_t *htdbm, int *changed)
236 apr_datum_t key, val;
238 if (!htdbm->username)
241 key.dptr = htdbm->username;
242 key.dsize = strlen(htdbm->username);
243 if (apr_dbm_exists(htdbm->dbm, key))
246 val.dsize = strlen(htdbm->userpass);
248 val.dptr = htdbm->userpass;
250 val.dptr = apr_pstrcat(htdbm->pool, htdbm->userpass, ";",
251 htdbm->comment, NULL);
252 val.dsize += (strlen(htdbm->comment) + 1);
254 return apr_dbm_store(htdbm->dbm, key, val);
257 static apr_status_t htdbm_del(htdbm_t *htdbm)
261 key.dptr = htdbm->username;
262 key.dsize = strlen(htdbm->username);
263 if (!apr_dbm_exists(htdbm->dbm, key))
266 return apr_dbm_delete(htdbm->dbm, key);
269 static apr_status_t htdbm_verify(htdbm_t *htdbm)
271 apr_datum_t key, val;
272 char pwd[MAX_STRING_LEN] = {0};
275 key.dptr = htdbm->username;
276 key.dsize = strlen(htdbm->username);
277 if (!apr_dbm_exists(htdbm->dbm, key))
279 if (apr_dbm_fetch(htdbm->dbm, key, &val) != APR_SUCCESS)
281 rec = apr_pstrndup(htdbm->pool, val.dptr, val.dsize);
282 cmnt = strchr(rec, ';');
284 strncpy(pwd, rec, cmnt - rec);
287 return apr_password_validate(htdbm->userpass, pwd);
290 static apr_status_t htdbm_list(htdbm_t *htdbm)
293 apr_datum_t key, val;
295 char kb[MAX_STRING_LEN];
298 rv = apr_dbm_firstkey(htdbm->dbm, &key);
299 if (rv != APR_SUCCESS) {
300 fprintf(stderr, "Empty database -- %s\n", htdbm->filename);
303 rec = apr_pcalloc(htdbm->pool, HUGE_STRING_LEN);
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);
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, ';');
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");
328 fprintf(stderr, "Total #records : %d\n", i);
332 static void to64(char *s, unsigned long v, int n)
334 static unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */
335 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
338 *s++ = itoa64[v&0x3f];
343 static apr_status_t htdbm_make(htdbm_t *htdbm)
345 char cpw[MAX_STRING_LEN];
348 switch (htdbm->alg) {
350 /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
351 apr_sha1_base64(htdbm->userpass,strlen(htdbm->userpass),cpw);
355 (void) srand((int) time((time_t *) NULL));
356 to64(&salt[0], rand(), 8);
358 apr_md5_encode((const char *)htdbm->userpass, (const char *)salt,
362 /* XXX this len limitation is not in sync with any HTTPd len. */
363 apr_cpystrn(cpw,htdbm->userpass,sizeof(cpw));
367 (void) srand((int) time((time_t *) NULL));
368 to64(&salt[0], rand(), 8);
370 apr_cpystrn(cpw, (char *)crypt(htdbm->userpass, salt), sizeof(cpw) - 1);
371 fprintf(stderr, "CRYPT is now depriciated, use MD5 instead !\n");
376 htdbm->userpass = apr_pstrdup(htdbm->pool, cpw);
380 static apr_status_t htdbm_valid_username(htdbm_t *htdbm)
382 if (!htdbm->username || (strlen(htdbm->username) > 64) || (strlen(htdbm->username) < 1)) {
383 fprintf(stderr, "Invalid username length\n");
386 if (strchr(htdbm->username, ':')) {
387 fprintf(stderr, "Username contains invalid characters\n");
393 static void htdbm_usage(void)
397 #define CRYPT_OPTION "d"
399 #define CRYPT_OPTION ""
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");
417 fprintf(stderr, " -d Force CRYPT encryption of the password (now depriciated).\n");
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");
430 int main(int argc, const char *argv[])
435 char pwi[MAX_STRING_LEN];
436 char pwc[MAX_STRING_LEN];
437 char errbuf[MAX_STRING_LEN];
443 int pwd_supplied = 0;
445 int cmd = HTDBM_MAKE;
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));
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.
462 * Go through the argument list and pick out any options. They
463 * have to precede any other arguments.
465 for (i = 1; i < argc; i++) {
470 while (*++arg != '\0') {
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
529 if ((argc - i) != args_left)
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));
543 h->username = apr_pstrdup(pool, argv[i+1]);
544 if (htdbm_valid_username(h) != APR_SUCCESS)
548 h->userpass = apr_pstrdup(pool, argv[i+2]);
552 if (apr_password_get("Enter password : ", pwi, &l) != APR_SUCCESS) {
553 fprintf(stderr, "Password too long\n");
557 if (apr_password_get("Re-type password : ", pwc, &l) != APR_SUCCESS) {
558 fprintf(stderr, "Password too long\n");
561 if (strcmp(pwi, pwc) != 0) {
562 fprintf(stderr, "Password verification error\n");
563 exit(ERR_PWMISMATCH);
566 h->userpass = apr_pstrdup(pool, pwi);
568 if (need_cmnt && pwd_supplied)
569 h->comment = apr_pstrdup(pool, argv[i+3]);
571 h->comment = apr_pstrdup(pool, argv[i+2]);
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);
581 fprintf(stderr, "Password mismatch for user '%s'\n", h->username);
582 exit(ERR_PWMISMATCH);
586 fprintf(stderr, "Password validated for user '%s'\n", h->username);
589 if (htdbm_del(h) != APR_SUCCESS) {
590 fprintf(stderr, "Cannot find user '%s' in database\n", h->username);
604 if (need_file && !h->rdonly) {
605 if ((rv = htdbm_save(h, &changed)) != APR_SUCCESS) {
606 apr_strerror(rv, errbuf, sizeof(errbuf));
609 fprintf(stdout, "Database %s %s.\n", h->filename,
610 h->create ? "created" : (changed ? "modified" : "updated"));
612 if (cmd == HTDBM_NOFILE)
613 fprintf(stderr, "%s:%s\n", h->username, h->userpass);
617 return 0; /* Supress compiler warning. */