Blame support/passwd_common.c

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