]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Allow local md4 functions to be selected at runtime depending on OpenSSL's FIPS setting
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 7 Jan 2019 11:59:47 +0000 (19:59 +0800)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 8 Jan 2019 04:03:19 +0000 (12:03 +0800)
src/lib/server/main_config.c
src/lib/server/xlat_func.c
src/lib/util/md4.c
src/lib/util/md4.h
src/modules/rlm_cram/rlm_cram.c
src/tests/keywords/md4 [new file with mode: 0644]

index a4bf0f09adeb7eb7095feb0f86ade86f4a91b5ab..fdb7073010f3cd592877f2231c15c2f76de5d0f1 100644 (file)
@@ -220,6 +220,7 @@ static const CONF_PARSER security_config[] = {
 
 #ifdef ENABLE_OPENSSL_VERSION_CHECK
        { FR_CONF_OFFSET("allow_vulnerable_openssl", FR_TYPE_STRING, main_config_t, allow_vulnerable_openssl), .dflt = "no" },
+#endif
 
 #ifdef HAVE_OPENSSL_CRYPTO_H
        { FR_CONF_OFFSET_IS_SET("openssl_fips_mode", FR_TYPE_BOOL, main_config_t, openssl_fips_mode), .dflt = "no" },
index b02bc6aa72d0837f062f3e4903d0582c003b8356..dc2bd44dda1266c81233acf8f5868d8a308c879b 100644 (file)
@@ -1126,6 +1126,45 @@ static xlat_action_t toupper_xlat(TALLOC_CTX *ctx, fr_cursor_t *out,
        return _xlat_change_case(true, ctx, out, request, in);
 }
 
+
+/** Calculate the MD4 hash of a string or attribute.
+ *
+ * Example: "%{md4:foo}" == "0ac6700c491d70fb8650940b1ca1e4b2"
+ */
+static xlat_action_t md4_xlat(TALLOC_CTX *ctx, fr_cursor_t *out,
+                             REQUEST *request, UNUSED void const *xlat_inst, UNUSED void *xlat_thread_inst,
+                             fr_value_box_t **in)
+{
+       uint8_t         digest[MD5_DIGEST_LENGTH];
+       fr_md4_ctx_t    *md4_ctx;
+       fr_value_box_t  *vb;
+
+       /*
+        * Concatenate all input if there is some
+        */
+       if (*in && fr_value_box_list_concat(ctx, *in, in, FR_TYPE_OCTETS, true) < 0) {
+               RPEDEBUG("Failed concatenating input");
+               return XLAT_ACTION_FAIL;
+       }
+
+       md4_ctx = fr_md4_ctx_alloc(true);
+       if (*in) {
+               fr_md4_update(md4_ctx, (*in)->vb_octets, (*in)->vb_length);
+       } else {
+               /* MD4 of empty string */
+               fr_md4_update(md4_ctx, NULL, 0);
+       }
+       fr_md4_final(digest, md4_ctx);
+       fr_md4_ctx_free(&md4_ctx);
+
+       MEM(vb = fr_value_box_alloc_null(ctx));
+       fr_value_box_memdup(vb, vb, NULL, digest, sizeof(digest), false);
+
+       fr_cursor_append(out, vb);
+
+       return XLAT_ACTION_DONE;
+}
+
 /** Calculate the MD5 hash of a string or attribute.
  *
  * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
@@ -2535,6 +2574,7 @@ int xlat_init(void)
        xlat_async_register(NULL, "hex", hex_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
        xlat_async_register(NULL, "hmacmd5", hmac_md5_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
        xlat_async_register(NULL, "hmacsha1", hmac_sha1_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+       xlat_async_register(NULL, "md4", md4_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
        xlat_async_register(NULL, "md5", md5_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
        xlat_async_register(NULL, "rand", rand_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
        xlat_async_register(NULL, "randstr", randstr_xlat, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
index 493a256c2278978cc4da57c81e1f89b9e452a542..bfa694efbcf6de617595060b19c78869e247f88b 100644 (file)
  */
 RCSID("$Id$")
 
+#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/strerror.h>
+#include <freeradius-devel/util/talloc.h>
+#include <freeradius-devel/util/thread_local.h>
+
 /*
  *  FORCE MD4 TO USE OUR MD4 HEADER FILE!
  *  If we don't do this, it might pick up the systems broken MD4.
  */
-#include <freeradius-devel/util/md4.h>
+#include "md4.h"
 
-/** Calculate the MD4 hash of the contents of a buffer
+fr_thread_local_setup(fr_md4_ctx_t *, md4_ctx)
+
+/*
+ *     If we have OpenSSL's EVP API available, then build wrapper functions.
  *
- * @param[out] out Where to write the MD4 digest. Must be a minimum of MD4_DIGEST_LENGTH.
- * @param[in] in Data to hash.
- * @param[in] inlen Length of the data.
+ *     We always need to build the local MD4 functions as OpenSSL could
+ *     be operating in FIPS mode where MD4 digest functions are unavailable.
  */
-void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
+#ifdef HAVE_OPENSSL_EVP_H
+#  include <openssl/evp.h>
+#  include <openssl/crypto.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#  define EVP_MD_CTX_new EVP_MD_CTX_create
+#  define EVP_MD_CTX_free EVP_MD_CTX_destroy
+#  define EVP_MD_CTX_reset EVP_MD_CTX_cleanup
+#endif
+
+static int have_openssl_md4 = -1;
+
+static void _md4_ctx_openssl_free_on_exit(void *arg)
 {
-       FR_MD4_CTX ctx;
+       EVP_MD_CTX_free(arg);
+}
 
-       fr_md4_init(&ctx);
-       fr_md4_update(&ctx, in, inlen);
-       fr_md4_final(out, &ctx);
+/** @copydoc fr_md4_ctx_reset
+ *
+ */
+static void fr_md4_openssl_ctx_reset(fr_md4_ctx_t *ctx)
+{
+       EVP_MD_CTX *md_ctx = ctx;
+
+       EVP_MD_CTX_reset(md_ctx);
+       EVP_DigestInit_ex(md_ctx, EVP_md4(), NULL);
+}
+
+/** @copydoc fr_md4_ctx_copy
+ *
+ */
+static void fr_md4_openssl_ctx_copy(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src)
+{
+       EVP_MD_CTX_copy_ex(dst, src);
 }
 
-#ifndef HAVE_OPENSSL_EVP_H
+/** @copydoc fr_md4_ctx_alloc
+ *
+ */
+static fr_md4_ctx_t *fr_md4_openssl_ctx_alloc(bool thread_local)
+{
+       EVP_MD_CTX *md_ctx;
+
+       /*
+        *      Use the thread local ctx to avoid heap allocations.
+        */
+       if (thread_local) {
+               if (unlikely(!md4_ctx)) {
+                       md_ctx = EVP_MD_CTX_new();
+                       if (unlikely(!md_ctx)) {
+                       oom:
+                               fr_strerror_printf("Out of memory");
+                               return NULL;
+                       }
+                       fr_thread_local_set_destructor(md4_ctx, _md4_ctx_openssl_free_on_exit, md_ctx);
+                       EVP_DigestInit_ex(md_ctx, EVP_md4(), NULL);
+               } else {
+                       md_ctx = md4_ctx;
+               }
+       /*
+        *      If the MD4 ctx might be used across a yield point
+        *      shared should be set to false, and new contexts
+        *      should be allocated.
+        */
+       } else {
+               md_ctx = EVP_MD_CTX_new();
+               if (unlikely(!md_ctx)) goto oom;
+               EVP_DigestInit_ex(md_ctx, EVP_md4(), NULL);
+       }
+
+       return md_ctx;
+}
+
+/** @copydoc fr_md4_ctx_free
+ *
+ */
+static void fr_md4_openssl_ctx_free(fr_md4_ctx_t **ctx)
+{
+       if (md4_ctx && (md4_ctx == *ctx)) {
+               fr_md4_openssl_ctx_reset(*ctx);
+               *ctx = NULL;
+               return;
+       }
+
+       EVP_MD_CTX_free(*ctx);
+       *ctx = NULL;
+}
+
+/** @copydoc fr_md4_update
+ *
+ */
+static void fr_md4_openssl_update(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen)
+{
+       EVP_DigestUpdate(ctx, in, inlen);
+}
+
+/** @copydoc fr_md4_final
+ *
+ */
+static void fr_md4_openssl_final(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx)
+{
+       unsigned int len;
+
+       EVP_DigestFinal(ctx, out, &len);
+
+       if (!fr_cond_assert(len == MD4_DIGEST_LENGTH)) return;
+}
+#endif
+
 /*
  * This code implements the MD4 message-digest algorithm.
  * The algorithm is due to Ron Rivest. This code was
@@ -45,7 +151,6 @@ void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_
  * needed on buffers full of bytes, and then call fr_md4_final, which
  * will fill a supplied 16-byte array with the digest.
  */
-
 #ifndef WORDS_BIGENDIAN
 #  define htole32_4(buf)               /* Nothing */
 #  define htole32_14(buf)              /* Nothing */
@@ -103,122 +208,7 @@ void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_
 } while (0)
 #endif
 
-/** Initialise a new MD4 context
- *
- * Set bit count to 0 and buffer to mysterious initialization constants.
- *
- * @param[out] ctx to initialise.
- */
-void fr_md4_init(FR_MD4_CTX *ctx)
-{
-       ctx->count[0] = 0;
-       ctx->count[1] = 0;
-       ctx->state[0] = 0x67452301;
-       ctx->state[1] = 0xefcdab89;
-       ctx->state[2] = 0x98badcfe;
-       ctx->state[3] = 0x10325476;
-}
-
-/** Feed additional data into the MD4 hashing function
- *
- * @param[in,out] ctx to update.
- * @param[in] in Data to hash.
- * @param[in] inlen Length of the data.
- */
-void fr_md4_update(FR_MD4_CTX *ctx, uint8_t const *in, size_t inlen)
-{
-       uint32_t count;
-
-       /* Bytes already stored in ctx->buffer */
-       count = (uint32_t)((ctx->count[0] >> 3) & 0x3f);
-
-       /* Update bitcount */
-/*     ctx->count += (uint64_t)inlen << 3;*/
-       if ((ctx->count[0] += ((uint32_t)inlen << 3)) < (uint32_t)inlen) {
-               /* Overflowed ctx->count[0] */
-               ctx->count[1]++;
-       }
-       ctx->count[1] += ((uint32_t)inlen >> 29);
-
-       /* Handle any leading odd-sized chunks */
-       if (count) {
-               unsigned char *p = (unsigned char *)ctx->buffer + count;
-
-               count = MD4_BLOCK_LENGTH - count;
-               if (inlen < count) {
-                       memcpy(p, in, inlen);
-                       return;
-               }
-               memcpy(p, in, count);
-               htole32_16((uint32_t *)ctx->buffer);
-               fr_md4_transform(ctx->state, ctx->buffer);
-               in += count;
-               inlen -= count;
-       }
-
-       /* Process data in MD4_BLOCK_LENGTH-byte chunks */
-       while (inlen >= MD4_BLOCK_LENGTH) {
-               memcpy(ctx->buffer, in, MD4_BLOCK_LENGTH);
-               htole32_16((uint32_t *)ctx->buffer);
-               fr_md4_transform(ctx->state, ctx->buffer);
-               in += MD4_BLOCK_LENGTH;
-               inlen -= MD4_BLOCK_LENGTH;
-       }
-
-       /* Handle any remaining bytes of data. */
-       memcpy(ctx->buffer, in, inlen);
-}
-
-/** Finalise the MD4 context and write out the hash
- *
- * Final wrapup - pad to 64-byte boundary with the bit pattern 1 0*
- * (64-bit count of bits processed, MSB-first).
- *
- * @param[out] out Where to write the MD4 digest. Minimum length of MD4_DIGEST_LENGTH.
- * @param[in,out] ctx to finalise.
- */
-void fr_md4_final(uint8_t out[static MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx)
-{
-       uint32_t count;
-       unsigned char *p;
-
-       /* number of bytes mod 64 */
-       count = (uint32_t)(ctx->count[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->buffer + 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);
-               htole32_16((uint32_t *)ctx->buffer);
-               fr_md4_transform(ctx->state, ctx->buffer);
-
-               /* Now fill the next block with 56 bytes */
-               memset(ctx->buffer, 0, 56);
-       } else {
-               /* Pad block to 56 bytes */
-               memset(p, 0, count - 8);
-       }
-       htole32_14((uint32_t *)ctx->buffer);
-
-       /* Append bit count and transform */
-       ((uint32_t *)ctx->buffer)[14] = ctx->count[0];
-       ((uint32_t *)ctx->buffer)[15] = ctx->count[1];
-
-       fr_md4_transform(ctx->state, ctx->buffer);
-       htole32_4(ctx->state);
-       memcpy(out, ctx->state, MD4_DIGEST_LENGTH);
-       memset(ctx, 0, sizeof(*ctx));   /* in case it's sensitive */
-}
+#define MD4_BLOCK_LENGTH 64
 
 /* The three core functions - F1 is optimized somewhat */
 #define F1(x, y, z) (z ^ (x & (y ^ z)))
@@ -237,7 +227,7 @@ void fr_md4_final(uint8_t out[static MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx)
  * @param[in] state 16 bytes of data to feed into the hashing function.
  * @param[in,out] block MD4 digest block to update.
  */
-void fr_md4_transform(uint32_t state[static 4], uint8_t const block[static MD4_BLOCK_LENGTH])
+static void fr_md4_local_transform(uint32_t state[static 4], uint8_t const block[static MD4_BLOCK_LENGTH])
 {
        uint32_t a, b, c, d;
        uint32_t const *in = (uint32_t const *)block;
@@ -303,4 +293,237 @@ void fr_md4_transform(uint32_t state[static 4], uint8_t const block[static MD4_B
        state[2] += c;
        state[3] += d;
 }
+
+typedef struct {
+       uint32_t state[4];                      //!< State.
+       uint32_t count[2];                      //!< Number of bits, mod 2^64.
+       uint8_t buffer[MD4_BLOCK_LENGTH];       //!< Input buffer.
+} fr_md4_ctx_local_t;
+
+/** @copydoc fr_md4_ctx_reset
+ *
+ */
+static void fr_md4_local_ctx_reset(fr_md4_ctx_t *ctx)
+{
+       fr_md4_ctx_local_t      *ctx_local = talloc_get_type_abort(ctx, fr_md4_ctx_local_t);
+
+       ctx_local->count[0] = 0;
+       ctx_local->count[1] = 0;
+       ctx_local->state[0] = 0x67452301;
+       ctx_local->state[1] = 0xefcdab89;
+       ctx_local->state[2] = 0x98badcfe;
+       ctx_local->state[3] = 0x10325476;
+}
+
+/** @copydoc fr_md4_ctx_copy
+ *
+ */
+static void fr_md4_local_ctx_copy(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src)
+{
+       fr_md4_ctx_local_t const *ctx_local_src = talloc_get_type_abort_const(src, fr_md4_ctx_local_t);
+       fr_md4_ctx_local_t *ctx_local_dst = talloc_get_type_abort(dst, fr_md4_ctx_local_t);
+
+       memcpy(ctx_local_dst, ctx_local_src, sizeof(*ctx_local_dst));
+}
+
+static void _md4_ctx_local_free_on_exit(void *arg)
+{
+       talloc_free(arg);
+}
+
+/** @copydoc fr_md4_ctx_alloc
+ *
+ */
+static fr_md4_ctx_t *fr_md4_local_ctx_alloc(bool thread_local)
+{
+       fr_md4_ctx_local_t *ctx_local;
+
+#ifdef HAVE_OPENSSL_EVP_H
+       if (unlikely(have_openssl_md4 == -1)) {
+               /*
+                *      If we're not in FIPS mode, then swap out the
+                *      md4 functions, and call the OpenSSL init
+                *      function.
+                */
+               if (FIPS_mode() == 0) {
+                       have_openssl_md4 = 1;
+
+                       /*
+                        *      Swap out the functions pointers
+                        *      for the OpenSSL versions.
+                        */
+                       fr_md4_ctx_reset = fr_md4_openssl_ctx_reset;
+                       fr_md4_ctx_copy = fr_md4_openssl_ctx_copy;
+                       fr_md4_ctx_alloc = fr_md4_openssl_ctx_alloc;
+                       fr_md4_ctx_free = fr_md4_openssl_ctx_free;
+                       fr_md4_update = fr_md4_openssl_update;
+                       fr_md4_final = fr_md4_openssl_final;
+
+                       return fr_md4_ctx_alloc(thread_local);
+               }
+
+               have_openssl_md4 = 0;
+       }
 #endif
+
+       /*
+        *      Use the thread local ctx to avoid heap allocations.
+        */
+       if (thread_local) {
+               if (unlikely(!md4_ctx)) {
+                       ctx_local = talloc(NULL, fr_md4_ctx_local_t);
+                       if (unlikely(!ctx_local)) return NULL;
+                       fr_md4_local_ctx_reset(ctx_local);
+                       fr_thread_local_set_destructor(md4_ctx, _md4_ctx_local_free_on_exit, ctx_local);
+               } else {
+                       ctx_local = md4_ctx;
+               }
+       /*
+        *      If the MD4 ctx might be used across a yield point
+        *      shared should be set to false, and new contexts
+        *      should be allocated.
+        */
+       } else {
+               ctx_local = talloc(NULL, fr_md4_ctx_local_t);
+               if (unlikely(!ctx_local)) return NULL;
+               fr_md4_local_ctx_reset(ctx_local);
+       }
+
+       return ctx_local;
+}
+
+/** @copydoc fr_md4_ctx_free
+ *
+ */
+static void fr_md4_local_ctx_free(fr_md4_ctx_t **ctx)
+{
+       if (md4_ctx && (md4_ctx == *ctx)) {
+               fr_md4_local_ctx_reset(*ctx);
+               *ctx = NULL;
+               return; /* Don't free the thread_local ctx */
+       }
+
+       talloc_free(*ctx);
+       *ctx = NULL;
+}
+
+/** @copydoc fr_md4_update
+ *
+ */
+static void fr_md4_local_update(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen)
+{
+       uint32_t                count;
+       fr_md4_ctx_local_t      *ctx_local = talloc_get_type_abort(ctx, fr_md4_ctx_local_t);
+
+       /* Bytes already stored in ctx_local->buffer */
+       count = (uint32_t)((ctx_local->count[0] >> 3) & 0x3f);
+
+       /* Update bitcount */
+/*     ctx_local->count += (uint64_t)inlen << 3;*/
+       if ((ctx_local->count[0] += ((uint32_t)inlen << 3)) < (uint32_t)inlen) {
+               /* Overflowed ctx_local->count[0] */
+               ctx_local->count[1]++;
+       }
+       ctx_local->count[1] += ((uint32_t)inlen >> 29);
+
+       /* Handle any leading odd-sized chunks */
+       if (count) {
+               unsigned char *p = (unsigned char *)ctx_local->buffer + count;
+
+               count = MD4_BLOCK_LENGTH - count;
+               if (inlen < count) {
+                       memcpy(p, in, inlen);
+                       return;
+               }
+               memcpy(p, in, count);
+               htole32_16((uint32_t *)ctx_local->buffer);
+               fr_md4_local_transform(ctx_local->state, ctx_local->buffer);
+               in += count;
+               inlen -= count;
+       }
+
+       /* Process data in MD4_BLOCK_LENGTH-byte chunks */
+       while (inlen >= MD4_BLOCK_LENGTH) {
+               memcpy(ctx_local->buffer, in, MD4_BLOCK_LENGTH);
+               htole32_16((uint32_t *)ctx_local->buffer);
+               fr_md4_local_transform(ctx_local->state, ctx_local->buffer);
+               in += MD4_BLOCK_LENGTH;
+               inlen -= MD4_BLOCK_LENGTH;
+       }
+
+       /* Handle any remaining bytes of data. */
+       memcpy(ctx_local->buffer, in, inlen);
+}
+
+/** @copydoc fr_md4_final
+ *
+ */
+static void fr_md4_local_final(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx)
+{
+       uint32_t                count;
+       unsigned char           *p;
+       fr_md4_ctx_local_t      *ctx_local = talloc_get_type_abort(ctx, fr_md4_ctx_local_t);
+
+       /* number of bytes mod 64 */
+       count = (uint32_t)(ctx_local->count[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_local->buffer + 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);
+               htole32_16((uint32_t *)ctx_local->buffer);
+               fr_md4_local_transform(ctx_local->state, ctx_local->buffer);
+
+               /* Now fill the next block with 56 bytes */
+               memset(ctx_local->buffer, 0, 56);
+       } else {
+               /* Pad block to 56 bytes */
+               memset(p, 0, count - 8);
+       }
+       htole32_14((uint32_t *)ctx_local->buffer);
+
+       /* Append bit count and transform */
+       ((uint32_t *)ctx_local->buffer)[14] = ctx_local->count[0];
+       ((uint32_t *)ctx_local->buffer)[15] = ctx_local->count[1];
+
+       fr_md4_local_transform(ctx_local->state, ctx_local->buffer);
+       htole32_4(ctx_local->state);
+       memcpy(out, ctx_local->state, MD4_DIGEST_LENGTH);
+       memset(ctx_local, 0, sizeof(*ctx_local));       /* in case it's sensitive */
+}
+
+/*
+ *     Digest function pointers
+ */
+fr_md4_ctx_reset_t fr_md4_ctx_reset = fr_md4_local_ctx_reset;
+fr_md4_ctx_copy_t fr_md4_ctx_copy = fr_md4_local_ctx_copy;
+fr_md4_ctx_alloc_t fr_md4_ctx_alloc = fr_md4_local_ctx_alloc;
+fr_md4_ctx_free_t fr_md4_ctx_free = fr_md4_local_ctx_free;
+fr_md4_update_t fr_md4_update = fr_md4_local_update;
+fr_md4_final_t fr_md4_final = fr_md4_local_final;
+
+/** Calculate the MD4 hash of the contents of a buffer
+ *
+ * @param[out] out Where to write the MD4 digest. Must be a minimum of MD4_DIGEST_LENGTH.
+ * @param[in] in Data to hash.
+ * @param[in] inlen Length of the data.
+ */
+void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
+{
+       fr_md4_ctx_t *ctx;
+
+       ctx = fr_md4_ctx_alloc(true);
+       fr_md4_update(ctx, in, inlen);
+       fr_md4_final(out, ctx);
+       fr_md4_ctx_free(&ctx);
+}
index f9ec4e06245e160346b5b8d377dbedc067e0aced..b4eb5ca6b063c329539f56d2573e1a28fb3a2b31 100644 (file)
@@ -4,7 +4,7 @@
  * @note license is LGPL, but largely derived from a public domain source.
  *
  * @file src/lib/util/md4.h
- * @brief Structures and prototypes for md4.
+ * @brief Structures and declarations for md4.
  */
 RCSIDH(md4_h, "$Id$")
 
@@ -15,70 +15,75 @@ extern "C" {
 #include <freeradius-devel/build.h>
 #include <freeradius-devel/missing.h>
 
-#ifdef HAVE_INTTYPES_H
-#  include <inttypes.h>
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
+#include <inttypes.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
 
-#ifdef HAVE_STDINT_H
-#  include <stdint.h>
+#ifndef MD4_DIGEST_LENGTH
+#  define MD4_DIGEST_LENGTH 16
 #endif
 
-#include <string.h>
+typedef void fr_md4_ctx_t;
 
-#ifdef HAVE_OPENSSL_EVP_H
-#  include <openssl/evp.h>
-#endif
+/* md4.c */
 
+/** Reset the ctx to allow reuse
+ *
+ * @param[in] ctx      To reuse.
+ */
+typedef                void (*fr_md4_ctx_reset_t)(fr_md4_ctx_t *ctx);
+extern         fr_md4_ctx_reset_t      fr_md4_ctx_reset;
 
-#ifndef MD4_DIGEST_LENGTH
-#  define MD4_DIGEST_LENGTH 16
-#endif
+/** Copy the contents of a ctx
+ *
+ * @param[in] dst      Where to copy the context to.
+ * @param[in] src      Where to copy the context from.
+ */
+typedef                void (*fr_md4_ctx_copy_t)(fr_md4_ctx_t *dst, fr_md4_ctx_t const *src);
+extern         fr_md4_ctx_copy_t       fr_md4_ctx_copy;
 
-#ifndef HAVE_OPENSSL_EVP_H
-/*
- * The MD4 code used here and in md4.c was originally retrieved from:
- *   http://www.openbsd.org/cgi-bin/cvsweb/src/include/md4.h?rev=1.12
+/** Allocation function for MD4 digest context
  *
- * This code implements the MD4 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.
- * Todd C. Miller modified the MD4 code to do MD4 based on RFC 1186.
+ * @param[in] shared   Whether we allocate a new context or use the thread local context.
+ * @return
+ *     - An MD4 ctx.
+ *     - NULL if out of memory.
+ */
+typedef                fr_md4_ctx_t *(*fr_md4_ctx_alloc_t)(bool shared);
+extern         fr_md4_ctx_alloc_t      fr_md4_ctx_alloc;
+
+/** Free function for MD4 digest ctx
  *
- * 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.
+ * @param[in] ctx      MD4 ctx to free.  If the shared ctx is passed in
+ *                     then the ctx is reset but not freed.
  */
-#  define MD4_BLOCK_LENGTH 64
-#  define MD4_DIGEST_STRING_LENGTH (MD4_DIGEST_LENGTH * 2 + 1)
+typedef                void (*fr_md4_ctx_free_t)(fr_md4_ctx_t **ctx);
+extern         fr_md4_ctx_free_t       fr_md4_ctx_free;
 
-typedef struct {
-       uint32_t state[4];                      //!< State.
-       uint32_t count[2];                      //!< Number of bits, mod 2^64.
-       uint8_t buffer[MD4_BLOCK_LENGTH];       //!< Input buffer.
-} FR_MD4_CTX;
 
-void   fr_md4_init(FR_MD4_CTX *ctx);
-void   fr_md4_update(FR_MD4_CTX *ctx, uint8_t const *in, size_t inlen);
-void   fr_md4_final(uint8_t out[static MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx);
-void   fr_md4_transform(uint32_t buf[static 4], uint8_t const inc[static MD4_BLOCK_LENGTH]);
-#else  /* HAVE_OPENSSL_EVP_H */
-USES_APPLE_DEPRECATED_API
-#include <openssl/md4.h>
-#  define FR_MD4_CTX                   MD4_CTX
-#  define fr_md4_init                  MD4_Init
-#  define fr_md4_update                        MD4_Update
-#  define fr_md4_final                 MD4_Final
-#  define fr_md4_transform             MD4_Transform
-#endif
+/** Ingest plaintext into the digest
+ *
+ * @param[in] ctx      To ingest data into.
+ * @param[in] in       Data to ingest.
+ * @param[in] inlen    Length of data to ingest.
+ */
+typedef                void (*fr_md4_update_t)(fr_md4_ctx_t *ctx, uint8_t const *in, size_t inlen);
+extern         fr_md4_update_t         fr_md4_update;
 
-/* md4.c */
-void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen);
+/** Finalise the ctx, producing the digest
+ *
+ * @param[out] out     The MD4 digest.
+ * @param[in] ctx      To finalise.
+ */
+typedef                void (*fr_md4_final_t)(uint8_t out[static MD4_DIGEST_LENGTH], fr_md4_ctx_t *ctx);
+extern         fr_md4_final_t          fr_md4_final;
+
+/** Perform a single digest operation on a single input buffer
+ *
+ */
+void           fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen);
 
 #ifdef __cplusplus
 }
index cfc8f8944ef04f8a98b20cf71947779092480cb2..a3c5292cb35c4b842ad9090b90a81175995a26da 100644 (file)
@@ -105,22 +105,27 @@ static void calc_md5_digest(uint8_t *buffer, uint8_t const *challenge, size_t ch
 
 static void calc_md4_digest(uint8_t *buffer, uint8_t const *challenge, size_t challen, char const *password)
 {
-       uint8_t buf[1024];
-       int i;
-       FR_MD4_CTX context;
+       uint8_t         buf[1024];
+       int             i;
+       fr_md4_ctx_t    *md4_ctx;
 
        memset(buf, 0, 1024);
        memset(buf, 0x36, 64);
-       for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
-       memcpy(buf+64, challenge, challen);
-       fr_md4_init(&context);
-       fr_md4_update(&context,buf,64+challen);
+       for (i = 0; i < 64 && password[i]; i++) buf[i] ^= password[i];
+       memcpy(buf + 64, challenge, challen);
+
+       md4_ctx = fr_md4_ctx_alloc(true);
+
+       fr_md4_update(md4_ctx, buf, 64 + challen);
        memset(buf, 0x5c, 64);
-       for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
-       fr_md4_final(buf+64,&context);
-       fr_md4_init(&context);
-       fr_md4_update(&context,buf,64+16);
-       fr_md4_final(buffer,&context);
+       for (i = 0; i < 64 && password[i]; i++) buf[i] ^= password[i];
+       fr_md4_final(buf + 64, md4_ctx);
+
+       fr_md4_ctx_reset(md4_ctx);
+
+       fr_md4_update(md4_ctx, buf, 64 + 16);
+       fr_md4_final(buffer, md4_ctx);
+       fr_md4_ctx_free(&md4_ctx);
 }
 
 static void calc_sha1_digest(uint8_t *buffer, uint8_t const *challenge,
diff --git a/src/tests/keywords/md4 b/src/tests/keywords/md4
new file mode 100644 (file)
index 0000000..516baa3
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# PRE: update if
+#
+update {
+       &control:Cleartext-Password := 'hello'
+       &request:Tmp-String-0 := "This is a string\n"
+       &request:Tmp-Octets-0 := 0x000504030201
+       &request:Tmp-String-1 := "what do ya want for nothing?"
+       &request:Tmp-String-2 := "Jefe"
+       &request:Tmp-String-3 := ""
+}
+
+
+update request {
+        &Tmp-Octets-1           := "%{md4:This is a string\n}"
+        &Tmp-Octets-2           := "%{md4:%{Tmp-String-0}}"
+        &Tmp-Octets-3           := "%{md4:%{request:Tmp-String-0}}"
+        &Tmp-Octets-4           := "%{md4:%{request:Tmp-Octets-0}}"
+        &Tmp-Octets-5           := "%{md4:%{Tmp-String-9}}"
+}
+
+#
+#  Put "This is a string" into a file and call "md4sum" on it.
+#  You should get this string.
+#
+if (&Tmp-Octets-1 != 0x1f60d5cd85e17bfbdda7c923822f060c) {
+       test_fail
+}
+
+if (&Tmp-Octets-2 != 0x1f60d5cd85e17bfbdda7c923822f060c) {
+       test_fail
+}
+
+if (&Tmp-Octets-3 != 0x1f60d5cd85e17bfbdda7c923822f060c) {
+       test_fail
+}
+
+#
+#  MD5 should also be able to cope with references to octet attributes
+#
+if (&Tmp-Octets-4 != 0xac3ed17b3cf19ec38352ec534a932fc6) {
+       test_fail
+}
+
+#
+#  MD5 of null string is d41d8cd98f00b204e9800998ecf8427e
+#
+if (&Tmp-Octets-5 != 0x31d6cfe0d16ae931b73c59d7e0c089c0) {
+       test_fail
+}
+
+success