From b02101b083aad839c4b5427f435d4de27498e935 Mon Sep 17 00:00:00 2001 From: Stefan Fritsch Date: Fri, 5 Apr 2013 20:20:33 +0000 Subject: [PATCH] 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 --- CHANGES | 2 + docs/manual/programs/htpasswd.xml | 10 ++- support/htpasswd.c | 143 ++++++++++++++++++++---------- support/passwd_common.c | 8 ++ support/passwd_common.h | 3 +- 5 files changed, 116 insertions(+), 50 deletions(-) diff --git a/CHANGES b/CHANGES index 502bdd2f0e..b92581439a 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/docs/manual/programs/htpasswd.xml b/docs/manual/programs/htpasswd.xml index adf3348540..b452f4f5e7 100644 --- a/docs/manual/programs/htpasswd.xml +++ b/docs/manual/programs/htpasswd.xml @@ -68,7 +68,8 @@ distribution. -s | -p ] [ -C cost ] - [ -D ] passwdfile username

+ [ -D ] + [ -v ] passwdfile username

htpasswd -b [ -c ] @@ -78,7 +79,8 @@ distribution. -s | -p ] [ -C cost ] - [ -D ] passwdfile username + [ -D ] + [ -v ] passwdfile username password

htpasswd -n @@ -155,6 +157,10 @@ distribution.

Delete user. If the username exists in the specified htpasswd file, it will be deleted.
+
-v
+
Verify password. Verify that the given password matches the password + of the user stored in the specified htpasswd file.
+
passwdfile
Name of the file to contain the user name and password. If -c is given, this file is created if it does not already exist, diff --git a/support/htpasswd.c b/support/htpasswd.c index 84c1a204f9..11023499a4 100644 --- a/support/htpasswd.c +++ b/support/htpasswd.c @@ -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. diff --git a/support/passwd_common.c b/support/passwd_common.c index 95612b70c3..c03242be2f 100644 --- a/support/passwd_common.c +++ b/support/passwd_common.c @@ -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) diff --git a/support/passwd_common.h b/support/passwd_common.h index 672ad5c3c7..00680624ce 100644 --- a/support/passwd_common.h +++ b/support/passwd_common.h @@ -80,7 +80,8 @@ struct passwd_ctx { enum { PW_PROMPT = 0, PW_ARG, - PW_STDIN + PW_STDIN, + PW_PROMPT_VERIFY, } passwd_src; }; -- 2.40.0