/* 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 & 0 x3f];
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[17 ];
apr_status_t rv;
int ret = 0 ;
#if CRYPT_ALGO_SUPPORTED
char *cbuf;
#endif
#ifdef HAVE_CRYPT_SHA2
const char *setting;
char method;
#endif
if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT
&& ctx->alg != ALG_CRYPT_SHA256 && ctx->alg != ALG_CRYPT_SHA512 ) {
apr_file_printf(errfile,
"Warning: Ignoring -C/-r 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 */
#ifdef HAVE_CRYPT_SHA2
case ALG_CRYPT_SHA256:
case ALG_CRYPT_SHA512:
ret = generate_salt(salt, 16 , &ctx->errstr, ctx->pool);
if (ret != 0 )
break ;
method = ctx->alg == ALG_CRYPT_SHA256 ? '5' : '6' ;
if (ctx->cost)
setting = apr_psprintf(ctx->pool, "$%c$rounds=%d$%s" ,
method, ctx->cost, salt);
else
setting = apr_psprintf(ctx->pool, "$%c$%s" ,
method, salt);
cbuf = crypt(pw, setting);
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 );
break ;
#endif /* HAVE_CRYPT_SHA2 */
#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 ;
#ifdef HAVE_CRYPT_SHA2
case '2' :
ctx->alg = ALG_CRYPT_SHA256;
break ;
case '5' :
ctx->alg = ALG_CRYPT_SHA512;
break ;
#else
case '2' :
case '5' :
ctx->errstr = "SHA-2 crypt() algorithms are not supported on this platform." ;
return ERR_ALG_NOT_SUPP;
#endif
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' :
case 'r' : {
char *endptr;
long num = strtol(opt_arg, &endptr, 10 );
if (*endptr != '\0' || num <= 0 ) {
ctx->errstr = "argument to -C/-r 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 ;
}
Messung V0.5 in Prozent C=97 H=93 G=94
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland