From 1c581a8364ab18a6938f3153d7bea793d06a4652 Mon Sep 17 00:00:00 2001
From: Ludwig Nussel <ludwig.nussel@suse.de>
Date: Thu, 25 Aug 2011 14:00:38 +0200
Subject: [PATCH crypt_blowfish] support for sha256 and sha512

---
 crypt.3         |   14 +++++++++++++
 crypt_gensalt.c |   58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 wrapper.c       |   23 +++++++++++++++++++++
 3 files changed, 95 insertions(+), 0 deletions(-)

diff --git a/crypt.3 b/crypt.3
index e2f25bd..40a3538 100644
--- a/crypt.3
+++ b/crypt.3
@@ -399,6 +399,20 @@ too low for the currently available hardware.
 .hash "$1$" "\e$1\e$[^$]{1,8}\e$[./0-9A-Za-z]{22}" unlimited 8 "" 128 "6 to 48" 1000
 .PP
 .ti -2
+.B SHA256 based
+.br
+This is Ulrich Drepper's SHA256-based password hashing method originally
+developed for Linux.
+.hash "$5$" "\e$5\e$(rounds=[0-9]{1,9}\e$)?([./0-9A-Za-z]{1,16})?\e$[./0-9A-Za-z]{43}" unlimited 8 "" 256 "0 to 96" "1000 to 999999999 (default 5000)"
+.PP
+.ti -2
+.B SHA512 based
+.br
+This is Ulrich Drepper's SHA512-based password hashing method originally
+developed for Linux.
+.hash "$6$" "\e$6\e$(rounds=[0-9]{1,9}\e$)?([./0-9A-Za-z]{1,16})?\e$[./0-9A-Za-z]{86}" unlimited 8 "" 512 "0 to 96" "1000 to 999999999 (default 5000)"
+.PP
+.ti -2
 .BR "OpenBSD-style Blowfish-based" " (" bcrypt )
 .br
 .B bcrypt
diff --git a/crypt_gensalt.c b/crypt_gensalt.c
index 73c15a1..5cf9812 100644
--- a/crypt_gensalt.c
+++ b/crypt_gensalt.c
@@ -19,6 +19,7 @@
  */
 
 #include <string.h>
+#include <stdio.h>
 
 #include <errno.h>
 #ifndef __set_errno
@@ -122,3 +123,60 @@ char *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
 
 	return output;
 }
+
+#define SHA2_SALT_LEN_MAX 16
+#define SHA2_ROUNDS_MIN   1000
+#define SHA2_ROUNDS_MAX   999999999
+char *_crypt_gensalt_sha2_rn (const char *prefix, unsigned long count,
+	const char *input, int size, char *output, int output_size)
+
+{
+	char *o = output;
+	const char *i = input;
+	unsigned needed = 3 + MIN(size/3*4, SHA2_SALT_LEN_MAX) + 1;
+
+	if (size < 3 || output_size < needed)
+		goto error;
+
+	size = MIN(size, SHA2_SALT_LEN_MAX/4*3);
+
+	o[0] = prefix[0];
+	o[1] = prefix[1];
+	o[2] = prefix[2];
+	o += 3;
+
+	if (count) {
+		count = MAX(SHA2_ROUNDS_MIN, MIN(count, SHA2_ROUNDS_MAX));
+		int n = snprintf (o, output_size-3, "rounds=%ld$", count);
+		if (n < 0 || n >= output_size-3)
+			goto error;
+		needed += n;
+		o += n;
+	}
+
+	if (output_size < needed)
+		goto error;
+
+	while (size >= 3) {
+		unsigned long value =
+			(unsigned long)(unsigned char)i[0] |
+			((unsigned long)(unsigned char)i[1] << 8) |
+			((unsigned long)(unsigned char)i[2] << 16);
+		o[0] = _crypt_itoa64[value & 0x3f];
+		o[1] = _crypt_itoa64[(value >> 6) & 0x3f];
+		o[2] = _crypt_itoa64[(value >> 12) & 0x3f];
+		o[3] = _crypt_itoa64[(value >> 18) & 0x3f];
+		size -= 3;
+		i += 3;
+		o += 3;
+	}
+	o[0] = '\0';
+
+	return output;
+
+error:
+	if (output_size > 0)
+		output[0] = '\0';
+	errno = ENOMEM;
+	return NULL;
+}
diff --git a/wrapper.c b/wrapper.c
index 344053b..070d91d 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -44,12 +44,18 @@
 
 #include "crypt_blowfish.h"
 #include "crypt_gensalt.h"
+extern char *_crypt_gensalt_sha2_rn(const char *prefix, unsigned long count,
+	const char *input, int size, char *output, int output_size);
 
 #if defined(__GLIBC__) && defined(_LIBC)
 /* crypt.h from glibc-crypt-2.1 will define struct crypt_data for us */
 #include "crypt.h"
 extern char *__md5_crypt_r(const char *key, const char *salt,
 	char *buffer, int buflen);
+extern char *__sha256_crypt_r (const char *key, const char *salt,
+	char *buffer, int buflen);
+extern char *__sha512_crypt_r (const char *key, const char *salt,
+	char *buffer, int buflen);
 /* crypt-entry.c needs to be patched to define __des_crypt_r rather than
  * __crypt_r, and not define crypt_r and crypt at all */
 extern char *__des_crypt_r(const char *key, const char *salt,
@@ -112,6 +118,10 @@ static char *_crypt_retval_magic(char *retval, const char *setting,
 char *__crypt_rn(__const char *key, __const char *setting,
 	void *data, int size)
 {
+	if (setting[0] == '$' && setting[1] == '6')
+		return __sha512_crypt_r(key, setting, (char *)data, size);
+	if (setting[0] == '$' && setting[1] == '5')
+		return __sha256_crypt_r(key, setting, (char *)data, size);
 	if (setting[0] == '$' && setting[1] == '2')
 		return _crypt_blowfish_rn(key, setting, (char *)data, size);
 	if (setting[0] == '$' && setting[1] == '1')
@@ -129,6 +139,16 @@ char *__crypt_rn(__const char *key, __const char *setting,
 char *__crypt_ra(__const char *key, __const char *setting,
 	void **data, int *size)
 {
+	if (setting[0] == '$' && setting[1] == '6') {
+		if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
+			return NULL;
+		return __sha512_crypt_r(key, setting, (char *)*data, *size);
+	}
+	if (setting[0] == '$' && setting[1] == '5') {
+		if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
+			return NULL;
+		return __sha256_crypt_r(key, setting, (char *)*data, *size);
+	}
 	if (setting[0] == '$' && setting[1] == '2') {
 		if (_crypt_data_alloc(data, size, CRYPT_OUTPUT_SIZE))
 			return NULL;
@@ -210,6 +230,9 @@ char *__crypt_gensalt_rn(const char *prefix, unsigned long count,
 		return NULL;
 	}
 
+	if (!strncmp(prefix, "$5$", 3) || !strncmp(prefix, "$6$", 3))
+		use = _crypt_gensalt_sha2_rn;
+	else
 	if (!strncmp(prefix, "$2a$", 4) || !strncmp(prefix, "$2y$", 4))
 		use = _crypt_gensalt_blowfish_rn;
 	else
-- 
1.7.3.4