📄 radius.c
字号:
/* * hostapd / RADIUS message processing * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */#include "includes.h"#include "common.h"#include "radius.h"#include "md5.h"#include "crypto.h"static struct radius_attr_hdr *radius_get_attr_hdr(struct radius_msg *msg, int idx){ return (struct radius_attr_hdr *) (msg->buf + msg->attr_pos[idx]);}struct radius_msg *radius_msg_new(u8 code, u8 identifier){ struct radius_msg *msg; msg = os_malloc(sizeof(*msg)); if (msg == NULL) return NULL; if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) { os_free(msg); return NULL; } radius_msg_set_hdr(msg, code, identifier); return msg;}int radius_msg_initialize(struct radius_msg *msg, size_t init_len){ if (msg == NULL || init_len < sizeof(struct radius_hdr)) return -1; os_memset(msg, 0, sizeof(*msg)); msg->buf = os_zalloc(init_len); if (msg->buf == NULL) return -1; msg->buf_size = init_len; msg->hdr = (struct radius_hdr *) msg->buf; msg->buf_used = sizeof(*msg->hdr); msg->attr_pos = os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); if (msg->attr_pos == NULL) { os_free(msg->buf); msg->buf = NULL; msg->hdr = NULL; return -1; } msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; msg->attr_used = 0; return 0;}void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier){ msg->hdr->code = code; msg->hdr->identifier = identifier;}void radius_msg_free(struct radius_msg *msg){ os_free(msg->buf); msg->buf = NULL; msg->hdr = NULL; msg->buf_size = msg->buf_used = 0; os_free(msg->attr_pos); msg->attr_pos = NULL; msg->attr_size = msg->attr_used = 0;}static const char *radius_code_string(u8 code){ switch (code) { case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; case RADIUS_CODE_RESERVED: return "Reserved"; default: return "?Unknown?"; }}struct radius_attr_type { u8 type; char *name; enum { RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 } data_type;};static 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_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },};#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))static struct radius_attr_type *radius_get_attr_type(u8 type){ size_t i; for (i = 0; i < RADIUS_ATTRS; i++) { if (type == radius_attrs[i].type) return &radius_attrs[i]; } return NULL;}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; unsigned char *pos; attr = radius_get_attr_type(hdr->type); printf(" Attribute %d (%s) length=%d\n", hdr->type, attr ? attr->name : "?Unknown?", hdr->length); if (attr == NULL) return; len = hdr->length - sizeof(struct radius_attr_hdr); pos = (unsigned char *) (hdr + 1); switch (attr->data_type) { case RADIUS_ATTR_TEXT: printf(" Value: '"); for (i = 0; i < len; i++) print_char(pos[i]); printf("'\n"); 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); 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); 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"); break; case RADIUS_ATTR_INT32: if (len == 4) printf(" Value: %u\n", WPA_GET_BE32(pos)); else printf(" Invalid INT32 length %d\n", len); break; default: break; }}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); }}int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len){ if (secret) { u8 auth[MD5_MAC_LEN]; struct radius_attr_hdr *attr; os_memset(auth, 0, MD5_MAC_LEN); 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"); return -1; } msg->hdr->length = htons(msg->buf_used); hmac_md5(secret, secret_len, msg->buf, msg->buf_used, (u8 *) (attr + 1)); } else msg->hdr->length = htons(msg->buf_used); if (msg->buf_used > 0xffff) { printf("WARNING: too long RADIUS message (%lu)\n", (unsigned long) msg->buf_used); return -1; } return 0;}int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_authenticator){ u8 auth[MD5_MAC_LEN]; struct radius_attr_hdr *attr; const u8 *addr[4]; size_t len[4]; os_memset(auth, 0, MD5_MAC_LEN); 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"); return -1; } msg->hdr->length = htons(msg->buf_used); os_memcpy(msg->hdr->authenticator, req_authenticator, sizeof(msg->hdr->authenticator)); hmac_md5(secret, secret_len, msg->buf, msg->buf_used, (u8 *) (attr + 1)); /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ addr[0] = (u8 *) msg->hdr; len[0] = 1 + 1 + 2; addr[1] = req_authenticator; len[1] = MD5_MAC_LEN; addr[2] = (u8 *) (msg->hdr + 1); len[2] = msg->buf_used - sizeof(*msg->hdr); addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, msg->hdr->authenticator); if (msg->buf_used > 0xffff) { printf("WARNING: too long RADIUS message (%lu)\n", (unsigned long) msg->buf_used); return -1; } return 0;}void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, size_t secret_len){ const u8 *addr[2]; size_t len[2]; msg->hdr->length = htons(msg->buf_used); os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); addr[0] = msg->buf; len[0] = msg->buf_used; addr[1] = secret; len[1] = secret_len; md5_vector(2, addr, len, msg->hdr->authenticator); if (msg->buf_used > 0xffff) { printf("WARNING: too long RADIUS messages (%lu)\n", (unsigned long) msg->buf_used); }}static int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr *attr){ if (msg->attr_used >= msg->attr_size) { size_t *nattr_pos; int nlen = msg->attr_size * 2; nattr_pos = os_realloc(msg->attr_pos, nlen * sizeof(*msg->attr_pos)); if (nattr_pos == NULL) return -1; msg->attr_pos = nattr_pos; msg->attr_size = nlen; } msg->attr_pos[msg->attr_used++] = (unsigned char *) attr - msg->buf; return 0;}struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, const u8 *data, size_t data_len){ size_t buf_needed; struct radius_attr_hdr *attr; if (data_len > RADIUS_MAX_ATTR_LEN) { printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", (unsigned long) data_len); return NULL; } buf_needed = msg->buf_used + sizeof(*attr) + data_len; if (msg->buf_size < buf_needed) { /* allocate more space for message buffer */ unsigned char *nbuf; size_t nlen = msg->buf_size; while (nlen < buf_needed) nlen *= 2; nbuf = os_realloc(msg->buf, nlen); if (nbuf == NULL) return NULL; msg->buf = nbuf; msg->hdr = (struct radius_hdr *) msg->buf; os_memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); msg->buf_size = nlen; } attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used); attr->type = type; attr->length = sizeof(*attr) + data_len; if (data_len > 0) os_memcpy(attr + 1, data, data_len); msg->buf_used += sizeof(*attr) + data_len; if (radius_msg_add_attr_to_array(msg, attr)) return NULL; return attr;}struct radius_msg *radius_msg_parse(const u8 *data, size_t len){ struct radius_msg *msg; struct radius_hdr *hdr; struct radius_attr_hdr *attr; size_t msg_len; unsigned char *pos, *end; if (data == NULL || len < sizeof(*hdr)) return NULL; hdr = (struct radius_hdr *) data; msg_len = ntohs(hdr->length); if (msg_len < sizeof(*hdr) || msg_len > len) { printf("Invalid RADIUS message length\n"); return NULL; } if (msg_len < len) { printf("Ignored %lu extra bytes after RADIUS message\n", (unsigned long) len - msg_len); } msg = os_malloc(sizeof(*msg)); if (msg == NULL) return NULL; if (radius_msg_initialize(msg, msg_len)) { os_free(msg); return NULL; } os_memcpy(msg->buf, data, msg_len); msg->buf_size = msg->buf_used = msg_len; /* parse attributes */ pos = (unsigned char *) (msg->hdr + 1); end = msg->buf + msg->buf_used; while (pos < end) { if ((size_t) (end - pos) < sizeof(*attr)) goto fail; attr = (struct radius_attr_hdr *) pos; if (pos + attr->length > end || attr->length < sizeof(*attr)) goto fail; /* TODO: check that attr->length is suitable for attr->type */ if (radius_msg_add_attr_to_array(msg, attr)) goto fail; pos += attr->length; } return msg; fail: radius_msg_free(msg); os_free(msg); return NULL;}int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len){ const u8 *pos = data; size_t left = data_len; while (left > 0) { int len; if (left > RADIUS_MAX_ATTR_LEN) len = RADIUS_MAX_ATTR_LEN; else len = left; if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, pos, len)) return 0; pos += len; left -= len; } return 1;}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_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, msg->buf, msg->buf_used, auth);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -