Main Page | Modules | Namespace List | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages | Examples

htdbm.c

Go to the documentation of this file.
00001 /* Copyright 2001-2005 The Apache Software Foundation or its licensors, as
00002  * applicable.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 /*
00018  * htdbm.c: simple program for manipulating DBM
00019  * password databases for the Apache HTTP server
00020  *
00021  * Contributed by Mladen Turk <mturk@mappingsoft.com>
00022  * 12 Oct 2001
00023  */
00024 
00025 #include "apr.h"
00026 #include "apr_lib.h"
00027 #include "apr_strings.h"
00028 #include "apr_file_io.h"
00029 #include "apr_file_info.h"
00030 #include "apr_pools.h"
00031 #include "apr_signal.h"
00032 #include "apr_md5.h"
00033 #include "apr_sha1.h"
00034 #include "apr_dbm.h"
00035 
00036 #if APR_HAVE_STDLIB_H
00037 #include <stdlib.h>
00038 #endif
00039 #if APR_HAVE_STRING_H
00040 #include <string.h>
00041 #endif
00042 #if APR_HAVE_STRINGS_H
00043 #include <strings.h>
00044 #endif
00045 #include <time.h>
00046 
00047 #if APR_CHARSET_EBCDIC
00048 #include "apr_xlate.h"
00049 #endif /*APR_CHARSET_EBCDIC*/
00050 
00051 #if APR_HAVE_CRYPT_H
00052 #include <crypt.h>
00053 #endif
00054 
00055 
00056 #if !APR_CHARSET_EBCDIC
00057 #define LF 10
00058 #define CR 13
00059 #else /*APR_CHARSET_EBCDIC*/
00060 #define LF '\n'
00061 #define CR '\r'
00062 #endif /*APR_CHARSET_EBCDIC*/
00063 
00064 #define MAX_STRING_LEN 256
00065 #define ALG_PLAIN 0
00066 #define ALG_APMD5 1
00067 #define ALG_APSHA 2
00068  
00069 #if APR_HAVE_CRYPT_H
00070 #define ALG_CRYPT 3
00071 #endif
00072 
00073 
00074 #define ERR_FILEPERM    1
00075 #define ERR_SYNTAX      2
00076 #define ERR_PWMISMATCH  3
00077 #define ERR_INTERRUPTED 4
00078 #define ERR_OVERFLOW    5
00079 #define ERR_BADUSER     6
00080 #define ERR_EMPTY       7
00081 
00082 
00083 typedef struct htdbm_t htdbm_t;
00084 
00085 struct htdbm_t {
00086     apr_dbm_t               *dbm;
00087     apr_pool_t              *pool;
00088 #if APR_CHARSET_EBCDIC
00089     apr_xlate_t             *to_ascii;
00090 #endif
00091     char                    *filename;
00092     char                    *username;
00093     char                    *userpass;
00094     char                    *comment;
00095     char                    *type;
00096     int                     create;
00097     int                     rdonly;
00098     int                     alg;
00099 };
00100 
00101 
00102 #define HTDBM_MAKE   0
00103 #define HTDBM_DELETE 1
00104 #define HTDBM_VERIFY 2
00105 #define HTDBM_LIST   3
00106 #define HTDBM_NOFILE 4
00107 #define HTDBM_STDIN  5
00108 
00109 static void terminate(void)
00110 {
00111     apr_terminate();
00112 #ifdef NETWARE
00113     pressanykey();
00114 #endif
00115 }
00116 
00117 static void htdbm_terminate(htdbm_t *htdbm) 
00118 {
00119     if (htdbm->dbm)
00120         apr_dbm_close(htdbm->dbm);
00121     htdbm->dbm = NULL;
00122 }
00123 
00124 static htdbm_t *h;
00125   
00126 static void htdbm_interrupted(void) 
00127 {
00128     htdbm_terminate(h);
00129     fprintf(stderr, "htdbm Interrupted !\n");
00130     exit(ERR_INTERRUPTED);
00131 }
00132 
00133 static apr_status_t htdbm_init(apr_pool_t **pool, htdbm_t **hdbm) 
00134 {
00135 
00136 #if APR_CHARSET_EBCDIC
00137     apr_status_t rv;
00138 #endif
00139 
00140     apr_pool_create( pool, NULL);
00141     apr_signal(SIGINT, (void (*)(int)) htdbm_interrupted);
00142 
00143     (*hdbm) = (htdbm_t *)apr_pcalloc(*pool, sizeof(htdbm_t));
00144     (*hdbm)->pool = *pool;
00145 
00146 #if APR_CHARSET_EBCDIC
00147     rv = apr_xlate_open(&((*hdbm)->to_ascii), "ISO8859-1", APR_DEFAULT_CHARSET, (*hdbm)->pool);
00148     if (rv) {
00149         fprintf(stderr, "apr_xlate_open(to ASCII)->%d\n", rv);
00150         return APR_EGENERAL;
00151     }
00152     rv = apr_SHA1InitEBCDIC((*hdbm)->to_ascii);
00153     if (rv) {
00154         fprintf(stderr, "apr_SHA1InitEBCDIC()->%d\n", rv);
00155         return APR_EGENERAL;
00156     }
00157     rv = apr_MD5InitEBCDIC((*hdbm)->to_ascii);
00158     if (rv) {
00159         fprintf(stderr, "apr_MD5InitEBCDIC()->%d\n", rv);
00160         return APR_EGENERAL;
00161     }
00162 #endif /*APR_CHARSET_EBCDIC*/
00163 
00164     /* Set MD5 as default */
00165     (*hdbm)->alg = ALG_APMD5;
00166     (*hdbm)->type = "default";
00167     return APR_SUCCESS;
00168 }
00169 
00170 static apr_status_t htdbm_open(htdbm_t *htdbm) 
00171 {
00172     if (htdbm->create)
00173         return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename, APR_DBM_RWCREATE, 
00174                             APR_OS_DEFAULT, htdbm->pool);
00175     else
00176         return apr_dbm_open_ex(&htdbm->dbm, htdbm->type, htdbm->filename, 
00177                             htdbm->rdonly ? APR_DBM_READONLY : APR_DBM_READWRITE, 
00178                             APR_OS_DEFAULT, htdbm->pool);
00179 }
00180 
00181 static apr_status_t htdbm_save(htdbm_t *htdbm, int *changed) 
00182 {
00183     apr_datum_t key, val;
00184 
00185     if (!htdbm->username)
00186         return APR_SUCCESS;
00187 
00188     key.dptr = htdbm->username;
00189     key.dsize = strlen(htdbm->username);
00190     if (apr_dbm_exists(htdbm->dbm, key))
00191         *changed = 1;
00192 
00193     val.dsize = strlen(htdbm->userpass);
00194     if (!htdbm->comment)
00195         val.dptr  = htdbm->userpass;
00196     else {
00197         val.dptr = apr_pstrcat(htdbm->pool, htdbm->userpass, ":",
00198                                htdbm->comment, NULL);
00199         val.dsize += (strlen(htdbm->comment) + 1);
00200     }
00201     return apr_dbm_store(htdbm->dbm, key, val);
00202 }
00203 
00204 static apr_status_t htdbm_del(htdbm_t *htdbm) 
00205 {
00206     apr_datum_t key;
00207 
00208     key.dptr = htdbm->username;
00209     key.dsize = strlen(htdbm->username);
00210     if (!apr_dbm_exists(htdbm->dbm, key))
00211         return APR_ENOENT;
00212 
00213     return apr_dbm_delete(htdbm->dbm, key);
00214 }
00215 
00216 static apr_status_t htdbm_verify(htdbm_t *htdbm) 
00217 {
00218     apr_datum_t key, val;
00219     char pwd[MAX_STRING_LEN] = {0};
00220     char *rec, *cmnt;
00221 
00222     key.dptr = htdbm->username;
00223     key.dsize = strlen(htdbm->username);
00224     if (!apr_dbm_exists(htdbm->dbm, key))
00225         return APR_ENOENT;    
00226     if (apr_dbm_fetch(htdbm->dbm, key, &val) != APR_SUCCESS)
00227         return APR_ENOENT;
00228     rec = apr_pstrndup(htdbm->pool, val.dptr, val.dsize);
00229     cmnt = strchr(rec, ';');
00230     if (cmnt)
00231         strncpy(pwd, rec, cmnt - rec);
00232     else
00233         strcpy(pwd, rec);
00234     return apr_password_validate(htdbm->userpass, pwd);
00235 }
00236 
00237 static apr_status_t htdbm_list(htdbm_t *htdbm) 
00238 {
00239     apr_status_t rv;
00240     apr_datum_t key, val;
00241     char *rec, *cmnt;
00242     char kb[MAX_STRING_LEN];
00243     int i = 0;
00244 
00245     rv = apr_dbm_firstkey(htdbm->dbm, &key);
00246     if (rv != APR_SUCCESS) {
00247         fprintf(stderr, "Empty database -- %s\n", htdbm->filename); 
00248         return APR_ENOENT;
00249     }
00250     rec = apr_pcalloc(htdbm->pool, HUGE_STRING_LEN);
00251 
00252     fprintf(stderr, "Dumping records from database -- %s\n", htdbm->filename); 
00253     fprintf(stderr, "    %-32sComment\n", "Username");    
00254     while (key.dptr != NULL) {
00255         rv = apr_dbm_fetch(htdbm->dbm, key, &val);
00256         if (rv != APR_SUCCESS) {
00257             fprintf(stderr, "Failed getting data from %s\n", htdbm->filename);
00258             return APR_EGENERAL;
00259         }
00260         strncpy(kb, key.dptr, key.dsize);
00261         kb[key.dsize] = '\0';
00262         fprintf(stderr, "    %-32s", kb);
00263         strncpy(rec, val.dptr, val.dsize);
00264         rec[val.dsize] = '\0';
00265         cmnt = strchr(rec, ':');
00266         if (cmnt)
00267             fprintf(stderr, cmnt + 1);
00268         fprintf(stderr, "\n");
00269         rv = apr_dbm_nextkey(htdbm->dbm, &key);
00270         if (rv != APR_SUCCESS)
00271             fprintf(stderr, "Failed getting NextKey\n");
00272         ++i;
00273     }
00274 
00275     fprintf(stderr, "Total #records : %d\n", i);
00276     return APR_SUCCESS;
00277 }
00278 
00279 static void to64(char *s, unsigned long v, int n)
00280 {
00281     static unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */
00282     "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
00283 
00284     while (--n >= 0) {
00285         *s++ = itoa64[v&0x3f];
00286         v >>= 6;
00287     }
00288 }
00289 
00290 static apr_status_t htdbm_make(htdbm_t *htdbm) 
00291 {
00292     char cpw[MAX_STRING_LEN];
00293     char salt[9];
00294 
00295     switch (htdbm->alg) {
00296         case ALG_APSHA:
00297             /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
00298             apr_sha1_base64(htdbm->userpass,strlen(htdbm->userpass),cpw);
00299         break;
00300 
00301         case ALG_APMD5: 
00302             (void) srand((int) time((time_t *) NULL));
00303             to64(&salt[0], rand(), 8);
00304             salt[8] = '\0';
00305             apr_md5_encode((const char *)htdbm->userpass, (const char *)salt,
00306                             cpw, sizeof(cpw));
00307         break;
00308         case ALG_PLAIN:
00309             /* XXX this len limitation is not in sync with any HTTPd len. */
00310             apr_cpystrn(cpw,htdbm->userpass,sizeof(cpw));
00311         break;
00312 #if APR_HAVE_CRYPT_H
00313         case ALG_CRYPT:
00314             (void) srand((int) time((time_t *) NULL));
00315             to64(&salt[0], rand(), 8);
00316             salt[8] = '\0';
00317             apr_cpystrn(cpw, (char *)crypt(htdbm->userpass, salt), sizeof(cpw) - 1);
00318             fprintf(stderr, "CRYPT is now deprecated, use MD5 instead!\n");
00319 #endif
00320         default:
00321         break;
00322     }
00323     htdbm->userpass = apr_pstrdup(htdbm->pool, cpw);
00324     return APR_SUCCESS;
00325 }
00326 
00327 static apr_status_t htdbm_valid_username(htdbm_t *htdbm)
00328 {
00329     if (!htdbm->username || (strlen(htdbm->username) > 64) || (strlen(htdbm->username) < 1)) {
00330         fprintf(stderr, "Invalid username length\n");
00331         return APR_EINVAL;
00332     }
00333     if (strchr(htdbm->username, ':')) {
00334         fprintf(stderr, "Username contains invalid characters\n");
00335         return APR_EINVAL;
00336     }
00337     return APR_SUCCESS;
00338 }
00339 
00340 static void htdbm_usage(void)
00341 {
00342 
00343 #if APR_HAVE_CRYPT_H
00344 #define CRYPT_OPTION "d"
00345 #else
00346 #define CRYPT_OPTION ""
00347 #endif
00348     fprintf(stderr, "htdbm -- program for manipulating DBM password databases.\n\n");
00349     fprintf(stderr, "Usage: htdbm    [-cm"CRYPT_OPTION"pstvx] [-TDBTYPE] database username\n");
00350     fprintf(stderr, "                -b[cm"CRYPT_OPTION"ptsv] [-TDBTYPE] database username password\n");
00351     fprintf(stderr, "                -n[m"CRYPT_OPTION"pst]   username\n");
00352     fprintf(stderr, "                -nb[m"CRYPT_OPTION"pst]  username password\n");
00353     fprintf(stderr, "                -v[m"CRYPT_OPTION"ps]    [-TDBTYPE] database username\n");
00354     fprintf(stderr, "                -vb[m"CRYPT_OPTION"ps]   [-TDBTYPE] database username password\n");
00355     fprintf(stderr, "                -x[m"CRYPT_OPTION"ps]    [-TDBTYPE] database username\n");
00356     fprintf(stderr, "                -l                       [-TDBTYPE] database\n");
00357     fprintf(stderr, "Options:\n");
00358     fprintf(stderr, "   -b   Use the password from the command line rather "
00359                     "than prompting for it.\n");
00360     fprintf(stderr, "   -c   Create a new database.\n");
00361     fprintf(stderr, "   -n   Don't update database; display results on stdout.\n");
00362     fprintf(stderr, "   -m   Force MD5 encryption of the password (default).\n");
00363 #if APR_HAVE_CRYPT_H
00364     fprintf(stderr, "   -d   Force CRYPT encryption of the password (now deprecated).\n");
00365 #endif
00366     fprintf(stderr, "   -p   Do not encrypt the password (plaintext).\n");
00367     fprintf(stderr, "   -s   Force SHA encryption of the password.\n");
00368     fprintf(stderr, "   -T   DBM Type (SDBM|GDBM|DB|default).\n");
00369     fprintf(stderr, "   -l   Display usernames from database on stdout.\n");
00370     fprintf(stderr, "   -t   The last param is username comment.\n");
00371     fprintf(stderr, "   -v   Verify the username/password.\n");
00372     fprintf(stderr, "   -x   Remove the username record from database.\n");
00373     exit(ERR_SYNTAX);
00374 
00375 }
00376 
00377 
00378 int main(int argc, const char * const argv[])
00379 {
00380     apr_pool_t *pool;
00381     apr_status_t rv;
00382     apr_size_t l;
00383     char pwi[MAX_STRING_LEN];
00384     char pwc[MAX_STRING_LEN];
00385     char errbuf[MAX_STRING_LEN];
00386     const char *arg;
00387     int  need_file = 1;
00388     int  need_user = 1;
00389     int  need_pwd  = 1;
00390     int  need_cmnt = 0;
00391     int  pwd_supplied = 0;
00392     int  changed;
00393     int  cmd = HTDBM_MAKE;
00394     int  i;
00395     int args_left = 2;
00396 
00397     apr_app_initialize(&argc, &argv, NULL);
00398     atexit(terminate);
00399 
00400     if ((rv = htdbm_init(&pool, &h)) != APR_SUCCESS) {
00401         fprintf(stderr, "Unable to initialize htdbm terminating!\n");
00402         apr_strerror(rv, errbuf, sizeof(errbuf));
00403         exit(1);
00404     }
00405     /*
00406      * Preliminary check to make sure they provided at least
00407      * three arguments, we'll do better argument checking as 
00408      * we parse the command line.
00409      */
00410     if (argc < 3)
00411        htdbm_usage();
00412     /*
00413      * Go through the argument list and pick out any options.  They
00414      * have to precede any other arguments.
00415      */
00416     for (i = 1; i < argc; i++) {
00417         arg = argv[i];
00418         if (*arg != '-')
00419             break;
00420         
00421         while (*++arg != '\0') {
00422             switch (*arg) {
00423             case 'b':
00424                 pwd_supplied = 1;
00425                 need_pwd = 0;
00426                 args_left++;
00427                 break;
00428             case 'c':
00429                 h->create = 1;
00430                 break;
00431             case 'n':
00432                 need_file = 0;
00433                 cmd = HTDBM_NOFILE;
00434                     args_left--;
00435                 break;
00436             case 'l':
00437                 need_pwd = 0;
00438                 need_user = 0;
00439                 cmd = HTDBM_LIST;
00440                 h->rdonly = 1;
00441                 args_left--;
00442                 break;
00443             case 't':
00444                 need_cmnt = 1;
00445                 args_left++;
00446                 break;
00447             case 'T':
00448                 h->type = apr_pstrdup(h->pool, ++arg);
00449                 while (*arg != '\0')
00450                     ++arg;
00451                 --arg; /* so incrementing this in the loop with find a null */
00452                 break;
00453             case 'v':
00454                 h->rdonly = 1;
00455                 cmd = HTDBM_VERIFY;
00456                 break;
00457             case 'x':
00458                 need_pwd = 0;
00459                 cmd = HTDBM_DELETE;
00460                 break;
00461             case 'm':
00462                 h->alg = ALG_APMD5;
00463                 break;
00464             case 'p':
00465                 h->alg = ALG_PLAIN;
00466                 break;
00467             case 's':
00468                 h->alg = ALG_APSHA;
00469                 break;
00470 #if APR_HAVE_CRYPT_H
00471             case 'd':
00472                 h->alg = ALG_CRYPT;
00473                 break;
00474 #endif
00475             default:
00476                 htdbm_usage();
00477                 break;
00478             }
00479         }
00480     }
00481     /*
00482      * Make sure we still have exactly the right number of arguments left
00483      * (the filename, the username, and possibly the password if -b was
00484      * specified).
00485      */
00486     if ((argc - i) != args_left)
00487         htdbm_usage();
00488 
00489     if (!need_file)
00490         i--;
00491     else {
00492         h->filename = apr_pstrdup(h->pool, argv[i]);
00493             if ((rv = htdbm_open(h)) != APR_SUCCESS) {
00494             fprintf(stderr, "Error opening database %s\n", argv[i]);
00495             apr_strerror(rv, errbuf, sizeof(errbuf));
00496             fprintf(stderr,"%s\n",errbuf);
00497             exit(ERR_FILEPERM);
00498         }
00499     }
00500     if (need_user) {
00501         h->username = apr_pstrdup(pool, argv[i+1]);
00502         if (htdbm_valid_username(h) != APR_SUCCESS)
00503             exit(ERR_BADUSER);
00504     }
00505     if (pwd_supplied)
00506         h->userpass = apr_pstrdup(pool, argv[i+2]);
00507 
00508     if (need_pwd) {
00509         l = sizeof(pwc);
00510         if (apr_password_get("Enter password        : ", pwi, &l) != APR_SUCCESS) {
00511             fprintf(stderr, "Password too long\n");
00512             exit(ERR_OVERFLOW);
00513         }
00514         l = sizeof(pwc);
00515         if (apr_password_get("Re-type password      : ", pwc, &l) != APR_SUCCESS) {
00516             fprintf(stderr, "Password too long\n");
00517             exit(ERR_OVERFLOW);
00518         }
00519         if (strcmp(pwi, pwc) != 0) {
00520             fprintf(stderr, "Password verification error\n");
00521             exit(ERR_PWMISMATCH);
00522         }
00523             
00524         h->userpass = apr_pstrdup(pool,  pwi);
00525     }
00526     if (need_cmnt && pwd_supplied)
00527         h->comment = apr_pstrdup(pool, argv[i+3]);
00528     else if (need_cmnt)
00529         h->comment = apr_pstrdup(pool, argv[i+2]);
00530 
00531     switch (cmd) {
00532         case HTDBM_VERIFY:
00533             if ((rv = htdbm_verify(h)) != APR_SUCCESS) {
00534                 if(rv == APR_ENOENT) {
00535                     fprintf(stderr, "The user '%s' could not be found in database\n", h->username);
00536                     exit(ERR_BADUSER);
00537                 }
00538                 else {
00539                     fprintf(stderr, "Password mismatch for user '%s'\n", h->username);
00540                     exit(ERR_PWMISMATCH);
00541                 }
00542             }
00543             else
00544                 fprintf(stderr, "Password validated for user '%s'\n", h->username);
00545             break;
00546         case HTDBM_DELETE:
00547             if (htdbm_del(h) != APR_SUCCESS) {
00548                 fprintf(stderr, "Cannot find user '%s' in database\n", h->username);
00549                 exit(ERR_BADUSER);
00550             }
00551             h->username = NULL;
00552             changed = 1;
00553             break;
00554         case HTDBM_LIST:
00555             htdbm_list(h);
00556             break;
00557         default:
00558             htdbm_make(h);
00559             break;
00560 
00561     }    
00562     if (need_file && !h->rdonly) {
00563         if ((rv = htdbm_save(h, &changed)) != APR_SUCCESS) {
00564             apr_strerror(rv, errbuf, sizeof(errbuf));
00565             exit(ERR_FILEPERM);
00566         }
00567         fprintf(stdout, "Database %s %s.\n", h->filename, 
00568                 h->create ? "created" : (changed ? "modified" : "updated"));
00569     }
00570     if (cmd == HTDBM_NOFILE) {
00571         if (!need_cmnt) {
00572             fprintf(stderr, "%s:%s\n", h->username, h->userpass);
00573         }
00574         else {
00575             fprintf(stderr, "%s:%s:%s\n", h->username, h->userpass,
00576                     h->comment);
00577         }
00578     }
00579     htdbm_terminate(h);
00580     
00581     return 0; /* Suppress compiler warning. */
00582 }