]> granicus.if.org Git - apache/blob - support/htdigest.c
apply Apache License, Version 2.0
[apache] / support / htdigest.c
1 /* Copyright 2000-2004 Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /******************************************************************************
17  ******************************************************************************
18  * NOTE! This program is not safe as a setuid executable!  Do not make it
19  * setuid!
20  ******************************************************************************
21  *****************************************************************************/
22 /*
23  * htdigest.c: simple program for manipulating digest passwd file for Apache
24  *
25  * by Alexei Kosut, based on htpasswd.c, by Rob McCool
26  */
27
28 #include "apr.h"
29 #include "apr_file_io.h"
30 #include "apr_md5.h"
31 #include "apr_lib.h"            /* for apr_getpass() */
32 #include "apr_general.h"
33 #include "apr_signal.h"
34 #include "apr_strings.h"        /* for apr_pstrdup() */
35
36 #define APR_WANT_STDIO
37 #define APR_WANT_STRFUNC
38 #include "apr_want.h"
39
40 #if APR_HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43 #if APR_HAVE_STDLIB_H
44 #include <stdlib.h>
45 #endif
46
47 #ifdef WIN32
48 #include <conio.h>
49 #endif
50
51
52 #if APR_CHARSET_EBCDIC
53 #define LF '\n'
54 #define CR '\r'
55 #else
56 #define LF 10
57 #define CR 13
58 #endif /* APR_CHARSET_EBCDIC */
59
60 #define MAX_STRING_LEN 256
61
62 /* DELONCLOSE is quite cool, but:
63  * we need to close the file before we can copy it.
64  * otherwise it's locked by the system ;-(
65  *
66  * XXX: Other systems affected? (Netware?, OS2?)
67  */
68 #if (defined(WIN32))
69 #define OMIT_DELONCLOSE 1
70 #endif
71
72 apr_file_t *tfp = NULL;
73 apr_pool_t *cntxt;
74 #if APR_CHARSET_EBCDIC
75 apr_xlate_t *to_ascii;
76 #endif
77
78 static void cleanup_tempfile_and_exit(int rc)
79 {
80     if (tfp) {
81 #ifdef OMIT_DELONCLOSE
82         const char *cfilename;
83         char *filename = NULL;
84
85         if (apr_file_name_get(&cfilename, tfp) == APR_SUCCESS) {
86             filename = apr_pstrdup(cntxt, cfilename);
87         }
88 #endif
89         apr_file_close(tfp);
90
91 #ifdef OMIT_DELONCLOSE
92         if (filename) {
93             apr_file_remove(filename, cntxt);
94         }
95 #endif
96     }
97
98     exit(rc);
99 }
100
101 static void getword(char *word, char *line, char stop)
102 {
103     int x = 0, y;
104
105     for (x = 0; ((line[x]) && (line[x] != stop)); x++)
106         word[x] = line[x];
107
108     word[x] = '\0';
109     if (line[x])
110         ++x;
111     y = 0;
112
113     while ((line[y++] = line[x++]));
114 }
115
116 static int get_line(char *s, int n, apr_file_t *f)
117 {
118     register int i = 0;
119     char ch;
120     apr_status_t rv = APR_EINVAL;
121
122     while (i < (n - 1) && 
123            ((rv = apr_file_getc(&ch, f)) == APR_SUCCESS) && (ch != '\n')) {
124         s[i++] = ch;
125     }
126     if (ch == '\n')
127         s[i++] = ch;
128     s[i] = '\0';
129
130     if (rv != APR_SUCCESS) 
131         return 1;
132
133     return 0;
134 }
135
136 static void putline(apr_file_t *f, char *l)
137 {
138     int x;
139
140     for (x = 0; l[x]; x++)
141         apr_file_putc(l[x], f);
142 }
143
144
145 static void add_password(const char *user, const char *realm, apr_file_t *f)
146 {
147     char *pw;
148     apr_md5_ctx_t context;
149     unsigned char digest[16];
150     char string[MAX_STRING_LEN];
151     char pwin[MAX_STRING_LEN];
152     char pwv[MAX_STRING_LEN];
153     unsigned int i;
154     apr_size_t len = sizeof(pwin);
155
156     if (apr_password_get("New password: ", pwin, &len) != APR_SUCCESS) {
157         fprintf(stderr, "password too long");
158         cleanup_tempfile_and_exit(5);
159     }
160     len = sizeof(pwin);
161     apr_password_get("Re-type new password: ", pwv, &len);
162     if (strcmp(pwin, pwv) != 0) {
163         fprintf(stderr, "They don't match, sorry.\n");
164         cleanup_tempfile_and_exit(1);
165     }
166     pw = pwin;
167     apr_file_printf(f, "%s:%s:", user, realm);
168
169     /* Do MD5 stuff */
170     sprintf(string, "%s:%s:%s", user, realm, pw);
171
172     apr_md5_init(&context);
173 #if APR_CHARSET_EBCDIC
174     apr_md5_set_xlate(&context, to_ascii);
175 #endif
176     apr_md5_update(&context, (unsigned char *) string, strlen(string));
177     apr_md5_final(digest, &context);
178
179     for (i = 0; i < 16; i++)
180         apr_file_printf(f, "%02x", digest[i]);
181
182     apr_file_printf(f, "\n");
183 }
184
185 static void usage(void)
186 {
187     fprintf(stderr, "Usage: htdigest [-c] passwordfile realm username\n");
188     fprintf(stderr, "The -c flag creates a new file.\n");
189     exit(1);
190 }
191
192 static void interrupted(void)
193 {
194     fprintf(stderr, "Interrupted.\n");
195     cleanup_tempfile_and_exit(1);
196 }
197
198 static void terminate(void)
199 {
200 #ifdef NETWARE
201     pressanykey();
202 #endif
203     apr_terminate();
204 }
205
206 int main(int argc, const char * const argv[])
207 {
208     apr_file_t *f;
209     apr_status_t rv;
210     char tn[] = "htdigest.tmp.XXXXXX";
211     char user[MAX_STRING_LEN];
212     char realm[MAX_STRING_LEN];
213     char line[MAX_STRING_LEN];
214     char l[MAX_STRING_LEN];
215     char w[MAX_STRING_LEN];
216     char x[MAX_STRING_LEN];
217     char command[MAX_STRING_LEN];
218     int found;
219    
220     apr_app_initialize(&argc, &argv, NULL);
221     atexit(terminate); 
222     apr_pool_create(&cntxt, NULL);
223
224 #if APR_CHARSET_EBCDIC
225     rv = apr_xlate_open(&to_ascii, "ISO8859-1", APR_DEFAULT_CHARSET, cntxt);
226     if (rv) {
227         fprintf(stderr, "apr_xlate_open(): %s (%d)\n",
228                 apr_strerror(rv, line, sizeof(line)), rv);
229         exit(1);
230     }
231 #endif
232     
233     apr_signal(SIGINT, (void (*)(int)) interrupted);
234     if (argc == 5) {
235         if (strcmp(argv[1], "-c"))
236             usage();
237         rv = apr_file_open(&f, argv[2], APR_WRITE | APR_CREATE, -1, cntxt);
238         if (rv != APR_SUCCESS) {
239             char errmsg[120];
240
241             fprintf(stderr, "Could not open passwd file %s for writing: %s\n",
242                     argv[2],
243                     apr_strerror(rv, errmsg, sizeof errmsg));
244             exit(1);
245         }
246         printf("Adding password for %s in realm %s.\n", argv[4], argv[3]);
247         add_password(argv[4], argv[3], f);
248         apr_file_close(f);
249         exit(0);
250     }
251     else if (argc != 4)
252         usage();
253
254     if (apr_file_mktemp(&tfp, tn,
255 #ifdef OMIT_DELONCLOSE
256     APR_CREATE | APR_READ | APR_WRITE | APR_EXCL
257 #else
258     0
259 #endif
260     , cntxt) != APR_SUCCESS) {
261         fprintf(stderr, "Could not open temp file.\n");
262         exit(1);
263     }
264
265     if (apr_file_open(&f, argv[1], APR_READ, -1, cntxt) != APR_SUCCESS) {
266         fprintf(stderr,
267                 "Could not open passwd file %s for reading.\n", argv[1]);
268         fprintf(stderr, "Use -c option to create new one.\n");
269         cleanup_tempfile_and_exit(1);
270     }
271     apr_cpystrn(user, argv[3], sizeof(user));
272     apr_cpystrn(realm, argv[2], sizeof(realm));
273
274     found = 0;
275     while (!(get_line(line, MAX_STRING_LEN, f))) {
276         if (found || (line[0] == '#') || (!line[0])) {
277             putline(tfp, line);
278             continue;
279         }
280         strcpy(l, line);
281         getword(w, l, ':');
282         getword(x, l, ':');
283         if (strcmp(user, w) || strcmp(realm, x)) {
284             putline(tfp, line);
285             continue;
286         }
287         else {
288             printf("Changing password for user %s in realm %s\n", user, realm);
289             add_password(user, realm, tfp);
290             found = 1;
291         }
292     }
293     if (!found) {
294         printf("Adding user %s in realm %s\n", user, realm);
295         add_password(user, realm, tfp);
296     }
297     apr_file_close(f);
298 #if defined(OS2) || defined(WIN32)
299     sprintf(command, "copy \"%s\" \"%s\"", tn, argv[1]);
300 #else
301     sprintf(command, "cp %s %s", tn, argv[1]);
302 #endif
303
304 #ifdef OMIT_DELONCLOSE
305     apr_file_close(tfp);
306     system(command);
307     apr_file_remove(tn, cntxt);
308 #else
309     system(command);
310     apr_file_close(tfp);
311 #endif
312
313     return 0;
314 }