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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "passwd_common.h"
18 #include "apr_strings.h"
19 #include "apr_errno.h"
53 int abort_on_oom(int rc)
55 const char *buf = "Error: out of memory\n";
56 int written, count = strlen(buf);
58 written = write(STDERR_FILENO, buf, count);
65 } while (written >= 0 || errno == EINTR);
71 static int generate_salt(char *s, size_t size, const char **errstr,
74 unsigned char rnd[32];
75 static const char itoa64[] =
76 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
78 unsigned int val = 0, bits = 0;
82 if (n > sizeof(rnd)) {
83 apr_file_printf(errfile, "generate_salt(): BUG: Buffer too small");
86 rv = apr_generate_random_bytes(rnd, n);
88 *errstr = apr_psprintf(pool, "Unable to generate random bytes: %pm",
95 val |= (rnd[n++] << bits);
98 *s++ = itoa64[val & 0x3f];
107 void putline(apr_file_t *f, const char *l)
112 rv = apr_file_puts(l, f);
113 if (rv != APR_SUCCESS) {
114 apr_file_printf(errfile, "Error writing temp file: %pm", &rv);
120 int get_password(struct passwd_ctx *ctx)
122 char buf[MAX_STRING_LEN + 1];
123 if (ctx->passwd_src == PW_STDIN) {
124 apr_file_t *file_stdin;
126 if (apr_file_open_stdin(&file_stdin, ctx->pool) != APR_SUCCESS) {
127 ctx->errstr = "Unable to read from stdin.";
130 if (apr_file_read_full(file_stdin, buf, sizeof(buf) - 1,
132 || nread == sizeof(buf) - 1) {
136 if (nread >= 1 && buf[nread-1] == '\n') {
138 if (nread >= 2 && buf[nread-2] == '\r')
141 apr_file_close(file_stdin);
142 ctx->passwd = apr_pstrdup(ctx->pool, buf);
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)
148 ctx->passwd = apr_pstrdup(ctx->pool, buf);
151 apr_size_t bufsize = sizeof(buf);
152 if (apr_password_get("New password: ", buf, &bufsize) != 0)
154 ctx->passwd = apr_pstrdup(ctx->pool, buf);
155 bufsize = sizeof(buf);
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;
165 memset(buf, '\0', sizeof(buf));
169 ctx->errstr = apr_psprintf(ctx->pool,
170 "password too long (>%" APR_SIZE_T_FMT ")",
176 * Make a password record from the given information. A zero return
177 * indicates success; on failure, ctx->errstr points to the error message.
179 int mkhash(struct passwd_ctx *ctx)
185 #if CRYPT_ALGO_SUPPORTED
188 #ifdef HAVE_CRYPT_SHA2
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);
199 if (ctx->passwd == NULL) {
200 if ((ret = get_password(ctx)) != 0)
207 /* XXX out >= 28 + strlen(sha1) chars - fixed len SHA */
208 apr_sha1_base64(pw, strlen(pw), ctx->out);
212 ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
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);
224 /* XXX this len limitation is not in sync with any HTTPd len. */
225 apr_cpystrn(ctx->out, pw, ctx->out_len);
228 #if CRYPT_ALGO_SUPPORTED
230 ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool);
233 cbuf = crypt(pw, salt);
235 rv = APR_FROM_OS_ERROR(errno);
236 ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv);
237 ret = ERR_PWMISMATCH;
241 apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1);
242 if (strlen(pw) > 8) {
243 char *truncpw = apr_pstrdup(ctx->pool, pw);
245 if (!strcmp(ctx->out, crypt(truncpw, salt))) {
246 apr_file_printf(errfile, "Warning: Password truncated to 8 "
247 "characters by CRYPT algorithm." NL);
249 memset(truncpw, '\0', strlen(pw));
252 #endif /* CRYPT_ALGO_SUPPORTED */
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);
261 method = ctx->alg == ALG_CRYPT_SHA256 ? '5': '6';
264 setting = apr_psprintf(ctx->pool, "$%c$rounds=%d$%s",
265 method, ctx->cost, salt);
267 setting = apr_psprintf(ctx->pool, "$%c$%s",
270 cbuf = crypt(pw, setting);
272 rv = APR_FROM_OS_ERROR(errno);
273 ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv);
274 ret = ERR_PWMISMATCH;
278 apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1);
280 #endif /* HAVE_CRYPT_SHA2 */
282 #if BCRYPT_ALGO_SUPPORTED
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 "
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 "
299 ret = ERR_PWMISMATCH;
303 #endif /* BCRYPT_ALGO_SUPPORTED */
306 apr_file_printf(errfile, "mkhash(): BUG: invalid algorithm %d",
310 memset(pw, '\0', strlen(pw));
314 int parse_common_options(struct passwd_ctx *ctx, char opt,
319 ctx->passwd_src = PW_ARG;
322 ctx->passwd_src = PW_STDIN;
325 ctx->alg = ALG_APMD5;
328 ctx->alg = ALG_APSHA;
330 #ifdef HAVE_CRYPT_SHA2
332 ctx->alg = ALG_CRYPT_SHA256;
335 ctx->alg = ALG_CRYPT_SHA512;
340 ctx->errstr = "SHA-2 crypt() algorithms are not supported on this platform.";
341 return ERR_ALG_NOT_SUPP;
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);
353 #if CRYPT_ALGO_SUPPORTED
354 ctx->alg = ALG_CRYPT;
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 "
360 "Automatically using MD5 format." NL);
361 ctx->alg = ALG_APMD5;
365 #if BCRYPT_ALGO_SUPPORTED
366 ctx->alg = ALG_BCRYPT;
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;
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";
385 apr_file_printf(errfile,
386 "parse_common_options(): BUG: invalid option %c",