diff options
Diffstat (limited to 'support/passwd_common.c')
-rw-r--r-- | support/passwd_common.c | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/support/passwd_common.c b/support/passwd_common.c new file mode 100644 index 0000000..664e509 --- /dev/null +++ b/support/passwd_common.c @@ -0,0 +1,344 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "passwd_common.h" +#include "apr_strings.h" +#include "apr_errno.h" + +#if APR_HAVE_STDIO_H +#include <stdio.h> +#endif + +#include "apr_md5.h" +#include "apr_sha1.h" + +#if APR_HAVE_TIME_H +#include <time.h> +#endif +#if APR_HAVE_CRYPT_H +#include <crypt.h> +#endif +#if APR_HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if APR_HAVE_STRING_H +#include <string.h> +#endif +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif +#if APR_HAVE_IO_H +#include <io.h> +#endif + +#ifdef _MSC_VER +#define write _write +#endif + +apr_file_t *errfile; + +int abort_on_oom(int rc) +{ + const char *buf = "Error: out of memory\n"; + int written, count = strlen(buf); + do { + written = write(STDERR_FILENO, buf, count); + if (written == count) + break; + if (written > 0) { + buf += written; + count -= written; + } + } while (written >= 0 || errno == EINTR); + abort(); + /* NOTREACHED */ + return 0; +} + +static int generate_salt(char *s, size_t size, const char **errstr, + apr_pool_t *pool) +{ + unsigned char rnd[32]; + static const char itoa64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + apr_size_t n; + unsigned int val = 0, bits = 0; + apr_status_t rv; + + n = (size * 6 + 7)/8; + if (n > sizeof(rnd)) { + apr_file_printf(errfile, "generate_salt(): BUG: Buffer too small"); + abort(); + } + rv = apr_generate_random_bytes(rnd, n); + if (rv) { + *errstr = apr_psprintf(pool, "Unable to generate random bytes: %pm", + &rv); + return ERR_RANDOM; + } + n = 0; + while (size > 0) { + if (bits < 6) { + val |= (rnd[n++] << bits); + bits += 8; + } + *s++ = itoa64[val & 0x3f]; + size--; + val >>= 6; + bits -= 6; + } + *s = '\0'; + return 0; +} + +void putline(apr_file_t *f, const char *l) +{ + apr_status_t rv; + if (f == NULL) + return; + rv = apr_file_puts(l, f); + if (rv != APR_SUCCESS) { + apr_file_printf(errfile, "Error writing temp file: %pm", &rv); + apr_file_close(f); + exit(ERR_FILEPERM); + } +} + +int get_password(struct passwd_ctx *ctx) +{ + char buf[MAX_STRING_LEN + 1]; + if (ctx->passwd_src == PW_STDIN) { + apr_file_t *file_stdin; + apr_size_t nread; + if (apr_file_open_stdin(&file_stdin, ctx->pool) != APR_SUCCESS) { + ctx->errstr = "Unable to read from stdin."; + return ERR_GENERAL; + } + if (apr_file_read_full(file_stdin, buf, sizeof(buf) - 1, + &nread) != APR_EOF + || nread == sizeof(buf) - 1) { + goto err_too_long; + } + buf[nread] = '\0'; + if (nread >= 1 && buf[nread-1] == '\n') { + buf[nread-1] = '\0'; + if (nread >= 2 && buf[nread-2] == '\r') + buf[nread-2] = '\0'; + } + apr_file_close(file_stdin); + ctx->passwd = apr_pstrdup(ctx->pool, buf); + } + else if (ctx->passwd_src == PW_PROMPT_VERIFY) { + apr_size_t bufsize = sizeof(buf); + if (apr_password_get("Enter password: ", buf, &bufsize) != 0) + goto err_too_long; + ctx->passwd = apr_pstrdup(ctx->pool, buf); + } + else { + apr_size_t bufsize = sizeof(buf); + if (apr_password_get("New password: ", buf, &bufsize) != 0) + goto err_too_long; + ctx->passwd = apr_pstrdup(ctx->pool, buf); + bufsize = sizeof(buf); + buf[0] = '\0'; + apr_password_get("Re-type new password: ", buf, &bufsize); + if (strcmp(ctx->passwd, buf) != 0) { + ctx->errstr = "password verification error"; + memset(ctx->passwd, '\0', strlen(ctx->passwd)); + memset(buf, '\0', sizeof(buf)); + return ERR_PWMISMATCH; + } + } + memset(buf, '\0', sizeof(buf)); + return 0; + +err_too_long: + ctx->errstr = apr_psprintf(ctx->pool, + "password too long (>%" APR_SIZE_T_FMT ")", + sizeof(buf) - 1); + return ERR_OVERFLOW; +} + +/* + * Make a password record from the given information. A zero return + * indicates success; on failure, ctx->errstr points to the error message. + */ +int mkhash(struct passwd_ctx *ctx) +{ + char *pw; + char salt[16]; + apr_status_t rv; + int ret = 0; +#if CRYPT_ALGO_SUPPORTED + char *cbuf; +#endif + + if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT) { + apr_file_printf(errfile, + "Warning: Ignoring -C argument for this algorithm." NL); + } + + if (ctx->passwd == NULL) { + if ((ret = get_password(ctx)) != 0) + return ret; + } + pw = ctx->passwd; + + switch (ctx->alg) { + case ALG_APSHA: + /* XXX out >= 28 + strlen(sha1) chars - fixed len SHA */ + apr_sha1_base64(pw, strlen(pw), ctx->out); + break; + + case ALG_APMD5: + ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool); + if (ret != 0) + break; + rv = apr_md5_encode(pw, salt, ctx->out, ctx->out_len); + if (rv != APR_SUCCESS) { + ctx->errstr = apr_psprintf(ctx->pool, + "could not encode password: %pm", &rv); + ret = ERR_GENERAL; + } + break; + + case ALG_PLAIN: + /* XXX this len limitation is not in sync with any HTTPd len. */ + apr_cpystrn(ctx->out, pw, ctx->out_len); + break; + +#if CRYPT_ALGO_SUPPORTED + case ALG_CRYPT: + ret = generate_salt(salt, 8, &ctx->errstr, ctx->pool); + if (ret != 0) + break; + cbuf = crypt(pw, salt); + if (cbuf == NULL) { + rv = APR_FROM_OS_ERROR(errno); + ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv); + ret = ERR_PWMISMATCH; + break; + } + + apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1); + if (strlen(pw) > 8) { + char *truncpw = apr_pstrdup(ctx->pool, pw); + truncpw[8] = '\0'; + if (!strcmp(ctx->out, crypt(truncpw, salt))) { + apr_file_printf(errfile, "Warning: Password truncated to 8 " + "characters by CRYPT algorithm." NL); + } + memset(truncpw, '\0', strlen(pw)); + } + break; +#endif /* CRYPT_ALGO_SUPPORTED */ + +#if BCRYPT_ALGO_SUPPORTED + case ALG_BCRYPT: + rv = apr_generate_random_bytes((unsigned char*)salt, 16); + if (rv != APR_SUCCESS) { + ctx->errstr = apr_psprintf(ctx->pool, "Unable to generate random " + "bytes: %pm", &rv); + ret = ERR_RANDOM; + break; + } + + if (ctx->cost == 0) + ctx->cost = BCRYPT_DEFAULT_COST; + rv = apr_bcrypt_encode(pw, ctx->cost, (unsigned char*)salt, 16, + ctx->out, ctx->out_len); + if (rv != APR_SUCCESS) { + ctx->errstr = apr_psprintf(ctx->pool, "Unable to encode with " + "bcrypt: %pm", &rv); + ret = ERR_PWMISMATCH; + break; + } + break; +#endif /* BCRYPT_ALGO_SUPPORTED */ + + default: + apr_file_printf(errfile, "mkhash(): BUG: invalid algorithm %d", + ctx->alg); + abort(); + } + memset(pw, '\0', strlen(pw)); + return ret; +} + +int parse_common_options(struct passwd_ctx *ctx, char opt, + const char *opt_arg) +{ + switch (opt) { + case 'b': + ctx->passwd_src = PW_ARG; + break; + case 'i': + ctx->passwd_src = PW_STDIN; + break; + case 'm': + ctx->alg = ALG_APMD5; + break; + case 's': + ctx->alg = ALG_APSHA; + break; + case 'p': + ctx->alg = ALG_PLAIN; +#if !PLAIN_ALGO_SUPPORTED + /* Backward compatible behavior: Just print a warning */ + apr_file_printf(errfile, + "Warning: storing passwords as plain text might just " + "not work on this platform." NL); +#endif + break; + case 'd': +#if CRYPT_ALGO_SUPPORTED + ctx->alg = ALG_CRYPT; +#else + /* Backward compatible behavior: Use MD5. OK since MD5 is more secure */ + apr_file_printf(errfile, + "Warning: CRYPT algorithm not supported on this " + "platform." NL + "Automatically using MD5 format." NL); + ctx->alg = ALG_APMD5; +#endif + break; + case 'B': +#if BCRYPT_ALGO_SUPPORTED + ctx->alg = ALG_BCRYPT; +#else + /* Don't fall back to something less secure */ + ctx->errstr = "BCRYPT algorithm not supported on this platform"; + return ERR_ALG_NOT_SUPP; +#endif + break; + case 'C': { + char *endptr; + long num = strtol(opt_arg, &endptr, 10); + if (*endptr != '\0' || num <= 0) { + ctx->errstr = "argument to -C must be a positive integer"; + return ERR_SYNTAX; + } + ctx->cost = num; + break; + } + default: + apr_file_printf(errfile, + "parse_common_options(): BUG: invalid option %c", + opt); + abort(); + } + return 0; +} |