Be moe forgiving about the Digest attributes.
[freeradius.git] / src / modules / rlm_digest / rlm_digest.c
index bffafb8..6d84eea 100644 (file)
  *
  *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2002  The FreeRADIUS server project
+ * Copyright 2002,2006  The FreeRADIUS server project
  * Copyright 2002  Alan DeKok <aland@ox.org>
  */
 
-#include "autoconf.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
 
-#include "radiusd.h"
-#include "modules.h"
-#include "conffile.h"
-#include "rad_assert.h"
-
-static const char rcsid[] = "$Id$";
-
-static int digest_authorize(void *instance, REQUEST *request)
+static int digest_fix(REQUEST *request)
 {
        VALUE_PAIR *vp;
 
-       /* quiet the compiler */
-       instance = instance;
-
        /*
         *      We need both of these attributes to do the authentication.
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_RESPONSE);
+       vp = pairfind(request->packet->vps, PW_DIGEST_RESPONSE, 0);
        if (vp == NULL) {
                return RLM_MODULE_NOOP;
        }
@@ -53,38 +43,86 @@ static int digest_authorize(void *instance, REQUEST *request)
         *      Check the sanity of the attribute.
         */
        if (vp->length != 32) {
-               DEBUG("ERROR: Received invalid Digest-Response attribute (length %d should be 32)", vp->length);
-               return RLM_MODULE_INVALID;
+               return RLM_MODULE_NOOP;
        }
 
        /*
         *      We need these, too.
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_ATTRIBUTES);
+       vp = pairfind(request->packet->vps, PW_DIGEST_ATTRIBUTES, 0);
        if (vp == NULL) {
-               DEBUG("ERROR: Received Digest-Response without Digest-Attributes");
-               return RLM_MODULE_INVALID;
+               return RLM_MODULE_NOOP;
+       }
+
+       /*
+        *      Check for proper format of the Digest-Attributes
+        */
+       RDEBUG("Checking for correctly formatted Digest-Attributes");
+       while (vp) {
+               int length = vp->length;
+               int attrlen;
+               uint8_t *p = &vp->vp_octets[0];
+
+               /*
+                *      Until this stupidly encoded attribute is exhausted.
+                */
+               while (length > 0) {
+                       /*
+                        *      The attribute type must be valid
+                        */
+                       if ((p[0] == 0) || (p[0] > 10)) {
+                               RDEBUG("Not formatted as Digest-Attributes");
+                               return RLM_MODULE_NOOP;
+                       }
+
+                       attrlen = p[1]; /* stupid VSA format */
+
+                       /*
+                        *      Too short.
+                        */
+                       if (attrlen < 3) {
+                               RDEBUG("Not formatted as Digest-Attributes");
+                               return RLM_MODULE_NOOP;
+                       }
+
+                       /*
+                        *      Too long.
+                        */
+                       if (attrlen > length) {
+                               RDEBUG("Not formatted as Digest-Attributes");
+                               return RLM_MODULE_NOOP;
+                       }
+
+                       length -= attrlen;
+                       p += attrlen;
+               } /* loop over this one attribute */
+
+               /*
+                *      Find the next one, if it exists.
+                */
+               vp = pairfind(vp->next, PW_DIGEST_ATTRIBUTES, 0);
        }
 
        /*
-        *      Loop through the Digest-Attributes, sanity checking them.
+        *      Convert them to something sane.
         */
-       DEBUG("    rlm_digest: Converting Digest-Attributes to something sane...");
+       RDEBUG("Digest-Attributes look OK.  Converting them to something more usful.");
+       vp = pairfind(request->packet->vps, PW_DIGEST_ATTRIBUTES, 0);
        while (vp) {
                int length = vp->length;
                int attrlen;
-               uint8_t *p = &vp->vp_strvalue[0];
+               uint8_t *p = &vp->vp_octets[0];
                VALUE_PAIR *sub;
 
                /*
-                *      Until this stupidly encoded attribure is exhausted.
+                *      Until this stupidly encoded attribute is exhausted.
                 */
                while (length > 0) {
                        /*
                         *      The attribute type must be valid
                         */
                        if ((p[0] == 0) || (p[0] > 10)) {
-                               DEBUG("ERROR: Received Digest-Attributes with invalid sub-attribute %d", p[0]);
+                               RDEBUG("ERROR: Received Digest-Attributes with invalid sub-attribute %d", p[0]);
                                return RLM_MODULE_INVALID;
                        }
 
@@ -94,7 +132,7 @@ static int digest_authorize(void *instance, REQUEST *request)
                         *      Too short.
                         */
                        if (attrlen < 3) {
-                               DEBUG("ERROR: Received Digest-Attributes with short sub-attribute %d, of length %d", p[0], attrlen);
+                               RDEBUG("ERROR: Received Digest-Attributes with short sub-attribute %d, of length %d", p[0], attrlen);
                                return RLM_MODULE_INVALID;
                        }
 
@@ -102,7 +140,7 @@ static int digest_authorize(void *instance, REQUEST *request)
                         *      Too long.
                         */
                        if (attrlen > length) {
-                               DEBUG("ERROR: Received Digest-Attributes with long sub-attribute %d, of length %d", p[0], attrlen);
+                               RDEBUG("ERROR: Received Digest-Attributes with long sub-attribute %d, of length %d", p[0], attrlen);
                                return RLM_MODULE_INVALID;
                        }
 
@@ -112,27 +150,20 @@ static int digest_authorize(void *instance, REQUEST *request)
                         *
                         *      Didn't they know that VSA's exist?
                         */
-                       sub = paircreate(PW_DIGEST_REALM - 1 + p[0],
-                                        PW_TYPE_STRING);
-                       if (!sub) {
-                               return RLM_MODULE_FAIL; /* out of memory */
-                       }
-                       memcpy(&sub->vp_strvalue[0], &p[2], attrlen - 2);
-                       sub->vp_strvalue[attrlen - 2] = '\0';
+                       sub = radius_paircreate(request, &request->packet->vps,
+                                               PW_DIGEST_REALM - 1 + p[0], 0,
+                                               PW_TYPE_STRING);
+                       memcpy(&sub->vp_octets[0], &p[2], attrlen - 2);
+                       sub->vp_octets[attrlen - 2] = '\0';
                        sub->length = attrlen - 2;
 
-                       if (debug_flag) {
-                         putchar('\t');
-                         vp_print(stdout, sub);
-                         putchar('\n');
+                       if ((debug_flag > 1) && fr_log_fp) {
+                         fputc('\t', fr_log_fp);
+                         vp_print(fr_log_fp, sub);
+                         fputc('\n', fr_log_fp);
                        }
 
                        /*
-                        *      And add it to the request pairs.
-                        */
-                       pairadd(&request->packet->vps, sub);
-
-                       /*
                         *      FIXME: Check for the existence
                         *      of the necessary attributes!
                         */
@@ -144,14 +175,30 @@ static int digest_authorize(void *instance, REQUEST *request)
                /*
                 *      Find the next one, if it exists.
                 */
-               vp = pairfind(vp->next, PW_DIGEST_ATTRIBUTES);
+               vp = pairfind(vp->next, PW_DIGEST_ATTRIBUTES, 0);
        }
 
+       return RLM_MODULE_OK;
+}
+
+static int digest_authorize(void *instance, REQUEST *request)
+{
+       int rcode;
+
+       /* quiet the compiler */
+       instance = instance;
+
+       /*
+        *      Double-check and fix the attributes.
+        */       
+       rcode = digest_fix(request);
+       if (rcode != RLM_MODULE_OK) return rcode;
+
        /*
         *      Everything's OK, add a digest authentication type.
         */
-       if (pairfind(request->config_items, PW_AUTHTYPE) == NULL) {
-               DEBUG("rlm_digest: Adding Auth-Type = DIGEST");
+       if (pairfind(request->config_items, PW_AUTHTYPE, 0) == NULL) {
+               RDEBUG("Adding Auth-Type = DIGEST");
                pairadd(&request->config_items,
                        pairmake("Auth-Type", "DIGEST", T_OP_EQ));
        }
@@ -160,157 +207,215 @@ static int digest_authorize(void *instance, REQUEST *request)
 }
 
 /*
- *     Convert a string in hex to one in binary
- */
-static void hex2bin(uint8_t *out, const uint8_t *in)
-{
-       unsigned int tmp;
-
-       while (*in) {
-               sscanf(in, "%02x", &tmp);
-               *out = tmp;
-               out++;
-               in += 2;
-       }
-}
-
-/*
  *     Perform all of the wondrous variants of digest authentication.
  */
 static int digest_authenticate(void *instance, REQUEST *request)
 {
        int i;
-       int a1_len, a2_len, kd_len;
+       size_t a1_len, a2_len, kd_len;
        uint8_t a1[(MAX_STRING_LEN + 1) * 5]; /* can be 5 attributes */
        uint8_t a2[(MAX_STRING_LEN + 1) * 3]; /* can be 3 attributes */
        uint8_t kd[(MAX_STRING_LEN + 1) * 5];
        uint8_t hash[16];       /* MD5 output */
-       VALUE_PAIR *vp;
+       VALUE_PAIR *vp, *passwd, *algo;
        VALUE_PAIR *qop, *nonce;
 
        instance = instance;    /* -Wunused */
 
        /*
-        *      We require access to the plain-text password.
+        *      We require access to the plain-text password, or to the
+        *      Digest-HA1 parameter.
         */
-       vp = pairfind(request->config_items, PW_PASSWORD);
-       if (!vp) {
-               radlog(L_AUTH, "rlm_digest: Configuration item \"User-Password\" is required for authentication.");
+       passwd = pairfind(request->config_items, PW_DIGEST_HA1, 0);
+       if (passwd) {
+               if (passwd->length != 32) {
+                       radlog_request(L_AUTH, 0, request, "Digest-HA1 has invalid length, authentication failed.");
+                       return RLM_MODULE_INVALID;
+               }
+       } else {
+               passwd = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0);
+       }
+       if (!passwd) {
+               radlog_request(L_AUTH, 0, request, "Cleartext-Password or Digest-HA1 is required for authentication.");
                return RLM_MODULE_INVALID;
        }
 
        /*
+        *      We need these, too.
+        */
+       vp = pairfind(request->packet->vps, PW_DIGEST_ATTRIBUTES, 0);
+       if (vp == NULL) {
+       error:
+               RDEBUG("ERROR: You set 'Auth-Type = Digest' for a request that did not contain any digest attributes!w");
+               return RLM_MODULE_INVALID;
+       }
+
+       /*
+        *      Look for the "internal" FreeRADIUS Digest attributes.
+        *      If they don't exist, it means that someone forced
+        *      Auth-Type = digest, without putting "digest" into the
+        *      "authorize" section.  In that case, try to decode the
+        *      attributes here.
+        */
+       if (!pairfind(request->packet->vps, PW_DIGEST_NONCE, 0)) {
+               int rcode;
+
+               rcode = digest_fix(request);
+
+               /*
+                *      NOOP means "couldn't find the attributes".
+                *      That's bad.
+                */
+               if (rcode == RLM_MODULE_NOOP) goto error;
+
+               if (rcode != RLM_MODULE_OK) return rcode;
+       }
+
+       /*
         *      We require access to the Digest-Nonce-Value
         */
-       nonce = pairfind(request->packet->vps, PW_DIGEST_NONCE);
+       nonce = pairfind(request->packet->vps, PW_DIGEST_NONCE, 0);
        if (!nonce) {
-               DEBUG("ERROR: No Digest-Nonce: Cannot perform Digest authentication");
+               RDEBUG("ERROR: No Digest-Nonce: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
        }
 
        /*
         *      A1 = Digest-User-Name ":" Realm ":" Password
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_USER_NAME);
+       vp = pairfind(request->packet->vps, PW_DIGEST_USER_NAME, 0);
        if (!vp) {
-               DEBUG("ERROR: No Digest-User-Name: Cannot perform Digest authentication");
+               RDEBUG("ERROR: No Digest-User-Name: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
        }
-       memcpy(&a1[0], &vp->vp_strvalue[0], vp->length);
+       memcpy(&a1[0], &vp->vp_octets[0], vp->length);
        a1_len = vp->length;
 
        a1[a1_len] = ':';
        a1_len++;
 
-       vp = pairfind(request->packet->vps, PW_DIGEST_REALM);
+       vp = pairfind(request->packet->vps, PW_DIGEST_REALM, 0);
        if (!vp) {
-               DEBUG("ERROR: No Digest-Realm: Cannot perform Digest authentication");
+               RDEBUG("ERROR: No Digest-Realm: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
        }
-       memcpy(&a1[a1_len], &vp->vp_strvalue[0], vp->length);
+       memcpy(&a1[a1_len], &vp->vp_octets[0], vp->length);
        a1_len += vp->length;
 
        a1[a1_len] = ':';
        a1_len++;
 
-       vp = pairfind(request->config_items, PW_PASSWORD);
-       if (!vp) {
-               DEBUG("ERROR: No User-Password: Cannot perform Digest authentication");
-               return RLM_MODULE_INVALID;
+       if (passwd->attribute == PW_CLEARTEXT_PASSWORD) {
+               memcpy(&a1[a1_len], &passwd->vp_octets[0], passwd->length);
+               a1_len += passwd->length;
+               a1[a1_len] = '\0';
+               RDEBUG2("A1 = %s", a1);
+       } else {
+               a1[a1_len] = '\0';
+               RDEBUG2("A1 = %s (using Digest-HA1)", a1);
+               a1_len = 16;
        }
-       memcpy(&a1[a1_len], &vp->vp_strvalue[0], vp->length);
-       a1_len += vp->length;
-
-       a1[a1_len] = '\0';
-       DEBUG2("A1 = %s", a1);
 
        /*
         *      See which variant we calculate.
+        *      Assume MD5 if no Digest-Algorithm attribute received
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_ALGORITHM);
-       if ((vp != NULL) &&
-           (strcasecmp(vp->vp_strvalue, "MD5-sess") == 0)) {
-               librad_md5_calc(hash, &a1[0], a1_len);
-               memcpy(&a1[0], hash, 16);
-               a1_len = 16;
+       algo = pairfind(request->packet->vps, PW_DIGEST_ALGORITHM, 0);
+       if ((algo == NULL) ||
+           (strcasecmp(algo->vp_strvalue, "MD5") == 0)) {
+               /*
+                *      Set A1 to Digest-HA1 if no User-Password found
+                */
+               if (passwd->attribute == PW_DIGEST_HA1) {
+                       if (fr_hex2bin(passwd->vp_strvalue, &a1[0], 16) != 16) {
+                               RDEBUG2("Invalid text in Digest-HA1");
+                               return RLM_MODULE_INVALID;
+                       }
+               }
+
+       } else if (strcasecmp(algo->vp_strvalue, "MD5-sess") == 0) {
+               /*
+                *      K1 = H(A1) : Digest-Nonce ... : H(A2)
+                *
+                *      If we find Digest-HA1, we assume it contains
+                *      H(A1).
+                */
+               if (passwd->attribute == PW_CLEARTEXT_PASSWORD) {
+                       fr_md5_calc(hash, &a1[0], a1_len);
+                       fr_bin2hex(hash, (char *) &a1[0], 16);
+               } else {        /* MUST be Digest-HA1 */
+                       memcpy(&a1[0], passwd->vp_strvalue, 32);
+               }
+               a1_len = 32;
 
                a1[a1_len] = ':';
                a1_len++;
 
                /*
-                *      Tack on the Digest-Nonce
+                *      Tack on the Digest-Nonce. Length must be even
                 */
-               hex2bin(&a1[a1_len], &nonce->vp_strvalue[0]);
-               a1_len += (nonce->length >> 1); /* FIXME: CHECK LENGTH */
+               if ((nonce->length & 1) != 0) {
+                       RDEBUG("ERROR: Received Digest-Nonce hex string with invalid length: Cannot perform Digest authentication");
+                       return RLM_MODULE_INVALID;
+               }
+               memcpy(&a1[a1_len], &nonce->vp_octets[0], nonce->length);
+               a1_len += nonce->length;
 
                a1[a1_len] = ':';
                a1_len++;
 
-               vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE);
+               vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE, 0);
                if (!vp) {
-                 DEBUG("ERROR: No Digest-CNonce: Cannot perform Digest authentication");
-                 return RLM_MODULE_INVALID;
+                       RDEBUG("ERROR: No Digest-CNonce: Cannot perform Digest authentication");
+                       return RLM_MODULE_INVALID;
                }
 
-               hex2bin(&a1[a1_len], &vp->vp_strvalue[0]);
-               a1_len += (vp->length >> 1); /* FIXME: CHECK LENGTH */
+               /*
+                *      Digest-CNonce length must be even
+                */
+               if ((vp->length & 1) != 0) {
+                       RDEBUG("ERROR: Received Digest-CNonce hex string with invalid length: Cannot perform Digest authentication");
+                       return RLM_MODULE_INVALID;
+               }
+               memcpy(&a1[a1_len], &vp->vp_octets[0], vp->length);
+               a1_len += vp->length;
 
-       } else if ((vp != NULL) &&
-                  (strcasecmp(vp->vp_strvalue, "MD5") != 0)) {
+       } else if ((algo != NULL) &&
+                  (strcasecmp(algo->vp_strvalue, "MD5") != 0)) {
                /*
                 *      We check for "MD5-sess" and "MD5".
                 *      Anything else is an error.
                 */
-               DEBUG("ERROR: Unknown Digest-Algorithm \"%s\": Cannot perform Digest authentication", vp->vp_strvalue);
+               RDEBUG("ERROR: Unknown Digest-Algorithm \"%s\": Cannot perform Digest authentication", vp->vp_strvalue);
                return RLM_MODULE_INVALID;
        }
 
        /*
         *      A2 = Digest-Method ":" Digest-URI
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_METHOD);
+       vp = pairfind(request->packet->vps, PW_DIGEST_METHOD, 0);
        if (!vp) {
-               DEBUG("ERROR: No Digest-Method: Cannot perform Digest authentication");
+               RDEBUG("ERROR: No Digest-Method: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
        }
-       memcpy(&a2[0], &vp->vp_strvalue[0], vp->length);
+       memcpy(&a2[0], &vp->vp_octets[0], vp->length);
        a2_len = vp->length;
 
        a2[a2_len] = ':';
        a2_len++;
 
-       vp = pairfind(request->packet->vps, PW_DIGEST_URI);
+       vp = pairfind(request->packet->vps, PW_DIGEST_URI, 0);
        if (!vp) {
-               DEBUG("ERROR: No Digest-URI: Cannot perform Digest authentication");
+               RDEBUG("ERROR: No Digest-URI: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
        }
-       memcpy(&a2[a2_len], &vp->vp_strvalue[0], vp->length);
+       memcpy(&a2[a2_len], &vp->vp_octets[0], vp->length);
        a2_len += vp->length;
 
        /*
         *  QOP is "auth-int", tack on ": Digest-Body-Digest"
         */
-       qop = pairfind(request->packet->vps, PW_DIGEST_QOP);
+       qop = pairfind(request->packet->vps, PW_DIGEST_QOP, 0);
        if ((qop != NULL) &&
            (strcasecmp(qop->vp_strvalue, "auth-int") == 0)) {
                VALUE_PAIR *body;
@@ -324,41 +429,51 @@ static int digest_authenticate(void *instance, REQUEST *request)
                /*
                 *  Must be a hex representation of an MD5 digest.
                 */
-               body = pairfind(request->packet->vps, PW_DIGEST_BODY_DIGEST);
+               body = pairfind(request->packet->vps, PW_DIGEST_BODY_DIGEST, 0);
                if (!body) {
-                       DEBUG("ERROR: No Digest-Body-Digest: Cannot perform Digest authentication");
+                       RDEBUG("ERROR: No Digest-Body-Digest: Cannot perform Digest authentication");
+                       return RLM_MODULE_INVALID;
+               }
+
+               if ((a2_len + body->length) > sizeof(a2)) {
+                       RDEBUG("ERROR: Digest-Body-Digest is too long");
                        return RLM_MODULE_INVALID;
                }
 
-               rad_assert(body->length == 32); /* FIXME: check in 'auth' */
-               hex2bin(&a2[a2_len], &body->vp_strvalue[0]);
-               a2_len += (body->length >> 1);
+               memcpy(a2 + a2_len, body->vp_octets, body->length);
+               a2_len += body->length;
 
        } else if ((qop != NULL) &&
                   (strcasecmp(qop->vp_strvalue, "auth") != 0)) {
-               DEBUG("ERROR: Unknown Digest-QOP \"%s\": Cannot perform Digest authentication", qop->vp_strvalue);
+               RDEBUG("ERROR: Unknown Digest-QOP \"%s\": Cannot perform Digest authentication", qop->vp_strvalue);
                return RLM_MODULE_INVALID;
        }
 
        a2[a2_len] = '\0';
-       DEBUG2("A2 = %s", a2);
+       RDEBUG2("A2 = %s", a2);
 
        /*
-        *     KD = H(A1) : Digest-Nonce ... : H(A2)
+        *     KD = H(A1) : Digest-Nonce ... : H(A2).
+        *     Compute MD5 if Digest-Algorithm == "MD5-Sess",
+        *     or if we found a User-Password.
         */
-       librad_md5_calc(&hash[0], &a1[0], a1_len);
-
-       for (i = 0; i < 16; i++) {
-               sprintf(&kd[i * 2], "%02x", hash[i]);
+       if (((algo != NULL) &&
+            (strcasecmp(algo->vp_strvalue, "MD5-Sess") == 0)) ||
+           (passwd->attribute == PW_CLEARTEXT_PASSWORD)) {
+               a1[a1_len] = '\0';
+               fr_md5_calc(&hash[0], &a1[0], a1_len);
+       } else {
+               memcpy(&hash[0], &a1[0], a1_len);
        }
+       fr_bin2hex(hash, (char *) kd, sizeof(hash));
 
-#ifndef NDEBUG
-       if (debug_flag) {
-               printf("H(A1) = ");
+#ifndef NRDEBUG
+       if (debug_flag > 1) {
+               fr_printf_log("H(A1) = ");
                for (i = 0; i < 16; i++) {
-                       printf("%02x", hash[i]);
+                       fr_printf_log("%02x", hash[i]);
                }
-               printf("\n");
+               fr_printf_log("\n");
        }
 #endif
        kd_len = 32;
@@ -366,7 +481,7 @@ static int digest_authenticate(void *instance, REQUEST *request)
        kd[kd_len] = ':';
        kd_len++;
 
-       memcpy(&kd[kd_len], nonce->vp_strvalue, nonce->length);
+       memcpy(&kd[kd_len], nonce->vp_octets, nonce->length);
        kd_len += nonce->length;
 
        /*
@@ -385,29 +500,29 @@ static int digest_authenticate(void *instance, REQUEST *request)
                kd[kd_len] = ':';
                kd_len++;
 
-               vp = pairfind(request->packet->vps, PW_DIGEST_NONCE_COUNT);
+               vp = pairfind(request->packet->vps, PW_DIGEST_NONCE_COUNT, 0);
                if (!vp) {
-                       DEBUG("ERROR: No Digest-Nonce-Count: Cannot perform Digest authentication");
+                       RDEBUG("ERROR: No Digest-Nonce-Count: Cannot perform Digest authentication");
                        return RLM_MODULE_INVALID;
                }
-               memcpy(&kd[kd_len], &vp->vp_strvalue[0], vp->length);
+               memcpy(&kd[kd_len], &vp->vp_octets[0], vp->length);
                kd_len += vp->length;
 
                kd[kd_len] = ':';
                kd_len++;
 
-               vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE);
+               vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE, 0);
                if (!vp) {
-                       DEBUG("ERROR: No Digest-CNonce: Cannot perform Digest authentication");
+                       RDEBUG("ERROR: No Digest-CNonce: Cannot perform Digest authentication");
                        return RLM_MODULE_INVALID;
                }
-               memcpy(&kd[kd_len], &vp->vp_strvalue[0], vp->length);
+               memcpy(&kd[kd_len], &vp->vp_octets[0], vp->length);
                kd_len += vp->length;
 
                kd[kd_len] = ':';
                kd_len++;
 
-               memcpy(&kd[kd_len], &qop->vp_strvalue[0], qop->length);
+               memcpy(&kd[kd_len], &qop->vp_octets[0], qop->length);
                kd_len += qop->length;
        }
 
@@ -417,54 +532,58 @@ static int digest_authenticate(void *instance, REQUEST *request)
        kd[kd_len] = ':';
        kd_len++;
 
-       librad_md5_calc(&hash[0], &a2[0], a2_len);
+       fr_md5_calc(&hash[0], &a2[0], a2_len);
 
-       for (i = 0; i < 16; i++) {
-               sprintf(&kd[kd_len + (i * 2)], "%02x", hash[i]);
-       }
+       fr_bin2hex(hash, (char *) kd + kd_len, sizeof(hash));
 
-#ifndef NDEBUG
-       if (debug_flag) {
-               printf("H(A2) = ");
+#ifndef NRDEBUG
+       if (debug_flag > 1) {
+               fr_printf_log("H(A2) = ");
                for (i = 0; i < 16; i++) {
-                       printf("%02x", hash[i]);
+                       fr_printf_log("%02x", hash[i]);
                }
-               printf("\n");
+               fr_printf_log("\n");
        }
 #endif
        kd_len += 32;
 
        kd[kd_len] = 0;
 
-       DEBUG2("KD = %s\n", &kd[0]);
+       RDEBUG2("KD = %s\n", &kd[0]);
 
        /*
         *      Take the hash of KD.
         */
-       librad_md5_calc(&hash[0], &kd[0], kd_len);
+       fr_md5_calc(&hash[0], &kd[0], kd_len);
        memcpy(&kd[0], &hash[0], 16);
 
        /*
         *      Get the binary value of Digest-Response
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_RESPONSE);
-       rad_assert(vp != NULL);
+       vp = pairfind(request->packet->vps, PW_DIGEST_RESPONSE, 0);
+       if (!vp) {
+               RDEBUG("ERROR: No Digest-Response attribute in the request.  Cannot perform digest authentication");
+               return RLM_MODULE_INVALID;
+       }
 
-       hex2bin(&hash[0], &vp->vp_strvalue[0]);
+       if (fr_hex2bin(&vp->vp_strvalue[0], &hash[0], vp->length >> 1) != (vp->length >> 1)) {
+               RDEBUG2("Invalid text in Digest-Response");
+               return RLM_MODULE_INVALID;
+       }
 
-#ifndef NDEBUG
-       if (debug_flag) {
-               printf("EXPECTED ");
+#ifndef NRDEBUG
+       if (debug_flag > 1) {
+               fr_printf_log("EXPECTED ");
                for (i = 0; i < 16; i++) {
-                       printf("%02x", kd[i]);
+                       fr_printf_log("%02x", kd[i]);
                }
-               printf("\n");
+               fr_printf_log("\n");
 
-               printf("RECEIVED ");
+               fr_printf_log("RECEIVED ");
                for (i = 0; i < 16; i++) {
-                       printf("%02x", hash[i]);
+                       fr_printf_log("%02x", hash[i]);
                }
-               printf("\n");
+               fr_printf_log("\n");
        }
 #endif
 
@@ -475,7 +594,7 @@ static int digest_authenticate(void *instance, REQUEST *request)
                return RLM_MODULE_OK;
        }
 
-       DEBUG("rlm_digest: FAILED authentication");
+       RDEBUG("FAILED authentication");
        return RLM_MODULE_REJECT;
 }
 
@@ -491,7 +610,7 @@ static int digest_authenticate(void *instance, REQUEST *request)
 module_t rlm_digest = {
        RLM_MODULE_INIT,
        "digest",
-       0,                              /* type */
+       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
        NULL,                           /* instantiation */
        NULL,                           /* detach */
        {