htpasswd: Add -v option to verify a password

htpasswd and htdbm could use some more refactoring...


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1465116 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Fritsch
2013-04-05 20:20:33 +00:00
parent e84335b00d
commit b02101b083
5 changed files with 116 additions and 50 deletions

View File

@ -1,6 +1,8 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.0
*) htpasswd: Add -v option to verify a password. [Stefan Fritsch]
*) htpasswd, htdbm: Fix password generation. PR 54735. [Stefan Fritsch]
*) mod_dav: Improve error handling in dav_method_put(), add new

View File

@ -68,7 +68,8 @@ distribution.</seealso>
-<strong>s</strong> |
-<strong>p</strong> ]
[ -<strong>C</strong> <var>cost</var> ]
[ -<strong>D</strong> ] <var>passwdfile</var> <var>username</var></code></p>
[ -<strong>D</strong> ]
[ -<strong>v</strong> ] <var>passwdfile</var> <var>username</var></code></p>
<p><code><strong>htpasswd</strong> -<strong>b</strong>
[ -<strong>c</strong> ]
@ -78,7 +79,8 @@ distribution.</seealso>
-<strong>s</strong> |
-<strong>p</strong> ]
[ -<strong>C</strong> <var>cost</var> ]
[ -<strong>D</strong> ] <var>passwdfile</var> <var>username</var>
[ -<strong>D</strong> ]
[ -<strong>v</strong> ] <var>passwdfile</var> <var>username</var>
<var>password</var></code></p>
<p><code><strong>htpasswd</strong> -<strong>n</strong>
@ -155,6 +157,10 @@ distribution.</seealso>
<dd>Delete user. If the username exists in the specified htpasswd file, it
will be deleted.</dd>
<dt><code>-v</code></dt>
<dd>Verify password. Verify that the given password matches the password
of the user stored in the specified htpasswd file.</dd>
<dt><code><var>passwdfile</var></code></dt>
<dd>Name of the file to contain the user name and password. If
<code>-c</code> is given, this file is created if it does not already exist,

View File

@ -67,6 +67,7 @@
#define APHTP_NEWFILE 1
#define APHTP_NOFILE 2
#define APHTP_DELUSER 4
#define APHTP_VERIFY 8
apr_file_t *ftemp = NULL;
@ -92,8 +93,8 @@ static int mkrecord(struct passwd_ctx *ctx, char *user)
static void usage(void)
{
apr_file_printf(errfile, "Usage:" NL
"\thtpasswd [-cimBdpsD] [-C cost] passwordfile username" NL
"\thtpasswd -b[cmBdpsD] [-C cost] passwordfile username password" NL
"\thtpasswd [-cimBdpsDv] [-C cost] passwordfile username" NL
"\thtpasswd -b[cmBdpsDv] [-C cost] passwordfile username password" NL
NL
"\thtpasswd -n[imBdps] [-C cost] username" NL
"\thtpasswd -nb[mBdps] [-C cost] username password" NL
@ -110,6 +111,7 @@ static void usage(void)
" -s Force SHA encryption of the password (insecure)." NL
" -p Do not encrypt the password (plaintext, insecure)." NL
" -D Delete the specified user." NL
" -v Verify password for the specified user." NL
"On other systems than Windows and NetWare the '-p' flag will "
"probably not work." NL
"The SHA algorithm does not use a salt and is less secure than the "
@ -155,7 +157,7 @@ static void terminate(void)
}
static void check_args(int argc, const char *const argv[],
struct passwd_ctx *ctx, int *mask, char **user,
struct passwd_ctx *ctx, unsigned *mask, char **user,
char **pwfilename)
{
const char *arg;
@ -171,7 +173,7 @@ static void check_args(int argc, const char *const argv[],
if (rv != APR_SUCCESS)
exit(ERR_SYNTAX);
while ((rv = apr_getopt(state, "cnmspdBbDiC:", &opt, &opt_arg)) == APR_SUCCESS) {
while ((rv = apr_getopt(state, "cnmspdBbDiC:v", &opt, &opt_arg)) == APR_SUCCESS) {
switch (opt) {
case 'c':
*mask |= APHTP_NEWFILE;
@ -183,6 +185,9 @@ static void check_args(int argc, const char *const argv[],
case 'D':
*mask |= APHTP_DELUSER;
break;
case 'v':
*mask |= APHTP_VERIFY;
break;
default:
ret = parse_common_options(ctx, opt, opt_arg);
if (ret) {
@ -196,18 +201,15 @@ static void check_args(int argc, const char *const argv[],
if (rv != APR_EOF)
usage();
if ((*mask & APHTP_NEWFILE) && (*mask & APHTP_NOFILE)) {
apr_file_printf(errfile, "%s: -c and -n options conflict" NL, argv[0]);
exit(ERR_SYNTAX);
}
if ((*mask & APHTP_NEWFILE) && (*mask & APHTP_DELUSER)) {
apr_file_printf(errfile, "%s: -c and -D options conflict" NL, argv[0]);
exit(ERR_SYNTAX);
}
if ((*mask & APHTP_NOFILE) && (*mask & APHTP_DELUSER)) {
apr_file_printf(errfile, "%s: -n and -D options conflict" NL, argv[0]);
if ((*mask) & (*mask - 1)) {
/* not a power of two, i.e. more than one flag specified */
apr_file_printf(errfile, "%s: only one of -c -n -v -D may be specified" NL,
argv[0]);
exit(ERR_SYNTAX);
}
if ((*mask & APHTP_VERIFY) && ctx->passwd_src == PW_PROMPT)
ctx->passwd_src = PW_PROMPT_VERIFY;
/*
* Make sure we still have exactly the right number of arguments left
* (the filename, the username, and possibly the password if -b was
@ -246,6 +248,25 @@ static void check_args(int argc, const char *const argv[],
}
}
static int verify(struct passwd_ctx *ctx, const char *hash)
{
apr_status_t rv;
int ret;
if (ctx->passwd == NULL && (ret = get_password(ctx)) != 0)
return ret;
rv = apr_password_validate(ctx->passwd, hash);
if (rv == APR_SUCCESS)
return 0;
if (APR_STATUS_IS_EMISMATCH(rv)) {
ctx->errstr = "password verification failed";
return ERR_PWMISMATCH;
}
ctx->errstr = apr_psprintf(ctx->pool, "Could not verify password: %pm",
&rv);
return ERR_GENERAL;
}
/*
* Let's do it. We end up doing a lot of file opening and closing,
* but what do we care? This application isn't run constantly.
@ -261,7 +282,7 @@ int main(int argc, const char * const argv[])
char *scratch, cp[MAX_STRING_LEN];
int found = 0;
int i;
int mask = 0;
unsigned mask = 0;
apr_pool_t *pool;
int existing_file = 0;
struct passwd_ctx ctx = { 0 };
@ -341,7 +362,7 @@ int main(int argc, const char * const argv[])
* Any error message text is returned in the record buffer, since
* the mkrecord() routine doesn't have access to argv[].
*/
if (!(mask & APHTP_DELUSER)) {
if ((mask & (APHTP_DELUSER|APHTP_VERIFY)) == 0) {
i = mkrecord(&ctx, user);
if (i != 0) {
apr_file_printf(errfile, "%s: %s" NL, argv[0], ctx.errstr);
@ -353,21 +374,23 @@ int main(int argc, const char * const argv[])
}
}
/*
* We can access the files the right way, and we have a record
* to add or update. Let's do it..
*/
if (apr_temp_dir_get((const char**)&dirname, pool) != APR_SUCCESS) {
apr_file_printf(errfile, "%s: could not determine temp dir" NL,
argv[0]);
exit(ERR_FILEPERM);
}
dirname = apr_psprintf(pool, "%s/%s", dirname, tn);
if ((mask & APHTP_VERIFY) == 0) {
/*
* We can access the files the right way, and we have a record
* to add or update. Let's do it..
*/
if (apr_temp_dir_get((const char**)&dirname, pool) != APR_SUCCESS) {
apr_file_printf(errfile, "%s: could not determine temp dir" NL,
argv[0]);
exit(ERR_FILEPERM);
}
dirname = apr_psprintf(pool, "%s/%s", dirname, tn);
if (apr_file_mktemp(&ftemp, dirname, 0, pool) != APR_SUCCESS) {
apr_file_printf(errfile, "%s: unable to create temporary file %s" NL,
argv[0], dirname);
exit(ERR_FILEPERM);
if (apr_file_mktemp(&ftemp, dirname, 0, pool) != APR_SUCCESS) {
apr_file_printf(errfile, "%s: unable to create temporary file %s" NL,
argv[0], dirname);
exit(ERR_FILEPERM);
}
}
/*
@ -418,33 +441,59 @@ int main(int argc, const char * const argv[])
continue;
}
else {
if (!(mask & APHTP_DELUSER)) {
/* We found the user we were looking for.
* Add him to the file.
*/
apr_file_printf(errfile, "Updating ");
putline(ftemp, ctx.out);
found++;
/* We found the user we were looking for */
found++;
if ((mask & APHTP_DELUSER)) {
/* Delete entry from the file */
apr_file_printf(errfile, "Deleting ");
}
else if ((mask & APHTP_VERIFY)) {
/* Verify */
char *hash = colon + 1;
size_t len;
len = strcspn(hash, "\r\n");
if (len == 0) {
apr_file_printf(errfile, "Empty hash for user %s" NL,
user);
exit(ERR_INVALID);
}
hash[len] = '\0';
i = verify(&ctx, hash);
if (i != 0) {
apr_file_printf(errfile, "%s" NL, ctx.errstr);
exit(i);
}
}
else {
/* We found the user we were looking for.
* Delete them from the file.
*/
apr_file_printf(errfile, "Deleting ");
found++;
/* Update entry */
apr_file_printf(errfile, "Updating ");
putline(ftemp, ctx.out);
}
}
}
apr_file_close(fpw);
}
if (!found && !(mask & APHTP_DELUSER)) {
apr_file_printf(errfile, "Adding ");
putline(ftemp, ctx.out);
if (!found) {
if (mask & APHTP_DELUSER) {
apr_file_printf(errfile, "User %s not found" NL, user);
exit(0);
}
else if (mask & APHTP_VERIFY) {
apr_file_printf(errfile, "User %s not found" NL, user);
exit(ERR_BADUSER);
}
else {
apr_file_printf(errfile, "Adding ");
putline(ftemp, ctx.out);
}
}
else if (!found && (mask & APHTP_DELUSER)) {
apr_file_printf(errfile, "User %s not found" NL, user);
if (mask & APHTP_VERIFY) {
apr_file_printf(errfile, "Password for user %s correct." NL, user);
exit(0);
}
apr_file_printf(errfile, "password for user %s" NL, user);
/* The temporary file has all the data, just copy it to the new location.

View File

@ -103,6 +103,8 @@ static int generate_salt(char *s, size_t size, const char **errstr,
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);
@ -135,6 +137,12 @@ int get_password(struct passwd_ctx *ctx)
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)

View File

@ -80,7 +80,8 @@ struct passwd_ctx {
enum {
PW_PROMPT = 0,
PW_ARG,
PW_STDIN
PW_STDIN,
PW_PROMPT_VERIFY,
} passwd_src;
};