]> granicus.if.org Git - apache/blob - support/passwd_common.c
htdbm:
[apache] / support / passwd_common.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 #include "passwd_common.h"
18 #include "apr_strings.h"
19 #include "apr_errno.h"
20
21 #if APR_HAVE_STDIO_H
22 #include <stdio.h>
23 #endif
24
25 #include "apr_md5.h"
26 #include "apr_sha1.h"
27 #include <time.h>
28
29 #if APR_HAVE_CRYPT_H
30 #include <crypt.h>
31 #endif
32 #if APR_HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 #if APR_HAVE_STRING_H
36 #include <string.h>
37 #endif
38 #if APR_HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 #ifdef WIN32
43 #include <conio.h>
44 #define unlink _unlink
45 #endif
46
47 apr_file_t *errfile;
48
49 static int generate_salt(char *s, size_t size, const char **errstr,
50                          apr_pool_t *pool)
51 {
52     unsigned char rnd[32];
53     static const char itoa64[] =
54         "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
55     apr_size_t n;
56     unsigned int val = 0, bits = 0;
57     apr_status_t rv;
58
59     n = (size * 6 + 7)/8;
60     if (n > sizeof(rnd)) {
61         apr_file_printf(errfile, "generate_salt(): BUG: Buffer too small");
62         abort();
63     }
64     rv = apr_generate_random_bytes(rnd, n);
65     if (rv) {
66         *errstr = apr_psprintf(pool, "Unable to generate random bytes: %pm",
67                                &rv);
68         return ERR_RANDOM;
69     }
70     n = 0;
71     while (size > 0) {
72         if (bits < 6) {
73             val |= (rnd[n++] << bits);
74             bits += 8;
75         }
76         *s++ = itoa64[val & 0x3f];
77         size--;
78         val >>= 6;
79         bits -= 6;
80    }
81    *s = '\0';
82    return 0;
83 }
84
85 void putline(apr_file_t *f, const char *l)
86 {
87     apr_status_t rv;
88     rv = apr_file_puts(l, f);
89     if (rv != APR_SUCCESS) {
90         apr_file_printf(errfile, "Error writing temp file: %pm", &rv);
91         apr_file_close(f);
92         exit(ERR_FILEPERM);
93     }
94 }
95
96 int get_password(struct passwd_ctx *ctx)
97 {
98     if (ctx->passwd_src == PW_STDIN) {
99         char *buf = ctx->out;
100         apr_file_t *file_stdin;
101         apr_size_t nread;
102         if (apr_file_open_stdin(&file_stdin, ctx->pool) != APR_SUCCESS) {
103             ctx->errstr = "Unable to read from stdin.";
104             return ERR_GENERAL;
105         }
106         if (apr_file_read_full(file_stdin, buf, ctx->out_len - 1,
107                                &nread) != APR_EOF
108             || nread == ctx->out_len - 1) {
109             goto err_too_long;
110         }
111         buf[nread] = '\0';
112         if (nread >= 1 && buf[nread-1] == '\n') {
113             buf[nread-1] = '\0';
114             if (nread >= 2 && buf[nread-2] == '\r')
115                 buf[nread-2] = '\0';
116         }
117         apr_file_close(file_stdin);
118     }
119     else {
120         char buf[MAX_STRING_LEN + 1];
121         apr_size_t bufsize = sizeof(buf);
122         if (apr_password_get("New password: ", ctx->out, &ctx->out_len) != 0)
123             goto err_too_long;
124         apr_password_get("Re-type new password: ", buf, &bufsize);
125         if (strcmp(ctx->out, buf) != 0) {
126             ctx->errstr = "password verification error";
127             memset(ctx->out, '\0', ctx->out_len);
128             memset(buf, '\0', sizeof(buf));
129             return ERR_PWMISMATCH;
130         }
131         memset(buf, '\0', sizeof(buf));
132     }
133     return 0;
134
135 err_too_long:
136     ctx->errstr = apr_psprintf(ctx->pool,
137                                "password too long (>%" APR_SIZE_T_FMT ")",
138                                ctx->out_len - 1);
139     return ERR_OVERFLOW;
140 }
141
142 /*
143  * Make a password record from the given information.  A zero return
144  * indicates success; on failure, ctx->errstr points to the error message.
145  */
146 int mkhash(struct passwd_ctx *ctx)
147 {
148     char *pw;
149     char pwin[MAX_STRING_LEN];
150     char salt[16];
151     apr_status_t rv;
152     int ret = 0;
153 #if CRYPT_ALGO_SUPPORTED
154     char *cbuf;
155 #endif
156
157     if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT) {
158         apr_file_printf(errfile,
159                         "Warning: Ignoring -C argument for this algorithm." NL);
160     }
161
162     if (ctx->passwd != NULL) {
163         pw = ctx->passwd;
164     }
165     else {
166         if ((ret = get_password(ctx)) != 0)
167             return ret;
168         pw = pwin;
169     }
170
171     switch (ctx->alg) {
172     case ALG_APSHA:
173         /* XXX out >= 28 + strlen(sha1) chars - fixed len SHA */
174         apr_sha1_base64(pw, strlen(pw), ctx->out);
175         break;
176
177     case ALG_APMD5:
178         ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
179         if (ret != 0)
180             break;
181         rv = apr_md5_encode(pw, salt, ctx->out, ctx->out_len);
182         if (rv != APR_SUCCESS) {
183             ctx->errstr = apr_psprintf(ctx->pool,
184                                        "could not encode password: %pm", &rv);
185             ret = ERR_GENERAL;
186         }
187         break;
188
189     case ALG_PLAIN:
190         /* XXX this len limitation is not in sync with any HTTPd len. */
191         apr_cpystrn(ctx->out, pw, ctx->out_len);
192         break;
193
194 #if CRYPT_ALGO_SUPPORTED
195     case ALG_CRYPT:
196         ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
197         if (ret != 0)
198             break;
199         cbuf = crypt(pw, salt);
200         if (cbuf == NULL) {
201             rv = APR_FROM_OS_ERROR(errno);
202             ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv);
203             ret = ERR_PWMISMATCH;
204             break;
205         }
206
207         apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1);
208         if (strlen(pw) > 8) {
209             char *truncpw = strdup(pw);
210             truncpw[8] = '\0';
211             if (!strcmp(ctx->out, crypt(truncpw, salt))) {
212                 apr_file_printf(errfile, "Warning: Password truncated to 8 "
213                                 "characters by CRYPT algorithm." NL);
214             }
215             memset(truncpw, '\0', strlen(pw));
216             free(truncpw);
217         }
218         break;
219 #endif /* CRYPT_ALGO_SUPPORTED */
220
221 #if BCRYPT_ALGO_SUPPORTED
222     case ALG_BCRYPT:
223         rv = apr_generate_random_bytes((unsigned char*)salt, 16);
224         if (rv != APR_SUCCESS) {
225             ctx->errstr = apr_psprintf(ctx->pool, "Unable to generate random "
226                                        "bytes: %pm", &rv);
227             ret = ERR_RANDOM;
228             break;
229         }
230
231         if (ctx->cost == 0)
232             ctx->cost = BCRYPT_DEFAULT_COST;
233         rv = apr_bcrypt_encode(pw, ctx->cost, (unsigned char*)salt, 16,
234                                ctx->out, ctx->out_len);
235         if (rv != APR_SUCCESS) {
236             ctx->errstr = apr_psprintf(ctx->pool, "Unable to encode with "
237                                        "bcrypt: %pm", &rv);
238             ret = ERR_PWMISMATCH;
239             break;
240         }
241         break;
242 #endif /* BCRYPT_ALGO_SUPPORTED */
243
244     default:
245         apr_file_printf(errfile, "mkhash(): BUG: invalid algorithm %d",
246                         ctx->alg);
247         abort();
248     }
249     memset(pw, '\0', strlen(pw));
250     return ret;
251 }
252
253 int parse_common_options(struct passwd_ctx *ctx, char opt,
254                           const char *opt_arg)
255 {
256     switch (opt) {
257     case 'b':
258         ctx->passwd_src = PW_ARG;
259         break;
260     case 'i':
261         ctx->passwd_src = PW_STDIN;
262         break;
263     case 'm':
264         ctx->alg = ALG_APMD5;
265         break;
266     case 's':
267         ctx->alg = ALG_APSHA;
268         break;
269     case 'p':
270         ctx->alg = ALG_PLAIN;
271 #if !PLAIN_ALGO_SUPPORTED
272         /* Backward compatible behavior: Just print a warning */
273         apr_file_printf(errfile,
274                         "Warning: storing passwords as plain text might just "
275                         "not work on this platform." NL);
276 #endif
277         break;
278     case 'd':
279 #if CRYPT_ALGO_SUPPORTED
280         ctx->alg = ALG_CRYPT;
281 #else
282         /* Backward compatible behavior: Use MD5. OK since MD5 is more secure */
283         apr_file_printf(errfile,
284                         "Warning: CRYPT algorithm not supported on this "
285                         "platform." NL
286                         "Automatically using MD5 format." NL);
287         ctx->alg = ALG_APMD5;
288 #endif
289         break;
290     case 'B':
291 #if BCRYPT_ALGO_SUPPORTED
292         ctx->alg = ALG_BCRYPT;
293 #else
294         /* Don't fall back to something less secure */
295         ctx->errstr = "BCRYPT algorithm not supported on this platform";
296         return ERR_ALG_NOT_SUPP;
297 #endif
298         break;
299     case 'C': {
300             char *endptr;
301             long num = strtol(opt_arg, &endptr, 10);
302             if (*endptr != '\0' || num <= 0) {
303                 ctx->errstr = "argument to -C must be a positive integer";
304                 return ERR_SYNTAX;
305             }
306             ctx->cost = num;
307             break;
308         }
309     default:
310         apr_file_printf(errfile, 
311                         "parse_common_options(): BUG: invalid option %c",
312                         opt);
313         abort();
314     }
315     return 0;
316 }