]> granicus.if.org Git - apache/blob - support/htdigest.c
detab
[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
63 apr_file_t *tfp = NULL;
64 apr_file_t *errfile;
65 apr_pool_t *cntxt;
66 #if APR_CHARSET_EBCDIC
67 apr_xlate_t *to_ascii;
68 #endif
69
70 static void cleanup_tempfile_and_exit(int rc)
71 {
72     if (tfp) {
73         apr_file_close(tfp);
74     }
75     exit(rc);
76 }
77
78 static void getword(char *word, char *line, char stop)
79 {
80     int x = 0, y;
81
82     for (x = 0; ((line[x]) && (line[x] != stop)); x++)
83         word[x] = line[x];
84
85     word[x] = '\0';
86     if (line[x])
87         ++x;
88     y = 0;
89
90     while ((line[y++] = line[x++]));
91 }
92
93 static int get_line(char *s, int n, apr_file_t *f)
94 {
95     register int i = 0;
96     char ch;
97     apr_status_t rv = APR_EINVAL;
98
99     while (i < (n - 1) &&
100            ((rv = apr_file_getc(&ch, f)) == APR_SUCCESS) && (ch != '\n')) {
101         s[i++] = ch;
102     }
103     if (ch == '\n')
104         s[i++] = ch;
105     s[i] = '\0';
106
107     if (rv != APR_SUCCESS)
108         return 1;
109
110     return 0;
111 }
112
113 static void putline(apr_file_t *f, char *l)
114 {
115     int x;
116
117     for (x = 0; l[x]; x++)
118         apr_file_putc(l[x], f);
119 }
120
121
122 static void add_password(const char *user, const char *realm, apr_file_t *f)
123 {
124     char *pw;
125     apr_md5_ctx_t context;
126     unsigned char digest[16];
127     char string[MAX_STRING_LEN];
128     char pwin[MAX_STRING_LEN];
129     char pwv[MAX_STRING_LEN];
130     unsigned int i;
131     apr_size_t len = sizeof(pwin);
132
133     if (apr_password_get("New password: ", pwin, &len) != APR_SUCCESS) {
134         apr_file_printf(errfile, "password too long");
135         cleanup_tempfile_and_exit(5);
136     }
137     len = sizeof(pwin);
138     apr_password_get("Re-type new password: ", pwv, &len);
139     if (strcmp(pwin, pwv) != 0) {
140         apr_file_printf(errfile, "They don't match, sorry.\n");
141         cleanup_tempfile_and_exit(1);
142     }
143     pw = pwin;
144     apr_file_printf(f, "%s:%s:", user, realm);
145
146     /* Do MD5 stuff */
147     sprintf(string, "%s:%s:%s", user, realm, pw);
148
149     apr_md5_init(&context);
150 #if APR_CHARSET_EBCDIC
151     apr_md5_set_xlate(&context, to_ascii);
152 #endif
153     apr_md5_update(&context, (unsigned char *) string, strlen(string));
154     apr_md5_final(digest, &context);
155
156     for (i = 0; i < 16; i++)
157         apr_file_printf(f, "%02x", digest[i]);
158
159     apr_file_printf(f, "\n");
160 }
161
162 static void usage(void)
163 {
164     apr_file_printf(errfile, "Usage: htdigest [-c] passwordfile realm username\n");
165     apr_file_printf(errfile, "The -c flag creates a new file.\n");
166     exit(1);
167 }
168
169 static void interrupted(void)
170 {
171     apr_file_printf(errfile, "Interrupted.\n");
172     cleanup_tempfile_and_exit(1);
173 }
174
175 static void terminate(void)
176 {
177     apr_terminate();
178 #ifdef NETWARE
179     pressanykey();
180 #endif
181 }
182
183 int main(int argc, const char * const argv[])
184 {
185     apr_file_t *f;
186     apr_status_t rv;
187     char tn[] = "htdigest.tmp.XXXXXX";
188     char *dirname;
189     char user[MAX_STRING_LEN];
190     char realm[MAX_STRING_LEN];
191     char line[MAX_STRING_LEN];
192     char l[MAX_STRING_LEN];
193     char w[MAX_STRING_LEN];
194     char x[MAX_STRING_LEN];
195     int found;
196
197     apr_app_initialize(&argc, &argv, NULL);
198     atexit(terminate);
199     apr_pool_create(&cntxt, NULL);
200     apr_file_open_stderr(&errfile, cntxt);
201
202 #if APR_CHARSET_EBCDIC
203     rv = apr_xlate_open(&to_ascii, "ISO-8859-1", APR_DEFAULT_CHARSET, cntxt);
204     if (rv) {
205         apr_file_printf(errfile, "apr_xlate_open(): %s (%d)\n",
206                 apr_strerror(rv, line, sizeof(line)), rv);
207         exit(1);
208     }
209 #endif
210
211     apr_signal(SIGINT, (void (*)(int)) interrupted);
212     if (argc == 5) {
213         if (strcmp(argv[1], "-c"))
214             usage();
215         rv = apr_file_open(&f, argv[2], APR_WRITE | APR_CREATE,
216                            APR_OS_DEFAULT, cntxt);
217         if (rv != APR_SUCCESS) {
218             char errmsg[120];
219
220             apr_file_printf(errfile, "Could not open passwd file %s for writing: %s\n",
221                     argv[2],
222                     apr_strerror(rv, errmsg, sizeof errmsg));
223             exit(1);
224         }
225         apr_cpystrn(user, argv[4], sizeof(user));
226         apr_cpystrn(realm, argv[3], sizeof(realm));
227         apr_file_printf(errfile, "Adding password for %s in realm %s.\n",
228                     user, realm);
229         add_password(user, realm, f);
230         apr_file_close(f);
231         exit(0);
232     }
233     else if (argc != 4)
234         usage();
235
236     if (apr_temp_dir_get((const char**)&dirname, cntxt) != APR_SUCCESS) {
237         apr_file_printf(errfile, "%s: could not determine temp dir\n",
238                         argv[0]);
239         exit(1);
240     }
241     dirname = apr_psprintf(cntxt, "%s/%s", dirname, tn);
242
243     if (apr_file_mktemp(&tfp, dirname, 0, cntxt) != APR_SUCCESS) {
244         apr_file_printf(errfile, "Could not open temp file %s.\n", dirname);
245         exit(1);
246     }
247
248     if (apr_file_open(&f, argv[1], APR_READ, APR_OS_DEFAULT, cntxt) != APR_SUCCESS) {
249         apr_file_printf(errfile,
250                 "Could not open passwd file %s for reading.\n", argv[1]);
251         apr_file_printf(errfile, "Use -c option to create new one.\n");
252         cleanup_tempfile_and_exit(1);
253     }
254     apr_cpystrn(user, argv[3], sizeof(user));
255     apr_cpystrn(realm, argv[2], sizeof(realm));
256
257     found = 0;
258     while (!(get_line(line, MAX_STRING_LEN, f))) {
259         if (found || (line[0] == '#') || (!line[0])) {
260             putline(tfp, line);
261             continue;
262         }
263         strcpy(l, line);
264         getword(w, l, ':');
265         getword(x, l, ':');
266         if (strcmp(user, w) || strcmp(realm, x)) {
267             putline(tfp, line);
268             continue;
269         }
270         else {
271             apr_file_printf(errfile, "Changing password for user %s in realm %s\n",
272                     user, realm);
273             add_password(user, realm, tfp);
274             found = 1;
275         }
276     }
277     if (!found) {
278         apr_file_printf(errfile, "Adding user %s in realm %s\n", user, realm);
279         add_password(user, realm, tfp);
280     }
281     apr_file_close(f);
282
283     /* The temporary file has all the data, just copy it to the new location.
284      */
285     if (apr_file_copy(dirname, argv[1], APR_FILE_SOURCE_PERMS, cntxt) !=
286                 APR_SUCCESS) {
287         apr_file_printf(errfile, "%s: unable to update file %s\n",
288                         argv[0], argv[1]);
289     }
290     apr_file_close(tfp);
291
292     return 0;
293 }