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;
144 #define HTDBM_DELETE 1
145 #define HTDBM_VERIFY 2
147 #define HTDBM_NOFILE 4
148 #define HTDBM_STDIN 5
150 static void htdbm_terminate(htdbm_t *htdbm)
154 apr_dbm_close(htdbm->dbm);
160 static void htdbm_interrupted(void)
163 fprintf(stderr, "htdbm Interrupted !\n");
164 exit(ERR_INTERRUPTED);
167 static apr_status_t htdbm_init(apr_pool_t **pool, htdbm_t **hdbm)
170 #if APR_CHARSET_EBCDIC
175 atexit(apr_terminate);
176 apr_pool_create( pool, NULL);
177 apr_signal(SIGINT, (void (*)(int)) htdbm_interrupted);
179 (*hdbm) = (htdbm_t *)apr_pcalloc(*pool, sizeof(htdbm_t));
180 (*hdbm)->pool = *pool;
182 #if APR_CHARSET_EBCDIC
183 rv = apr_xlate_open(to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, (*hdbm)->pool);
185 fprintf(stderr, "apr_xlate_open(to ASCII)->%d\n", rv);
188 rv = apr_SHA1InitEBCDIC((*hdbm)->to_ascii);
190 fprintf(stderr, "apr_SHA1InitEBCDIC()->%d\n", rv);
193 rv = apr_MD5InitEBCDIC((*hdbm)->to_ascii);
195 fprintf(stderr, "apr_MD5InitEBCDIC()->%d\n", rv);
198 #endif /*APR_CHARSET_EBCDIC*/
200 /* Set MD5 as default */
201 (*hdbm)->alg = ALG_APMD5;
202 (*hdbm)->type = "default";
206 static apr_status_t htdbm_open(htdbm_t *htdbm)
209 return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename, APR_DBM_RWCREATE,
210 APR_OS_DEFAULT, htdbm->pool);
212 return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename,
213 htdbm->rdonly ? APR_DBM_READONLY : APR_DBM_READWRITE,
214 APR_OS_DEFAULT, htdbm->pool);
217 static char * ap_getword(apr_pool_t *atrans, char **line, char stop)
219 char *pos = strrchr(*line, stop);
223 res = apr_pstrdup(atrans, *line);
224 *line += strlen(*line);
228 res = apr_pstrndup(atrans, *line, pos - *line);
236 static apr_status_t htdbm_save(htdbm_t *htdbm, int *changed)
238 apr_datum_t key, val;
240 if (!htdbm->username)
243 key.dptr = htdbm->username;
244 key.dsize = strlen(htdbm->username);
245 if (apr_dbm_exists(htdbm->dbm, key))
248 val.dsize = strlen(htdbm->userpass);
250 val.dptr = htdbm->userpass;
252 val.dptr = apr_pstrcat(htdbm->pool, htdbm->userpass, ";",
253 htdbm->comment, NULL);
254 val.dsize += (strlen(htdbm->comment) + 1);
256 return apr_dbm_store(htdbm->dbm, key, val);
259 static apr_status_t htdbm_del(htdbm_t *htdbm)
263 key.dptr = htdbm->username;
264 key.dsize = strlen(htdbm->username);
265 if (!apr_dbm_exists(htdbm->dbm, key))
268 return apr_dbm_delete(htdbm->dbm, key);
271 static apr_status_t htdbm_verify(htdbm_t *htdbm)
273 apr_datum_t key, val;
274 char pwd[MAX_STRING_LEN] = {0};
277 key.dptr = htdbm->username;
278 key.dsize = strlen(htdbm->username);
279 if (!apr_dbm_exists(htdbm->dbm, key))
281 if (apr_dbm_fetch(htdbm->dbm, key, &val) != APR_SUCCESS)
283 rec = apr_pstrndup(htdbm->pool, val.dptr, val.dsize);
284 cmnt = strchr(rec, ';');
286 strncpy(pwd, rec, cmnt - rec);
289 return apr_password_validate(htdbm->userpass, pwd);
292 static apr_status_t htdbm_list(htdbm_t *htdbm)
295 apr_datum_t key, val;
297 char kb[MAX_STRING_LEN];
300 rv = apr_dbm_firstkey(htdbm->dbm, &key);
301 if (rv != APR_SUCCESS) {
302 fprintf(stderr, "Empty database -- %s\n", htdbm->filename);
305 rec = apr_pcalloc(htdbm->pool, HUGE_STRING_LEN);
307 fprintf(stderr, "Dumping records from database -- %s\n", htdbm->filename);
308 fprintf(stderr, " %-32sComment\n", "Username");
309 while (key.dptr != NULL) {
310 rv = apr_dbm_fetch(htdbm->dbm, key, &val);
311 if (rv != APR_SUCCESS) {
312 fprintf(stderr, "Failed getting data from %s\n", htdbm->filename);
315 strncpy(kb, key.dptr, key.dsize);
316 kb[key.dsize] = '\0';
317 fprintf(stderr, " %-32s", kb);
318 strncpy(rec, val.dptr, val.dsize);
319 rec[val.dsize] = '\0';
320 cmnt = strchr(rec, ';');
322 fprintf(stderr, cmnt + 1);
323 fprintf(stderr, "\n");
324 rv = apr_dbm_nextkey(htdbm->dbm, &key);
325 if (rv != APR_SUCCESS)
326 fprintf(stderr, "Failed getting NextKey\n");
330 fprintf(stderr, "Total #records : %d\n", i);
334 static void to64(char *s, unsigned long v, int n)
336 static unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */
337 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
340 *s++ = itoa64[v&0x3f];
345 static apr_status_t htdbm_make(htdbm_t *htdbm)
347 char cpw[MAX_STRING_LEN];
350 switch (htdbm->alg) {
352 /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
353 apr_sha1_base64(htdbm->userpass,strlen(htdbm->userpass),cpw);
357 (void) srand((int) time((time_t *) NULL));
358 to64(&salt[0], rand(), 8);
360 apr_md5_encode((const char *)htdbm->userpass, (const char *)salt,
364 /* XXX this len limitation is not in sync with any HTTPd len. */
365 apr_cpystrn(cpw,htdbm->userpass,sizeof(cpw));
369 (void) srand((int) time((time_t *) NULL));
370 to64(&salt[0], rand(), 8);
372 apr_cpystrn(cpw, (char *)crypt(htdbm->userpass, salt), sizeof(cpw) - 1);
373 fprintf(stderr, "CRYPT is now depriciated, use MD5 instead !\n");
378 htdbm->userpass = apr_pstrdup(htdbm->pool, cpw);
382 static apr_status_t htdbm_valid_username(htdbm_t *htdbm)
384 if (!htdbm->username || (strlen(htdbm->username) > 64) || (strlen(htdbm->username) < 1)) {
385 fprintf(stderr, "Invalid username length\n");
388 if (strchr(htdbm->username, ':')) {
389 fprintf(stderr, "Username contains invalid characters\n");
395 static void htdbm_usage(void)
399 #define CRYPT_OPTION "d"
401 #define CRYPT_OPTION ""
403 fprintf(stderr, "htdbm -- program for manipulating DBM password databases.\n\n");
404 fprintf(stderr, "Usage: htdbm [-cm"CRYPT_OPTION"pstvx] [-TDBTYPE] database username\n");
405 fprintf(stderr, " -b[cm"CRYPT_OPTION"ptsv] [-TDBTYPE] database username password\n");
406 fprintf(stderr, " -n[m"CRYPT_OPTION"pst] username\n");
407 fprintf(stderr, " -nb[m"CRYPT_OPTION"pst] username password\n");
408 fprintf(stderr, " -v[m"CRYPT_OPTION"ps] [-TDBTYPE] database username\n");
409 fprintf(stderr, " -vb[m"CRYPT_OPTION"ps] [-TDBTYPE] database username password\n");
410 fprintf(stderr, " -x[m"CRYPT_OPTION"ps] [-TDBTYPE] database username\n");
411 fprintf(stderr, " -l [-TDBTYPE] database\n");
412 fprintf(stderr, "Options:\n");
413 fprintf(stderr, " -b Use the password from the command line rather"
414 "than prompting for it.\n");
415 fprintf(stderr, " -c Create a new database.\n");
416 fprintf(stderr, " -n Don't update database; display results on stdout.\n");
417 fprintf(stderr, " -m Force MD5 encryption of the password (default).\n");
419 fprintf(stderr, " -d Force CRYPT encryption of the password (now depriciated).\n");
421 fprintf(stderr, " -p Do not encrypt the password (plaintext).\n");
422 fprintf(stderr, " -s Force SHA encryption of the password.\n");
423 fprintf(stderr, " -T DBM Type (SDBM|GDBM|DB|default).\n");
424 fprintf(stderr, " -l Display usernames from database on stdout.\n");
425 fprintf(stderr, " -t The last param is username comment.\n");
426 fprintf(stderr, " -v Verify the username/password.\n");
427 fprintf(stderr, " -x Remove the username record from database.\n");
433 int main(int argc, const char *argv[])
438 char pwi[MAX_STRING_LEN];
439 char pwc[MAX_STRING_LEN];
440 char errbuf[MAX_STRING_LEN];
446 int pwd_supplied = 0;
448 int cmd = HTDBM_MAKE;
452 if ((rv = htdbm_init(&pool, &h)) != APR_SUCCESS) {
453 fprintf(stderr, "Unable to initialize htdbm terminating!\n");
454 apr_strerror(rv, errbuf, sizeof(errbuf));
458 * Preliminary check to make sure they provided at least
459 * three arguments, we'll do better argument checking as
460 * we parse the command line.
465 * Go through the argument list and pick out any options. They
466 * have to precede any other arguments.
468 for (i = 1; i < argc; i++) {
473 while (*++arg != '\0') {
500 h->type = apr_pstrdup(h->pool, ++arg);
503 *--arg; /* so incrementing this in the loop with find a null */
534 * Make sure we still have exactly the right number of arguments left
535 * (the filename, the username, and possibly the password if -b was
538 if ((argc - i) != args_left)
544 h->filename = apr_pstrdup(h->pool, argv[i]);
545 if ((rv = htdbm_open(h)) != APR_SUCCESS) {
546 fprintf(stderr, "Error opening database %s\n", argv[i]);
547 apr_strerror(rv, errbuf, sizeof(errbuf));
548 fprintf(stderr,"%s\n",errbuf);
553 h->username = apr_pstrdup(pool, argv[i+1]);
554 if (htdbm_valid_username(h) != APR_SUCCESS)
558 h->userpass = apr_pstrdup(pool, argv[i+2]);
562 if (apr_password_get("Enter password : ", pwi, &l) != APR_SUCCESS) {
563 fprintf(stderr, "Password too long\n");
567 if (apr_password_get("Re-type password : ", pwc, &l) != APR_SUCCESS) {
568 fprintf(stderr, "Password too long\n");
571 if (strcmp(pwi, pwc) != 0) {
572 fprintf(stderr, "Password verification error\n");
573 exit(ERR_PWMISMATCH);
576 h->userpass = apr_pstrdup(pool, pwi);
578 if (need_cmnt && pwd_supplied)
579 h->comment = apr_pstrdup(pool, argv[i+3]);
581 h->comment = apr_pstrdup(pool, argv[i+2]);
585 if ((rv = htdbm_verify(h)) != APR_SUCCESS) {
586 if(rv == APR_ENOENT) {
587 fprintf(stderr, "The user '%s' cold not be found in database\n", h->username);
591 fprintf(stderr, "Password mismatch for user '%s'\n", h->username);
592 exit(ERR_PWMISMATCH);
596 fprintf(stderr, "Password validated for user '%s'\n", h->username);
599 if (htdbm_del(h) != APR_SUCCESS) {
600 fprintf(stderr, "Cannot find user '%s' in database\n", h->username);
614 if (need_file && !h->rdonly) {
615 if ((rv = htdbm_save(h, &changed)) != APR_SUCCESS) {
616 apr_strerror(rv, errbuf, sizeof(errbuf));
619 fprintf(stdout, "Database %s %s.\n", h->filename,
620 h->create ? "created" : (changed ? "modified" : "updated"));
622 if (cmd == HTDBM_NOFILE)
623 fprintf(stderr, "%s:%s\n", h->username, h->userpass);
627 return 0; /* Supress compiler warning. */