radius_das.c 8.4 KB

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