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