]> granicus.if.org Git - apache/blob - support/passwd_common.c
Add CHANGES' security entries for 2.4.27.
[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                                ctx->out_len - 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
189     if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT) {
190         apr_file_printf(errfile,
191                         "Warning: Ignoring -C argument for this algorithm." NL);
192     }
193
194     if (ctx->passwd == NULL) {
195         if ((ret = get_password(ctx)) != 0)
196             return ret;
197     }
198     pw = ctx->passwd;
199
200     switch (ctx->alg) {
201     case ALG_APSHA:
202         /* XXX out >= 28 + strlen(sha1) chars - fixed len SHA */
203         apr_sha1_base64(pw, strlen(pw), ctx->out);
204         break;
205
206     case ALG_APMD5:
207         ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
208         if (ret != 0)
209             break;
210         rv = apr_md5_encode(pw, salt, ctx->out, ctx->out_len);
211         if (rv != APR_SUCCESS) {
212             ctx->errstr = apr_psprintf(ctx->pool,
213                                        "could not encode password: %pm", &rv);
214             ret = ERR_GENERAL;
215         }
216         break;
217
218     case ALG_PLAIN:
219         /* XXX this len limitation is not in sync with any HTTPd len. */
220         apr_cpystrn(ctx->out, pw, ctx->out_len);
221         break;
222
223 #if CRYPT_ALGO_SUPPORTED
224     case ALG_CRYPT:
225         ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
226         if (ret != 0)
227             break;
228         cbuf = crypt(pw, salt);
229         if (cbuf == NULL) {
230             rv = APR_FROM_OS_ERROR(errno);
231             ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv);
232             ret = ERR_PWMISMATCH;
233             break;
234         }
235
236         apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1);
237         if (strlen(pw) > 8) {
238             char *truncpw = apr_pstrdup(ctx->pool, pw);
239             truncpw[8] = '\0';
240             if (!strcmp(ctx->out, crypt(truncpw, salt))) {
241                 apr_file_printf(errfile, "Warning: Password truncated to 8 "
242                                 "characters by CRYPT algorithm." NL);
243             }
244             memset(truncpw, '\0', strlen(pw));
245         }
246         break;
247 #endif /* CRYPT_ALGO_SUPPORTED */
248
249 #if BCRYPT_ALGO_SUPPORTED
250     case ALG_BCRYPT:
251         rv = apr_generate_random_bytes((unsigned char*)salt, 16);
252         if (rv != APR_SUCCESS) {
253             ctx->errstr = apr_psprintf(ctx->pool, "Unable to generate random "
254                                        "bytes: %pm", &rv);
255             ret = ERR_RANDOM;
256             break;
257         }
258
259         if (ctx->cost == 0)
260             ctx->cost = BCRYPT_DEFAULT_COST;
261         rv = apr_bcrypt_encode(pw, ctx->cost, (unsigned char*)salt, 16,
262                                ctx->out, ctx->out_len);
263         if (rv != APR_SUCCESS) {
264             ctx->errstr = apr_psprintf(ctx->pool, "Unable to encode with "
265                                        "bcrypt: %pm", &rv);
266             ret = ERR_PWMISMATCH;
267             break;
268         }
269         break;
270 #endif /* BCRYPT_ALGO_SUPPORTED */
271
272     default:
273         apr_file_printf(errfile, "mkhash(): BUG: invalid algorithm %d",
274                         ctx->alg);
275         abort();
276     }
277     memset(pw, '\0', strlen(pw));
278     return ret;
279 }
280
281 int parse_common_options(struct passwd_ctx *ctx, char opt,
282                           const char *opt_arg)
283 {
284     switch (opt) {
285     case 'b':
286         ctx->passwd_src = PW_ARG;
287         break;
288     case 'i':
289         ctx->passwd_src = PW_STDIN;
290         break;
291     case 'm':
292         ctx->alg = ALG_APMD5;
293         break;
294     case 's':
295         ctx->alg = ALG_APSHA;
296         break;
297     case 'p':
298         ctx->alg = ALG_PLAIN;
299 #if !PLAIN_ALGO_SUPPORTED
300         /* Backward compatible behavior: Just print a warning */
301         apr_file_printf(errfile,
302                         "Warning: storing passwords as plain text might just "
303                         "not work on this platform." NL);
304 #endif
305         break;
306     case 'd':
307 #if CRYPT_ALGO_SUPPORTED
308         ctx->alg = ALG_CRYPT;
309 #else
310         /* Backward compatible behavior: Use MD5. OK since MD5 is more secure */
311         apr_file_printf(errfile,
312                         "Warning: CRYPT algorithm not supported on this "
313                         "platform." NL
314                         "Automatically using MD5 format." NL);
315         ctx->alg = ALG_APMD5;
316 #endif
317         break;
318     case 'B':
319 #if BCRYPT_ALGO_SUPPORTED
320         ctx->alg = ALG_BCRYPT;
321 #else
322         /* Don't fall back to something less secure */
323         ctx->errstr = "BCRYPT algorithm not supported on this platform";
324         return ERR_ALG_NOT_SUPP;
325 #endif
326         break;
327     case 'C': {
328             char *endptr;
329             long num = strtol(opt_arg, &endptr, 10);
330             if (*endptr != '\0' || num <= 0) {
331                 ctx->errstr = "argument to -C must be a positive integer";
332                 return ERR_SYNTAX;
333             }
334             ctx->cost = num;
335             break;
336         }
337     default:
338         apr_file_printf(errfile, 
339                         "parse_common_options(): BUG: invalid option %c",
340                         opt);
341         abort();
342     }
343     return 0;
344 }