LCOV - code coverage report
Current view: top level - src/radius - radius_das.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1475438200 Lines: 144 186 77.4 %
Date: 2016-10-02 Functions: 5 5 100.0 %

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

Generated by: LCOV version 1.10