struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) { struct wpabuf *eap; size_t len, i; struct radius_attr_hdr *attr; if (msg == NULL) return NULL; len = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); 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 = wpabuf_alloc(len); if (eap == NULL) return NULL; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); if (attr->type == RADIUS_ATTR_EAP_MESSAGE && attr->length > sizeof(struct radius_attr_hdr)) { int flen = attr->length - sizeof(*attr); wpabuf_put_data(eap, attr + 1, flen); } } return eap; }
/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. * Returns the Attribute payload and sets alen to indicate the length of the * payload if a vendor attribute with subtype is found, otherwise returns NULL. * The returned payload is allocated with os_malloc() and caller must free it * by calling os_free(). */ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, u8 subtype, size_t *alen) { u8 *data, *pos; size_t i, len; if (msg == NULL) return NULL; for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); size_t left; u32 vendor_id; struct radius_attr_vendor *vhdr; if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || attr->length < sizeof(*attr)) continue; left = attr->length - sizeof(*attr); if (left < 4) continue; pos = (u8 *) (attr + 1); os_memcpy(&vendor_id, pos, 4); pos += 4; left -= 4; if (ntohl(vendor_id) != vendor) continue; while (left >= sizeof(*vhdr)) { vhdr = (struct radius_attr_vendor *) pos; if (vhdr->vendor_length > left || vhdr->vendor_length < sizeof(*vhdr)) { left = 0; break; } if (vhdr->vendor_type != subtype) { pos += vhdr->vendor_length; left -= vhdr->vendor_length; continue; } len = vhdr->vendor_length - sizeof(*vhdr); data = os_malloc(len); if (data == NULL) return NULL; os_memcpy(data, pos + sizeof(*vhdr), len); if (alen) *alen = len; return data; } } return NULL; }
int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, size_t secret_len) { const u8 *addr[4]; size_t len[4]; u8 zero[MD5_MAC_LEN]; u8 hash[MD5_MAC_LEN]; u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; u8 orig_authenticator[16]; struct radius_attr_hdr *attr = NULL, *tmp; size_t i; os_memset(zero, 0, sizeof(zero)); addr[0] = (u8 *) msg->hdr; len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; addr[1] = zero; len[1] = MD5_MAC_LEN; addr[2] = (u8 *) (msg->hdr + 1); len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) return 1; for (i = 0; i < msg->attr_used; i++) { tmp = radius_get_attr_hdr(msg, i); if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { if (attr != NULL) { wpa_printf(MSG_WARNING, "Multiple " "Message-Authenticator attributes " "in RADIUS message"); return 1; } attr = tmp; } } if (attr == NULL) { /* Message-Authenticator is MAY; not required */ return 0; } os_memcpy(orig, attr + 1, MD5_MAC_LEN); os_memset(attr + 1, 0, MD5_MAC_LEN); os_memcpy(orig_authenticator, msg->hdr->authenticator, sizeof(orig_authenticator)); os_memset(msg->hdr->authenticator, 0, sizeof(msg->hdr->authenticator)); hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), auth); os_memcpy(attr + 1, orig, MD5_MAC_LEN); os_memcpy(msg->hdr->authenticator, orig_authenticator, sizeof(orig_authenticator)); return os_memcmp(orig, auth, MD5_MAC_LEN) != 0; }
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)); for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); radius_msg_dump_attr(attr); } }
void radius_msg_dump(struct radius_msg *msg) { size_t i; 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); radius_msg_dump_attr(attr); } }
u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) { u8 *eap, *pos; size_t len, i; struct radius_attr_hdr *attr; if (msg == NULL) return NULL; len = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); if (attr->type == RADIUS_ATTR_EAP_MESSAGE) len += attr->length - sizeof(struct radius_attr_hdr); } if (len == 0) return NULL; eap = os_malloc(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) { int flen = attr->length - sizeof(*attr); os_memcpy(pos, attr + 1, flen); pos += flen; } } if (eap_len) *eap_len = len; return eap; }
int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) { size_t i; int count; for (count = 0, i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); if (attr->type == type && attr->length >= sizeof(struct radius_attr_hdr) + min_len) count++; } return count; }
int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_auth) { u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; u8 orig_authenticator[16]; struct radius_attr_hdr *attr = NULL, *tmp; size_t i; for (i = 0; i < msg->attr_used; i++) { 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"); return 1; } attr = tmp; } } if (attr == NULL) { printf("No Message-Authenticator attribute found\n"); return 1; } os_memcpy(orig, attr + 1, MD5_MAC_LEN); os_memset(attr + 1, 0, MD5_MAC_LEN); if (req_auth) { os_memcpy(orig_authenticator, msg->hdr->authenticator, sizeof(orig_authenticator)); os_memcpy(msg->hdr->authenticator, req_auth, sizeof(msg->hdr->authenticator)); } hmac_md5(secret, secret_len, wpabuf_head(msg->buf), wpabuf_len(msg->buf), auth); os_memcpy(attr + 1, orig, MD5_MAC_LEN); if (req_auth) { os_memcpy(msg->hdr->authenticator, orig_authenticator, sizeof(orig_authenticator)); } if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) { printf("Invalid Message-Authenticator!\n"); return 1; } return 0; }
int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type) { struct radius_attr_hdr *attr; size_t i; int count = 0; for (i = 0; i < src->attr_used; i++) { attr = radius_get_attr_hdr(src, i); if (attr->type == type) { if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), attr->length - sizeof(*attr))) return -1; count++; } } return count; }
u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) { size_t i, j; struct radius_attr_hdr *attr; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); for (j = 0; attrs[j]; j++) { if (attr->type == attrs[j]) break; } if (attrs[j] == 0) return attr->type; /* unlisted attr */ } return 0; }
int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) { struct radius_attr_hdr *attr = NULL, *tmp; size_t i, dlen; for (i = 0; i < msg->attr_used; i++) { tmp = radius_get_attr_hdr(msg, i); if (tmp->type == type) { attr = tmp; break; } } if (!attr) return -1; dlen = attr->length - sizeof(*attr); if (buf) os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); return dlen; }
int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, size_t *len, const u8 *start) { size_t i; struct radius_attr_hdr *attr = NULL, *tmp; for (i = 0; i < msg->attr_used; i++) { tmp = radius_get_attr_hdr(msg, i); if (tmp->type == type && (start == NULL || (u8 *) tmp > start)) { attr = tmp; break; } } if (!attr) return -1; *buf = (u8 *) (attr + 1); *len = attr->length - sizeof(*attr); return 0; }
/** * 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 */ int radius_msg_get_vlanid(struct radius_msg *msg) { struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; size_t i; struct radius_attr_hdr *attr = NULL; const u8 *data; char buf[10]; size_t dlen; os_memset(&tunnel, 0, sizeof(tunnel)); for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (attr->length < 3) continue; if (data[0] >= RADIUS_TUNNEL_TAGS) tun = &tunnel[0]; else tun = &tunnel[data[0]]; switch (attr->type) { case RADIUS_ATTR_TUNNEL_TYPE: if (attr->length != 6) break; tun->tag_used++; tun->type = WPA_GET_BE24(data + 1); break; case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: if (attr->length != 6) break; tun->tag_used++; tun->medium_type = WPA_GET_BE24(data + 1); break; case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: if (data[0] < RADIUS_TUNNEL_TAGS) { data++; dlen--; } if (dlen >= sizeof(buf)) break; os_memcpy(buf, data, dlen); buf[dlen] = '\0'; tun->tag_used++; tun->vlanid = atoi(buf); break; } } for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { tun = &tunnel[i]; if (tun->tag_used && tun->type == RADIUS_TUNNEL_TYPE_VLAN && tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && tun->vlanid > 0) return tun->vlanid; } return -1; }
/** * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password * @msg: Received RADIUS message * @keylen: Length of returned password * @secret: RADIUS shared secret * @secret_len: Length of secret * @sent_msg: Sent RADIUS message * @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, size_t n) { u8 *buf = NULL; size_t buflen; const u8 *salt; u8 *str; const u8 *addr[3]; size_t len[3]; u8 hash[16]; u8 *pos; size_t i, j = 0; struct radius_attr_hdr *attr; const u8 *data; size_t dlen; const u8 *fdata = NULL; /* points to found item */ size_t fdlen = -1; char *ret = NULL; /* 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 || attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { continue; } if (attr->length <= 5) continue; data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (dlen <= 3 || dlen % 16 != 3) continue; j++; if (j <= n) continue; fdata = data; fdlen = dlen; break; } if (fdata == NULL) goto out; /* alloc writable memory for decryption */ buf = os_malloc(fdlen); if (buf == NULL) goto out; os_memcpy(buf, fdata, fdlen); buflen = fdlen; /* init pointers */ salt = buf + 1; str = buf + 3; /* decrypt blocks */ pos = buf + buflen - 16; /* last block */ while (pos >= str + 16) { /* all but the first block */ addr[0] = secret; len[0] = secret_len; addr[1] = pos - 16; len[1] = 16; md5_vector(2, addr, len, hash); for (i = 0; i < 16; i++) pos[i] ^= hash[i]; pos -= 16; } /* decrypt first block */ if (str != pos) goto out; addr[0] = secret; len[0] = secret_len; addr[1] = sent_msg->hdr->authenticator; len[1] = 16; addr[2] = salt; len[2] = 2; md5_vector(3, addr, len, hash); for (i = 0; i < 16; i++) pos[i] ^= hash[i]; /* derive plaintext length from first subfield */ *keylen = (unsigned char) str[0]; if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { /* decryption error - invalid key length */ goto out; } if (*keylen == 0) { /* empty password */ goto out; } /* copy passphrase into new buffer */ ret = os_malloc(*keylen); if (ret) os_memcpy(ret, str + 1, *keylen); out: /* return new buffer */ os_free(buf); return ret; }
/** * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information * The k tagged vlans found are sorted by vlan_id and stored in the first k * items of tagged. * * @msg: RADIUS message * @untagged: Pointer to store untagged vid * @numtagged: Size of tagged * @tagged: Pointer to store tagged list * * Returns: 0 if neither tagged nor untagged configuration is found, 1 otherwise */ int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, int *tagged) { struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; size_t i; struct radius_attr_hdr *attr = NULL; const u8 *data; char buf[10]; size_t dlen; int j, taggedidx = 0, vlan_id; os_memset(&tunnel, 0, sizeof(tunnel)); for (j = 0; j < numtagged; j++) tagged[j] = 0; *untagged = 0; 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) continue; if (data[0] >= RADIUS_TUNNEL_TAGS) tun = &tunnel[0]; else tun = &tunnel[data[0]]; switch (attr->type) { case RADIUS_ATTR_TUNNEL_TYPE: if (attr->length != 6) break; tun->tag_used++; tun->type = WPA_GET_BE24(data + 1); break; case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: if (attr->length != 6) break; tun->tag_used++; tun->medium_type = WPA_GET_BE24(data + 1); break; case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: if (data[0] < RADIUS_TUNNEL_TAGS) { data++; dlen--; } if (dlen >= sizeof(buf)) break; os_memcpy(buf, data, dlen); buf[dlen] = '\0'; vlan_id = atoi(buf); if (vlan_id <= 0) break; tun->tag_used++; tun->vlanid = vlan_id; break; case RADIUS_ATTR_EGRESS_VLANID: /* RFC 4675 */ if (attr->length != 6) break; vlan_id = WPA_GET_BE24(data + 1); if (vlan_id <= 0) break; if (data[0] == 0x32) *untagged = vlan_id; else if (data[0] == 0x31 && tagged && taggedidx < numtagged) tagged[taggedidx++] = vlan_id; break; } } /* Use tunnel with the lowest tag for untagged VLAN id */ for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { tun = &tunnel[i]; if (tun->tag_used && tun->type == RADIUS_TUNNEL_TYPE_VLAN && tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && tun->vlanid > 0) { *untagged = tun->vlanid; break; } } if (taggedidx) qsort(tagged, taggedidx, sizeof(int), cmp_int); if (*untagged > 0 || taggedidx) return 1; return 0; }