Massively cleaned up #include's, so they're in a consistent
[freeradius.git] / src / modules / rlm_digest / rlm_digest.c
index 8835572..7576d4a 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 "radiusd.h"
-#include "modules.h"
-#include "conffile.h"
-#include "rad_assert.h"
-
-static const char rcsid[] = "$Id$";
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
 
 static int digest_authorize(void *instance, REQUEST *request)
 {
@@ -79,34 +72,17 @@ static int digest_authorize(void *instance, REQUEST *request)
 }
 
 /*
- *     Convert a string in hex to one in binary
- *
- *     FIXME: call lrad_hex2bin
- */
-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 */
@@ -114,9 +90,17 @@ static int digest_authenticate(void *instance, REQUEST *request)
        /*
         *      We require access to the plain-text password.
         */
-       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);
+       if (passwd) {
+               if (passwd->length != 32) {
+                       radlog(L_AUTH, "rlm_digest: Digest-HA1 has invalid length, authentication failed.");
+                       return RLM_MODULE_INVALID;
+               }
+       } else {
+               passwd = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD);
+       }
+       if (!passwd) {
+               radlog(L_AUTH, "rlm_digest: Cleartext-Password or Digest-HA1 is required for authentication.");
                return RLM_MODULE_INVALID;
        }
 
@@ -128,6 +112,7 @@ static int digest_authenticate(void *instance, REQUEST *request)
                DEBUG("ERROR: You set 'Auth-Type = Digest' for a request that did not contain any digest attributes!");
                return RLM_MODULE_INVALID;
        }
+
        /*
         *      Loop through the Digest-Attributes, sanity checking them.
         */
@@ -243,50 +228,83 @@ static int digest_authenticate(void *instance, REQUEST *request)
        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';
+               DEBUG2("A1 = %s", a1);
+       } else {
+               a1[a1_len] = '\0';
+               DEBUG2("A1 = %s (using Digest-HA1)", a1);
+               a1_len = 16;
        }
-       memcpy(&a1[a1_len], &vp->vp_octets[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);
+       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 (lrad_hex2bin(passwd->vp_strvalue, &a1[0], 16) != 16) {
+                               DEBUG2("rlm_digest: 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) {
+                       librad_md5_calc(hash, &a1[0], a1_len);
+                       lrad_bin2hex(hash, &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_octets[0]);
-               a1_len += (nonce->length >> 1); /* FIXME: CHECK LENGTH */
+               if ((nonce->length & 1) != 0) {
+                       DEBUG("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);
                if (!vp) {
-                 DEBUG("ERROR: No Digest-CNonce: Cannot perform Digest authentication");
-                 return RLM_MODULE_INVALID;
+                       DEBUG("ERROR: No Digest-CNonce: Cannot perform Digest authentication");
+                       return RLM_MODULE_INVALID;
                }
 
-               hex2bin(&a1[a1_len], &vp->vp_octets[0]);
-               a1_len += (vp->length >> 1); /* FIXME: CHECK LENGTH */
+               /*
+                *      Digest-CNonce length must be even
+                */
+               if ((vp->length & 1) != 0) {
+                       DEBUG("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.
@@ -340,9 +358,13 @@ static int digest_authenticate(void *instance, REQUEST *request)
                        return RLM_MODULE_INVALID;
                }
 
-               rad_assert(body->length == 32); /* FIXME: check in 'auth' */
-               hex2bin(&a2[a2_len], &body->vp_octets[0]);
-               a2_len += (body->length >> 1);
+               if ((a2_len + body->length) > sizeof(a2)) {
+                       DEBUG("ERROR: Digest-Body-Digest is too long");
+                       return RLM_MODULE_INVALID;
+               }
+
+               memcpy(a2 + a2_len, body->vp_octets, body->length);
+               a2_len += body->length;
 
        } else if ((qop != NULL) &&
                   (strcasecmp(qop->vp_strvalue, "auth") != 0)) {
@@ -354,13 +376,19 @@ static int digest_authenticate(void *instance, REQUEST *request)
        DEBUG2("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';
+               librad_md5_calc(&hash[0], &a1[0], a1_len);
+       } else {
+               memcpy(&hash[0], &a1[0], a1_len);
        }
+       lrad_bin2hex(hash, kd, sizeof(hash));
 
 #ifndef NDEBUG
        if (debug_flag) {
@@ -429,9 +457,7 @@ static int digest_authenticate(void *instance, REQUEST *request)
 
        librad_md5_calc(&hash[0], &a2[0], a2_len);
 
-       for (i = 0; i < 16; i++) {
-               sprintf(&kd[kd_len + (i * 2)], "%02x", hash[i]);
-       }
+       lrad_bin2hex(hash, kd + kd_len, sizeof(hash));
 
 #ifndef NDEBUG
        if (debug_flag) {
@@ -458,9 +484,15 @@ static int digest_authenticate(void *instance, REQUEST *request)
         *      Get the binary value of Digest-Response
         */
        vp = pairfind(request->packet->vps, PW_DIGEST_RESPONSE);
-       rad_assert(vp != NULL);
+       if (!vp) {
+               DEBUG("ERROR: No Digest-Response attribute in the request.  Cannot perform digest authentication");
+               return RLM_MODULE_INVALID;
+       }
 
-       hex2bin(&hash[0], &vp->vp_octets[0]);
+       if (lrad_hex2bin(&vp->vp_octets[0], &hash[0], vp->length >> 1) != (vp->length >> 1)) {
+               DEBUG2("rlm_digest: Invalid text in Digest-Response");
+               return RLM_MODULE_INVALID;
+       }
 
 #ifndef NDEBUG
        if (debug_flag) {