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
89 #if APR_CHARSET_EBCDIC
90 #include "apr_xlate.h"
91 #endif /*APR_CHARSET_EBCDIC*/
98 #if !APR_CHARSET_EBCDIC
101 #else /*APR_CHARSET_EBCDIC*/
104 #endif /*APR_CHARSET_EBCDIC*/
106 #define MAX_STRING_LEN 256
116 #define ERR_FILEPERM 1
118 #define ERR_PWMISMATCH 3
119 #define ERR_INTERRUPTED 4
120 #define ERR_OVERFLOW 5
121 #define ERR_BADUSER 6
125 typedef struct htdbm_t htdbm_t;
130 #if APR_CHARSET_EBCDIC
131 apr_xlate_t *to_ascii;
145 #define HTDBM_DELETE 1
146 #define HTDBM_VERIFY 2
148 #define HTDBM_NOFILE 4
149 #define HTDBM_STDIN 5
151 static void htdbm_terminate(htdbm_t *htdbm)
155 apr_dbm_close(htdbm->dbm);
161 static void htdbm_interrupted(void)
164 fprintf(stderr, "htdbm Interrupted !\n");
165 exit(ERR_INTERRUPTED);
168 static apr_status_t htdbm_init(apr_pool_t **pool, htdbm_t **hdbm)
171 #if APR_CHARSET_EBCDIC
176 atexit(apr_terminate);
177 apr_pool_create( pool, NULL);
178 apr_signal(SIGINT, (void (*)(int)) htdbm_interrupted);
180 (*hdbm) = (htdbm_t *)apr_pcalloc(*pool, sizeof(htdbm_t));
181 (*hdbm)->pool = *pool;
183 #if APR_CHARSET_EBCDIC
184 rv = apr_xlate_open(to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, (*hdbm)->pool);
186 fprintf(stderr, "apr_xlate_open(to ASCII)->%d\n", rv);
189 rv = apr_SHA1InitEBCDIC((*hdbm)->to_ascii);
191 fprintf(stderr, "apr_SHA1InitEBCDIC()->%d\n", rv);
194 rv = apr_MD5InitEBCDIC((*hdbm)->to_ascii);
196 fprintf(stderr, "apr_MD5InitEBCDIC()->%d\n", rv);
199 #endif /*APR_CHARSET_EBCDIC*/
201 /* Set MD5 as default */
202 (*hdbm)->alg = ALG_APMD5;
203 (*hdbm)->type = "default";
207 static apr_status_t htdbm_open(htdbm_t *htdbm)
210 return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename, APR_DBM_RWCREATE,
211 APR_OS_DEFAULT, htdbm->pool);
213 return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename,
214 htdbm->rdonly ? APR_DBM_READONLY : APR_DBM_READWRITE,
215 APR_OS_DEFAULT, htdbm->pool);
218 static apr_status_t htdbm_save(htdbm_t *htdbm, int *changed)
220 apr_datum_t key, val;
222 if (!htdbm->username)
225 key.dptr = htdbm->username;
226 key.dsize = strlen(htdbm->username);
227 if (apr_dbm_exists(htdbm->dbm, key))
230 val.dsize = strlen(htdbm->userpass);
232 val.dptr = htdbm->userpass;
234 val.dptr = apr_pstrcat(htdbm->pool, htdbm->userpass, ";",
235 htdbm->comment, NULL);
236 val.dsize += (strlen(htdbm->comment) + 1);
238 return apr_dbm_store(htdbm->dbm, key, val);
241 static apr_status_t htdbm_del(htdbm_t *htdbm)
245 key.dptr = htdbm->username;
246 key.dsize = strlen(htdbm->username);
247 if (!apr_dbm_exists(htdbm->dbm, key))
250 return apr_dbm_delete(htdbm->dbm, key);
253 static apr_status_t htdbm_verify(htdbm_t *htdbm)
255 apr_datum_t key, val;
256 char pwd[MAX_STRING_LEN] = {0};
259 key.dptr = htdbm->username;
260 key.dsize = strlen(htdbm->username);
261 if (!apr_dbm_exists(htdbm->dbm, key))
263 if (apr_dbm_fetch(htdbm->dbm, key, &val) != APR_SUCCESS)
265 rec = apr_pstrndup(htdbm->pool, val.dptr, val.dsize);
266 cmnt = strchr(rec, ';');
268 strncpy(pwd, rec, cmnt - rec);
271 return apr_password_validate(htdbm->userpass, pwd);
274 static apr_status_t htdbm_list(htdbm_t *htdbm)
277 apr_datum_t key, val;
279 char kb[MAX_STRING_LEN];
282 rv = apr_dbm_firstkey(htdbm->dbm, &key);
283 if (rv != APR_SUCCESS) {
284 fprintf(stderr, "Empty database -- %s\n", htdbm->filename);
287 rec = apr_pcalloc(htdbm->pool, HUGE_STRING_LEN);
289 fprintf(stderr, "Dumping records from database -- %s\n", htdbm->filename);
290 fprintf(stderr, " %-32sComment\n", "Username");
291 while (key.dptr != NULL) {
292 rv = apr_dbm_fetch(htdbm->dbm, key, &val);
293 if (rv != APR_SUCCESS) {
294 fprintf(stderr, "Failed getting data from %s\n", htdbm->filename);
297 strncpy(kb, key.dptr, key.dsize);
298 kb[key.dsize] = '\0';
299 fprintf(stderr, " %-32s", kb);
300 strncpy(rec, val.dptr, val.dsize);
301 rec[val.dsize] = '\0';
302 cmnt = strchr(rec, ';');
304 fprintf(stderr, cmnt + 1);
305 fprintf(stderr, "\n");
306 rv = apr_dbm_nextkey(htdbm->dbm, &key);
307 if (rv != APR_SUCCESS)
308 fprintf(stderr, "Failed getting NextKey\n");
312 fprintf(stderr, "Total #records : %d\n", i);
316 static void to64(char *s, unsigned long v, int n)
318 static unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */
319 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
322 *s++ = itoa64[v&0x3f];
327 static apr_status_t htdbm_make(htdbm_t *htdbm)
329 char cpw[MAX_STRING_LEN];
332 switch (htdbm->alg) {
334 /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
335 apr_sha1_base64(htdbm->userpass,strlen(htdbm->userpass),cpw);
339 (void) srand((int) time((time_t *) NULL));
340 to64(&salt[0], rand(), 8);
342 apr_md5_encode((const char *)htdbm->userpass, (const char *)salt,
346 /* XXX this len limitation is not in sync with any HTTPd len. */
347 apr_cpystrn(cpw,htdbm->userpass,sizeof(cpw));
351 (void) srand((int) time((time_t *) NULL));
352 to64(&salt[0], rand(), 8);
354 apr_cpystrn(cpw, (char *)crypt(htdbm->userpass, salt), sizeof(cpw) - 1);
355 fprintf(stderr, "CRYPT is now deprecated, use MD5 instead!\n");
360 htdbm->userpass = apr_pstrdup(htdbm->pool, cpw);
364 static apr_status_t htdbm_valid_username(htdbm_t *htdbm)
366 if (!htdbm->username || (strlen(htdbm->username) > 64) || (strlen(htdbm->username) < 1)) {
367 fprintf(stderr, "Invalid username length\n");
370 if (strchr(htdbm->username, ':')) {
371 fprintf(stderr, "Username contains invalid characters\n");
377 static void htdbm_usage(void)
381 #define CRYPT_OPTION "d"
383 #define CRYPT_OPTION ""
385 fprintf(stderr, "htdbm -- program for manipulating DBM password databases.\n\n");
386 fprintf(stderr, "Usage: htdbm [-cm"CRYPT_OPTION"pstvx] [-TDBTYPE] database username\n");
387 fprintf(stderr, " -b[cm"CRYPT_OPTION"ptsv] [-TDBTYPE] database username password\n");
388 fprintf(stderr, " -n[m"CRYPT_OPTION"pst] username\n");
389 fprintf(stderr, " -nb[m"CRYPT_OPTION"pst] username password\n");
390 fprintf(stderr, " -v[m"CRYPT_OPTION"ps] [-TDBTYPE] database username\n");
391 fprintf(stderr, " -vb[m"CRYPT_OPTION"ps] [-TDBTYPE] database username password\n");
392 fprintf(stderr, " -x[m"CRYPT_OPTION"ps] [-TDBTYPE] database username\n");
393 fprintf(stderr, " -l [-TDBTYPE] database\n");
394 fprintf(stderr, "Options:\n");
395 fprintf(stderr, " -b Use the password from the command line rather "
396 "than prompting for it.\n");
397 fprintf(stderr, " -c Create a new database.\n");
398 fprintf(stderr, " -n Don't update database; display results on stdout.\n");
399 fprintf(stderr, " -m Force MD5 encryption of the password (default).\n");
401 fprintf(stderr, " -d Force CRYPT encryption of the password (now deprecated).\n");
403 fprintf(stderr, " -p Do not encrypt the password (plaintext).\n");
404 fprintf(stderr, " -s Force SHA encryption of the password.\n");
405 fprintf(stderr, " -T DBM Type (SDBM|GDBM|DB|default).\n");
406 fprintf(stderr, " -l Display usernames from database on stdout.\n");
407 fprintf(stderr, " -t The last param is username comment.\n");
408 fprintf(stderr, " -v Verify the username/password.\n");
409 fprintf(stderr, " -x Remove the username record from database.\n");
415 int main(int argc, const char *argv[])
420 char pwi[MAX_STRING_LEN];
421 char pwc[MAX_STRING_LEN];
422 char errbuf[MAX_STRING_LEN];
428 int pwd_supplied = 0;
430 int cmd = HTDBM_MAKE;
434 if ((rv = htdbm_init(&pool, &h)) != APR_SUCCESS) {
435 fprintf(stderr, "Unable to initialize htdbm terminating!\n");
436 apr_strerror(rv, errbuf, sizeof(errbuf));
440 * Preliminary check to make sure they provided at least
441 * three arguments, we'll do better argument checking as
442 * we parse the command line.
447 * Go through the argument list and pick out any options. They
448 * have to precede any other arguments.
450 for (i = 1; i < argc; i++) {
455 while (*++arg != '\0') {
482 h->type = apr_pstrdup(h->pool, ++arg);
485 --arg; /* so incrementing this in the loop with find a null */
516 * Make sure we still have exactly the right number of arguments left
517 * (the filename, the username, and possibly the password if -b was
520 if ((argc - i) != args_left)
526 h->filename = apr_pstrdup(h->pool, argv[i]);
527 if ((rv = htdbm_open(h)) != APR_SUCCESS) {
528 fprintf(stderr, "Error opening database %s\n", argv[i]);
529 apr_strerror(rv, errbuf, sizeof(errbuf));
530 fprintf(stderr,"%s\n",errbuf);
535 h->username = apr_pstrdup(pool, argv[i+1]);
536 if (htdbm_valid_username(h) != APR_SUCCESS)
540 h->userpass = apr_pstrdup(pool, argv[i+2]);
544 if (apr_password_get("Enter password : ", pwi, &l) != APR_SUCCESS) {
545 fprintf(stderr, "Password too long\n");
549 if (apr_password_get("Re-type password : ", pwc, &l) != APR_SUCCESS) {
550 fprintf(stderr, "Password too long\n");
553 if (strcmp(pwi, pwc) != 0) {
554 fprintf(stderr, "Password verification error\n");
555 exit(ERR_PWMISMATCH);
558 h->userpass = apr_pstrdup(pool, pwi);
560 if (need_cmnt && pwd_supplied)
561 h->comment = apr_pstrdup(pool, argv[i+3]);
563 h->comment = apr_pstrdup(pool, argv[i+2]);
567 if ((rv = htdbm_verify(h)) != APR_SUCCESS) {
568 if(rv == APR_ENOENT) {
569 fprintf(stderr, "The user '%s' could not be found in database\n", h->username);
573 fprintf(stderr, "Password mismatch for user '%s'\n", h->username);
574 exit(ERR_PWMISMATCH);
578 fprintf(stderr, "Password validated for user '%s'\n", h->username);
581 if (htdbm_del(h) != APR_SUCCESS) {
582 fprintf(stderr, "Cannot find user '%s' in database\n", h->username);
596 if (need_file && !h->rdonly) {
597 if ((rv = htdbm_save(h, &changed)) != APR_SUCCESS) {
598 apr_strerror(rv, errbuf, sizeof(errbuf));
601 fprintf(stdout, "Database %s %s.\n", h->filename,
602 h->create ? "created" : (changed ? "modified" : "updated"));
604 if (cmd == HTDBM_NOFILE)
605 fprintf(stderr, "%s:%s\n", h->username, h->userpass);
609 return 0; /* Suppress compiler warning. */