]> granicus.if.org Git - apache/blob - support/htdigest.c
tab vs space
[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     int i = 0;
96     char ch;
97     apr_status_t rv = APR_EINVAL;
98
99     /* we need 2 remaining bytes in buffer */
100     while (i < (n - 2) &&
101            ((rv = apr_file_getc(&ch, f)) == APR_SUCCESS) && (ch != '\n')) {
102         s[i++] = ch;
103     }
104     /* First remaining byte potentially used here */
105     if (ch == '\n')
106         s[i++] = ch;
107     /* Second remaining byte used here */
108     s[i] = '\0';
109
110     if (rv != APR_SUCCESS)
111         return 1;
112
113     return 0;
114 }
115
116 static void putline(apr_file_t *f, char *l)
117 {
118     int x;
119
120     for (x = 0; l[x]; x++)
121         apr_file_putc(l[x], f);
122 }
123
124
125 static void add_password(const char *user, const char *realm, apr_file_t *f)
126 {
127     char *pw;
128     apr_md5_ctx_t context;
129     unsigned char digest[16];
130     char string[3 * MAX_STRING_LEN]; /* this includes room for 2 * ':' + '\0' */
131     char pwin[MAX_STRING_LEN];
132     char pwv[MAX_STRING_LEN];
133     unsigned int i;
134     apr_size_t len = sizeof(pwin);
135
136     if (apr_password_get("New password: ", pwin, &len) != APR_SUCCESS) {
137         apr_file_printf(errfile, "password too long");
138         cleanup_tempfile_and_exit(5);
139     }
140     len = sizeof(pwin);
141     apr_password_get("Re-type new password: ", pwv, &len);
142     if (strcmp(pwin, pwv) != 0) {
143         apr_file_printf(errfile, "They don't match, sorry.\n");
144         cleanup_tempfile_and_exit(1);
145     }
146     pw = pwin;
147     apr_file_printf(f, "%s:%s:", user, realm);
148
149     /* Do MD5 stuff */
150     apr_snprintf(string, sizeof(string), "%s:%s:%s", user, realm, pw);
151
152     apr_md5_init(&context);
153 #if APR_CHARSET_EBCDIC
154     apr_md5_set_xlate(&context, to_ascii);
155 #endif
156     apr_md5_update(&context, (unsigned char *) string, strlen(string));
157     apr_md5_final(digest, &context);
158
159     for (i = 0; i < 16; i++)
160         apr_file_printf(f, "%02x", digest[i]);
161
162     apr_file_printf(f, "\n");
163 }
164
165 static void usage(void)
166 {
167     apr_file_printf(errfile, "Usage: htdigest [-c] passwordfile realm username\n");
168     apr_file_printf(errfile, "The -c flag creates a new file.\n");
169     exit(1);
170 }
171
172 static void interrupted(void)
173 {
174     apr_file_printf(errfile, "Interrupted.\n");
175     cleanup_tempfile_and_exit(1);
176 }
177
178 static void terminate(void)
179 {
180     apr_terminate();
181 #ifdef NETWARE
182     pressanykey();
183 #endif
184 }
185
186 int main(int argc, const char * const argv[])
187 {
188     apr_file_t *f;
189     apr_status_t rv;
190     char tn[] = "htdigest.tmp.XXXXXX";
191     char *dirname;
192     char user[MAX_STRING_LEN];
193     char realm[MAX_STRING_LEN];
194     char line[3 * MAX_STRING_LEN];
195     char l[3 * MAX_STRING_LEN];
196     char w[MAX_STRING_LEN];
197     char x[MAX_STRING_LEN];
198     int found;
199
200     apr_app_initialize(&argc, &argv, NULL);
201     atexit(terminate);
202     apr_pool_create(&cntxt, NULL);
203     apr_file_open_stderr(&errfile, cntxt);
204
205 #if APR_CHARSET_EBCDIC
206     rv = apr_xlate_open(&to_ascii, "ISO-8859-1", APR_DEFAULT_CHARSET, cntxt);
207     if (rv) {
208         apr_file_printf(errfile, "apr_xlate_open(): %pm (%d)\n",
209                 &rv, rv);
210         exit(1);
211     }
212 #endif
213
214     apr_signal(SIGINT, (void (*)(int)) interrupted);
215     if (argc == 5) {
216         if (strcmp(argv[1], "-c"))
217             usage();
218         rv = apr_file_open(&f, argv[2], APR_WRITE | APR_CREATE,
219                            APR_OS_DEFAULT, cntxt);
220         if (rv != APR_SUCCESS) {
221             apr_file_printf(errfile, "Could not open passwd file %s for writing: %pm\n",
222                     argv[2], &rv);
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, sizeof(line), 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 }