Assume password src encoding is UTF8 and convert it to UCS2 before hashing with MD4
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 25 Sep 2013 22:48:08 +0000 (23:48 +0100)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 26 Sep 2013 12:42:44 +0000 (13:42 +0100)
Patch by Andrei Korostelev
Closes #437

src/include/libradius.h
src/lib/misc.c
src/main/radclient.c
src/modules/rlm_eap/types/rlm_eap_leap/eap_leap.c
src/modules/rlm_mschap/mschap.c
src/modules/rlm_mschap/mschap.h
src/modules/rlm_mschap/rlm_mschap.c
src/modules/rlm_mschap/smbencrypt.c

index 4f3c0ec..29cf74a 100644 (file)
@@ -571,6 +571,7 @@ int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, int port,
                       struct sockaddr_storage *sa, socklen_t *salen);
 int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
                       fr_ipaddr_t *ipaddr, int * port);
+ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen);
 
 
 #ifdef WITH_ASCEND_BINARY
index a9cba70..d94ecca 100644 (file)
@@ -29,13 +29,20 @@ RCSID("$Id$")
 #include       <fcntl.h>
 #include       <signal.h>
 
-int            fr_dns_lookups = 0;
-int            fr_debug_flag = 0;
-
+#define FR_PUT_LE16(a, val)\
+       do {\
+               a[1] = ((uint16_t) (val)) >> 8;\
+               a[0] = ((uint16_t) (val)) & 0xff;\
+       } while (0)
 
 static int     fr_debugger_present = -1;
 
-/** Stub callback to see if the SIGTRAP handler is overriden
+int    fr_dns_lookups = 0;
+int    fr_debug_flag = 0;
+
+fr_thread_local_setup(char *, fr_inet_ntop_buffer);    /* macro */
+
+/** Allocates a new talloc context from the root autofree context
  *
  * @param signum signal raised.
  */
@@ -685,3 +692,59 @@ int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
 
        return 1;
 }
+
+/** Convert UTF8 string to UCS2 encoding
+ *
+ * @note Borrowed from src/crypto/ms_funcs.c of wpa_supplicant project (http://hostap.epitest.fi/wpa_supplicant/)
+ *
+ * @param[out] out Where to write the ucs2 string.
+ * @param[in] outlen Size of output buffer.
+ * @param[in] in UTF8 string to convert.
+ * @param[in] inlen length of UTF8 string.
+ * @return the size of the UCS2 string written to the output buffer (in bytes).
+ */
+ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen)
+{
+       size_t i;
+       uint8_t *start = out;
+
+       for (i = 0; i < inlen; i++) {
+               uint8_t c, c2, c3;
+
+               c = in[i];
+               if ((size_t)(out - start) >= outlen) {
+                       /* input too long */
+                       return -1;
+               }
+
+               /* One-byte encoding */
+               if (c <= 0x7f) {
+                       FR_PUT_LE16(out, c);
+                       out += 2;
+                       continue;
+               } else if ((i == (inlen - 1)) || ((size_t)(out - start) >= (outlen - 1))) {
+                       /* Incomplete surrogate */
+                       return -1;
+               }
+
+               c2 = in[++i];
+               /* Two-byte encoding */
+               if ((c & 0xe0) == 0xc0) {
+                       FR_PUT_LE16(out, ((c & 0x1f) << 6) | (c2 & 0x3f));
+                       out += 2;
+                       continue;
+               }
+               if ((i == inlen) || ((size_t)(out - start) >= (outlen - 1))) {
+                       /* Incomplete surrogate */
+                       return -1;
+               }
+
+               /* Three-byte encoding */
+               c3 = in[++i];
+               FR_PUT_LE16(out, ((c & 0xf) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f));
+               out += 2;
+       }
+
+       return out - start;
+}
+
index d07e86e..3f884dc 100644 (file)
@@ -191,10 +191,11 @@ static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request,
 
        p[1] = 0x01; /* NT hash */
 
-       mschap_ntpwdhash(nthash, password);
+       if (mschap_ntpwdhash(nthash, password) < 0) {
+               return 0;
+       }
 
-       smbdes_mschap(nthash, challenge->vp_octets,
-                     p + 26);
+       smbdes_mschap(nthash, challenge->vp_octets, p + 26);
        return 1;
 }
 
index 6898348..5b4349a 100644 (file)
@@ -161,30 +161,26 @@ leap_packet_t *eapleap_extract(EAP_DS *eap_ds)
 /*
  *  Get the NT-Password hash.
  */
-static int eapleap_ntpwdhash(unsigned char *ntpwdhash, VALUE_PAIR *password)
+static int eapleap_ntpwdhash(uint8_t *out, VALUE_PAIR *password)
 {
        if ((password->da->attr == PW_USER_PASSWORD) ||
            (password->da->attr == PW_CLEARTEXT_PASSWORD)) {
-               size_t i;
-               unsigned char unicode[512];
+               ssize_t len;
+               uint8_t ucs2_password[512];
 
                /*
                 *      Convert the password to NT's weird Unicode format.
                 */
-               memset(unicode, 0, sizeof(unicode));
-               for (i = 0; i < password->length; i++) {
-                       /*
-                        *  Yes, the *even* bytes have the values,
-                        *  and the *odd* bytes are zero.
-                        */
-                       unicode[(i << 1)] = password->vp_strvalue[i];
+               len = fr_utf8_to_ucs2(ucs2_password, sizeof(ucs2_password), password->vp_strvalue, password->length);
+               if (len < 0) {
+                       ERROR("rlm_eap_leap: Error converting password to UCS2");
+                       return 0;
                }
 
                /*
                 *  Get the NT Password hash.
                 */
-               fr_md4_calc(ntpwdhash, unicode, password->length * 2);
-
+               fr_md4_calc(out, ucs2_password, len);
        } else {                /* MUST be NT-Password */
                uint8_t *p = NULL;
 
@@ -202,7 +198,7 @@ static int eapleap_ntpwdhash(unsigned char *ntpwdhash, VALUE_PAIR *password)
                        talloc_free(p);
                }
 
-               memcpy(ntpwdhash, password->vp_octets, 16);
+               memcpy(out, password->vp_octets, 16);
        }
        return 1;
 }
@@ -214,7 +210,7 @@ static int eapleap_ntpwdhash(unsigned char *ntpwdhash, VALUE_PAIR *password)
 int eapleap_stage4(leap_packet_t *packet, VALUE_PAIR* password,
                   leap_session_t *session)
 {
-       unsigned char ntpwdhash[16];
+       unsigned char out[16];
        unsigned char response[24];
 
 
@@ -225,14 +221,14 @@ int eapleap_stage4(leap_packet_t *packet, VALUE_PAIR* password,
                return 0;
        }
 
-       if (!eapleap_ntpwdhash(ntpwdhash, password)) {
+       if (!eapleap_ntpwdhash(out, password)) {
                return 0;
        }
 
        /*
         *      Calculate and verify the CHAP challenge.
         */
-       eapleap_mschap(ntpwdhash, session->peer_challenge, response);
+       eapleap_mschap(out, session->peer_challenge, response);
        if (memcmp(response, packet->challenge, 24) == 0) {
                DEBUG2("  rlm_eap_leap: NtChallengeResponse from AP is valid");
                memcpy(session->peer_response, response, sizeof(response));
@@ -246,12 +242,11 @@ int eapleap_stage4(leap_packet_t *packet, VALUE_PAIR* password,
 /*
  *     Verify ourselves to the AP
  */
-leap_packet_t *eapleap_stage6(leap_packet_t *packet, REQUEST *request,
-                           VALUE_PAIR *user_name, VALUE_PAIR* password,
-                           leap_session_t *session)
+leap_packet_t *eapleap_stage6(leap_packet_t *packet, REQUEST *request, VALUE_PAIR *user_name, VALUE_PAIR *password,
+                             leap_session_t *session)
 {
        size_t i;
-       uint8_t ntpwdhash[16], ntpwdhashhash[16];
+       uint8_t out[16], outhash[16];
        uint8_t *p, buffer[256];
        leap_packet_t *reply;
        char *q;
@@ -296,20 +291,19 @@ leap_packet_t *eapleap_stage6(leap_packet_t *packet, REQUEST *request,
        /*
         *  MPPE hash = ntpwdhash(ntpwdhash(unicode(pw)))
         */
-       if (!eapleap_ntpwdhash(ntpwdhash, password)) {
+       if (!eapleap_ntpwdhash(out, password)) {
                talloc_free(reply);
                return NULL;
        }
-       fr_md4_calc(ntpwdhashhash, ntpwdhash, 16);
+       fr_md4_calc(outhash, out, 16);
 
        /*
-        *      Calculate our response, to authenticate ourselves
-        *      to the AP.
+        *      Calculate our response, to authenticate ourselves to the AP.
         */
-       eapleap_mschap(ntpwdhashhash, packet->challenge, reply->challenge);
+       eapleap_mschap(outhash, packet->challenge, reply->challenge);
 
        /*
-        *  Calculate the leap:session-key attribute
+        *      Calculate the leap:session-key attribute
         */
        vp = pairmake_reply("Cisco-AVPair", NULL, T_OP_ADD);
        if (!vp) {
@@ -322,7 +316,7 @@ leap_packet_t *eapleap_stage6(leap_packet_t *packet, REQUEST *request,
         *      And calculate the MPPE session key.
         */
        p = buffer;
-       memcpy(p, ntpwdhashhash, 16); /* MPPEHASH */
+       memcpy(p, outhash, 16); /* MPPEHASH */
        p += 16;
        memcpy(p, packet->challenge, 8); /* APC */
        p += 8;
@@ -335,12 +329,12 @@ leap_packet_t *eapleap_stage6(leap_packet_t *packet, REQUEST *request,
        /*
         *      These 16 bytes are the session key to use.
         */
-       fr_md5_calc(ntpwdhash, buffer, 16 + 8 + 24 + 8 + 24);
+       fr_md5_calc(out, buffer, 16 + 8 + 24 + 8 + 24);
 
        q = talloc_array(vp, char, 16 + sizeof("leap:session-key="));
        strcpy(q, "leap:session-key=");
 
-       memcpy(q + 17, ntpwdhash, 16);
+       memcpy(q + 17, out, 16);
 
        i = 16;
        rad_tunnel_pwencode(q + 17, &i,
index 073c046..6af61f0 100644 (file)
@@ -41,31 +41,27 @@ RCSID("$Id$")
 #include       "smbdes.h"
 #include       "mschap.h"
 
-/*
- *     ntpwdhash converts Unicode password to 16-byte NT hash
- *     with MD4
+/** Converts Unicode password to 16-byte NT hash with MD4
+ *
+ * @param[out] out Pointer to 16 byte output buffer.
+ * @param[in] password to encode.
+ * @return 0 on success else -1 on failure.
  */
-void mschap_ntpwdhash (uint8_t *szHash, char const *szPassword)
+int mschap_ntpwdhash(uint8_t *out, char const *password)
 {
-       char szUnicodePass[513];
-       int nPasswordLen;
-       int i;
+       ssize_t len;
+       uint8_t ucs2_password[512];
 
-       /*
-        *      NT passwords are unicode.  Convert plain text password
-        *      to unicode by inserting a zero every other byte
-        */
-       nPasswordLen = strlen(szPassword);
-       for (i = 0; i < nPasswordLen; i++) {
-               szUnicodePass[i << 1] = szPassword[i];
-               szUnicodePass[(i << 1) + 1] = 0;
+       len = fr_utf8_to_ucs2(ucs2_password, sizeof(ucs2_password), password, strlen(password));
+       if (len < 0) {
+               *out = '\0';
+               return -1;
        }
+       fr_md4_calc(out, (uint8_t *) ucs2_password, len);
 
-       /* Encrypt Unicode password to a 16-byte MD4 hash */
-       fr_md4_calc(szHash, (uint8_t *) szUnicodePass, (nPasswordLen<<1) );
+       return 0;
 }
 
-
 /*
  *     challenge_hash() is used by mschap2() and auth_response()
  *     implements RFC2759 ChallengeHash()
index b5d6ab7..9c4fcd8 100644 (file)
@@ -5,7 +5,7 @@
 
 RCSIDH(mschap_h, "$Id$")
 
-void mschap_ntpwdhash (uint8_t *szHash, char const *szPassword);
+int mschap_ntpwdhash(uint8_t *out, char const *password);
 void mschap_challenge_hash(uint8_t const *peer_challenge,
                            uint8_t const *auth_challenge,
                            char const *user_name, uint8_t *challenge );
index 691e428..50d9ade 100644 (file)
@@ -460,7 +460,11 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                        return 0;
                }
 
-               mschap_ntpwdhash(buffer,buf2);
+               if (mschap_ntpwdhash(buffer, buf2) < 0) {
+                       RERROR("Failed generating NT-Password");
+                       *buffer = '\0';
+                       return 0;
+               }
 
                fr_bin2hex(out, buffer, 16);
                out[32] = '\0';
@@ -1402,7 +1406,11 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                } else {
                        nt_password->length = 16;
                        nt_password->vp_octets = p = talloc_array(nt_password, uint8_t, nt_password->length);
-                       mschap_ntpwdhash(p, password->vp_strvalue);
+
+                       if (mschap_ntpwdhash(p, password->vp_strvalue) < 0) {
+                               RERROR("Failed generating NT-Password");
+                               return RLM_MODULE_FAIL;
+                       }
                }
        }
 
index 9245d4c..69c1e0f 100644 (file)
@@ -46,28 +46,19 @@ static void tohex (unsigned char const  *src, size_t len, char *dst)
        dst[(i*2)] = 0;
 }
 
-static void ntpwdhash (uint8_t *szHash, char const *szPassword)
+static void ntpwdhash(uint8_t *out, char const *password)
 {
-       char szUnicodePass[513];
-       char nPasswordLen;
-       int i;
+       ssize_t len;
+       uint8_t ucs2_password[512];
 
-       /*
-        *      NT passwords are unicode.  Convert plain text password
-        *      to unicode by inserting a zero every other byte
-        */
-       nPasswordLen = strlen(szPassword);
-       for (i = 0; i < nPasswordLen; i++) {
-               szUnicodePass[i << 1] = szPassword[i];
-               szUnicodePass[(i << 1) + 1] = 0;
+       len = fr_utf8_to_ucs2(ucs2_password, sizeof(ucs2_password), password, strlen(password));
+       if (len < 0) {
+               *out = '\0';
+               return;
        }
-
-       /* Encrypt Unicode password to a 16-byte MD4 hash */
-       fr_md4_calc(szHash, (uint8_t *) szUnicodePass, (nPasswordLen<<1) );
+       fr_md4_calc(out, (uint8_t *) ucs2_password, len);
 }
 
-
-
 int main (int argc, char *argv[])
 {
        int i, l;