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