diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-02-25 23:02:34 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2020-02-27 17:40:57 +0200 |
commit | a9a6567bac8ab7729c243b555e7fb3c4bb2c6e51 (patch) | |
tree | 3c23c862fe8531f8c113526e879aa56fed330c04 | |
parent | cc58c3358b9495044af074484dfa17b2fb65f193 (diff) | |
download | ping903-a9a6567bac8ab7729c243b555e7fb3c4bb2c6e51.tar.gz ping903-a9a6567bac8ab7729c243b555e7fb3c4bb2c6e51.tar.bz2 |
Implement basic auth
* Makefile.am (SUBDIRS): Add lib
* configure.ac: Build lib/Makefile
* lib/Makefile.am: New file.
* lib/apr.c: New file.
* lib/basicauth.c: New file.
* lib/basicauth.h: New file.
* lib/md5.c: New file.
* lib/md5.h: New file.
* lib/sha1.c: New file.
* lib/sha1.h: New file.
* src/Makefile.am: Add lib to the include path
* src/config.c: New statement "auth".
* src/ping903.c: Implement basic auth.
* src/ping903.h (cf_auth): New proto.
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | lib/Makefile.am | 10 | ||||
-rw-r--r-- | lib/apr.c | 196 | ||||
-rw-r--r-- | lib/basicauth.c | 199 | ||||
-rw-r--r-- | lib/basicauth.h | 8 | ||||
-rw-r--r-- | lib/md5.c | 281 | ||||
-rw-r--r-- | lib/md5.h | 15 | ||||
-rw-r--r-- | lib/sha1.c | 418 | ||||
-rw-r--r-- | lib/sha1.h | 83 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/config.c | 1 | ||||
-rw-r--r-- | src/ping903.c | 253 | ||||
-rw-r--r-- | src/ping903.h | 1 |
14 files changed, 1470 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am index 7f08c55..f8d42f0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS=src doc +SUBDIRS=lib src doc EXTRA_DIST=\ rc/README\ rc/debian.rc\ diff --git a/configure.ac b/configure.ac index 0831e7e..dd8dc8b 100644 --- a/configure.ac +++ b/configure.ac @@ -59,5 +59,5 @@ AC_TYPE_SIZE_T # Checks for library functions. AC_CHECK_FUNCS([gettimeofday]) -AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile]) +AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile doc/Makefile]) AC_OUTPUT diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..dd9386d --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,10 @@ +noinst_LIBRARIES = libdigest.a +libdigest_a_SOURCES = \ + apr.c\ + basicauth.c\ + md5.c\ + sha1.c +noinst_HEADERS = \ + basicauth.h\ + md5.h\ + sha1.h diff --git a/lib/apr.c b/lib/apr.c new file mode 100644 index 0000000..1036841 --- /dev/null +++ b/lib/apr.c @@ -0,0 +1,196 @@ +#include <config.h> +#include <sys/types.h> +#include <stdint.h> +#include <string.h> +#include "md5.h" + +/* APR-specific code */ + +/* + * Define the Magic String prefix that identifies a password as being + * hashed using our algorithm. + */ +#define APR1_ID_STR "$apr1$" +#define APR1_ID_LEN (sizeof(APR1_ID_STR)-1) + +/* + * The following MD5 password encryption code was largely borrowed from + * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is + * licenced as stated at the top of this file. + */ + +static void +to64(char *s, unsigned long v, int n) +{ + static unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +#define APR_MD5_DIGESTSIZE 16 + +char * +apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) +{ + /* + * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL, + * plus 4 for the '$' separators, plus the password hash itself. + * Let's leave a goodly amount of leeway. + */ + + char passwd[120], *p; + const char *sp, *ep; + unsigned char final[APR_MD5_DIGESTSIZE]; + ssize_t sl, pl, i; + MD5_CTX ctx, ctx1; + unsigned long l; + + /* + * Refine the salt first. It's possible we were given an already-hashed + * string as the salt argument, so extract the actual salt value from it + * if so. Otherwise just use the string up to the first '$' as the salt. + */ + sp = salt; + + /* + * If it starts with the magic string, then skip that. + */ + if (!strncmp(sp, APR1_ID_STR, APR1_ID_LEN)) { + sp += APR1_ID_LEN; + } + + /* + * It stops at the first '$' or 8 chars, whichever comes first + */ + for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) + ; + + /* + * Get the length of the true salt + */ + sl = ep - sp; + + /* + * 'Time to make the doughnuts..' + */ + MD5Init(&ctx); + + /* + * The password first, since that is what is most unknown + */ + MD5Update(&ctx, (unsigned char const*) pw, strlen(pw)); + + /* + * Then our magic string + */ + MD5Update(&ctx, (unsigned char const*)APR1_ID_STR, APR1_ID_LEN); + + /* + * Then the raw salt + */ + MD5Update(&ctx, (unsigned char const*)sp, sl); + + /* + * Then just as many characters of the MD5(pw, salt, pw) + */ + MD5Init(&ctx1); + MD5Update(&ctx1, (unsigned char const*)pw, strlen(pw)); + MD5Update(&ctx1, (unsigned char const*)sp, sl); + MD5Update(&ctx1, (unsigned char const*)pw, strlen(pw)); + MD5Final(final, &ctx1); + for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { + MD5Update(&ctx, final, + (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); + } + + /* + * Don't leave anything around in vm they could use. + */ + memset(final, 0, sizeof(final)); + + /* + * Then something really weird... + */ + for (i = strlen(pw); i != 0; i >>= 1) { + if (i & 1) + MD5Update(&ctx, final, 1); + else + MD5Update(&ctx, (unsigned char const*)pw, 1); + } + + /* + * Now make the output string. We know our limitations, so we + * can use the string routines without bounds checking. + */ + strcpy(passwd, APR1_ID_STR); + strncat(passwd, sp, sl); + strcat(passwd, "$"); + + MD5Final(final, &ctx); + + /* + * And now, just to make sure things don't run too fast.. + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { + MD5Init(&ctx1); + /* + * apr_md5_final clears out ctx1.xlate at the end of each loop, + * so need to to set it each time through + */ + if (i & 1) + MD5Update(&ctx1, (unsigned char const*)pw, strlen(pw)); + else + MD5Update(&ctx1, final, APR_MD5_DIGESTSIZE); + if (i % 3) + MD5Update(&ctx1, (unsigned char const*)sp, sl); + + if (i % 7) + MD5Update(&ctx1, (unsigned char const*)pw, strlen(pw)); + + if (i & 1) + MD5Update(&ctx1, final, APR_MD5_DIGESTSIZE); + else + MD5Update(&ctx1, (unsigned char const*)pw, strlen(pw)); + MD5Final(final,&ctx1); + } + + p = passwd + strlen(passwd); + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4; + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4; + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4; + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4; + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4; + l = final[11] ; to64(p, l, 2); p += 2; + *p = '\0'; + + /* + * Don't leave anything around in vm they could use. + */ + memset(final, 0, sizeof(final)); + + i = strlen(passwd); + if (i >= nbytes) + i = nbytes - 1; + memcpy(result, passwd, i); + result[i] = 0; + return result; +} + +#ifdef STANDALONE +int +main(int argc, char **argv) +{ + unsigned char result[120]; + if (argc != 3) + exit(1); + apr_md5_encode(argv[1], argv[2], result, sizeof(result)); + printf("%s\n",result); +} +#endif diff --git a/lib/basicauth.c b/lib/basicauth.c new file mode 100644 index 0000000..4d313e6 --- /dev/null +++ b/lib/basicauth.c @@ -0,0 +1,199 @@ +#include <config.h> +#include <errno.h> +#include <pthread.h> +#include <crypt.h> +#include <string.h> +#include "sha1.h" +#include "md5.h" +#include "basicauth.h" + +static int b64val[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 +}; + +static int +base64_decode(const unsigned char *input, size_t input_len, + unsigned char *output, size_t output_len) +{ + unsigned char *out = output; +#define AC(c) do { if (output_len-- == 0) return -1; *out++ = (c); } while (0) + + if (!out) + return -1; + + do { + if (input[0] > 127 || b64val[input[0]] == -1 || + input[1] > 127 || b64val[input[1]] == -1 || + input[2] > 127 || + ((input[2] != '=') && (b64val[input[2]] == -1)) || + input[3] > 127 || + ((input[3] != '=') && (b64val[input[3]] == -1))) { + errno = EINVAL; + return -1; + } + AC((b64val[input[0]] << 2) | (b64val[input[1]] >> 4)); + if (input[2] != '=') { + AC(((b64val[input[1]] << 4) & 0xf0) | + (b64val[input[2]] >> 2)); + if (input[3] != '=') + AC(((b64val[input[2]] << 6) & 0xc0) | + b64val[input[3]]); + } + input += 4; + input_len -= 4; + } while (input_len > 0); + return out - output; +} + +static pthread_mutex_t crypt_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int +crypt_match(const char *pass, const char *hash) +{ + int res = 1; + char *cp; + + pthread_mutex_lock(&crypt_mutex); + cp = crypt(pass, hash); + if (cp) + res = strcmp(cp, hash); + pthread_mutex_unlock(&crypt_mutex); + return res; +} + +static int +plain_match(const char *pass, const char *hash) +{ + return strcmp(pass, hash); +} + +static int +apr_match(const char *pass, const char *hash) +{ + char buf[120]; + char *cp = apr_md5_encode(pass, hash, buf, sizeof(buf)); + return cp ? strcmp(cp, hash) : 1; +} + +#define SHA1_DIGEST_SIZE 20 + +static int +sha1_match(const char *pass, const char *hash) +{ + char hashbuf[SHA1_DIGEST_SIZE], resbuf[SHA1_DIGEST_SIZE]; + int n; + + hash += 5; /* Skip past {SHA} */ + n = base64_decode((const unsigned char *)hash, strlen(hash), + (unsigned char *)hashbuf, sizeof(hashbuf)); + if (n < 0) { + /* FIXME: error("cannot decode %s", hash); */ + return 1; + } + if (n != SHA1_DIGEST_SIZE) { + /* FIXME: error("bad hash length: %s %d", hash, n); */ + return 1; + } + sha1_buffer(pass, strlen(pass), resbuf); + + return memcmp(resbuf, hashbuf, SHA1_DIGEST_SIZE); +} + +/* Matcher table */ +struct matcher { + char *cm_pfx; + size_t cm_len; + int (*cm_match)(const char *, const char *); +}; + +static struct matcher match_tab[] = { +#define S(s) #s, sizeof(#s)-1 + { S($apr1$), apr_match }, + { S({SHA}), sha1_match }, + { "", 0, crypt_match }, + { "", 0, plain_match }, + { NULL } +}; + +static int +match(const char *pass, const char *hash) +{ + struct matcher *p; + size_t plen = strlen(hash); + + for (p = match_tab; p->cm_match; p++) { + if (p->cm_len < plen && + memcmp(p->cm_pfx, hash, p->cm_len) == 0 && + p->cm_match(pass, hash) == 0) + return 0; + } + return 1; +} + +#define BASICPREF "Basic " +#define BASICLEN (sizeof(BASICPREF)-1) + +BASICAUTH_RESULT +basicauth(char const *file, char const *input) +{ + char buf[1024]; + char lbuf[1024]; + char *pass; + int n; + FILE *fp; + BASICAUTH_RESULT rc; + + if (!input || strncmp(input, BASICPREF, BASICLEN)) + return BASICAUTH_BAD_INPUT; + input += BASICLEN; + n = base64_decode((const unsigned char *)input, strlen(input), + (unsigned char *)buf, sizeof(buf)); + if (n < 0) { + return BASICAUTH_BAD_INPUT; + } else if (n == sizeof(buf)) { + return BASICAUTH_BAD_INPUT; + } + buf[n] = 0; + + pass = strchr(buf, ':'); + if (!pass) + return BASICAUTH_BAD_INPUT; + *pass++ = 0; + + fp = fopen(file, "r"); + if (!fp) + return BASICAUTH_CANT_OPEN; + + rc = BASICAUTH_DENY; + while (fgets(lbuf, sizeof(lbuf), fp)) { + char *p, *q; + for (p = lbuf; *p && (*p == ' ' || *p == '\t'); p++); + if (*p == '#') + continue; + q = p + strlen(p); + if (q == p) + continue; + if (q[-1] == '\n') + *--q = 0; + if (!*p) + continue; + q = strchr(p, ':'); + if (!q) + continue; + *q++ = 0; + if (strcmp(p, buf)) + continue; + rc = match(pass, q) ? BASICAUTH_DENY : BASICAUTH_ALLOW; + break; + } + fclose(fp); + return rc; +} + diff --git a/lib/basicauth.h b/lib/basicauth.h new file mode 100644 index 0000000..49b504f --- /dev/null +++ b/lib/basicauth.h @@ -0,0 +1,8 @@ +typedef enum { + BASICAUTH_ALLOW, + BASICAUTH_DENY, + BASICAUTH_BAD_INPUT, + BASICAUTH_CANT_OPEN +} BASICAUTH_RESULT; + +BASICAUTH_RESULT basicauth(char const *file, char const *input); diff --git a/lib/md5.c b/lib/md5.c new file mode 100644 index 0000000..ea59196 --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,281 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ +/* + * Modified (2001-01-31) to work on Sparks <gray@farlep.net> + */ +#include <config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <string.h> /* for memcpy() */ +#include <md5.h> + +void +md5_calc(unsigned char *output, unsigned char const *input, + unsigned int inlen) +{ + MD5_CTX context; + + MD5Init(&context); + MD5Update(&context, input, inlen); + MD5Final(output, &context); +} + + +static void +bytes_encode(unsigned char *output, uint32_t *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +static void +bytes_decode(uint32_t *output, unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint32_t)input[j]) | + (((uint32_t)input[j+1]) << 8) | + (((uint32_t)input[j+2]) << 16) | + (((uint32_t)input[j+3]) << 24); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + MD5Transform(ctx->buf, (uint32_t const *) buf); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + + /* Append length in bits and transform */ + bytes_encode((unsigned char*)((uint32_t *) ctx->in + 14), ctx->bits, 8); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + bytes_encode(digest,ctx->buf,16); + memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +#define F1(x, y, z) ((x & y) | (~x & z)) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ); + +#if 0 +dump(char *label,unsigned char *p, int len) +{ + int i; + return; + printf("dump: %s\n", label); + for (i=0; i<len; i++) + printf("%x\n", p[i]); + printf("--\n"); + +} +#endif + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(uint32_t buf[4], uint32_t const cin[16]) +{ + register uint32_t a, b, c, d; + uint32_t in[16]; + + bytes_decode(in, (unsigned char *) cin, 64); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + diff --git a/lib/md5.h b/lib/md5.h new file mode 100644 index 0000000..ffbdfb3 --- /dev/null +++ b/lib/md5.h @@ -0,0 +1,15 @@ +struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +}; + +typedef struct MD5Context MD5_CTX; + +void MD5Init(struct MD5Context *ctx); +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *ctx); +void MD5Transform(uint32_t buf[4], uint32_t const cin[16]); + +char *apr_md5_encode(const char *pw, const char *salt, char *result, + size_t nbytes); diff --git a/lib/sha1.c b/lib/sha1.c new file mode 100644 index 0000000..a4380cb --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,418 @@ +/* sha1.c - Functions to compute SHA1 message digest of files or + memory blocks according to the NIST specification FIPS-180-1. + + Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Scott G. Miller + Credits: + Robert Klep <robert@ilse.nl> -- Expansion function fix +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sha1.h> + +#include <stddef.h> +#include <string.h> + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) (n) +#else +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#endif + +#define BLOCKSIZE 4096 +#if BLOCKSIZE % 64 != 0 +# error "invalid BLOCKSIZE" +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Take a pointer to a 160 bit block of data (five 32 bit ints) and + initialize it to the start constants of the SHA1 algorithm. This + must be called before using hash in the call to sha1_hash. */ +void +sha1_init_ctx (struct sha1_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + ctx->E = 0xc3d2e1f0; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 20 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) +{ + ((uint32_t *) resbuf)[0] = SWAP (ctx->A); + ((uint32_t *) resbuf)[1] = SWAP (ctx->B); + ((uint32_t *) resbuf)[2] = SWAP (ctx->C); + ((uint32_t *) resbuf)[3] = SWAP (ctx->D); + ((uint32_t *) resbuf)[4] = SWAP (ctx->E); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + uint32_t bytes = ctx->buflen; + size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); + ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3); + + memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + + /* Process last bytes. */ + sha1_process_block (ctx->buffer, size * 4, ctx); + + return sha1_read_ctx (ctx, resbuf); +} + +/* Compute SHA1 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +sha1_stream (FILE *stream, void *resblock) +{ + struct sha1_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + sha1_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + while (1) + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + + if (sum == BLOCKSIZE) + break; + + if (n == 0) + { + /* Check for the error flag IFF N == 0, so that we don't + exit the loop after a partial read due to e.g., EAGAIN + or EWOULDBLOCK. */ + if (ferror (stream)) + return 1; + goto process_partial_block; + } + + /* We've read at least one byte, so ignore errors. But always + check for EOF, since feof may be true even though N > 0. + Otherwise, we could end up calling fread after EOF. */ + if (feof (stream)) + goto process_partial_block; + } + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + sha1_process_block (buffer, BLOCKSIZE, &ctx); + } + + process_partial_block:; + + /* Process any remaining bytes. */ + if (sum > 0) + sha1_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + sha1_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +sha1_buffer (const char *buffer, size_t len, void *resblock) +{ + struct sha1_ctx ctx; + + /* Initialize the computation context. */ + sha1_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + sha1_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return sha1_finish_ctx (&ctx, resblock); +} + +void +sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, + &((char *) ctx->buffer)[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#if !_STRING_ARCH_unaligned +# define alignof(type) offsetof (struct { char c; type x; }, x) +# define UNALIGNED_P(p) (((size_t) p) % alignof (uint32_t) != 0) + if (UNALIGNED_P (buffer)) + while (len > 64) + { + sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else +#endif + { + sha1_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, len);< |