📄 radius.c
字号:
/* * hostapd / RADIUS message processing * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.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"struct radius_msg *radius_msg_new(u8 code, u8 identifier){ struct radius_msg *msg; msg = (struct radius_msg *) malloc(sizeof(*msg)); if (msg == NULL) return NULL; if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) { 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; memset(msg, 0, sizeof(*msg)); msg->buf = wpa_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->attrs = (struct radius_attr_hdr **) malloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attrs)); if (msg->attrs == NULL) { 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){ if (msg->buf != NULL) { free(msg->buf); msg->buf = NULL; msg->hdr = NULL; } msg->buf_size = msg->buf_used = 0; if (msg->attrs != NULL) { free(msg->attrs); msg->attrs = 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_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_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_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 = (struct in_addr *) pos; 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++) { radius_msg_dump_attr(msg->attrs[i]); }}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; 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]; 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); 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); 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) { struct radius_attr_hdr **nattrs; int nlen = msg->attr_size * 2; nattrs = (struct radius_attr_hdr **) realloc(msg->attrs, nlen * sizeof(*msg->attrs)); if (nattrs == NULL) return -1; msg->attrs = nattrs; msg->attr_size = nlen; } msg->attrs[msg->attr_used++] = attr; 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 i, nlen = msg->buf_size; int diff; while (nlen < buf_needed) nlen *= 2; nbuf = (unsigned char *) realloc(msg->buf, nlen); if (nbuf == NULL) return NULL; diff = nbuf - msg->buf; msg->buf = nbuf; msg->hdr = (struct radius_hdr *) msg->buf; /* adjust attr pointers to match with the new buffer */ for (i = 0; i < msg->attr_used; i++) msg->attrs[i] = (struct radius_attr_hdr *) (((u8 *) msg->attrs[i]) + diff); 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) 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 = (struct radius_msg *) malloc(sizeof(*msg)); if (msg == NULL) return NULL; if (radius_msg_initialize(msg, msg_len)) { free(msg); return NULL; } 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); 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; if (msg == NULL) return NULL; len = 0; for (i = 0; i < msg->attr_used; i++) { if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) len += msg->attrs[i]->length - sizeof(struct radius_attr_hdr); } if (len == 0) return NULL; eap = malloc(len); if (eap == NULL) return NULL; pos = eap; for (i = 0; i < msg->attr_used; i++) { if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) { struct radius_attr_hdr *attr = msg->attrs[i]; int flen = attr->length - sizeof(*attr); 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; size_t i; for (i = 0; i < msg->attr_used; i++) { if (msg->attrs[i]->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { if (attr != NULL) { printf("Multiple Message-Authenticator " "attributes in RADIUS message\n"); return 1; } attr = msg->attrs[i]; } } if (attr == NULL) { printf("No Message-Authenticator attribute found\n"); return 1; } memcpy(orig, attr + 1, MD5_MAC_LEN); memset(attr + 1, 0, MD5_MAC_LEN); if (req_auth) { memcpy(orig_authenticator, msg->hdr->authenticator, sizeof(orig_authenticator)); memcpy(msg->hdr->authenticator, req_auth, sizeof(msg->hdr->authenticator)); } hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth); memcpy(attr + 1, orig, MD5_MAC_LEN); if (req_auth) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -