Use stronger PRNG for MS-MPPE-Send/Recv-Key salt
[mech_eap.git] / src / radius / radius.c
index 2b9cbca..a6304e1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -78,8 +78,8 @@ static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
 
 static int radius_msg_initialize(struct radius_msg *msg)
 {
-       msg->attr_pos =
-               os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
+       msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT,
+                                 sizeof(*msg->attr_pos));
        if (msg->attr_pos == NULL)
                return -1;
 
@@ -167,12 +167,13 @@ struct radius_attr_type {
        } data_type;
 };
 
-static struct radius_attr_type radius_attrs[] =
+static const struct radius_attr_type radius_attrs[] =
 {
        { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
        { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
        { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
        { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP },
        { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
        { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
        { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
@@ -231,12 +232,35 @@ static struct radius_attr_type radius_attrs[] =
        { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
          RADIUS_ATTR_TEXT },
        { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
-       { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
+       { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_LOCATION_INFO, "Location-Information",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES,
+         "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES,
+         "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher",
+         RADIUS_ATTR_HEXDUMP },
 };
-#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
+#define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
 
 
-static struct radius_attr_type *radius_get_attr_type(u8 type)
+static const struct radius_attr_type *radius_get_attr_type(u8 type)
 {
        size_t i;
 
@@ -249,27 +273,19 @@ static struct radius_attr_type *radius_get_attr_type(u8 type)
 }
 
 
-static void print_char(char c)
-{
-       if (c >= 32 && c < 127)
-               printf("%c", c);
-       else
-               printf("<%02x>", c);
-}
-
-
 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 {
-       struct radius_attr_type *attr;
-       int i, len;
+       const struct radius_attr_type *attr;
+       int len;
        unsigned char *pos;
+       char buf[1000];
 
        attr = radius_get_attr_type(hdr->type);
 
-       printf("   Attribute %d (%s) length=%d\n",
-              hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+       wpa_printf(MSG_INFO, "   Attribute %d (%s) length=%d",
+                  hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
 
-       if (attr == NULL)
+       if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
                return;
 
        len = hdr->length - sizeof(struct radius_attr_hdr);
@@ -277,47 +293,50 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 
        switch (attr->data_type) {
        case RADIUS_ATTR_TEXT:
-               printf("      Value: '");
-               for (i = 0; i < len; i++)
-                       print_char(pos[i]);
-               printf("'\n");
+               printf_encode(buf, sizeof(buf), pos, len);
+               wpa_printf(MSG_INFO, "      Value: '%s'", buf);
                break;
 
        case RADIUS_ATTR_IP:
                if (len == 4) {
                        struct in_addr addr;
                        os_memcpy(&addr, pos, 4);
-                       printf("      Value: %s\n", inet_ntoa(addr));
-               } else
-                       printf("      Invalid IP address length %d\n", len);
+                       wpa_printf(MSG_INFO, "      Value: %s",
+                                  inet_ntoa(addr));
+               } else {
+                       wpa_printf(MSG_INFO, "      Invalid IP address length %d",
+                                  len);
+               }
                break;
 
 #ifdef CONFIG_IPV6
        case RADIUS_ATTR_IPV6:
                if (len == 16) {
-                       char buf[128];
                        const char *atxt;
                        struct in6_addr *addr = (struct in6_addr *) pos;
                        atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
-                       printf("      Value: %s\n", atxt ? atxt : "?");
-               } else
-                       printf("      Invalid IPv6 address length %d\n", len);
+                       wpa_printf(MSG_INFO, "      Value: %s",
+                                  atxt ? atxt : "?");
+               } else {
+                       wpa_printf(MSG_INFO, "      Invalid IPv6 address length %d",
+                                  len);
+               }
                break;
 #endif /* CONFIG_IPV6 */
 
        case RADIUS_ATTR_HEXDUMP:
        case RADIUS_ATTR_UNDIST:
-               printf("      Value:");
-               for (i = 0; i < len; i++)
-                       printf(" %02x", pos[i]);
-               printf("\n");
+               wpa_snprintf_hex(buf, sizeof(buf), pos, len);
+               wpa_printf(MSG_INFO, "      Value: %s", buf);
                break;
 
        case RADIUS_ATTR_INT32:
                if (len == 4)
-                       printf("      Value: %u\n", WPA_GET_BE32(pos));
+                       wpa_printf(MSG_INFO, "      Value: %u",
+                                  WPA_GET_BE32(pos));
                else
-                       printf("      Invalid INT32 length %d\n", len);
+                       wpa_printf(MSG_INFO, "      Invalid INT32 length %d",
+                                  len);
                break;
 
        default:
@@ -330,9 +349,9 @@ void radius_msg_dump(struct radius_msg *msg)
 {
        size_t i;
 
-       printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
-              msg->hdr->code, radius_code_string(msg->hdr->code),
-              msg->hdr->identifier, ntohs(msg->hdr->length));
+       wpa_printf(MSG_INFO, "RADIUS message: code=%d (%s) identifier=%d length=%d",
+                  msg->hdr->code, radius_code_string(msg->hdr->code),
+                  msg->hdr->identifier, be_to_host16(msg->hdr->length));
 
        for (i = 0; i < msg->attr_used; i++) {
                struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
@@ -357,11 +376,11 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
                                   "Message-Authenticator");
                        return -1;
                }
-               msg->hdr->length = htons(wpabuf_len(msg->buf));
+               msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
                hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
                         wpabuf_len(msg->buf), (u8 *) (attr + 1));
        } else
-               msg->hdr->length = htons(wpabuf_len(msg->buf));
+               msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 
        if (wpabuf_len(msg->buf) > 0xffff) {
                wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
@@ -384,10 +403,10 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
        attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
                                   auth, MD5_MAC_LEN);
        if (attr == NULL) {
-               printf("WARNING: Could not add Message-Authenticator\n");
+               wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator");
                return -1;
        }
-       msg->hdr->length = htons(wpabuf_len(msg->buf));
+       msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
        os_memcpy(msg->hdr->authenticator, req_authenticator,
                  sizeof(msg->hdr->authenticator));
        hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
@@ -430,7 +449,7 @@ int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
                return -1;
        }
 
-       msg->hdr->length = htons(wpabuf_len(msg->buf));
+       msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
        os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
        hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
                 wpabuf_len(msg->buf), (u8 *) (attr + 1));
@@ -458,7 +477,7 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
        const u8 *addr[2];
        size_t len[2];
 
-       msg->hdr->length = htons(wpabuf_len(msg->buf));
+       msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
        os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
        addr[0] = wpabuf_head(msg->buf);
        len[0] = wpabuf_len(msg->buf);
@@ -473,6 +492,27 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 }
 
 
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+                                size_t secret_len, const u8 *req_authenticator)
+{
+       const u8 *addr[2];
+       size_t len[2];
+
+       msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+       os_memcpy(msg->hdr->authenticator, req_authenticator, MD5_MAC_LEN);
+       addr[0] = wpabuf_head(msg->buf);
+       len[0] = wpabuf_len(msg->buf);
+       addr[1] = secret;
+       len[1] = secret_len;
+       md5_vector(2, addr, len, msg->hdr->authenticator);
+
+       if (wpabuf_len(msg->buf) > 0xffff) {
+               wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
+                          (unsigned long) wpabuf_len(msg->buf));
+       }
+}
+
+
 int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
                               size_t secret_len)
 {
@@ -491,7 +531,7 @@ int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
        addr[3] = secret;
        len[3] = secret_len;
        md5_vector(4, addr, len, hash);
-       return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
+       return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
 }
 
 
@@ -518,7 +558,7 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
        addr[3] = secret;
        len[3] = secret_len;
        md5_vector(4, addr, len, hash);
-       if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
+       if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
                return 1;
 
        for (i = 0; i < msg->attr_used; i++) {
@@ -551,7 +591,7 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
        os_memcpy(msg->hdr->authenticator, orig_authenticator,
                  sizeof(orig_authenticator));
 
-       return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
+       return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0;
 }
 
 
@@ -562,8 +602,8 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg,
                size_t *nattr_pos;
                int nlen = msg->attr_size * 2;
 
-               nattr_pos = os_realloc(msg->attr_pos,
-                                      nlen * sizeof(*msg->attr_pos));
+               nattr_pos = os_realloc_array(msg->attr_pos, nlen,
+                                            sizeof(*msg->attr_pos));
                if (nattr_pos == NULL)
                        return -1;
 
@@ -585,7 +625,7 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
        struct radius_attr_hdr *attr;
 
        if (data_len > RADIUS_MAX_ATTR_LEN) {
-               printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
+               wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
                       (unsigned long) data_len);
                return NULL;
        }
@@ -633,7 +673,7 @@ struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
 
        hdr = (struct radius_hdr *) data;
 
-       msg_len = ntohs(hdr->length);
+       msg_len = be_to_host16(hdr->length);
        if (msg_len < sizeof(*hdr) || msg_len > len) {
                wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
                return NULL;
@@ -664,7 +704,7 @@ struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
 
                attr = (struct radius_attr_hdr *) pos;
 
-               if (pos + attr->length > end || attr->length < sizeof(*attr))
+               if (attr->length > end - pos || attr->length < sizeof(*attr))
                        goto fail;
 
                /* TODO: check that attr->length is suitable for attr->type */
@@ -707,9 +747,9 @@ int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
 }
 
 
-u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
+struct wpabuf * radius_msg_get_eap(struct radius_msg *msg)
 {
-       u8 *eap, *pos;
+       struct wpabuf *eap;
        size_t len, i;
        struct radius_attr_hdr *attr;
 
@@ -719,30 +759,27 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
        len = 0;
        for (i = 0; i < msg->attr_used; i++) {
                attr = radius_get_attr_hdr(msg, i);
-               if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
+               if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
+                   attr->length > sizeof(struct radius_attr_hdr))
                        len += attr->length - sizeof(struct radius_attr_hdr);
        }
 
        if (len == 0)
                return NULL;
 
-       eap = os_malloc(len);
+       eap = wpabuf_alloc(len);
        if (eap == NULL)
                return NULL;
 
-       pos = eap;
        for (i = 0; i < msg->attr_used; i++) {
                attr = radius_get_attr_hdr(msg, i);
-               if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
+               if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
+                   attr->length > sizeof(struct radius_attr_hdr)) {
                        int flen = attr->length - sizeof(*attr);
-                       os_memcpy(pos, attr + 1, flen);
-                       pos += flen;
+                       wpabuf_put_data(eap, attr + 1, flen);
                }
        }
 
-       if (eap_len)
-               *eap_len = len;
-
        return eap;
 }
 
@@ -759,8 +796,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
                tmp = radius_get_attr_hdr(msg, i);
                if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
                        if (attr != NULL) {
-                               printf("Multiple Message-Authenticator "
-                                      "attributes in RADIUS message\n");
+                               wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message");
                                return 1;
                        }
                        attr = tmp;
@@ -768,7 +804,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
        }
 
        if (attr == NULL) {
-               printf("No Message-Authenticator attribute found\n");
+               wpa_printf(MSG_INFO, "No Message-Authenticator attribute found");
                return 1;
        }
 
@@ -788,8 +824,8 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
                          sizeof(orig_authenticator));
        }
 
-       if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
-               printf("Invalid Message-Authenticator!\n");
+       if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "Invalid Message-Authenticator!");
                return 1;
        }
 
@@ -805,7 +841,7 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
        u8 hash[MD5_MAC_LEN];
 
        if (sent_msg == NULL) {
-               printf("No matching Access-Request message found\n");
+               wpa_printf(MSG_INFO, "No matching Access-Request message found");
                return 1;
        }
 
@@ -825,8 +861,8 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
        addr[3] = secret;
        len[3] = secret_len;
        md5_vector(4, addr, len, hash);
-       if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
-               printf("Response Authenticator invalid!\n");
+       if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "Response Authenticator invalid!");
                return 1;
        }
 
@@ -843,7 +879,7 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
 
        for (i = 0; i < src->attr_used; i++) {
                attr = radius_get_attr_hdr(src, i);
-               if (attr->type == type) {
+               if (attr->type == type && attr->length >= sizeof(*attr)) {
                        if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
                                                 attr->length - sizeof(*attr)))
                                return -1;
@@ -857,25 +893,11 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
 
 /* Create Request Authenticator. The value should be unique over the lifetime
  * of the shared secret between authenticator and authentication server.
- * Use one-way MD5 hash calculated from current timestamp and some data given
- * by the caller. */
-void radius_msg_make_authenticator(struct radius_msg *msg,
-                                  const u8 *data, size_t len)
+ */
+int radius_msg_make_authenticator(struct radius_msg *msg)
 {
-       struct os_time tv;
-       long int l;
-       const u8 *addr[3];
-       size_t elen[3];
-
-       os_get_time(&tv);
-       l = os_random();
-       addr[0] = (u8 *) &tv;
-       elen[0] = sizeof(tv);
-       addr[1] = data;
-       elen[1] = len;
-       addr[2] = (u8 *) &l;
-       elen[2] = sizeof(l);
-       md5_vector(3, addr, elen, msg->hdr->authenticator);
+       return os_get_random((u8 *) &msg->hdr->authenticator,
+                            sizeof(msg->hdr->authenticator));
 }
 
 
@@ -900,7 +922,8 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
                u32 vendor_id;
                struct radius_attr_vendor *vhdr;
 
-               if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
+               if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC ||
+                   attr->length < sizeof(*attr))
                        continue;
 
                left = attr->length - sizeof(*attr);
@@ -920,7 +943,6 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
                        vhdr = (struct radius_attr_vendor *) pos;
                        if (vhdr->vendor_length > left ||
                            vhdr->vendor_length < sizeof(*vhdr)) {
-                               left = 0;
                                break;
                        }
                        if (vhdr->vendor_type != subtype) {
@@ -958,13 +980,17 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len,
 
        /* key: 16-bit salt followed by encrypted key info */
 
-       if (len < 2 + 16)
+       if (len < 2 + 16) {
+               wpa_printf(MSG_DEBUG, "RADIUS: %s: Len is too small: %d",
+                          __func__, (int) len);
                return NULL;
+       }
 
        pos = key + 2;
        left = len - 2;
        if (left % 16) {
-               printf("Invalid ms key len %lu\n", (unsigned long) left);
+               wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu",
+                          (unsigned long) left);
                return NULL;
        }
 
@@ -998,7 +1024,7 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len,
        }
 
        if (plain[0] == 0 || plain[0] > plen - 1) {
-               printf("Failed to decrypt MPPE key\n");
+               wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key");
                os_free(plain);
                return NULL;
        }
@@ -1087,6 +1113,10 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
                                            sent_msg->hdr->authenticator,
                                            secret, secret_len,
                                            &keys->send_len);
+               if (!keys->send) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS: Failed to decrypt send key");
+               }
                os_free(key);
        }
 
@@ -1098,6 +1128,10 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
                                            sent_msg->hdr->authenticator,
                                            secret, secret_len,
                                            &keys->recv_len);
+               if (!keys->recv) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS: Failed to decrypt recv key");
+               }
                os_free(key);
        }
 
@@ -1163,7 +1197,9 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg,
        vhdr = (struct radius_attr_vendor *) pos;
        vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
        pos = (u8 *) (vhdr + 1);
-       salt = os_random() | 0x8000;
+       if (os_get_random((u8 *) &salt, sizeof(salt)) < 0)
+               return 0;
+       salt |= 0x8000;
        WPA_PUT_BE16(pos, salt);
        pos += 2;
        encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
@@ -1178,7 +1214,7 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg,
        }
 
        /* MS-MPPE-Recv-Key */
-       buf = os_malloc(hlen + send_key_len + 16);
+       buf = os_malloc(hlen + recv_key_len + 16);
        if (buf == NULL) {
                return 0;
        }
@@ -1206,30 +1242,55 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg,
 }
 
 
-/* Add User-Password attribute to a RADIUS message and encrypt it as specified
- * in RFC 2865, Chap. 5.2 */
-struct radius_attr_hdr *
-radius_msg_add_attr_user_password(struct radius_msg *msg,
-                                 const u8 *data, size_t data_len,
-                                 const u8 *secret, size_t secret_len)
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+                      size_t len)
 {
-       u8 buf[128];
-       size_t padlen, i, buf_len, pos;
+       struct radius_attr_hdr *attr;
+       u8 *buf, *pos;
+       size_t alen;
+
+       alen = 4 + 2 + len;
+       buf = os_malloc(alen);
+       if (buf == NULL)
+               return 0;
+       pos = buf;
+       WPA_PUT_BE32(pos, RADIUS_VENDOR_ID_WFA);
+       pos += 4;
+       *pos++ = subtype;
+       *pos++ = 2 + len;
+       os_memcpy(pos, data, len);
+       attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+                                  buf, alen);
+       os_free(buf);
+       if (attr == NULL)
+               return 0;
+
+       return 1;
+}
+
+
+int radius_user_password_hide(struct radius_msg *msg,
+                             const u8 *data, size_t data_len,
+                             const u8 *secret, size_t secret_len,
+                             u8 *buf, size_t buf_len)
+{
+       size_t padlen, i, pos;
        const u8 *addr[2];
        size_t len[2];
        u8 hash[16];
 
-       if (data_len > 128)
-               return NULL;
+       if (data_len + 16 > buf_len)
+               return -1;
 
        os_memcpy(buf, data, data_len);
-       buf_len = data_len;
 
        padlen = data_len % 16;
-       if (padlen && data_len < sizeof(buf)) {
+       if (padlen && data_len < buf_len) {
                padlen = 16 - padlen;
                os_memset(buf + data_len, 0, padlen);
-               buf_len += padlen;
+               buf_len = data_len + padlen;
+       } else {
+               buf_len = data_len;
        }
 
        addr[0] = secret;
@@ -1255,8 +1316,27 @@ radius_msg_add_attr_user_password(struct radius_msg *msg,
                pos += 16;
        }
 
+       return buf_len;
+}
+
+
+/* Add User-Password attribute to a RADIUS message and encrypt it as specified
+ * in RFC 2865, Chap. 5.2 */
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+                                 const u8 *data, size_t data_len,
+                                 const u8 *secret, size_t secret_len)
+{
+       u8 buf[128];
+       int res;
+
+       res = radius_user_password_hide(msg, data, data_len,
+                                       secret, secret_len, buf, sizeof(buf));
+       if (res < 0)
+               return NULL;
+
        return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
-                                  buf, buf_len);
+                                  buf, res);
 }
 
 
@@ -1273,7 +1353,7 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
                }
        }
 
-       if (!attr)
+       if (!attr || attr->length < sizeof(*attr))
                return -1;
 
        dlen = attr->length - sizeof(*attr);
@@ -1298,7 +1378,7 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
                }
        }
 
-       if (!attr)
+       if (!attr || attr->length < sizeof(*attr))
                return -1;
 
        *buf = (u8 *) (attr + 1);
@@ -1334,7 +1414,7 @@ struct radius_tunnel_attrs {
 /**
  * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
  * @msg: RADIUS message
- * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
+ * Returns: VLAN ID for the first tunnel configuration or 0 if none is found
  */
 int radius_msg_get_vlanid(struct radius_msg *msg)
 {
@@ -1349,6 +1429,8 @@ int radius_msg_get_vlanid(struct radius_msg *msg)
 
        for (i = 0; i < msg->attr_used; i++) {
                attr = radius_get_attr_hdr(msg, i);
+               if (attr->length < sizeof(*attr))
+                       return -1;
                data = (const u8 *) (attr + 1);
                dlen = attr->length - sizeof(*attr);
                if (attr->length < 3)
@@ -1395,7 +1477,7 @@ int radius_msg_get_vlanid(struct radius_msg *msg)
                        return tun->vlanid;
        }
 
-       return -1;
+       return 0;
 }
 
 
@@ -1406,11 +1488,12 @@ int radius_msg_get_vlanid(struct radius_msg *msg)
  * @secret: RADIUS shared secret
  * @secret_len: Length of secret
  * @sent_msg: Sent RADIUS message
- * Returns: pointer to password (free with os_free) or %NULL
+ * @n: Number of password attribute to return (starting with 0)
+ * Returns: Pointer to n-th password (free with os_free) or %NULL
  */
 char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
                                      const u8 *secret, size_t secret_len,
-                                     struct radius_msg *sent_msg)
+                                     struct radius_msg *sent_msg, size_t n)
 {
        u8 *buf = NULL;
        size_t buflen;
@@ -1420,7 +1503,7 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
        size_t len[3];
        u8 hash[16];
        u8 *pos;
-       size_t i;
+       size_t i, j = 0;
        struct radius_attr_hdr *attr;
        const u8 *data;
        size_t dlen;
@@ -1428,7 +1511,7 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
        size_t fdlen = -1;
        char *ret = NULL;
 
-       /* find attribute with lowest tag and check it */
+       /* find n-th valid Tunnel-Password attribute */
        for (i = 0; i < msg->attr_used; i++) {
                attr = radius_get_attr_hdr(msg, i);
                if (attr == NULL ||
@@ -1441,11 +1524,13 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
                dlen = attr->length - sizeof(*attr);
                if (dlen <= 3 || dlen % 16 != 3)
                        continue;
-               if (fdata != NULL && fdata[0] <= data[0])
+               j++;
+               if (j <= n)
                        continue;
 
                fdata = data;
                fdlen = dlen;
+               break;
        }
        if (fdata == NULL)
                goto out;
@@ -1534,7 +1619,7 @@ int radius_copy_class(struct radius_class_data *dst,
        if (src->attr == NULL)
                return 0;
 
-       dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
+       dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data));
        if (dst->attr == NULL)
                return -1;
 
@@ -1573,3 +1658,14 @@ u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs)
 
        return 0;
 }
+
+
+int radius_gen_session_id(u8 *id, size_t len)
+{
+       /*
+        * Acct-Session-Id and Acct-Multi-Session-Id should be globally and
+        * temporarily unique. A high quality random number is required
+        * therefore. This could be be improved by switching to a GUID.
+        */
+       return os_get_random(id, len);
+}