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

htdigest.c

Go to the documentation of this file.
00001 /* Copyright 1999-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  ******************************************************************************
00019  * NOTE! This program is not safe as a setuid executable!  Do not make it
00020  * setuid!
00021  ******************************************************************************
00022  *****************************************************************************/
00023 /*
00024  * htdigest.c: simple program for manipulating digest passwd file for Apache
00025  *
00026  * by Alexei Kosut, based on htpasswd.c, by Rob McCool
00027  */
00028 
00029 #include "apr.h"
00030 #include "apr_file_io.h"
00031 #include "apr_md5.h"
00032 #include "apr_lib.h"            /* for apr_getpass() */
00033 #include "apr_general.h"
00034 #include "apr_signal.h"
00035 #include "apr_strings.h"        /* for apr_pstrdup() */
00036 
00037 #define APR_WANT_STDIO
00038 #define APR_WANT_STRFUNC
00039 #include "apr_want.h"
00040 
00041 #if APR_HAVE_SYS_TYPES_H
00042 #include <sys/types.h>
00043 #endif
00044 #if APR_HAVE_STDLIB_H
00045 #include <stdlib.h>
00046 #endif
00047 
00048 #ifdef WIN32
00049 #include <conio.h>
00050 #endif
00051 
00052 
00053 #if APR_CHARSET_EBCDIC
00054 #define LF '\n'
00055 #define CR '\r'
00056 #else
00057 #define LF 10
00058 #define CR 13
00059 #endif /* APR_CHARSET_EBCDIC */
00060 
00061 #define MAX_STRING_LEN 256
00062 
00063 apr_file_t *tfp = NULL;
00064 apr_file_t *errfile;
00065 apr_pool_t *cntxt;
00066 #if APR_CHARSET_EBCDIC
00067 apr_xlate_t *to_ascii;
00068 #endif
00069 
00070 static void cleanup_tempfile_and_exit(int rc)
00071 {
00072     if (tfp) {
00073         apr_file_close(tfp);
00074     }
00075     exit(rc);
00076 }
00077 
00078 static void getword(char *word, char *line, char stop)
00079 {
00080     int x = 0, y;
00081 
00082     for (x = 0; ((line[x]) && (line[x] != stop)); x++)
00083         word[x] = line[x];
00084 
00085     word[x] = '\0';
00086     if (line[x])
00087         ++x;
00088     y = 0;
00089 
00090     while ((line[y++] = line[x++]));
00091 }
00092 
00093 static int get_line(char *s, int n, apr_file_t *f)
00094 {
00095     register int i = 0;
00096     char ch;
00097     apr_status_t rv = APR_EINVAL;
00098 
00099     while (i < (n - 1) && 
00100            ((rv = apr_file_getc(&ch, f)) == APR_SUCCESS) && (ch != '\n')) {
00101         s[i++] = ch;
00102     }
00103     if (ch == '\n')
00104         s[i++] = ch;
00105     s[i] = '\0';
00106 
00107     if (rv != APR_SUCCESS) 
00108         return 1;
00109 
00110     return 0;
00111 }
00112 
00113 static void putline(apr_file_t *f, char *l)
00114 {
00115     int x;
00116 
00117     for (x = 0; l[x]; x++)
00118         apr_file_putc(l[x], f);
00119 }
00120 
00121 
00122 static void add_password(const char *user, const char *realm, apr_file_t *f)
00123 {
00124     char *pw;
00125     apr_md5_ctx_t context;
00126     unsigned char digest[16];
00127     char string[MAX_STRING_LEN];
00128     char pwin[MAX_STRING_LEN];
00129     char pwv[MAX_STRING_LEN];
00130     unsigned int i;
00131     apr_size_t len = sizeof(pwin);
00132 
00133     if (apr_password_get("New password: ", pwin, &len) != APR_SUCCESS) {
00134         apr_file_printf(errfile, "password too long");
00135         cleanup_tempfile_and_exit(5);
00136     }
00137     len = sizeof(pwin);
00138     apr_password_get("Re-type new password: ", pwv, &len);
00139     if (strcmp(pwin, pwv) != 0) {
00140         apr_file_printf(errfile, "They don't match, sorry.\n");
00141         cleanup_tempfile_and_exit(1);
00142     }
00143     pw = pwin;
00144     apr_file_printf(f, "%s:%s:", user, realm);
00145 
00146     /* Do MD5 stuff */
00147     sprintf(string, "%s:%s:%s", user, realm, pw);
00148 
00149     apr_md5_init(&context);
00150 #if APR_CHARSET_EBCDIC
00151     apr_md5_set_xlate(&context, to_ascii);
00152 #endif
00153     apr_md5_update(&context, (unsigned char *) string, strlen(string));
00154     apr_md5_final(digest, &context);
00155 
00156     for (i = 0; i < 16; i++)
00157         apr_file_printf(f, "%02x", digest[i]);
00158 
00159     apr_file_printf(f, "\n");
00160 }
00161 
00162 static void usage(void)
00163 {
00164     apr_file_printf(errfile, "Usage: htdigest [-c] passwordfile realm username\n");
00165     apr_file_printf(errfile, "The -c flag creates a new file.\n");
00166     exit(1);
00167 }
00168 
00169 static void interrupted(void)
00170 {
00171     apr_file_printf(errfile, "Interrupted.\n");
00172     cleanup_tempfile_and_exit(1);
00173 }
00174 
00175 static void terminate(void)
00176 {
00177     apr_terminate();
00178 #ifdef NETWARE
00179     pressanykey();
00180 #endif
00181 }
00182 
00183 int main(int argc, const char * const argv[])
00184 {
00185     apr_file_t *f;
00186     apr_status_t rv;
00187     char tn[] = "htdigest.tmp.XXXXXX";
00188     char *dirname;
00189     char user[MAX_STRING_LEN];
00190     char realm[MAX_STRING_LEN];
00191     char line[MAX_STRING_LEN];
00192     char l[MAX_STRING_LEN];
00193     char w[MAX_STRING_LEN];
00194     char x[MAX_STRING_LEN];
00195     int found;
00196    
00197     apr_app_initialize(&argc, &argv, NULL);
00198     atexit(terminate); 
00199     apr_pool_create(&cntxt, NULL);
00200     apr_file_open_stderr(&errfile, cntxt);
00201 
00202 #if APR_CHARSET_EBCDIC
00203     rv = apr_xlate_open(&to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, cntxt);
00204     if (rv) {
00205         apr_file_printf(errfile, "apr_xlate_open(): %s (%d)\n",
00206                 apr_strerror(rv, line, sizeof(line)), rv);
00207         exit(1);
00208     }
00209 #endif
00210     
00211     apr_signal(SIGINT, (void (*)(int)) interrupted);
00212     if (argc == 5) {
00213         if (strcmp(argv[1], "-c"))
00214             usage();
00215         rv = apr_file_open(&f, argv[2], APR_WRITE | APR_CREATE,
00216                            APR_OS_DEFAULT, cntxt);
00217         if (rv != APR_SUCCESS) {
00218             char errmsg[120];
00219 
00220             apr_file_printf(errfile, "Could not open passwd file %s for writing: %s\n",
00221                     argv[2],
00222                     apr_strerror(rv, errmsg, sizeof errmsg));
00223             exit(1);
00224         }
00225         apr_file_printf(errfile, "Adding password for %s in realm %s.\n", 
00226                     argv[4], argv[3]);
00227         add_password(argv[4], argv[3], f);
00228         apr_file_close(f);
00229         exit(0);
00230     }
00231     else if (argc != 4)
00232         usage();
00233 
00234     if (apr_temp_dir_get((const char**)&dirname, cntxt) != APR_SUCCESS) {
00235         apr_file_printf(errfile, "%s: could not determine temp dir\n",
00236                         argv[0]);
00237         exit(1);
00238     }
00239     dirname = apr_psprintf(cntxt, "%s/%s", dirname, tn);
00240 
00241     if (apr_file_mktemp(&tfp, dirname, 0, cntxt) != APR_SUCCESS) {
00242         apr_file_printf(errfile, "Could not open temp file %s.\n", dirname);
00243         exit(1);
00244     }
00245 
00246     if (apr_file_open(&f, argv[1], APR_READ, APR_OS_DEFAULT, cntxt) != APR_SUCCESS) {
00247         apr_file_printf(errfile,
00248                 "Could not open passwd file %s for reading.\n", argv[1]);
00249         apr_file_printf(errfile, "Use -c option to create new one.\n");
00250         cleanup_tempfile_and_exit(1);
00251     }
00252     apr_cpystrn(user, argv[3], sizeof(user));
00253     apr_cpystrn(realm, argv[2], sizeof(realm));
00254 
00255     found = 0;
00256     while (!(get_line(line, MAX_STRING_LEN, f))) {
00257         if (found || (line[0] == '#') || (!line[0])) {
00258             putline(tfp, line);
00259             continue;
00260         }
00261         strcpy(l, line);
00262         getword(w, l, ':');
00263         getword(x, l, ':');
00264         if (strcmp(user, w) || strcmp(realm, x)) {
00265             putline(tfp, line);
00266             continue;
00267         }
00268         else {
00269             apr_file_printf(errfile, "Changing password for user %s in realm %s\n", 
00270                     user, realm);
00271             add_password(user, realm, tfp);
00272             found = 1;
00273         }
00274     }
00275     if (!found) {
00276         apr_file_printf(errfile, "Adding user %s in realm %s\n", user, realm);
00277         add_password(user, realm, tfp);
00278     }
00279     apr_file_close(f);
00280 
00281     /* The temporary file has all the data, just copy it to the new location.
00282      */
00283     if (apr_file_copy(dirname, argv[1], APR_FILE_SOURCE_PERMS, cntxt) !=
00284                 APR_SUCCESS) {
00285         apr_file_printf(errfile, "%s: unable to update file %s\n", 
00286                         argv[0], argv[1]);
00287     }
00288     apr_file_close(tfp);
00289 
00290     return 0;
00291 }