26c8e2
diff --git a/configure.in b/configure.in
26c8e2
index de6a8ad..4ca489d 100644
26c8e2
--- a/configure.in
26c8e2
+++ b/configure.in
26c8e2
@@ -465,6 +465,28 @@ LIBS=""
26c8e2
 AC_SEARCH_LIBS(crypt, crypt)
26c8e2
 CRYPT_LIBS="$LIBS"
26c8e2
 APACHE_SUBST(CRYPT_LIBS)
26c8e2
+
26c8e2
+if test "$ac_cv_search_crypt" != "no"; then
26c8e2
+   # Test crypt() with the SHA-512 test vector from https://akkadia.org/drepper/SHA-crypt.txt
26c8e2
+   AC_CACHE_CHECK([whether crypt() supports SHA-2], [ap_cv_crypt_sha2], [
26c8e2
+    AC_RUN_IFELSE([AC_LANG_PROGRAM([[
26c8e2
+#include <crypt.h>
26c8e2
+#include <stdlib.h>
26c8e2
+#include <string.h>
26c8e2
+
26c8e2
+#define PASSWD_0 "Hello world!"
26c8e2
+#define SALT_0 "\$6\$saltstring"
26c8e2
+#define EXPECT_0 "\$6\$saltstring\$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu" \
26c8e2
+               "esI68u4OTLiBFdcbYEdFCoEOfaS35inz1"
26c8e2
+]], [char *result = crypt(PASSWD_0, SALT_0);
26c8e2
+     if (!result) return 1;
26c8e2
+     if (strcmp(result, EXPECT_0)) return 2;
26c8e2
+])], [ap_cv_crypt_sha2=yes], [ap_cv_crypt_sha2=no])])
26c8e2
+   if test "$ap_cv_crypt_sha2" = yes; then
26c8e2
+     AC_DEFINE([HAVE_CRYPT_SHA2], 1, [Define if crypt() supports SHA-2 hashes])
26c8e2
+   fi
26c8e2
+fi
26c8e2
+
26c8e2
 LIBS="$saved_LIBS"
26c8e2
 
26c8e2
 dnl See Comment #Spoon
26c8e2
diff --git a/support/htpasswd.c b/support/htpasswd.c
26c8e2
index 660a27c..136f62a 100644
26c8e2
--- a/support/htpasswd.c
26c8e2
+++ b/support/htpasswd.c
26c8e2
@@ -98,28 +98,32 @@ static int mkrecord(struct passwd_ctx *ctx, char *user)
26c8e2
 static void usage(void)
26c8e2
 {
26c8e2
     apr_file_printf(errfile, "Usage:" NL
26c8e2
-        "\thtpasswd [-cimBdpsDv] [-C cost] passwordfile username" NL
26c8e2
-        "\thtpasswd -b[cmBdpsDv] [-C cost] passwordfile username password" NL
26c8e2
+        "\thtpasswd [-cimB25dpsDv] [-C cost] [-r rounds] passwordfile username" NL
26c8e2
+        "\thtpasswd -b[cmB25dpsDv] [-C cost] [-r rounds] passwordfile username password" NL
26c8e2
         NL
26c8e2
-        "\thtpasswd -n[imBdps] [-C cost] username" NL
26c8e2
-        "\thtpasswd -nb[mBdps] [-C cost] username password" NL
26c8e2
+        "\thtpasswd -n[imB25dps] [-C cost] [-r rounds] username" NL
26c8e2
+        "\thtpasswd -nb[mB25dps] [-C cost] [-r rounds] username password" NL
26c8e2
         " -c  Create a new file." NL
26c8e2
         " -n  Don't update file; display results on stdout." NL
26c8e2
         " -b  Use the password from the command line rather than prompting "
26c8e2
             "for it." NL
26c8e2
         " -i  Read password from stdin without verification (for script usage)." NL
26c8e2
         " -m  Force MD5 encryption of the password (default)." NL
26c8e2
-        " -B  Force bcrypt encryption of the password (very secure)." NL
26c8e2
+        " -2  Force SHA-256 crypt() hash of the password (secure)." NL
26c8e2
+        " -5  Force SHA-512 crypt() hash of the password (secure)." NL
85cb4d
+        " -B  Force bcrypt encryption of the password (very secure)." NL
26c8e2
         " -C  Set the computing time used for the bcrypt algorithm" NL
26c8e2
         "     (higher is more secure but slower, default: %d, valid: 4 to 31)." NL
26c8e2
+        " -r  Set the number of rounds used for the SHA-256, SHA-512 algorithms" NL
26c8e2
+        "     (higher is more secure but slower, default: 5000)." NL
26c8e2
         " -d  Force CRYPT encryption of the password (8 chars max, insecure)." NL
26c8e2
-        " -s  Force SHA encryption of the password (insecure)." NL
26c8e2
+        " -s  Force SHA-1 encryption of the password (insecure)." NL
26c8e2
         " -p  Do not encrypt the password (plaintext, insecure)." NL
26c8e2
         " -D  Delete the specified user." NL
26c8e2
         " -v  Verify password for the specified user." NL
26c8e2
         "On other systems than Windows and NetWare the '-p' flag will "
26c8e2
             "probably not work." NL
26c8e2
-        "The SHA algorithm does not use a salt and is less secure than the "
26c8e2
+        "The SHA-1 algorithm does not use a salt and is less secure than the "
26c8e2
             "MD5 algorithm." NL,
26c8e2
         BCRYPT_DEFAULT_COST
26c8e2
     );
26c8e2
@@ -178,7 +182,7 @@ static void check_args(int argc, const char *const argv[],
26c8e2
     if (rv != APR_SUCCESS)
26c8e2
         exit(ERR_SYNTAX);
26c8e2
 
26c8e2
-    while ((rv = apr_getopt(state, "cnmspdBbDiC:v", &opt, &opt_arg)) == APR_SUCCESS) {
26c8e2
+    while ((rv = apr_getopt(state, "cnmspdBbDi25C:r:v", &opt, &opt_arg)) == APR_SUCCESS) {
26c8e2
         switch (opt) {
26c8e2
         case 'c':
26c8e2
             *mask |= APHTP_NEWFILE;
26c8e2
diff --git a/support/passwd_common.c b/support/passwd_common.c
26c8e2
index 664e509..d45657c 100644
26c8e2
--- a/support/passwd_common.c
26c8e2
+++ b/support/passwd_common.c
26c8e2
@@ -185,10 +185,15 @@ int mkhash(struct passwd_ctx *ctx)
26c8e2
 #if CRYPT_ALGO_SUPPORTED
26c8e2
     char *cbuf;
26c8e2
 #endif
26c8e2
+#ifdef HAVE_CRYPT_SHA2
26c8e2
+    const char *setting;
26c8e2
+    char method;
26c8e2
+#endif
26c8e2
 
26c8e2
-    if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT) {
26c8e2
+    if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT
26c8e2
+        && ctx->alg != ALG_CRYPT_SHA256 && ctx->alg != ALG_CRYPT_SHA512 ) {
26c8e2
         apr_file_printf(errfile,
26c8e2
-                        "Warning: Ignoring -C argument for this algorithm." NL);
26c8e2
+                        "Warning: Ignoring -C/-r argument for this algorithm." NL);
26c8e2
     }
26c8e2
 
26c8e2
     if (ctx->passwd == NULL) {
26c8e2
@@ -246,6 +251,34 @@ int mkhash(struct passwd_ctx *ctx)
26c8e2
         break;
26c8e2
 #endif /* CRYPT_ALGO_SUPPORTED */
26c8e2
 
26c8e2
+#ifdef HAVE_CRYPT_SHA2
26c8e2
+    case ALG_CRYPT_SHA256:
26c8e2
+    case ALG_CRYPT_SHA512:
26c8e2
+        ret = generate_salt(salt, 16, &ctx->errstr, ctx->pool);
26c8e2
+        if (ret != 0)
26c8e2
+            break;
26c8e2
+
26c8e2
+        method = ctx->alg == ALG_CRYPT_SHA256 ? '5': '6';
26c8e2
+
26c8e2
+        if (ctx->cost) 
26c8e2
+            setting = apr_psprintf(ctx->pool, "$%c$rounds=%d$%s",
26c8e2
+                                   method, ctx->cost, salt);
26c8e2
+        else
26c8e2
+            setting = apr_psprintf(ctx->pool, "$%c$%s",
26c8e2
+                                   method, salt);
26c8e2
+
26c8e2
+        cbuf = crypt(pw, setting);
26c8e2
+        if (cbuf == NULL) {
26c8e2
+            rv = APR_FROM_OS_ERROR(errno);
26c8e2
+            ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv;;
26c8e2
+            ret = ERR_PWMISMATCH;
26c8e2
+            break;
26c8e2
+        }
26c8e2
+
26c8e2
+        apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1);
26c8e2
+        break;
26c8e2
+#endif /* HAVE_CRYPT_SHA2 */
26c8e2
+
26c8e2
 #if BCRYPT_ALGO_SUPPORTED
26c8e2
     case ALG_BCRYPT:
26c8e2
         rv = apr_generate_random_bytes((unsigned char*)salt, 16);
26c8e2
@@ -294,6 +327,19 @@ int parse_common_options(struct passwd_ctx *ctx, char opt,
26c8e2
     case 's':
26c8e2
         ctx->alg = ALG_APSHA;
26c8e2
         break;
26c8e2
+#ifdef HAVE_CRYPT_SHA2
26c8e2
+    case '2':
26c8e2
+        ctx->alg = ALG_CRYPT_SHA256;
26c8e2
+        break;
26c8e2
+    case '5':
26c8e2
+        ctx->alg = ALG_CRYPT_SHA512;
26c8e2
+        break;
26c8e2
+#else
26c8e2
+    case '2':
26c8e2
+    case '5':
26c8e2
+        ctx->errstr = "SHA-2 crypt() algorithms are not supported on this platform.";
26c8e2
+        return ERR_ALG_NOT_SUPP;
26c8e2
+#endif
26c8e2
     case 'p':
26c8e2
         ctx->alg = ALG_PLAIN;
26c8e2
 #if !PLAIN_ALGO_SUPPORTED
26c8e2
@@ -324,11 +370,12 @@ int parse_common_options(struct passwd_ctx *ctx, char opt,
26c8e2
         return ERR_ALG_NOT_SUPP;
26c8e2
 #endif
26c8e2
         break;
26c8e2
-    case 'C': {
26c8e2
+    case 'C':
26c8e2
+    case 'r': {
26c8e2
             char *endptr;
26c8e2
             long num = strtol(opt_arg, &endptr, 10);
26c8e2
             if (*endptr != '\0' || num <= 0) {
26c8e2
-                ctx->errstr = "argument to -C must be a positive integer";
26c8e2
+                ctx->errstr = "argument to -C/-r must be a positive integer";
26c8e2
                 return ERR_SYNTAX;
26c8e2
             }
26c8e2
             ctx->cost = num;
26c8e2
diff --git a/support/passwd_common.h b/support/passwd_common.h
26c8e2
index 660081e..f1b3cd7 100644
26c8e2
--- a/support/passwd_common.h
26c8e2
+++ b/support/passwd_common.h
26c8e2
@@ -28,6 +28,8 @@
26c8e2
 #include "apu_version.h"
26c8e2
 #endif
26c8e2
 
26c8e2
+#include "ap_config_auto.h"
26c8e2
+
26c8e2
 #define MAX_STRING_LEN 256
26c8e2
 
26c8e2
 #define ALG_PLAIN 0
26c8e2
@@ -35,6 +37,8 @@
26c8e2
 #define ALG_APMD5 2
26c8e2
 #define ALG_APSHA 3
26c8e2
 #define ALG_BCRYPT 4
26c8e2
+#define ALG_CRYPT_SHA256 5
26c8e2
+#define ALG_CRYPT_SHA512 6
26c8e2
 
26c8e2
 #define BCRYPT_DEFAULT_COST 5
26c8e2
 
26c8e2
@@ -84,7 +88,7 @@ struct passwd_ctx {
26c8e2
     apr_size_t      out_len;
26c8e2
     char            *passwd;
26c8e2
     int             alg;
26c8e2
-    int             cost;
26c8e2
+    int             cost; /* cost for bcrypt, rounds for SHA-2 */
26c8e2
     enum {
26c8e2
         PW_PROMPT = 0,
26c8e2
         PW_ARG,