Add %{hmacmd5:} and %{hmacsha1} xlat expansions
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 6 Jun 2014 10:56:48 +0000 (11:56 +0100)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 6 Jun 2014 11:11:17 +0000 (12:11 +0100)
src/modules/rlm_expr/rlm_expr.c
src/tests/keywords/md5
src/tests/keywords/sha1

index b1140a3..c7ec77d 100644 (file)
@@ -712,6 +712,98 @@ EVP_MD_XLAT(sha256);
 EVP_MD_XLAT(sha512);
 #endif
 
+/** Generate the HMAC-MD5 of a string or attribute
+ *
+ * Example: "%{hmacmd5:foo bar}" == "Zm9v"
+ */
+static ssize_t hmac_md5_xlat(UNUSED void *instance, UNUSED REQUEST *request,
+                            char const *fmt, char *out, size_t outlen)
+{
+       uint8_t const *data, *key;
+       char const *p;
+       ssize_t data_len, key_len;
+       uint8_t digest[MD5_DIGEST_LENGTH];
+       char data_ref[256];
+
+       if (outlen <= (sizeof(digest) * 2)) {
+               REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
+                       (sizeof(digest) * 2) + 1, outlen);
+               return -1;
+       }
+
+       p = strchr(fmt, ' ');
+       if (!p) {
+               REDEBUG("HMAC requires exactly two arguments (&data &key)");
+               return -1;
+       }
+
+       if ((size_t)(p - fmt) >= sizeof(data_ref)) {
+               REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes",
+                      (p - fmt) + 1, sizeof(data_ref));
+
+               return -1;
+       }
+       strlcpy(data_ref, fmt, (p - fmt) + 1);
+
+       data_len = xlat_fmt_to_ref(&data, request, data_ref);
+       if (data_len < 0) return -1;
+
+       while (isspace(*p) && p++);
+
+       key_len = xlat_fmt_to_ref(&key, request, p);
+       if (key_len < 0) return -1;
+
+       fr_hmac_md5(digest, data, data_len, key, key_len);
+
+       return fr_bin2hex(out, digest, sizeof(digest));
+}
+
+/** Generate the HMAC-SHA1 of a string or attribute
+ *
+ * Example: "%{hmacsha1:foo bar}" == "Zm9v"
+ */
+static ssize_t hmac_sha1_xlat(UNUSED void *instance, UNUSED REQUEST *request,
+                             char const *fmt, char *out, size_t outlen)
+{
+       uint8_t const *data, *key;
+       char const *p;
+       ssize_t data_len, key_len;
+       uint8_t digest[SHA1_DIGEST_LENGTH];
+       char data_ref[256];
+
+       if (outlen <= (sizeof(digest) * 2)) {
+               REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes",
+                       (sizeof(digest) * 2) + 1, outlen);
+               return -1;
+       }
+
+       p = strchr(fmt, ' ');
+       if (!p) {
+               REDEBUG("HMAC requires exactly two arguments (&data &key)");
+               return -1;
+       }
+
+       if ((size_t)(p - fmt) >= sizeof(data_ref)) {
+               REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes",
+                       (p - fmt) + 1, sizeof(data_ref));
+
+               return -1;
+       }
+       strlcpy(data_ref, fmt, (p - fmt) + 1);
+
+       data_len = xlat_fmt_to_ref(&data, request, data_ref);
+       if (data_len < 0) return -1;
+
+       while (isspace(*p) && p++);
+
+       key_len = xlat_fmt_to_ref(&key, request, p);
+       if (key_len < 0) return -1;
+
+       fr_hmac_sha1(digest, data, data_len, key, key_len);
+
+       return fr_bin2hex(out, digest, sizeof(digest));
+}
+
 /** Encode string or attribute as base64
  *
  * Example: "%{base64:foo}" == "Zm9v"
@@ -806,6 +898,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        xlat_register("sha256", sha256_xlat, NULL, inst);
        xlat_register("sha512", sha512_xlat, NULL, inst);
 #endif
+       xlat_register("hmacmd5", hmac_md5_xlat, NULL, inst);
+       xlat_register("hmacsha1", hmac_sha1_xlat, NULL, inst);
        xlat_register("base64", base64_xlat, NULL, inst);
        xlat_register("base64tohex", base64_to_hex_xlat, NULL, inst);
 
index 7c3de2b..1be4cd3 100644 (file)
@@ -9,6 +9,8 @@ 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"
 }
 
 #
@@ -17,26 +19,26 @@ update {
 #
 if ("%{md5:This is a string\n}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 1'
        }
 }
 
 if ("%{md5:&Tmp-String-0}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 2'
        }
 }
 
 if ("%{md5:&request:Tmp-String-0}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 3'
        }
 }
 
 # This differs than hashing the attribute directly, as the \n is escaped and becomes chars \ and n
 if ("%{md5:%{request:Tmp-String-0}}" != 'b41832e1bd19528e1a09495922182579') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 4'
        }
 }
 
@@ -45,6 +47,15 @@ if ("%{md5:%{request:Tmp-String-0}}" != 'b41832e1bd19528e1a09495922182579') {
 #
 if ("%{md5:&request:Tmp-Octets-0}" != 'c1e7fa505b2fc1fd0da6cac3db6f6f44') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 5'
+       }
+}
+
+#
+#  MD5 HMAC with attribute references
+#
+if ("%{hmacmd5:&Tmp-String-1 &Tmp-String-2}" != '750c783e6ab0b503eaa86e310a5db738') {
+       update reply {
+               Filter-Id += 'fail 6'
        }
 }
index 37e0f3c..0e52b4b 100644 (file)
@@ -5,6 +5,8 @@ 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"
 }
 
 update reply {
@@ -17,26 +19,26 @@ update reply {
 #
 if ("%{sha1:This is a string\n}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 1'
        }
 }
 
 if ("%{sha1:&Tmp-String-0}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 2'
        }
 }
 
 if ("%{sha1:&request:Tmp-String-0}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 3'
        }
 }
 
 # This differs than hashing the attribute directly, as the \n is escaped and becomes chars \ and n
 if ("%{sha1:%{Tmp-String-0}}" != 'd79c6dae2d4b3f4d78d0c044b38e23f52f376726') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 4'
        }
 }
 
@@ -45,6 +47,15 @@ if ("%{sha1:%{Tmp-String-0}}" != 'd79c6dae2d4b3f4d78d0c044b38e23f52f376726') {
 #
 if ("%{sha1:&request:Tmp-Octets-0}" != '365b244645fe7294dff062174996572319d5a82c') {
        update reply {
-               Filter-Id += 'fail'
+               Filter-Id += 'fail 5'
+       }
+}
+
+#
+#  SHA1 HMAC with attribute references
+#
+if ("%{hmacsha1:&Tmp-String-1 &Tmp-String-2}" != 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79') {
+       update reply {
+               Filter-Id += 'fail 6'
        }
 }