radius_das.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
  3. * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
  4. *
  5. * This software may be distributed under the terms of the BSD license.
  6. * See README for more details.
  7. */
  8. #include "includes.h"
  9. #include <net/if.h>
  10. #include "utils/common.h"
  11. #include "utils/eloop.h"
  12. #include "utils/ip_addr.h"
  13. #include "radius.h"
  14. #include "radius_das.h"
  15. struct radius_das_data {
  16. int sock;
  17. u8 *shared_secret;
  18. size_t shared_secret_len;
  19. struct hostapd_ip_addr client_addr;
  20. unsigned int time_window;
  21. int require_event_timestamp;
  22. void *ctx;
  23. enum radius_das_res (*disconnect)(void *ctx,
  24. struct radius_das_attrs *attr);
  25. };
  26. static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
  27. struct radius_msg *msg,
  28. const char *abuf,
  29. int from_port)
  30. {
  31. struct radius_hdr *hdr;
  32. struct radius_msg *reply;
  33. u8 allowed[] = {
  34. RADIUS_ATTR_USER_NAME,
  35. RADIUS_ATTR_NAS_IP_ADDRESS,
  36. RADIUS_ATTR_CALLING_STATION_ID,
  37. RADIUS_ATTR_NAS_IDENTIFIER,
  38. RADIUS_ATTR_ACCT_SESSION_ID,
  39. RADIUS_ATTR_EVENT_TIMESTAMP,
  40. RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
  41. RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
  42. #ifdef CONFIG_IPV6
  43. RADIUS_ATTR_NAS_IPV6_ADDRESS,
  44. #endif /* CONFIG_IPV6 */
  45. 0
  46. };
  47. int error = 405;
  48. u8 attr;
  49. enum radius_das_res res;
  50. struct radius_das_attrs attrs;
  51. u8 *buf;
  52. size_t len;
  53. char tmp[100];
  54. u8 sta_addr[ETH_ALEN];
  55. hdr = radius_msg_get_hdr(msg);
  56. attr = radius_msg_find_unlisted_attr(msg, allowed);
  57. if (attr) {
  58. wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
  59. "Disconnect-Request from %s:%d", attr,
  60. abuf, from_port);
  61. error = 401;
  62. goto fail;
  63. }
  64. os_memset(&attrs, 0, sizeof(attrs));
  65. if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
  66. &buf, &len, NULL) == 0) {
  67. if (len != 4) {
  68. wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
  69. abuf, from_port);
  70. error = 407;
  71. goto fail;
  72. }
  73. attrs.nas_ip_addr = buf;
  74. }
  75. #ifdef CONFIG_IPV6
  76. if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
  77. &buf, &len, NULL) == 0) {
  78. if (len != 16) {
  79. wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
  80. abuf, from_port);
  81. error = 407;
  82. goto fail;
  83. }
  84. attrs.nas_ipv6_addr = buf;
  85. }
  86. #endif /* CONFIG_IPV6 */
  87. if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
  88. &buf, &len, NULL) == 0) {
  89. attrs.nas_identifier = buf;
  90. attrs.nas_identifier_len = len;
  91. }
  92. if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
  93. &buf, &len, NULL) == 0) {
  94. if (len >= sizeof(tmp))
  95. len = sizeof(tmp) - 1;
  96. os_memcpy(tmp, buf, len);
  97. tmp[len] = '\0';
  98. if (hwaddr_aton2(tmp, sta_addr) < 0) {
  99. wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
  100. "'%s' from %s:%d", tmp, abuf, from_port);
  101. error = 407;
  102. goto fail;
  103. }
  104. attrs.sta_addr = sta_addr;
  105. }
  106. if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
  107. &buf, &len, NULL) == 0) {
  108. attrs.user_name = buf;
  109. attrs.user_name_len = len;
  110. }
  111. if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
  112. &buf, &len, NULL) == 0) {
  113. attrs.acct_session_id = buf;
  114. attrs.acct_session_id_len = len;
  115. }
  116. if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
  117. &buf, &len, NULL) == 0) {
  118. attrs.cui = buf;
  119. attrs.cui_len = len;
  120. }
  121. res = das->disconnect(das->ctx, &attrs);
  122. switch (res) {
  123. case RADIUS_DAS_NAS_MISMATCH:
  124. wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
  125. abuf, from_port);
  126. error = 403;
  127. break;
  128. case RADIUS_DAS_SESSION_NOT_FOUND:
  129. wpa_printf(MSG_INFO, "DAS: Session not found for request from "
  130. "%s:%d", abuf, from_port);
  131. error = 503;
  132. break;
  133. case RADIUS_DAS_SUCCESS:
  134. error = 0;
  135. break;
  136. }
  137. fail:
  138. reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
  139. RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
  140. if (reply == NULL)
  141. return NULL;
  142. if (error) {
  143. if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
  144. error)) {
  145. radius_msg_free(reply);
  146. return NULL;
  147. }
  148. }
  149. return reply;
  150. }
  151. static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
  152. {
  153. struct radius_das_data *das = eloop_ctx;
  154. u8 buf[1500];
  155. union {
  156. struct sockaddr_storage ss;
  157. struct sockaddr_in sin;
  158. #ifdef CONFIG_IPV6
  159. struct sockaddr_in6 sin6;
  160. #endif /* CONFIG_IPV6 */
  161. } from;
  162. char abuf[50];
  163. int from_port = 0;
  164. socklen_t fromlen;
  165. int len;
  166. struct radius_msg *msg, *reply = NULL;
  167. struct radius_hdr *hdr;
  168. struct wpabuf *rbuf;
  169. u32 val;
  170. int res;
  171. struct os_time now;
  172. fromlen = sizeof(from);
  173. len = recvfrom(sock, buf, sizeof(buf), 0,
  174. (struct sockaddr *) &from.ss, &fromlen);
  175. if (len < 0) {
  176. wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
  177. return;
  178. }
  179. os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
  180. from_port = ntohs(from.sin.sin_port);
  181. wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
  182. len, abuf, from_port);
  183. if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
  184. wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
  185. return;
  186. }
  187. msg = radius_msg_parse(buf, len);
  188. if (msg == NULL) {
  189. wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
  190. "from %s:%d failed", abuf, from_port);
  191. return;
  192. }
  193. if (wpa_debug_level <= MSG_MSGDUMP)
  194. radius_msg_dump(msg);
  195. if (radius_msg_verify_das_req(msg, das->shared_secret,
  196. das->shared_secret_len)) {
  197. wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
  198. "from %s:%d - drop", abuf, from_port);
  199. goto fail;
  200. }
  201. os_get_time(&now);
  202. res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
  203. (u8 *) &val, 4);
  204. if (res == 4) {
  205. u32 timestamp = ntohl(val);
  206. if ((unsigned int) abs(now.sec - timestamp) >
  207. das->time_window) {
  208. wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
  209. "Event-Timestamp (%u; local time %u) in "
  210. "packet from %s:%d - drop",
  211. timestamp, (unsigned int) now.sec,
  212. abuf, from_port);
  213. goto fail;
  214. }
  215. } else if (das->require_event_timestamp) {
  216. wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
  217. "from %s:%d - drop", abuf, from_port);
  218. goto fail;
  219. }
  220. hdr = radius_msg_get_hdr(msg);
  221. switch (hdr->code) {
  222. case RADIUS_CODE_DISCONNECT_REQUEST:
  223. reply = radius_das_disconnect(das, msg, abuf, from_port);
  224. break;
  225. case RADIUS_CODE_COA_REQUEST:
  226. /* TODO */
  227. reply = radius_msg_new(RADIUS_CODE_COA_NAK,
  228. hdr->identifier);
  229. if (reply == NULL)
  230. break;
  231. /* Unsupported Service */
  232. if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
  233. 405)) {
  234. radius_msg_free(reply);
  235. reply = NULL;
  236. break;
  237. }
  238. break;
  239. default:
  240. wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
  241. "packet from %s:%d",
  242. hdr->code, abuf, from_port);
  243. }
  244. if (reply) {
  245. wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
  246. if (!radius_msg_add_attr_int32(reply,
  247. RADIUS_ATTR_EVENT_TIMESTAMP,
  248. now.sec)) {
  249. wpa_printf(MSG_DEBUG, "DAS: Failed to add "
  250. "Event-Timestamp attribute");
  251. }
  252. if (radius_msg_finish_das_resp(reply, das->shared_secret,
  253. das->shared_secret_len, hdr) <
  254. 0) {
  255. wpa_printf(MSG_DEBUG, "DAS: Failed to add "
  256. "Message-Authenticator attribute");
  257. }
  258. if (wpa_debug_level <= MSG_MSGDUMP)
  259. radius_msg_dump(reply);
  260. rbuf = radius_msg_get_buf(reply);
  261. res = sendto(das->sock, wpabuf_head(rbuf),
  262. wpabuf_len(rbuf), 0,
  263. (struct sockaddr *) &from.ss, fromlen);
  264. if (res < 0) {
  265. wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
  266. abuf, from_port, strerror(errno));
  267. }
  268. }
  269. fail:
  270. radius_msg_free(msg);
  271. radius_msg_free(reply);
  272. }
  273. static int radius_das_open_socket(int port)
  274. {
  275. int s;
  276. struct sockaddr_in addr;
  277. s = socket(PF_INET, SOCK_DGRAM, 0);
  278. if (s < 0) {
  279. wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
  280. return -1;
  281. }
  282. os_memset(&addr, 0, sizeof(addr));
  283. addr.sin_family = AF_INET;
  284. addr.sin_port = htons(port);
  285. if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
  286. wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
  287. close(s);
  288. return -1;
  289. }
  290. return s;
  291. }
  292. struct radius_das_data *
  293. radius_das_init(struct radius_das_conf *conf)
  294. {
  295. struct radius_das_data *das;
  296. if (conf->port == 0 || conf->shared_secret == NULL ||
  297. conf->client_addr == NULL)
  298. return NULL;
  299. das = os_zalloc(sizeof(*das));
  300. if (das == NULL)
  301. return NULL;
  302. das->time_window = conf->time_window;
  303. das->require_event_timestamp = conf->require_event_timestamp;
  304. das->ctx = conf->ctx;
  305. das->disconnect = conf->disconnect;
  306. os_memcpy(&das->client_addr, conf->client_addr,
  307. sizeof(das->client_addr));
  308. das->shared_secret = os_malloc(conf->shared_secret_len);
  309. if (das->shared_secret == NULL) {
  310. radius_das_deinit(das);
  311. return NULL;
  312. }
  313. os_memcpy(das->shared_secret, conf->shared_secret,
  314. conf->shared_secret_len);
  315. das->shared_secret_len = conf->shared_secret_len;
  316. das->sock = radius_das_open_socket(conf->port);
  317. if (das->sock < 0) {
  318. wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
  319. "DAS");
  320. radius_das_deinit(das);
  321. return NULL;
  322. }
  323. if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
  324. {
  325. radius_das_deinit(das);
  326. return NULL;
  327. }
  328. return das;
  329. }
  330. void radius_das_deinit(struct radius_das_data *das)
  331. {
  332. if (das == NULL)
  333. return;
  334. if (das->sock >= 0) {
  335. eloop_unregister_read_sock(das->sock);
  336. close(das->sock);
  337. }
  338. os_free(das->shared_secret);
  339. os_free(das);
  340. }