OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \
filtertest.o globals.o dkim.o dkim_transport.o hash.o \
- header.o host.o ip.o log.o lss.o match.o moan.o \
+ header.o host.o ip.o log.o lss.o match.o md5.o moan.o \
os.o parse.o queue.o \
rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
route.o search.o sieve.o smtp_in.o smtp_out.o spool_in.o spool_out.o \
# The utility for interrogating/fixing the contents of an exim database
-OBJ_FIXDB = exim_fixdb.o util-os.o util-store.o
+OBJ_FIXDB = exim_fixdb.o util-os.o util-store.o util-md5.o
-exim_fixdb: $(OBJ_FIXDB) buildauths
+exim_fixdb: $(OBJ_FIXDB)
@echo "$(LNCC) -o exim_fixdb"
$(FE)$(LNCC) $(CFLAGS) $(INCLUDE) -o exim_fixdb $(LFLAGS) $(OBJ_FIXDB) \
- auths/auths.a $(LIBS) $(EXTRALIBS) $(DBMLIB)
+ $(LIBS) $(EXTRALIBS) $(DBMLIB)
@if [ x"$(STRIP_COMMAND)" != x"" ]; then \
echo $(STRIP_COMMAND) exim_fixdb; \
$(STRIP_COMMAND) exim_fixdb; \
@echo "$(CC) -DCOMPILE_UTILITY string.c"
$(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-string.o string.c
+util-md5.o: $(HDRS) md5.c
+ @echo "$(CC) -DCOMPILE_UTILITY queue.c"
+ $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-md5.o md5.c
+
util-queue.o: $(HDRS) queue.c
@echo "$(CC) -DCOMPILE_UTILITY queue.c"
$(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-queue.o queue.c
log.o: $(HDRS) log.c
lss.o: $(HDRS) lss.c
match.o: $(HDRS) match.c
+md5.o: $(HDRS) md5.c
moan.o: $(HDRS) moan.c
os.o: $(HDRS) $(OS_C_INCLUDES) os.c
parse.o: $(HDRS) parse.c
for f in README Makefile call_pam.c call_pwcheck.c \
call_radius.c check_serv_cond.c cyrus_sasl.c cyrus_sasl.h gsasl_exim.c \
gsasl_exim.h get_data.c get_no64_data.c heimdal_gssapi.c heimdal_gssapi.h \
- md5.c xtextencode.c xtextdecode.c cram_md5.c cram_md5.h plaintext.c plaintext.h \
+ xtextencode.c xtextdecode.c cram_md5.c cram_md5.h plaintext.c plaintext.h \
pwcheck.c pwcheck.h auth-spa.c auth-spa.h dovecot.c dovecot.h sha1.c spa.c \
spa.h tls.c tls.h external.c external.h
do
hash.h local_scan.h \
macros.h mytypes.h osfunctions.h store.h structs.h lookupapi.h sha_ver.h \
\
- acl.c buildconfig.c base64.c child.c crypt16.c daemon.c dbfn.c debug.c deliver.c \
- directory.c dns.c drtables.c dummies.c enq.c exim.c exim_dbmbuild.c \
- exim_dbutil.c exim_lock.c expand.c filter.c filtertest.c globals.c \
- hash.c header.c host.c ip.c log.c lss.c match.c moan.c parse.c perl.c queue.c \
- rda.c readconf.c receive.c retry.c rewrite.c rfc2047.c route.c search.c \
- setenv.c environment.c \
+ acl.c buildconfig.c base64.c child.c crypt16.c daemon.c dbfn.c debug.c \
+ deliver.c directory.c dns.c drtables.c dummies.c enq.c exim.c \
+ exim_dbmbuild.c exim_dbutil.c exim_lock.c expand.c filter.c filtertest.c \
+ globals.c hash.c header.c host.c ip.c log.c lss.c match.c md5.c moan.c \
+ parse.c perl.c queue.c rda.c readconf.c receive.c retry.c rewrite.c \
+ rfc2047.c route.c search.c setenv.c environment.c \
sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \
string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-cipher-stdname.c \
tls-gnu.c tls-openssl.c \
OBJ = auth-spa.o call_pam.o call_pwcheck.o \
call_radius.o check_serv_cond.o cram_md5.o cyrus_sasl.o dovecot.o \
external.o get_data.o get_no64_data.o gsasl_exim.o heimdal_gssapi.o \
- md5.o plaintext.o pwcheck.o \
+ plaintext.o pwcheck.o \
spa.o tls.o xtextdecode.o xtextencode.o
auths.a: $(OBJ)
check_serv_cond.o: $(HDRS) check_serv_cond.c
get_data.o: $(HDRS) get_data.c
get_no64_data.o: $(HDRS) get_no64_data.c
-md5.o: $(HDRS) md5.c
pwcheck.o: $(HDRS) pwcheck.c pwcheck.h
xtextdecode.o: $(HDRS) xtextdecode.c
xtextencode.o: $(HDRS) xtextencode.c
+++ /dev/null
-/*************************************************
-* Exim - an Internet mail transport agent *
-*************************************************/
-
-/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* See the file NOTICE for conditions of use and distribution. */
-
-#ifndef STAND_ALONE
-#include "../exim.h"
-
-/* For stand-alone testing, we need to have the structure defined, and
-to be able to do I/O */
-
-#else
-#include <stdio.h>
-#include "../mytypes.h"
-typedef struct md5 {
- unsigned int length;
- unsigned int abcd[4];
- }
-md5;
-#endif
-
-
-
-/*************************************************
-* Start off a new MD5 computation. *
-*************************************************/
-
-/*
-Argument: pointer to md5 storage structure
-Returns: nothing
-*/
-
-void
-md5_start(md5 *base)
-{
-base->abcd[0] = 0x67452301;
-base->abcd[1] = 0xefcdab89;
-base->abcd[2] = 0x98badcfe;
-base->abcd[3] = 0x10325476;
-base->length = 0;
-}
-
-
-
-/*************************************************
-* Process another 64-byte block *
-*************************************************/
-
-/* This function implements central part of the algorithm which is described
-in RFC 1321.
-
-Arguments:
- base pointer to md5 storage structure
- text pointer to next 64 bytes of subject text
-
-Returns: nothing
-*/
-
-void
-md5_mid(md5 *base, const uschar *text)
-{
-register unsigned int a = base->abcd[0];
-register unsigned int b = base->abcd[1];
-register unsigned int c = base->abcd[2];
-register unsigned int d = base->abcd[3];
-unsigned int X[16];
-base->length += 64;
-
-/* Load the 64 bytes into a set of working integers, treating them as 32-bit
-numbers in little-endian order. */
-
-for (int i = 0; i < 16; i++)
- {
- X[i] = (unsigned int)(text[0]) |
- ((unsigned int)(text[1]) << 8) |
- ((unsigned int)(text[2]) << 16) |
- ((unsigned int)(text[3]) << 24);
- text += 4;
- }
-
-/* For each round of processing there is a function to be applied. We define it
-as a macro each time round. */
-
-/***********************************************
-* Round 1 *
-* F(X,Y,Z) = XY v not(X) Z *
-* a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) *
-***********************************************/
-
-#define OP(a, b, c, d, k, s, ti) \
- a += ((b & c) | (~b & d)) + X[k] + (unsigned int)ti; \
- a = b + ((a << s) | (a >> (32 - s)))
-
-OP(a, b, c, d, 0, 7, 0xd76aa478);
-OP(d, a, b, c, 1, 12, 0xe8c7b756);
-OP(c, d, a, b, 2, 17, 0x242070db);
-OP(b, c, d, a, 3, 22, 0xc1bdceee);
-OP(a, b, c, d, 4, 7, 0xf57c0faf);
-OP(d, a, b, c, 5, 12, 0x4787c62a);
-OP(c, d, a, b, 6, 17, 0xa8304613);
-OP(b, c, d, a, 7, 22, 0xfd469501);
-OP(a, b, c, d, 8, 7, 0x698098d8);
-OP(d, a, b, c, 9, 12, 0x8b44f7af);
-OP(c, d, a, b, 10, 17, 0xffff5bb1);
-OP(b, c, d, a, 11, 22, 0x895cd7be);
-OP(a, b, c, d, 12, 7, 0x6b901122);
-OP(d, a, b, c, 13, 12, 0xfd987193);
-OP(c, d, a, b, 14, 17, 0xa679438e);
-OP(b, c, d, a, 15, 22, 0x49b40821);
-
-#undef OP
-
-/***********************************************
-* Round 2 *
-* F(X,Y,Z) = XZ v Y not(Z) *
-* a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) *
-***********************************************/
-
-#define OP(a, b, c, d, k, s, ti) \
- a += ((b & d) | (c & ~d)) + X[k] + (unsigned int)ti; \
- a = b + ((a << s) | (a >> (32 - s)))
-
-OP(a, b, c, d, 1, 5, 0xf61e2562);
-OP(d, a, b, c, 6, 9, 0xc040b340);
-OP(c, d, a, b, 11, 14, 0x265e5a51);
-OP(b, c, d, a, 0, 20, 0xe9b6c7aa);
-OP(a, b, c, d, 5, 5, 0xd62f105d);
-OP(d, a, b, c, 10, 9, 0x02441453);
-OP(c, d, a, b, 15, 14, 0xd8a1e681);
-OP(b, c, d, a, 4, 20, 0xe7d3fbc8);
-OP(a, b, c, d, 9, 5, 0x21e1cde6);
-OP(d, a, b, c, 14, 9, 0xc33707d6);
-OP(c, d, a, b, 3, 14, 0xf4d50d87);
-OP(b, c, d, a, 8, 20, 0x455a14ed);
-OP(a, b, c, d, 13, 5, 0xa9e3e905);
-OP(d, a, b, c, 2, 9, 0xfcefa3f8);
-OP(c, d, a, b, 7, 14, 0x676f02d9);
-OP(b, c, d, a, 12, 20, 0x8d2a4c8a);
-
-#undef OP
-
-/***********************************************
-* Round 3 *
-* F(X,Y,Z) = X xor Y xor Z *
-* a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) *
-***********************************************/
-
-#define OP(a, b, c, d, k, s, ti) \
- a += (b ^ c ^ d) + X[k] + (unsigned int)ti; \
- a = b + ((a << s) | (a >> (32 - s)))
-
-OP(a, b, c, d, 5, 4, 0xfffa3942);
-OP(d, a, b, c, 8, 11, 0x8771f681);
-OP(c, d, a, b, 11, 16, 0x6d9d6122);
-OP(b, c, d, a, 14, 23, 0xfde5380c);
-OP(a, b, c, d, 1, 4, 0xa4beea44);
-OP(d, a, b, c, 4, 11, 0x4bdecfa9);
-OP(c, d, a, b, 7, 16, 0xf6bb4b60);
-OP(b, c, d, a, 10, 23, 0xbebfbc70);
-OP(a, b, c, d, 13, 4, 0x289b7ec6);
-OP(d, a, b, c, 0, 11, 0xeaa127fa);
-OP(c, d, a, b, 3, 16, 0xd4ef3085);
-OP(b, c, d, a, 6, 23, 0x04881d05);
-OP(a, b, c, d, 9, 4, 0xd9d4d039);
-OP(d, a, b, c, 12, 11, 0xe6db99e5);
-OP(c, d, a, b, 15, 16, 0x1fa27cf8);
-OP(b, c, d, a, 2, 23, 0xc4ac5665);
-
-#undef OP
-
-/***********************************************
-* Round 4 *
-* F(X,Y,Z) = Y xor (X v not(Z)) *
-* a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) *
-***********************************************/
-
-#define OP(a, b, c, d, k, s, ti) \
- a += (c ^ (b | ~d)) + X[k] + (unsigned int)ti; \
- a = b + ((a << s) | (a >> (32 - s)))
-
-OP(a, b, c, d, 0, 6, 0xf4292244);
-OP(d, a, b, c, 7, 10, 0x432aff97);
-OP(c, d, a, b, 14, 15, 0xab9423a7);
-OP(b, c, d, a, 5, 21, 0xfc93a039);
-OP(a, b, c, d, 12, 6, 0x655b59c3);
-OP(d, a, b, c, 3, 10, 0x8f0ccc92);
-OP(c, d, a, b, 10, 15, 0xffeff47d);
-OP(b, c, d, a, 1, 21, 0x85845dd1);
-OP(a, b, c, d, 8, 6, 0x6fa87e4f);
-OP(d, a, b, c, 15, 10, 0xfe2ce6e0);
-OP(c, d, a, b, 6, 15, 0xa3014314);
-OP(b, c, d, a, 13, 21, 0x4e0811a1);
-OP(a, b, c, d, 4, 6, 0xf7537e82);
-OP(d, a, b, c, 11, 10, 0xbd3af235);
-OP(c, d, a, b, 2, 15, 0x2ad7d2bb);
-OP(b, c, d, a, 9, 21, 0xeb86d391);
-
-#undef OP
-
-/* Add the new values back into the accumulators. */
-
-base->abcd[0] += a;
-base->abcd[1] += b;
-base->abcd[2] += c;
-base->abcd[3] += d;
-}
-
-
-
-
-/*************************************************
-* Process the final text string *
-*************************************************/
-
-/* The string may be of any length. It is padded out according to the rules
-for computing MD5 digests. The final result is then converted to text form
-and returned.
-
-Arguments:
- base pointer to the md5 storage structure
- text pointer to the final text vector
- length length of the final text vector
- digest points to 16 bytes in which to place the result
-
-Returns: nothing
-*/
-
-void
-md5_end(md5 *base, const uschar *text, int length, uschar *digest)
-{
-uschar work[64];
-
-/* Process in chunks of 64 until we have less than 64 bytes left. */
-
-while (length >= 64)
- {
- md5_mid(base, text);
- text += 64;
- length -= 64;
- }
-
-/* If the remaining string contains more than 55 bytes, we must pad it
-out to 64, process it, and then set up the final chunk as 56 bytes of
-padding. If it has less than 56 bytes, we pad it out to 56 bytes as the
-final chunk. */
-
-memcpy(work, text, length);
-work[length] = 0x80;
-
-if (length > 55)
- {
- memset(work+length+1, 0, 63-length);
- md5_mid(base, work);
- base->length -= 64;
- memset(work, 0, 56);
- }
-else
- {
- memset(work+length+1, 0, 55-length);
- }
-
-/* The final 8 bytes of the final chunk are a 64-bit representation of the
-length of the input string *bits*, before padding, low order word first, and
-low order bytes first in each word. This implementation is designed for short
-strings, and so operates with a single int counter only. */
-
-length += base->length; /* Total length in bytes */
-length <<= 3; /* Total length in bits */
-
-work[56] = length & 0xff;
-work[57] = (length >> 8) & 0xff;
-work[58] = (length >> 16) & 0xff;
-work[59] = (length >> 24) & 0xff;
-
-memset(work+60, 0, 4);
-
-/* Process the final 64-byte chunk */
-
-md5_mid(base, work);
-
-/* Pass back the result, low-order byte first in each word. */
-
-for (int i = 0; i < 4; i++)
- {
- register int x = base->abcd[i];
- *digest++ = x & 0xff;
- *digest++ = (x >> 8) & 0xff;
- *digest++ = (x >> 16) & 0xff;
- *digest++ = (x >> 24) & 0xff;
- }
-}
-
-
-
-/*************************************************
-**************************************************
-* Stand-alone test program *
-**************************************************
-*************************************************/
-
-#if defined STAND_ALONE & !defined CRAM_STAND_ALONE
-
-/* Test values */
-
-static uschar *tests[] = {
- "", "d41d8cd98f00b204e9800998ecf8427e",
-
- "a", "0cc175b9c0f1b6a831c399e269772661",
-
- "abc", "900150983cd24fb0d6963f7d28e17f72",
-
- "message digest", "f96b697d7cb7938d525a2f31aaf161d0",
-
- "abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b",
-
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
- "d174ab98d277d9f5a5611c2c9f419d9f",
-
- "1234567890123456789012345678901234567890123456789012345678901234567890"
- "1234567890",
- "57edf4a22be3c955ac49da2e2107b67a",
-
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
- "a0842fcc02167127b0bb9a7c38e71ba8"
-};
-
-int main(void)
-{
-md5 base;
-int i = 0x01020304;
-uschar *ctest = US (&i);
-uschar buffer[256];
-uschar digest[16];
-printf("Checking md5: %s-endian\n", (ctest[0] == 0x04)? "little" : "big");
-
-for (i = 0; i < sizeof(tests)/sizeof(uschar *); i += 2)
- {
- uschar s[33];
- printf("%s\nShould be: %s\n", tests[i], tests[i+1]);
- md5_start(&base);
- md5_end(&base, tests[i], strlen(tests[i]), digest);
- for (int j = 0; j < 16; j++) sprintf(s+2*j, "%02x", digest[j]);
- printf("Computed: %s\n", s);
- if (strcmp(s, tests[i+1]) != 0) printf("*** No match ***\n");
- printf("\n");
- }
-}
-#endif
-
-/* End of md5.c */
--- /dev/null
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+#ifndef STAND_ALONE
+#include "exim.h"
+
+/* For stand-alone testing, we need to have the structure defined, and
+to be able to do I/O */
+
+#else
+#include <stdio.h>
+#include "../mytypes.h"
+typedef struct md5 {
+ unsigned int length;
+ unsigned int abcd[4];
+ }
+md5;
+#endif
+
+
+
+/*************************************************
+* Start off a new MD5 computation. *
+*************************************************/
+
+/*
+Argument: pointer to md5 storage structure
+Returns: nothing
+*/
+
+void
+md5_start(md5 *base)
+{
+base->abcd[0] = 0x67452301;
+base->abcd[1] = 0xefcdab89;
+base->abcd[2] = 0x98badcfe;
+base->abcd[3] = 0x10325476;
+base->length = 0;
+}
+
+
+
+/*************************************************
+* Process another 64-byte block *
+*************************************************/
+
+/* This function implements central part of the algorithm which is described
+in RFC 1321.
+
+Arguments:
+ base pointer to md5 storage structure
+ text pointer to next 64 bytes of subject text
+
+Returns: nothing
+*/
+
+void
+md5_mid(md5 *base, const uschar *text)
+{
+register unsigned int a = base->abcd[0];
+register unsigned int b = base->abcd[1];
+register unsigned int c = base->abcd[2];
+register unsigned int d = base->abcd[3];
+unsigned int X[16];
+base->length += 64;
+
+/* Load the 64 bytes into a set of working integers, treating them as 32-bit
+numbers in little-endian order. */
+
+for (int i = 0; i < 16; i++)
+ {
+ X[i] = (unsigned int)(text[0]) |
+ ((unsigned int)(text[1]) << 8) |
+ ((unsigned int)(text[2]) << 16) |
+ ((unsigned int)(text[3]) << 24);
+ text += 4;
+ }
+
+/* For each round of processing there is a function to be applied. We define it
+as a macro each time round. */
+
+/***********************************************
+* Round 1 *
+* F(X,Y,Z) = XY v not(X) Z *
+* a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) *
+***********************************************/
+
+#define OP(a, b, c, d, k, s, ti) \
+ a += ((b & c) | (~b & d)) + X[k] + (unsigned int)ti; \
+ a = b + ((a << s) | (a >> (32 - s)))
+
+OP(a, b, c, d, 0, 7, 0xd76aa478);
+OP(d, a, b, c, 1, 12, 0xe8c7b756);
+OP(c, d, a, b, 2, 17, 0x242070db);
+OP(b, c, d, a, 3, 22, 0xc1bdceee);
+OP(a, b, c, d, 4, 7, 0xf57c0faf);
+OP(d, a, b, c, 5, 12, 0x4787c62a);
+OP(c, d, a, b, 6, 17, 0xa8304613);
+OP(b, c, d, a, 7, 22, 0xfd469501);
+OP(a, b, c, d, 8, 7, 0x698098d8);
+OP(d, a, b, c, 9, 12, 0x8b44f7af);
+OP(c, d, a, b, 10, 17, 0xffff5bb1);
+OP(b, c, d, a, 11, 22, 0x895cd7be);
+OP(a, b, c, d, 12, 7, 0x6b901122);
+OP(d, a, b, c, 13, 12, 0xfd987193);
+OP(c, d, a, b, 14, 17, 0xa679438e);
+OP(b, c, d, a, 15, 22, 0x49b40821);
+
+#undef OP
+
+/***********************************************
+* Round 2 *
+* F(X,Y,Z) = XZ v Y not(Z) *
+* a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) *
+***********************************************/
+
+#define OP(a, b, c, d, k, s, ti) \
+ a += ((b & d) | (c & ~d)) + X[k] + (unsigned int)ti; \
+ a = b + ((a << s) | (a >> (32 - s)))
+
+OP(a, b, c, d, 1, 5, 0xf61e2562);
+OP(d, a, b, c, 6, 9, 0xc040b340);
+OP(c, d, a, b, 11, 14, 0x265e5a51);
+OP(b, c, d, a, 0, 20, 0xe9b6c7aa);
+OP(a, b, c, d, 5, 5, 0xd62f105d);
+OP(d, a, b, c, 10, 9, 0x02441453);
+OP(c, d, a, b, 15, 14, 0xd8a1e681);
+OP(b, c, d, a, 4, 20, 0xe7d3fbc8);
+OP(a, b, c, d, 9, 5, 0x21e1cde6);
+OP(d, a, b, c, 14, 9, 0xc33707d6);
+OP(c, d, a, b, 3, 14, 0xf4d50d87);
+OP(b, c, d, a, 8, 20, 0x455a14ed);
+OP(a, b, c, d, 13, 5, 0xa9e3e905);
+OP(d, a, b, c, 2, 9, 0xfcefa3f8);
+OP(c, d, a, b, 7, 14, 0x676f02d9);
+OP(b, c, d, a, 12, 20, 0x8d2a4c8a);
+
+#undef OP
+
+/***********************************************
+* Round 3 *
+* F(X,Y,Z) = X xor Y xor Z *
+* a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) *
+***********************************************/
+
+#define OP(a, b, c, d, k, s, ti) \
+ a += (b ^ c ^ d) + X[k] + (unsigned int)ti; \
+ a = b + ((a << s) | (a >> (32 - s)))
+
+OP(a, b, c, d, 5, 4, 0xfffa3942);
+OP(d, a, b, c, 8, 11, 0x8771f681);
+OP(c, d, a, b, 11, 16, 0x6d9d6122);
+OP(b, c, d, a, 14, 23, 0xfde5380c);
+OP(a, b, c, d, 1, 4, 0xa4beea44);
+OP(d, a, b, c, 4, 11, 0x4bdecfa9);
+OP(c, d, a, b, 7, 16, 0xf6bb4b60);
+OP(b, c, d, a, 10, 23, 0xbebfbc70);
+OP(a, b, c, d, 13, 4, 0x289b7ec6);
+OP(d, a, b, c, 0, 11, 0xeaa127fa);
+OP(c, d, a, b, 3, 16, 0xd4ef3085);
+OP(b, c, d, a, 6, 23, 0x04881d05);
+OP(a, b, c, d, 9, 4, 0xd9d4d039);
+OP(d, a, b, c, 12, 11, 0xe6db99e5);
+OP(c, d, a, b, 15, 16, 0x1fa27cf8);
+OP(b, c, d, a, 2, 23, 0xc4ac5665);
+
+#undef OP
+
+/***********************************************
+* Round 4 *
+* F(X,Y,Z) = Y xor (X v not(Z)) *
+* a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) *
+***********************************************/
+
+#define OP(a, b, c, d, k, s, ti) \
+ a += (c ^ (b | ~d)) + X[k] + (unsigned int)ti; \
+ a = b + ((a << s) | (a >> (32 - s)))
+
+OP(a, b, c, d, 0, 6, 0xf4292244);
+OP(d, a, b, c, 7, 10, 0x432aff97);
+OP(c, d, a, b, 14, 15, 0xab9423a7);
+OP(b, c, d, a, 5, 21, 0xfc93a039);
+OP(a, b, c, d, 12, 6, 0x655b59c3);
+OP(d, a, b, c, 3, 10, 0x8f0ccc92);
+OP(c, d, a, b, 10, 15, 0xffeff47d);
+OP(b, c, d, a, 1, 21, 0x85845dd1);
+OP(a, b, c, d, 8, 6, 0x6fa87e4f);
+OP(d, a, b, c, 15, 10, 0xfe2ce6e0);
+OP(c, d, a, b, 6, 15, 0xa3014314);
+OP(b, c, d, a, 13, 21, 0x4e0811a1);
+OP(a, b, c, d, 4, 6, 0xf7537e82);
+OP(d, a, b, c, 11, 10, 0xbd3af235);
+OP(c, d, a, b, 2, 15, 0x2ad7d2bb);
+OP(b, c, d, a, 9, 21, 0xeb86d391);
+
+#undef OP
+
+/* Add the new values back into the accumulators. */
+
+base->abcd[0] += a;
+base->abcd[1] += b;
+base->abcd[2] += c;
+base->abcd[3] += d;
+}
+
+
+
+
+/*************************************************
+* Process the final text string *
+*************************************************/
+
+/* The string may be of any length. It is padded out according to the rules
+for computing MD5 digests. The final result is then converted to text form
+and returned.
+
+Arguments:
+ base pointer to the md5 storage structure
+ text pointer to the final text vector
+ length length of the final text vector
+ digest points to 16 bytes in which to place the result
+
+Returns: nothing
+*/
+
+void
+md5_end(md5 *base, const uschar *text, int length, uschar *digest)
+{
+uschar work[64];
+
+/* Process in chunks of 64 until we have less than 64 bytes left. */
+
+while (length >= 64)
+ {
+ md5_mid(base, text);
+ text += 64;
+ length -= 64;
+ }
+
+/* If the remaining string contains more than 55 bytes, we must pad it
+out to 64, process it, and then set up the final chunk as 56 bytes of
+padding. If it has less than 56 bytes, we pad it out to 56 bytes as the
+final chunk. */
+
+memcpy(work, text, length);
+work[length] = 0x80;
+
+if (length > 55)
+ {
+ memset(work+length+1, 0, 63-length);
+ md5_mid(base, work);
+ base->length -= 64;
+ memset(work, 0, 56);
+ }
+else
+ {
+ memset(work+length+1, 0, 55-length);
+ }
+
+/* The final 8 bytes of the final chunk are a 64-bit representation of the
+length of the input string *bits*, before padding, low order word first, and
+low order bytes first in each word. This implementation is designed for short
+strings, and so operates with a single int counter only. */
+
+length += base->length; /* Total length in bytes */
+length <<= 3; /* Total length in bits */
+
+work[56] = length & 0xff;
+work[57] = (length >> 8) & 0xff;
+work[58] = (length >> 16) & 0xff;
+work[59] = (length >> 24) & 0xff;
+
+memset(work+60, 0, 4);
+
+/* Process the final 64-byte chunk */
+
+md5_mid(base, work);
+
+/* Pass back the result, low-order byte first in each word. */
+
+for (int i = 0; i < 4; i++)
+ {
+ register int x = base->abcd[i];
+ *digest++ = x & 0xff;
+ *digest++ = (x >> 8) & 0xff;
+ *digest++ = (x >> 16) & 0xff;
+ *digest++ = (x >> 24) & 0xff;
+ }
+}
+
+
+
+/*************************************************
+**************************************************
+* Stand-alone test program *
+**************************************************
+*************************************************/
+
+#if defined STAND_ALONE & !defined CRAM_STAND_ALONE
+
+/* Test values */
+
+static uschar *tests[] = {
+ "", "d41d8cd98f00b204e9800998ecf8427e",
+
+ "a", "0cc175b9c0f1b6a831c399e269772661",
+
+ "abc", "900150983cd24fb0d6963f7d28e17f72",
+
+ "message digest", "f96b697d7cb7938d525a2f31aaf161d0",
+
+ "abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b",
+
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "d174ab98d277d9f5a5611c2c9f419d9f",
+
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890",
+ "57edf4a22be3c955ac49da2e2107b67a",
+
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "a0842fcc02167127b0bb9a7c38e71ba8"
+};
+
+int main(void)
+{
+md5 base;
+int i = 0x01020304;
+uschar *ctest = US (&i);
+uschar buffer[256];
+uschar digest[16];
+printf("Checking md5: %s-endian\n", (ctest[0] == 0x04)? "little" : "big");
+
+for (i = 0; i < sizeof(tests)/sizeof(uschar *); i += 2)
+ {
+ uschar s[33];
+ printf("%s\nShould be: %s\n", tests[i], tests[i+1]);
+ md5_start(&base);
+ md5_end(&base, tests[i], strlen(tests[i]), digest);
+ for (int j = 0; j < 16; j++) sprintf(s+2*j, "%02x", digest[j]);
+ printf("Computed: %s\n", s);
+ if (strcmp(s, tests[i+1]) != 0) printf("*** No match ***\n");
+ printf("\n");
+ }
+}
+#endif
+
+/* End of md5.c */