Build: move md5.c from auths library to main; exim_fixdb no longer depends on auths
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 26 Apr 2019 18:36:40 +0000 (19:36 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 26 Apr 2019 18:36:40 +0000 (19:36 +0100)
src/OS/Makefile-Base
src/scripts/MakeLinks
src/src/auths/Makefile
src/src/auths/md5.c [deleted file]
src/src/md5.c [new file with mode: 0644]

index b99cb9ffcc8275affc1c4604fcd2575ac7bde207..0fbee9d0379c3286abec23befd3e542ffc93d57b 100644 (file)
@@ -502,7 +502,7 @@ OBJ_LOOKUPS = lookups/lf_quote.o lookups/lf_check_file.o lookups/lf_sqlperform.o
 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 \
@@ -548,12 +548,12 @@ exim_dumpdb: $(OBJ_DUMPDB)
 
 # 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; \
@@ -745,6 +745,10 @@ util-string.o:   $(HDRS) string.c
        @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
@@ -794,6 +798,7 @@ ip.o:            $(HDRS) ip.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
index 8d8345c06f22790bf260514d9ac688187e82e6c8..14fdb00005ed2330a0b1d67076fb8fab8ec409ae 100755 (executable)
@@ -73,7 +73,7 @@ cd auths
 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
@@ -99,12 +99,12 @@ for f in blob.h dbfunctions.h dbstuff.h exim.h functions.h globals.h \
   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 \
index 402f1417a8d2d31920bf566025ffbf3cca001de8..e85b22a8ad8bdf20f2eee098a06425a7b01f8058 100644 (file)
@@ -8,7 +8,7 @@
 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)
@@ -28,7 +28,6 @@ call_radius.o:      $(HDRS) call_radius.c
 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
diff --git a/src/src/auths/md5.c b/src/src/auths/md5.c
deleted file mode 100644 (file)
index 0536fee..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-/*************************************************
-*     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 */
diff --git a/src/src/md5.c b/src/src/md5.c
new file mode 100644 (file)
index 0000000..cc66079
--- /dev/null
@@ -0,0 +1,354 @@
+/*************************************************
+*     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 */