LCOV - code coverage report
Current view: top level - src/radius - radius.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1388338050 Lines: 407 756 53.8 %
Date: 2013-12-29 Functions: 32 42 76.2 %
Branches: 158 388 40.7 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * RADIUS message processing
       3                 :            :  * Copyright (c) 2002-2009, 2011-2012, 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 "utils/includes.h"
      10                 :            : 
      11                 :            : #include "utils/common.h"
      12                 :            : #include "utils/wpabuf.h"
      13                 :            : #include "crypto/md5.h"
      14                 :            : #include "crypto/crypto.h"
      15                 :            : #include "radius.h"
      16                 :            : 
      17                 :            : 
      18                 :            : /**
      19                 :            :  * struct radius_msg - RADIUS message structure for new and parsed messages
      20                 :            :  */
      21                 :            : struct radius_msg {
      22                 :            :         /**
      23                 :            :          * buf - Allocated buffer for RADIUS message
      24                 :            :          */
      25                 :            :         struct wpabuf *buf;
      26                 :            : 
      27                 :            :         /**
      28                 :            :          * hdr - Pointer to the RADIUS header in buf
      29                 :            :          */
      30                 :            :         struct radius_hdr *hdr;
      31                 :            : 
      32                 :            :         /**
      33                 :            :          * attr_pos - Array of indexes to attributes
      34                 :            :          *
      35                 :            :          * The values are number of bytes from buf to the beginning of
      36                 :            :          * struct radius_attr_hdr.
      37                 :            :          */
      38                 :            :         size_t *attr_pos;
      39                 :            : 
      40                 :            :         /**
      41                 :            :          * attr_size - Total size of the attribute pointer array
      42                 :            :          */
      43                 :            :         size_t attr_size;
      44                 :            : 
      45                 :            :         /**
      46                 :            :          * attr_used - Total number of attributes in the array
      47                 :            :          */
      48                 :            :         size_t attr_used;
      49                 :            : };
      50                 :            : 
      51                 :            : 
      52                 :       4271 : struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
      53                 :            : {
      54                 :       4271 :         return msg->hdr;
      55                 :            : }
      56                 :            : 
      57                 :            : 
      58                 :        882 : struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
      59                 :            : {
      60                 :        882 :         return msg->buf;
      61                 :            : }
      62                 :            : 
      63                 :            : 
      64                 :            : static struct radius_attr_hdr *
      65                 :      43298 : radius_get_attr_hdr(struct radius_msg *msg, int idx)
      66                 :            : {
      67                 :      43298 :         return (struct radius_attr_hdr *)
      68                 :      43298 :                 (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
      69                 :            : }
      70                 :            : 
      71                 :            : 
      72                 :        882 : static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
      73                 :            : {
      74                 :        882 :         msg->hdr->code = code;
      75                 :        882 :         msg->hdr->identifier = identifier;
      76                 :        882 : }
      77                 :            : 
      78                 :            : 
      79                 :       1764 : static int radius_msg_initialize(struct radius_msg *msg)
      80                 :            : {
      81                 :       1764 :         msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT,
      82                 :            :                                   sizeof(*msg->attr_pos));
      83         [ -  + ]:       1764 :         if (msg->attr_pos == NULL)
      84                 :          0 :                 return -1;
      85                 :            : 
      86                 :       1764 :         msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
      87                 :       1764 :         msg->attr_used = 0;
      88                 :            : 
      89                 :       1764 :         return 0;
      90                 :            : }
      91                 :            : 
      92                 :            : 
      93                 :            : /**
      94                 :            :  * radius_msg_new - Create a new RADIUS message
      95                 :            :  * @code: Code for RADIUS header
      96                 :            :  * @identifier: Identifier for RADIUS header
      97                 :            :  * Returns: Context for RADIUS message or %NULL on failure
      98                 :            :  *
      99                 :            :  * The caller is responsible for freeing the returned data with
     100                 :            :  * radius_msg_free().
     101                 :            :  */
     102                 :        882 : struct radius_msg * radius_msg_new(u8 code, u8 identifier)
     103                 :            : {
     104                 :            :         struct radius_msg *msg;
     105                 :            : 
     106                 :        882 :         msg = os_zalloc(sizeof(*msg));
     107         [ -  + ]:        882 :         if (msg == NULL)
     108                 :          0 :                 return NULL;
     109                 :            : 
     110                 :        882 :         msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
     111 [ -  + ][ +  - ]:        882 :         if (msg->buf == NULL || radius_msg_initialize(msg)) {
     112                 :          0 :                 radius_msg_free(msg);
     113                 :          0 :                 return NULL;
     114                 :            :         }
     115                 :        882 :         msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
     116                 :            : 
     117                 :        882 :         radius_msg_set_hdr(msg, code, identifier);
     118                 :            : 
     119                 :        882 :         return msg;
     120                 :            : }
     121                 :            : 
     122                 :            : 
     123                 :            : /**
     124                 :            :  * radius_msg_free - Free a RADIUS message
     125                 :            :  * @msg: RADIUS message from radius_msg_new() or radius_msg_parse()
     126                 :            :  */
     127                 :       2048 : void radius_msg_free(struct radius_msg *msg)
     128                 :            : {
     129         [ +  + ]:       2048 :         if (msg == NULL)
     130                 :       2048 :                 return;
     131                 :            : 
     132                 :       1764 :         wpabuf_free(msg->buf);
     133                 :       1764 :         os_free(msg->attr_pos);
     134                 :       1764 :         os_free(msg);
     135                 :            : }
     136                 :            : 
     137                 :            : 
     138                 :        882 : static const char *radius_code_string(u8 code)
     139                 :            : {
     140   [ +  +  +  -  :        882 :         switch (code) {
          -  +  -  -  -  
          -  -  -  -  -  
                   -  - ]
     141                 :        441 :         case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
     142                 :         82 :         case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
     143                 :          3 :         case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
     144                 :          0 :         case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
     145                 :          0 :         case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
     146                 :        356 :         case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
     147                 :          0 :         case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
     148                 :          0 :         case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
     149                 :          0 :         case RADIUS_CODE_RESERVED: return "Reserved";
     150                 :          0 :         case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request";
     151                 :          0 :         case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK";
     152                 :          0 :         case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK";
     153                 :          0 :         case RADIUS_CODE_COA_REQUEST: return "CoA-Request";
     154                 :          0 :         case RADIUS_CODE_COA_ACK: return "CoA-ACK";
     155                 :          0 :         case RADIUS_CODE_COA_NAK: return "CoA-NAK";
     156                 :        882 :         default: return "?Unknown?";
     157                 :            :         }
     158                 :            : }
     159                 :            : 
     160                 :            : 
     161                 :            : struct radius_attr_type {
     162                 :            :         u8 type;
     163                 :            :         char *name;
     164                 :            :         enum {
     165                 :            :                 RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
     166                 :            :                 RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
     167                 :            :         } data_type;
     168                 :            : };
     169                 :            : 
     170                 :            : static struct radius_attr_type radius_attrs[] =
     171                 :            : {
     172                 :            :         { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
     173                 :            :         { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
     174                 :            :         { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
     175                 :            :         { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
     176                 :            :         { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
     177                 :            :         { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
     178                 :            :         { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
     179                 :            :         { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
     180                 :            :         { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
     181                 :            :         { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
     182                 :            :         { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
     183                 :            :         { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
     184                 :            :           RADIUS_ATTR_INT32 },
     185                 :            :         { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
     186                 :            :           RADIUS_ATTR_TEXT },
     187                 :            :         { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
     188                 :            :           RADIUS_ATTR_TEXT },
     189                 :            :         { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
     190                 :            :         { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
     191                 :            :         { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
     192                 :            :           RADIUS_ATTR_INT32 },
     193                 :            :         { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
     194                 :            :         { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
     195                 :            :           RADIUS_ATTR_INT32 },
     196                 :            :         { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
     197                 :            :           RADIUS_ATTR_INT32 },
     198                 :            :         { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
     199                 :            :         { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
     200                 :            :         { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
     201                 :            :           RADIUS_ATTR_INT32 },
     202                 :            :         { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
     203                 :            :           RADIUS_ATTR_INT32 },
     204                 :            :         { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
     205                 :            :           RADIUS_ATTR_INT32 },
     206                 :            :         { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
     207                 :            :           RADIUS_ATTR_INT32 },
     208                 :            :         { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
     209                 :            :           RADIUS_ATTR_TEXT },
     210                 :            :         { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
     211                 :            :         { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 
     212                 :            :           RADIUS_ATTR_INT32 },
     213                 :            :         { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
     214                 :            :           RADIUS_ATTR_INT32 },
     215                 :            :         { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
     216                 :            :           RADIUS_ATTR_INT32 },
     217                 :            :         { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
     218                 :            :         { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
     219                 :            :         { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
     220                 :            :           RADIUS_ATTR_HEXDUMP },
     221                 :            :         { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password",
     222                 :            :           RADIUS_ATTR_UNDIST },
     223                 :            :         { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
     224                 :            :         { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
     225                 :            :         { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
     226                 :            :           RADIUS_ATTR_UNDIST },
     227                 :            :         { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
     228                 :            :           RADIUS_ATTR_HEXDUMP },
     229                 :            :         { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
     230                 :            :           RADIUS_ATTR_INT32 },
     231                 :            :         { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
     232                 :            :           RADIUS_ATTR_TEXT },
     233                 :            :         { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
     234                 :            :         { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
     235                 :            : };
     236                 :            : #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
     237                 :            : 
     238                 :            : 
     239                 :       6768 : static struct radius_attr_type *radius_get_attr_type(u8 type)
     240                 :            : {
     241                 :            :         size_t i;
     242                 :            : 
     243         [ +  - ]:     144392 :         for (i = 0; i < RADIUS_ATTRS; i++) {
     244         [ +  + ]:     144392 :                 if (type == radius_attrs[i].type)
     245                 :       6768 :                         return &radius_attrs[i];
     246                 :            :         }
     247                 :            : 
     248                 :       6768 :         return NULL;
     249                 :            : }
     250                 :            : 
     251                 :            : 
     252                 :      45267 : static void print_char(char c)
     253                 :            : {
     254 [ +  - ][ +  - ]:      45267 :         if (c >= 32 && c < 127)
     255                 :      45267 :                 printf("%c", c);
     256                 :            :         else
     257                 :          0 :                 printf("<%02x>", c);
     258                 :      45267 : }
     259                 :            : 
     260                 :            : 
     261                 :       6768 : static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
     262                 :            : {
     263                 :            :         struct radius_attr_type *attr;
     264                 :            :         int i, len;
     265                 :            :         unsigned char *pos;
     266                 :            : 
     267                 :       6768 :         attr = radius_get_attr_type(hdr->type);
     268                 :            : 
     269         [ +  - ]:       6768 :         printf("   Attribute %d (%s) length=%d\n",
     270                 :      13536 :                hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
     271                 :            : 
     272 [ -  + ][ +  - ]:       6768 :         if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
     273                 :       6768 :                 return;
     274                 :            : 
     275                 :       6768 :         len = hdr->length - sizeof(struct radius_attr_hdr);
     276                 :       6768 :         pos = (unsigned char *) (hdr + 1);
     277                 :            : 
     278   [ +  -  -  +  :       6768 :         switch (attr->data_type) {
                   +  - ]
     279                 :            :         case RADIUS_ATTR_TEXT:
     280                 :       2477 :                 printf("      Value: '");
     281         [ +  + ]:      47744 :                 for (i = 0; i < len; i++)
     282                 :      45267 :                         print_char(pos[i]);
     283                 :       2477 :                 printf("'\n");
     284                 :       2477 :                 break;
     285                 :            : 
     286                 :            :         case RADIUS_ATTR_IP:
     287         [ #  # ]:          0 :                 if (len == 4) {
     288                 :            :                         struct in_addr addr;
     289                 :          0 :                         os_memcpy(&addr, pos, 4);
     290                 :          0 :                         printf("      Value: %s\n", inet_ntoa(addr));
     291                 :            :                 } else
     292                 :          0 :                         printf("      Invalid IP address length %d\n", len);
     293                 :          0 :                 break;
     294                 :            : 
     295                 :            : #ifdef CONFIG_IPV6
     296                 :            :         case RADIUS_ATTR_IPV6:
     297         [ #  # ]:          0 :                 if (len == 16) {
     298                 :            :                         char buf[128];
     299                 :            :                         const char *atxt;
     300                 :          0 :                         struct in6_addr *addr = (struct in6_addr *) pos;
     301                 :          0 :                         atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
     302         [ #  # ]:          0 :                         printf("      Value: %s\n", atxt ? atxt : "?");
     303                 :            :                 } else
     304                 :          0 :                         printf("      Invalid IPv6 address length %d\n", len);
     305                 :          0 :                 break;
     306                 :            : #endif /* CONFIG_IPV6 */
     307                 :            : 
     308                 :            :         case RADIUS_ATTR_HEXDUMP:
     309                 :            :         case RADIUS_ATTR_UNDIST:
     310                 :       2968 :                 printf("      Value:");
     311         [ +  + ]:     176931 :                 for (i = 0; i < len; i++)
     312                 :     173963 :                         printf(" %02x", pos[i]);
     313                 :       2968 :                 printf("\n");
     314                 :       2968 :                 break;
     315                 :            : 
     316                 :            :         case RADIUS_ATTR_INT32:
     317         [ +  - ]:       1323 :                 if (len == 4)
     318                 :       1323 :                         printf("      Value: %u\n", WPA_GET_BE32(pos));
     319                 :            :                 else
     320                 :          0 :                         printf("      Invalid INT32 length %d\n", len);
     321                 :       1323 :                 break;
     322                 :            : 
     323                 :            :         default:
     324                 :          0 :                 break;
     325                 :            :         }
     326                 :            : }
     327                 :            : 
     328                 :            : 
     329                 :        882 : void radius_msg_dump(struct radius_msg *msg)
     330                 :            : {
     331                 :            :         size_t i;
     332                 :            : 
     333                 :        882 :         printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
     334                 :       1764 :                msg->hdr->code, radius_code_string(msg->hdr->code),
     335                 :       1764 :                msg->hdr->identifier, be_to_host16(msg->hdr->length));
     336                 :            : 
     337         [ +  + ]:       7650 :         for (i = 0; i < msg->attr_used; i++) {
     338                 :       6768 :                 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
     339                 :       6768 :                 radius_msg_dump_attr(attr);
     340                 :            :         }
     341                 :        882 : }
     342                 :            : 
     343                 :            : 
     344                 :        441 : int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
     345                 :            :                       size_t secret_len)
     346                 :            : {
     347         [ +  - ]:        441 :         if (secret) {
     348                 :            :                 u8 auth[MD5_MAC_LEN];
     349                 :            :                 struct radius_attr_hdr *attr;
     350                 :            : 
     351                 :        441 :                 os_memset(auth, 0, MD5_MAC_LEN);
     352                 :        441 :                 attr = radius_msg_add_attr(msg,
     353                 :            :                                            RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
     354                 :            :                                            auth, MD5_MAC_LEN);
     355         [ -  + ]:        441 :                 if (attr == NULL) {
     356                 :          0 :                         wpa_printf(MSG_WARNING, "RADIUS: Could not add "
     357                 :            :                                    "Message-Authenticator");
     358                 :          0 :                         return -1;
     359                 :            :                 }
     360                 :        441 :                 msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
     361                 :        441 :                 hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
     362                 :        441 :                          wpabuf_len(msg->buf), (u8 *) (attr + 1));
     363                 :            :         } else
     364                 :          0 :                 msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
     365                 :            : 
     366         [ -  + ]:        441 :         if (wpabuf_len(msg->buf) > 0xffff) {
     367                 :          0 :                 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
     368                 :          0 :                            (unsigned long) wpabuf_len(msg->buf));
     369                 :          0 :                 return -1;
     370                 :            :         }
     371                 :        441 :         return 0;
     372                 :            : }
     373                 :            : 
     374                 :            : 
     375                 :        441 : int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
     376                 :            :                           size_t secret_len, const u8 *req_authenticator)
     377                 :            : {
     378                 :            :         u8 auth[MD5_MAC_LEN];
     379                 :            :         struct radius_attr_hdr *attr;
     380                 :            :         const u8 *addr[4];
     381                 :            :         size_t len[4];
     382                 :            : 
     383                 :        441 :         os_memset(auth, 0, MD5_MAC_LEN);
     384                 :        441 :         attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
     385                 :            :                                    auth, MD5_MAC_LEN);
     386         [ -  + ]:        441 :         if (attr == NULL) {
     387                 :          0 :                 printf("WARNING: Could not add Message-Authenticator\n");
     388                 :          0 :                 return -1;
     389                 :            :         }
     390                 :        441 :         msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
     391                 :        441 :         os_memcpy(msg->hdr->authenticator, req_authenticator,
     392                 :            :                   sizeof(msg->hdr->authenticator));
     393                 :        441 :         hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
     394                 :        441 :                  wpabuf_len(msg->buf), (u8 *) (attr + 1));
     395                 :            : 
     396                 :            :         /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
     397                 :        441 :         addr[0] = (u8 *) msg->hdr;
     398                 :        441 :         len[0] = 1 + 1 + 2;
     399                 :        441 :         addr[1] = req_authenticator;
     400                 :        441 :         len[1] = MD5_MAC_LEN;
     401                 :        441 :         addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
     402                 :        441 :         len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
     403                 :        441 :         addr[3] = secret;
     404                 :        441 :         len[3] = secret_len;
     405                 :        441 :         md5_vector(4, addr, len, msg->hdr->authenticator);
     406                 :            : 
     407         [ -  + ]:        441 :         if (wpabuf_len(msg->buf) > 0xffff) {
     408                 :          0 :                 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
     409                 :          0 :                            (unsigned long) wpabuf_len(msg->buf));
     410                 :          0 :                 return -1;
     411                 :            :         }
     412                 :        441 :         return 0;
     413                 :            : }
     414                 :            : 
     415                 :            : 
     416                 :          0 : int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
     417                 :            :                                size_t secret_len,
     418                 :            :                                const struct radius_hdr *req_hdr)
     419                 :            : {
     420                 :            :         const u8 *addr[2];
     421                 :            :         size_t len[2];
     422                 :            :         u8 auth[MD5_MAC_LEN];
     423                 :            :         struct radius_attr_hdr *attr;
     424                 :            : 
     425                 :          0 :         os_memset(auth, 0, MD5_MAC_LEN);
     426                 :          0 :         attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
     427                 :            :                                    auth, MD5_MAC_LEN);
     428         [ #  # ]:          0 :         if (attr == NULL) {
     429                 :          0 :                 wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
     430                 :          0 :                 return -1;
     431                 :            :         }
     432                 :            : 
     433                 :          0 :         msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
     434                 :          0 :         os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
     435                 :          0 :         hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
     436                 :          0 :                  wpabuf_len(msg->buf), (u8 *) (attr + 1));
     437                 :            : 
     438                 :            :         /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
     439                 :          0 :         addr[0] = wpabuf_head_u8(msg->buf);
     440                 :          0 :         len[0] = wpabuf_len(msg->buf);
     441                 :          0 :         addr[1] = secret;
     442                 :          0 :         len[1] = secret_len;
     443         [ #  # ]:          0 :         if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
     444                 :          0 :                 return -1;
     445                 :            : 
     446         [ #  # ]:          0 :         if (wpabuf_len(msg->buf) > 0xffff) {
     447                 :          0 :                 wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
     448                 :          0 :                            (unsigned long) wpabuf_len(msg->buf));
     449                 :          0 :                 return -1;
     450                 :            :         }
     451                 :          0 :         return 0;
     452                 :            : }
     453                 :            : 
     454                 :            : 
     455                 :          0 : void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
     456                 :            :                             size_t secret_len)
     457                 :            : {
     458                 :            :         const u8 *addr[2];
     459                 :            :         size_t len[2];
     460                 :            : 
     461                 :          0 :         msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
     462                 :          0 :         os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
     463                 :          0 :         addr[0] = wpabuf_head(msg->buf);
     464                 :          0 :         len[0] = wpabuf_len(msg->buf);
     465                 :          0 :         addr[1] = secret;
     466                 :          0 :         len[1] = secret_len;
     467                 :          0 :         md5_vector(2, addr, len, msg->hdr->authenticator);
     468                 :            : 
     469         [ #  # ]:          0 :         if (wpabuf_len(msg->buf) > 0xffff) {
     470                 :          0 :                 wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
     471                 :          0 :                            (unsigned long) wpabuf_len(msg->buf));
     472                 :            :         }
     473                 :          0 : }
     474                 :            : 
     475                 :            : 
     476                 :          0 : int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
     477                 :            :                                size_t secret_len)
     478                 :            : {
     479                 :            :         const u8 *addr[4];
     480                 :            :         size_t len[4];
     481                 :            :         u8 zero[MD5_MAC_LEN];
     482                 :            :         u8 hash[MD5_MAC_LEN];
     483                 :            : 
     484                 :          0 :         os_memset(zero, 0, sizeof(zero));
     485                 :          0 :         addr[0] = (u8 *) msg->hdr;
     486                 :          0 :         len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
     487                 :          0 :         addr[1] = zero;
     488                 :          0 :         len[1] = MD5_MAC_LEN;
     489                 :          0 :         addr[2] = (u8 *) (msg->hdr + 1);
     490                 :          0 :         len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
     491                 :          0 :         addr[3] = secret;
     492                 :          0 :         len[3] = secret_len;
     493                 :          0 :         md5_vector(4, addr, len, hash);
     494                 :          0 :         return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
     495                 :            : }
     496                 :            : 
     497                 :            : 
     498                 :          0 : int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
     499                 :            :                               size_t secret_len)
     500                 :            : {
     501                 :            :         const u8 *addr[4];
     502                 :            :         size_t len[4];
     503                 :            :         u8 zero[MD5_MAC_LEN];
     504                 :            :         u8 hash[MD5_MAC_LEN];
     505                 :            :         u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
     506                 :            :         u8 orig_authenticator[16];
     507                 :            : 
     508                 :          0 :         struct radius_attr_hdr *attr = NULL, *tmp;
     509                 :            :         size_t i;
     510                 :            : 
     511                 :          0 :         os_memset(zero, 0, sizeof(zero));
     512                 :          0 :         addr[0] = (u8 *) msg->hdr;
     513                 :          0 :         len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
     514                 :          0 :         addr[1] = zero;
     515                 :          0 :         len[1] = MD5_MAC_LEN;
     516                 :          0 :         addr[2] = (u8 *) (msg->hdr + 1);
     517                 :          0 :         len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
     518                 :          0 :         addr[3] = secret;
     519                 :          0 :         len[3] = secret_len;
     520                 :          0 :         md5_vector(4, addr, len, hash);
     521         [ #  # ]:          0 :         if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
     522                 :          0 :                 return 1;
     523                 :            : 
     524         [ #  # ]:          0 :         for (i = 0; i < msg->attr_used; i++) {
     525                 :          0 :                 tmp = radius_get_attr_hdr(msg, i);
     526         [ #  # ]:          0 :                 if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
     527         [ #  # ]:          0 :                         if (attr != NULL) {
     528                 :          0 :                                 wpa_printf(MSG_WARNING, "Multiple "
     529                 :            :                                            "Message-Authenticator attributes "
     530                 :            :                                            "in RADIUS message");
     531                 :          0 :                                 return 1;
     532                 :            :                         }
     533                 :          0 :                         attr = tmp;
     534                 :            :                 }
     535                 :            :         }
     536                 :            : 
     537         [ #  # ]:          0 :         if (attr == NULL) {
     538                 :            :                 /* Message-Authenticator is MAY; not required */
     539                 :          0 :                 return 0;
     540                 :            :         }
     541                 :            : 
     542                 :          0 :         os_memcpy(orig, attr + 1, MD5_MAC_LEN);
     543                 :          0 :         os_memset(attr + 1, 0, MD5_MAC_LEN);
     544                 :          0 :         os_memcpy(orig_authenticator, msg->hdr->authenticator,
     545                 :            :                   sizeof(orig_authenticator));
     546                 :          0 :         os_memset(msg->hdr->authenticator, 0,
     547                 :            :                   sizeof(msg->hdr->authenticator));
     548                 :          0 :         hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
     549                 :          0 :                  wpabuf_len(msg->buf), auth);
     550                 :          0 :         os_memcpy(attr + 1, orig, MD5_MAC_LEN);
     551                 :          0 :         os_memcpy(msg->hdr->authenticator, orig_authenticator,
     552                 :            :                   sizeof(orig_authenticator));
     553                 :            : 
     554                 :          0 :         return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
     555                 :            : }
     556                 :            : 
     557                 :            : 
     558                 :      13536 : static int radius_msg_add_attr_to_array(struct radius_msg *msg,
     559                 :            :                                         struct radius_attr_hdr *attr)
     560                 :            : {
     561         [ +  + ]:      13536 :         if (msg->attr_used >= msg->attr_size) {
     562                 :            :                 size_t *nattr_pos;
     563                 :          4 :                 int nlen = msg->attr_size * 2;
     564                 :            : 
     565                 :          4 :                 nattr_pos = os_realloc_array(msg->attr_pos, nlen,
     566                 :            :                                              sizeof(*msg->attr_pos));
     567         [ -  + ]:          4 :                 if (nattr_pos == NULL)
     568                 :          0 :                         return -1;
     569                 :            : 
     570                 :          4 :                 msg->attr_pos = nattr_pos;
     571                 :          4 :                 msg->attr_size = nlen;
     572                 :            :         }
     573                 :            : 
     574                 :      27072 :         msg->attr_pos[msg->attr_used++] =
     575                 :      13536 :                 (unsigned char *) attr - wpabuf_head_u8(msg->buf);
     576                 :            : 
     577                 :      13536 :         return 0;
     578                 :            : }
     579                 :            : 
     580                 :            : 
     581                 :       6768 : struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
     582                 :            :                                             const u8 *data, size_t data_len)
     583                 :            : {
     584                 :            :         size_t buf_needed;
     585                 :            :         struct radius_attr_hdr *attr;
     586                 :            : 
     587         [ -  + ]:       6768 :         if (data_len > RADIUS_MAX_ATTR_LEN) {
     588                 :          0 :                 printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
     589                 :            :                        (unsigned long) data_len);
     590                 :          0 :                 return NULL;
     591                 :            :         }
     592                 :            : 
     593                 :       6768 :         buf_needed = sizeof(*attr) + data_len;
     594                 :            : 
     595         [ +  + ]:       6768 :         if (wpabuf_tailroom(msg->buf) < buf_needed) {
     596                 :            :                 /* allocate more space for message buffer */
     597         [ -  + ]:        199 :                 if (wpabuf_resize(&msg->buf, buf_needed) < 0)
     598                 :          0 :                         return NULL;
     599                 :        199 :                 msg->hdr = wpabuf_mhead(msg->buf);
     600                 :            :         }
     601                 :            : 
     602                 :       6768 :         attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
     603                 :       6768 :         attr->type = type;
     604                 :       6768 :         attr->length = sizeof(*attr) + data_len;
     605                 :       6768 :         wpabuf_put_data(msg->buf, data, data_len);
     606                 :            : 
     607         [ -  + ]:       6768 :         if (radius_msg_add_attr_to_array(msg, attr))
     608                 :          0 :                 return NULL;
     609                 :            : 
     610                 :       6768 :         return attr;
     611                 :            : }
     612                 :            : 
     613                 :            : 
     614                 :            : /**
     615                 :            :  * radius_msg_parse - Parse a RADIUS message
     616                 :            :  * @data: RADIUS message to be parsed
     617                 :            :  * @len: Length of data buffer in octets
     618                 :            :  * Returns: Parsed RADIUS message or %NULL on failure
     619                 :            :  *
     620                 :            :  * This parses a RADIUS message and makes a copy of its data. The caller is
     621                 :            :  * responsible for freeing the returned data with radius_msg_free().
     622                 :            :  */
     623                 :        882 : struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
     624                 :            : {
     625                 :            :         struct radius_msg *msg;
     626                 :            :         struct radius_hdr *hdr;
     627                 :            :         struct radius_attr_hdr *attr;
     628                 :            :         size_t msg_len;
     629                 :            :         unsigned char *pos, *end;
     630                 :            : 
     631 [ +  - ][ -  + ]:        882 :         if (data == NULL || len < sizeof(*hdr))
     632                 :          0 :                 return NULL;
     633                 :            : 
     634                 :        882 :         hdr = (struct radius_hdr *) data;
     635                 :            : 
     636                 :        882 :         msg_len = be_to_host16(hdr->length);
     637 [ +  - ][ -  + ]:        882 :         if (msg_len < sizeof(*hdr) || msg_len > len) {
     638                 :          0 :                 wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
     639                 :          0 :                 return NULL;
     640                 :            :         }
     641                 :            : 
     642         [ -  + ]:        882 :         if (msg_len < len) {
     643                 :          0 :                 wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
     644                 :            :                            "RADIUS message", (unsigned long) len - msg_len);
     645                 :            :         }
     646                 :            : 
     647                 :        882 :         msg = os_zalloc(sizeof(*msg));
     648         [ -  + ]:        882 :         if (msg == NULL)
     649                 :          0 :                 return NULL;
     650                 :            : 
     651                 :        882 :         msg->buf = wpabuf_alloc_copy(data, msg_len);
     652 [ -  + ][ +  - ]:        882 :         if (msg->buf == NULL || radius_msg_initialize(msg)) {
     653                 :          0 :                 radius_msg_free(msg);
     654                 :          0 :                 return NULL;
     655                 :            :         }
     656                 :        882 :         msg->hdr = wpabuf_mhead(msg->buf);
     657                 :            : 
     658                 :            :         /* parse attributes */
     659                 :        882 :         pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
     660                 :        882 :         end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
     661         [ +  + ]:       7650 :         while (pos < end) {
     662         [ -  + ]:       6768 :                 if ((size_t) (end - pos) < sizeof(*attr))
     663                 :          0 :                         goto fail;
     664                 :            : 
     665                 :       6768 :                 attr = (struct radius_attr_hdr *) pos;
     666                 :            : 
     667 [ +  - ][ +  - ]:       6768 :                 if (pos + attr->length > end || attr->length < sizeof(*attr))
     668                 :            :                         goto fail;
     669                 :            : 
     670                 :            :                 /* TODO: check that attr->length is suitable for attr->type */
     671                 :            : 
     672         [ -  + ]:       6768 :                 if (radius_msg_add_attr_to_array(msg, attr))
     673                 :          0 :                         goto fail;
     674                 :            : 
     675                 :       6768 :                 pos += attr->length;
     676                 :            :         }
     677                 :            : 
     678                 :        882 :         return msg;
     679                 :            : 
     680                 :            :  fail:
     681                 :          0 :         radius_msg_free(msg);
     682                 :        882 :         return NULL;
     683                 :            : }
     684                 :            : 
     685                 :            : 
     686                 :        882 : int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
     687                 :            : {
     688                 :        882 :         const u8 *pos = data;
     689                 :        882 :         size_t left = data_len;
     690                 :            : 
     691         [ +  + ]:       2092 :         while (left > 0) {
     692                 :            :                 int len;
     693         [ +  + ]:       1210 :                 if (left > RADIUS_MAX_ATTR_LEN)
     694                 :        328 :                         len = RADIUS_MAX_ATTR_LEN;
     695                 :            :                 else
     696                 :        882 :                         len = left;
     697                 :            : 
     698         [ -  + ]:       1210 :                 if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
     699                 :            :                                          pos, len))
     700                 :          0 :                         return 0;
     701                 :            : 
     702                 :       1210 :                 pos += len;
     703                 :       1210 :                 left -= len;
     704                 :            :         }
     705                 :            : 
     706                 :        882 :         return 1;
     707                 :            : }
     708                 :            : 
     709                 :            : 
     710                 :        891 : struct wpabuf * radius_msg_get_eap(struct radius_msg *msg)
     711                 :            : {
     712                 :            :         struct wpabuf *eap;
     713                 :            :         size_t len, i;
     714                 :            :         struct radius_attr_hdr *attr;
     715                 :            : 
     716         [ -  + ]:        891 :         if (msg == NULL)
     717                 :          0 :                 return NULL;
     718                 :            : 
     719                 :        891 :         len = 0;
     720         [ +  + ]:       7762 :         for (i = 0; i < msg->attr_used; i++) {
     721                 :       6871 :                 attr = radius_get_attr_hdr(msg, i);
     722 [ +  - ][ +  + ]:       6871 :                 if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
     723                 :       1219 :                     attr->length > sizeof(struct radius_attr_hdr))
     724                 :       1219 :                         len += attr->length - sizeof(struct radius_attr_hdr);
     725                 :            :         }
     726                 :            : 
     727         [ -  + ]:        891 :         if (len == 0)
     728                 :          0 :                 return NULL;
     729                 :            : 
     730                 :        891 :         eap = wpabuf_alloc(len);
     731         [ -  + ]:        891 :         if (eap == NULL)
     732                 :          0 :                 return NULL;
     733                 :            : 
     734         [ +  + ]:       7762 :         for (i = 0; i < msg->attr_used; i++) {
     735                 :       6871 :                 attr = radius_get_attr_hdr(msg, i);
     736 [ +  - ][ +  + ]:       6871 :                 if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
     737                 :       1219 :                     attr->length > sizeof(struct radius_attr_hdr)) {
     738                 :       1219 :                         int flen = attr->length - sizeof(*attr);
     739                 :       1219 :                         wpabuf_put_data(eap, attr + 1, flen);
     740                 :            :                 }
     741                 :            :         }
     742                 :            : 
     743                 :        891 :         return eap;
     744                 :            : }
     745                 :            : 
     746                 :            : 
     747                 :        882 : int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
     748                 :            :                                size_t secret_len, const u8 *req_auth)
     749                 :            : {
     750                 :            :         u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
     751                 :            :         u8 orig_authenticator[16];
     752                 :        882 :         struct radius_attr_hdr *attr = NULL, *tmp;
     753                 :            :         size_t i;
     754                 :            : 
     755         [ +  + ]:       7650 :         for (i = 0; i < msg->attr_used; i++) {
     756                 :       6768 :                 tmp = radius_get_attr_hdr(msg, i);
     757         [ +  + ]:       6768 :                 if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
     758         [ -  + ]:        882 :                         if (attr != NULL) {
     759                 :          0 :                                 printf("Multiple Message-Authenticator "
     760                 :            :                                        "attributes in RADIUS message\n");
     761                 :          0 :                                 return 1;
     762                 :            :                         }
     763                 :        882 :                         attr = tmp;
     764                 :            :                 }
     765                 :            :         }
     766                 :            : 
     767         [ -  + ]:        882 :         if (attr == NULL) {
     768                 :          0 :                 printf("No Message-Authenticator attribute found\n");
     769                 :          0 :                 return 1;
     770                 :            :         }
     771                 :            : 
     772                 :        882 :         os_memcpy(orig, attr + 1, MD5_MAC_LEN);
     773                 :        882 :         os_memset(attr + 1, 0, MD5_MAC_LEN);
     774         [ +  + ]:        882 :         if (req_auth) {
     775                 :        441 :                 os_memcpy(orig_authenticator, msg->hdr->authenticator,
     776                 :            :                           sizeof(orig_authenticator));
     777                 :        441 :                 os_memcpy(msg->hdr->authenticator, req_auth,
     778                 :            :                           sizeof(msg->hdr->authenticator));
     779                 :            :         }
     780                 :        882 :         hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
     781                 :        882 :                  wpabuf_len(msg->buf), auth);
     782                 :        882 :         os_memcpy(attr + 1, orig, MD5_MAC_LEN);
     783         [ +  + ]:        882 :         if (req_auth) {
     784                 :        441 :                 os_memcpy(msg->hdr->authenticator, orig_authenticator,
     785                 :            :                           sizeof(orig_authenticator));
     786                 :            :         }
     787                 :            : 
     788         [ -  + ]:        882 :         if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
     789                 :          0 :                 printf("Invalid Message-Authenticator!\n");
     790                 :          0 :                 return 1;
     791                 :            :         }
     792                 :            : 
     793                 :        882 :         return 0;
     794                 :            : }
     795                 :            : 
     796                 :            : 
     797                 :        441 : int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
     798                 :            :                       size_t secret_len, struct radius_msg *sent_msg, int auth)
     799                 :            : {
     800                 :            :         const u8 *addr[4];
     801                 :            :         size_t len[4];
     802                 :            :         u8 hash[MD5_MAC_LEN];
     803                 :            : 
     804         [ -  + ]:        441 :         if (sent_msg == NULL) {
     805                 :          0 :                 printf("No matching Access-Request message found\n");
     806                 :          0 :                 return 1;
     807                 :            :         }
     808                 :            : 
     809   [ +  -  -  + ]:        882 :         if (auth &&
     810                 :        441 :             radius_msg_verify_msg_auth(msg, secret, secret_len,
     811                 :        441 :                                        sent_msg->hdr->authenticator)) {
     812                 :          0 :                 return 1;
     813                 :            :         }
     814                 :            : 
     815                 :            :         /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
     816                 :        441 :         addr[0] = (u8 *) msg->hdr;
     817                 :        441 :         len[0] = 1 + 1 + 2;
     818                 :        441 :         addr[1] = sent_msg->hdr->authenticator;
     819                 :        441 :         len[1] = MD5_MAC_LEN;
     820                 :        441 :         addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
     821                 :        441 :         len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
     822                 :        441 :         addr[3] = secret;
     823                 :        441 :         len[3] = secret_len;
     824                 :        441 :         md5_vector(4, addr, len, hash);
     825         [ -  + ]:        441 :         if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
     826                 :          0 :                 printf("Response Authenticator invalid!\n");
     827                 :          0 :                 return 1;
     828                 :            :         }
     829                 :            : 
     830                 :        441 :         return 0;
     831                 :            : }
     832                 :            : 
     833                 :            : 
     834                 :        797 : int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
     835                 :            :                          u8 type)
     836                 :            : {
     837                 :            :         struct radius_attr_hdr *attr;
     838                 :            :         size_t i;
     839                 :        797 :         int count = 0;
     840                 :            : 
     841         [ +  + ]:       7231 :         for (i = 0; i < src->attr_used; i++) {
     842                 :       6434 :                 attr = radius_get_attr_hdr(src, i);
     843 [ +  - ][ +  + ]:       6434 :                 if (attr->type == type && attr->length >= sizeof(*attr)) {
     844         [ -  + ]:        356 :                         if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
     845                 :        356 :                                                  attr->length - sizeof(*attr)))
     846                 :          0 :                                 return -1;
     847                 :        356 :                         count++;
     848                 :            :                 }
     849                 :            :         }
     850                 :            : 
     851                 :        797 :         return count;
     852                 :            : }
     853                 :            : 
     854                 :            : 
     855                 :            : /* Create Request Authenticator. The value should be unique over the lifetime
     856                 :            :  * of the shared secret between authenticator and authentication server.
     857                 :            :  * Use one-way MD5 hash calculated from current timestamp and some data given
     858                 :            :  * by the caller. */
     859                 :        441 : void radius_msg_make_authenticator(struct radius_msg *msg,
     860                 :            :                                    const u8 *data, size_t len)
     861                 :            : {
     862                 :            :         struct os_time tv;
     863                 :            :         long int l;
     864                 :            :         const u8 *addr[3];
     865                 :            :         size_t elen[3];
     866                 :            : 
     867                 :        441 :         os_get_time(&tv);
     868                 :        441 :         l = os_random();
     869                 :        441 :         addr[0] = (u8 *) &tv;
     870                 :        441 :         elen[0] = sizeof(tv);
     871                 :        441 :         addr[1] = data;
     872                 :        441 :         elen[1] = len;
     873                 :        441 :         addr[2] = (u8 *) &l;
     874                 :        441 :         elen[2] = sizeof(l);
     875                 :        441 :         md5_vector(3, addr, elen, msg->hdr->authenticator);
     876                 :        441 : }
     877                 :            : 
     878                 :            : 
     879                 :            : /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
     880                 :            :  * Returns the Attribute payload and sets alen to indicate the length of the
     881                 :            :  * payload if a vendor attribute with subtype is found, otherwise returns NULL.
     882                 :            :  * The returned payload is allocated with os_malloc() and caller must free it
     883                 :            :  * by calling os_free().
     884                 :            :  */
     885                 :        164 : static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
     886                 :            :                                       u8 subtype, size_t *alen)
     887                 :            : {
     888                 :            :         u8 *data, *pos;
     889                 :            :         size_t i, len;
     890                 :            : 
     891         [ -  + ]:        164 :         if (msg == NULL)
     892                 :          0 :                 return NULL;
     893                 :            : 
     894         [ +  - ]:        410 :         for (i = 0; i < msg->attr_used; i++) {
     895                 :        410 :                 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
     896                 :            :                 size_t left;
     897                 :            :                 u32 vendor_id;
     898                 :            :                 struct radius_attr_vendor *vhdr;
     899                 :            : 
     900 [ -  + ][ +  + ]:        410 :                 if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC ||
     901                 :        246 :                     attr->length < sizeof(*attr))
     902                 :        164 :                         continue;
     903                 :            : 
     904                 :        246 :                 left = attr->length - sizeof(*attr);
     905         [ -  + ]:        246 :                 if (left < 4)
     906                 :          0 :                         continue;
     907                 :            : 
     908                 :        246 :                 pos = (u8 *) (attr + 1);
     909                 :            : 
     910                 :        246 :                 os_memcpy(&vendor_id, pos, 4);
     911                 :        246 :                 pos += 4;
     912                 :        246 :                 left -= 4;
     913                 :            : 
     914         [ -  + ]:        246 :                 if (ntohl(vendor_id) != vendor)
     915                 :          0 :                         continue;
     916                 :            : 
     917         [ +  + ]:        328 :                 while (left >= sizeof(*vhdr)) {
     918                 :        246 :                         vhdr = (struct radius_attr_vendor *) pos;
     919 [ +  - ][ -  + ]:        246 :                         if (vhdr->vendor_length > left ||
     920                 :        246 :                             vhdr->vendor_length < sizeof(*vhdr)) {
     921                 :          0 :                                 left = 0;
     922                 :          0 :                                 break;
     923                 :            :                         }
     924         [ +  + ]:        246 :                         if (vhdr->vendor_type != subtype) {
     925                 :         82 :                                 pos += vhdr->vendor_length;
     926                 :         82 :                                 left -= vhdr->vendor_length;
     927                 :         82 :                                 continue;
     928                 :            :                         }
     929                 :            : 
     930                 :        164 :                         len = vhdr->vendor_length - sizeof(*vhdr);
     931                 :        164 :                         data = os_malloc(len);
     932         [ -  + ]:        164 :                         if (data == NULL)
     933                 :          0 :                                 return NULL;
     934                 :        164 :                         os_memcpy(data, pos + sizeof(*vhdr), len);
     935         [ +  - ]:        164 :                         if (alen)
     936                 :        164 :                                 *alen = len;
     937                 :        164 :                         return data;
     938                 :            :                 }
     939                 :            :         }
     940                 :            : 
     941                 :        164 :         return NULL;
     942                 :            : }
     943                 :            : 
     944                 :            : 
     945                 :        164 : static u8 * decrypt_ms_key(const u8 *key, size_t len,
     946                 :            :                            const u8 *req_authenticator,
     947                 :            :                            const u8 *secret, size_t secret_len, size_t *reslen)
     948                 :            : {
     949                 :            :         u8 *plain, *ppos, *res;
     950                 :            :         const u8 *pos;
     951                 :            :         size_t left, plen;
     952                 :            :         u8 hash[MD5_MAC_LEN];
     953                 :        164 :         int i, first = 1;
     954                 :            :         const u8 *addr[3];
     955                 :            :         size_t elen[3];
     956                 :            : 
     957                 :            :         /* key: 16-bit salt followed by encrypted key info */
     958                 :            : 
     959         [ -  + ]:        164 :         if (len < 2 + 16)
     960                 :          0 :                 return NULL;
     961                 :            : 
     962                 :        164 :         pos = key + 2;
     963                 :        164 :         left = len - 2;
     964         [ -  + ]:        164 :         if (left % 16) {
     965                 :          0 :                 printf("Invalid ms key len %lu\n", (unsigned long) left);
     966                 :          0 :                 return NULL;
     967                 :            :         }
     968                 :            : 
     969                 :        164 :         plen = left;
     970                 :        164 :         ppos = plain = os_malloc(plen);
     971         [ -  + ]:        164 :         if (plain == NULL)
     972                 :          0 :                 return NULL;
     973                 :        164 :         plain[0] = 0;
     974                 :            : 
     975         [ +  + ]:        656 :         while (left > 0) {
     976                 :            :                 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
     977                 :            :                  * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
     978                 :            : 
     979                 :        492 :                 addr[0] = secret;
     980                 :        492 :                 elen[0] = secret_len;
     981         [ +  + ]:        492 :                 if (first) {
     982                 :        164 :                         addr[1] = req_authenticator;
     983                 :        164 :                         elen[1] = MD5_MAC_LEN;
     984                 :        164 :                         addr[2] = key;
     985                 :        164 :                         elen[2] = 2; /* Salt */
     986                 :            :                 } else {
     987                 :        328 :                         addr[1] = pos - MD5_MAC_LEN;
     988                 :        328 :                         elen[1] = MD5_MAC_LEN;
     989                 :            :                 }
     990         [ +  + ]:        492 :                 md5_vector(first ? 3 : 2, addr, elen, hash);
     991                 :        492 :                 first = 0;
     992                 :            : 
     993         [ +  + ]:       8364 :                 for (i = 0; i < MD5_MAC_LEN; i++)
     994                 :       7872 :                         *ppos++ = *pos++ ^ hash[i];
     995                 :        492 :                 left -= MD5_MAC_LEN;
     996                 :            :         }
     997                 :            : 
     998 [ +  - ][ -  + ]:        164 :         if (plain[0] == 0 || plain[0] > plen - 1) {
     999                 :          0 :                 printf("Failed to decrypt MPPE key\n");
    1000                 :          0 :                 os_free(plain);
    1001                 :          0 :                 return NULL;
    1002                 :            :         }
    1003                 :            : 
    1004                 :        164 :         res = os_malloc(plain[0]);
    1005         [ -  + ]:        164 :         if (res == NULL) {
    1006                 :          0 :                 os_free(plain);
    1007                 :          0 :                 return NULL;
    1008                 :            :         }
    1009                 :        164 :         os_memcpy(res, plain + 1, plain[0]);
    1010         [ +  - ]:        164 :         if (reslen)
    1011                 :        164 :                 *reslen = plain[0];
    1012                 :        164 :         os_free(plain);
    1013                 :        164 :         return res;
    1014                 :            : }
    1015                 :            : 
    1016                 :            : 
    1017                 :        164 : static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
    1018                 :            :                            const u8 *req_authenticator,
    1019                 :            :                            const u8 *secret, size_t secret_len,
    1020                 :            :                            u8 *ebuf, size_t *elen)
    1021                 :            : {
    1022                 :        164 :         int i, len, first = 1;
    1023                 :            :         u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
    1024                 :            :         const u8 *addr[3];
    1025                 :            :         size_t _len[3];
    1026                 :            : 
    1027                 :        164 :         WPA_PUT_BE16(saltbuf, salt);
    1028                 :            : 
    1029                 :        164 :         len = 1 + key_len;
    1030         [ +  - ]:        164 :         if (len & 0x0f) {
    1031                 :        164 :                 len = (len & 0xf0) + 16;
    1032                 :            :         }
    1033                 :        164 :         os_memset(ebuf, 0, len);
    1034                 :        164 :         ebuf[0] = key_len;
    1035                 :        164 :         os_memcpy(ebuf + 1, key, key_len);
    1036                 :            : 
    1037                 :        164 :         *elen = len;
    1038                 :            : 
    1039                 :        164 :         pos = ebuf;
    1040         [ +  + ]:        656 :         while (len > 0) {
    1041                 :            :                 /* b(1) = MD5(Secret + Request-Authenticator + Salt)
    1042                 :            :                  * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
    1043                 :        492 :                 addr[0] = secret;
    1044                 :        492 :                 _len[0] = secret_len;
    1045         [ +  + ]:        492 :                 if (first) {
    1046                 :        164 :                         addr[1] = req_authenticator;
    1047                 :        164 :                         _len[1] = MD5_MAC_LEN;
    1048                 :        164 :                         addr[2] = saltbuf;
    1049                 :        164 :                         _len[2] = sizeof(saltbuf);
    1050                 :            :                 } else {
    1051                 :        328 :                         addr[1] = pos - MD5_MAC_LEN;
    1052                 :        328 :                         _len[1] = MD5_MAC_LEN;
    1053                 :            :                 }
    1054         [ +  + ]:        492 :                 md5_vector(first ? 3 : 2, addr, _len, hash);
    1055                 :        492 :                 first = 0;
    1056                 :            : 
    1057         [ +  + ]:       8364 :                 for (i = 0; i < MD5_MAC_LEN; i++)
    1058                 :       7872 :                         *pos++ ^= hash[i];
    1059                 :            : 
    1060                 :        492 :                 len -= MD5_MAC_LEN;
    1061                 :            :         }
    1062                 :        164 : }
    1063                 :            : 
    1064                 :            : 
    1065                 :            : struct radius_ms_mppe_keys *
    1066                 :         82 : radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
    1067                 :            :                        const u8 *secret, size_t secret_len)
    1068                 :            : {
    1069                 :            :         u8 *key;
    1070                 :            :         size_t keylen;
    1071                 :            :         struct radius_ms_mppe_keys *keys;
    1072                 :            : 
    1073 [ +  - ][ -  + ]:         82 :         if (msg == NULL || sent_msg == NULL)
    1074                 :          0 :                 return NULL;
    1075                 :            : 
    1076                 :         82 :         keys = os_zalloc(sizeof(*keys));
    1077         [ -  + ]:         82 :         if (keys == NULL)
    1078                 :          0 :                 return NULL;
    1079                 :            : 
    1080                 :         82 :         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
    1081                 :            :                                          RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
    1082                 :            :                                          &keylen);
    1083         [ +  - ]:         82 :         if (key) {
    1084                 :         82 :                 keys->send = decrypt_ms_key(key, keylen,
    1085                 :         82 :                                             sent_msg->hdr->authenticator,
    1086                 :            :                                             secret, secret_len,
    1087                 :            :                                             &keys->send_len);
    1088                 :         82 :                 os_free(key);
    1089                 :            :         }
    1090                 :            : 
    1091                 :         82 :         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
    1092                 :            :                                          RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
    1093                 :            :                                          &keylen);
    1094         [ +  - ]:         82 :         if (key) {
    1095                 :         82 :                 keys->recv = decrypt_ms_key(key, keylen,
    1096                 :         82 :                                             sent_msg->hdr->authenticator,
    1097                 :            :                                             secret, secret_len,
    1098                 :            :                                             &keys->recv_len);
    1099                 :         82 :                 os_free(key);
    1100                 :            :         }
    1101                 :            : 
    1102                 :         82 :         return keys;
    1103                 :            : }
    1104                 :            : 
    1105                 :            : 
    1106                 :            : struct radius_ms_mppe_keys *
    1107                 :          0 : radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
    1108                 :            :                           const u8 *secret, size_t secret_len)
    1109                 :            : {
    1110                 :            :         u8 *key;
    1111                 :            :         size_t keylen;
    1112                 :            :         struct radius_ms_mppe_keys *keys;
    1113                 :            : 
    1114 [ #  # ][ #  # ]:          0 :         if (msg == NULL || sent_msg == NULL)
    1115                 :          0 :                 return NULL;
    1116                 :            : 
    1117                 :          0 :         keys = os_zalloc(sizeof(*keys));
    1118         [ #  # ]:          0 :         if (keys == NULL)
    1119                 :          0 :                 return NULL;
    1120                 :            : 
    1121                 :          0 :         key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
    1122                 :            :                                          RADIUS_CISCO_AV_PAIR, &keylen);
    1123 [ #  # ][ #  # ]:          0 :         if (key && keylen == 51 &&
                 [ #  # ]
    1124                 :          0 :             os_memcmp(key, "leap:session-key=", 17) == 0) {
    1125                 :          0 :                 keys->recv = decrypt_ms_key(key + 17, keylen - 17,
    1126                 :          0 :                                             sent_msg->hdr->authenticator,
    1127                 :            :                                             secret, secret_len,
    1128                 :            :                                             &keys->recv_len);
    1129                 :            :         }
    1130                 :          0 :         os_free(key);
    1131                 :            : 
    1132                 :          0 :         return keys;
    1133                 :            : }
    1134                 :            : 
    1135                 :            : 
    1136                 :         82 : int radius_msg_add_mppe_keys(struct radius_msg *msg,
    1137                 :            :                              const u8 *req_authenticator,
    1138                 :            :                              const u8 *secret, size_t secret_len,
    1139                 :            :                              const u8 *send_key, size_t send_key_len,
    1140                 :            :                              const u8 *recv_key, size_t recv_key_len)
    1141                 :            : {
    1142                 :            :         struct radius_attr_hdr *attr;
    1143                 :         82 :         u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
    1144                 :            :         u8 *buf;
    1145                 :            :         struct radius_attr_vendor *vhdr;
    1146                 :            :         u8 *pos;
    1147                 :            :         size_t elen;
    1148                 :            :         int hlen;
    1149                 :            :         u16 salt;
    1150                 :            : 
    1151                 :         82 :         hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
    1152                 :            : 
    1153                 :            :         /* MS-MPPE-Send-Key */
    1154                 :         82 :         buf = os_malloc(hlen + send_key_len + 16);
    1155         [ -  + ]:         82 :         if (buf == NULL) {
    1156                 :          0 :                 return 0;
    1157                 :            :         }
    1158                 :         82 :         pos = buf;
    1159                 :         82 :         os_memcpy(pos, &vendor_id, sizeof(vendor_id));
    1160                 :         82 :         pos += sizeof(vendor_id);
    1161                 :         82 :         vhdr = (struct radius_attr_vendor *) pos;
    1162                 :         82 :         vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
    1163                 :         82 :         pos = (u8 *) (vhdr + 1);
    1164                 :         82 :         salt = os_random() | 0x8000;
    1165                 :         82 :         WPA_PUT_BE16(pos, salt);
    1166                 :         82 :         pos += 2;
    1167                 :         82 :         encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
    1168                 :            :                        secret_len, pos, &elen);
    1169                 :         82 :         vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
    1170                 :            : 
    1171                 :         82 :         attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
    1172                 :            :                                    buf, hlen + elen);
    1173                 :         82 :         os_free(buf);
    1174         [ -  + ]:         82 :         if (attr == NULL) {
    1175                 :          0 :                 return 0;
    1176                 :            :         }
    1177                 :            : 
    1178                 :            :         /* MS-MPPE-Recv-Key */
    1179                 :         82 :         buf = os_malloc(hlen + send_key_len + 16);
    1180         [ -  + ]:         82 :         if (buf == NULL) {
    1181                 :          0 :                 return 0;
    1182                 :            :         }
    1183                 :         82 :         pos = buf;
    1184                 :         82 :         os_memcpy(pos, &vendor_id, sizeof(vendor_id));
    1185                 :         82 :         pos += sizeof(vendor_id);
    1186                 :         82 :         vhdr = (struct radius_attr_vendor *) pos;
    1187                 :         82 :         vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
    1188                 :         82 :         pos = (u8 *) (vhdr + 1);
    1189                 :         82 :         salt ^= 1;
    1190                 :         82 :         WPA_PUT_BE16(pos, salt);
    1191                 :         82 :         pos += 2;
    1192                 :         82 :         encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
    1193                 :            :                        secret_len, pos, &elen);
    1194                 :         82 :         vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
    1195                 :            : 
    1196                 :         82 :         attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
    1197                 :            :                                    buf, hlen + elen);
    1198                 :         82 :         os_free(buf);
    1199         [ -  + ]:         82 :         if (attr == NULL) {
    1200                 :          0 :                 return 0;
    1201                 :            :         }
    1202                 :            : 
    1203                 :         82 :         return 1;
    1204                 :            : }
    1205                 :            : 
    1206                 :            : 
    1207                 :            : /* Add User-Password attribute to a RADIUS message and encrypt it as specified
    1208                 :            :  * in RFC 2865, Chap. 5.2 */
    1209                 :            : struct radius_attr_hdr *
    1210                 :          0 : radius_msg_add_attr_user_password(struct radius_msg *msg,
    1211                 :            :                                   const u8 *data, size_t data_len,
    1212                 :            :                                   const u8 *secret, size_t secret_len)
    1213                 :            : {
    1214                 :            :         u8 buf[128];
    1215                 :            :         size_t padlen, i, buf_len, pos;
    1216                 :            :         const u8 *addr[2];
    1217                 :            :         size_t len[2];
    1218                 :            :         u8 hash[16];
    1219                 :            : 
    1220         [ #  # ]:          0 :         if (data_len > 128)
    1221                 :          0 :                 return NULL;
    1222                 :            : 
    1223                 :          0 :         os_memcpy(buf, data, data_len);
    1224                 :          0 :         buf_len = data_len;
    1225                 :            : 
    1226                 :          0 :         padlen = data_len % 16;
    1227 [ #  # ][ #  # ]:          0 :         if (padlen && data_len < sizeof(buf)) {
    1228                 :          0 :                 padlen = 16 - padlen;
    1229                 :          0 :                 os_memset(buf + data_len, 0, padlen);
    1230                 :          0 :                 buf_len += padlen;
    1231                 :            :         }
    1232                 :            : 
    1233                 :          0 :         addr[0] = secret;
    1234                 :          0 :         len[0] = secret_len;
    1235                 :          0 :         addr[1] = msg->hdr->authenticator;
    1236                 :          0 :         len[1] = 16;
    1237                 :          0 :         md5_vector(2, addr, len, hash);
    1238                 :            : 
    1239         [ #  # ]:          0 :         for (i = 0; i < 16; i++)
    1240                 :          0 :                 buf[i] ^= hash[i];
    1241                 :          0 :         pos = 16;
    1242                 :            : 
    1243         [ #  # ]:          0 :         while (pos < buf_len) {
    1244                 :          0 :                 addr[0] = secret;
    1245                 :          0 :                 len[0] = secret_len;
    1246                 :          0 :                 addr[1] = &buf[pos - 16];
    1247                 :          0 :                 len[1] = 16;
    1248                 :          0 :                 md5_vector(2, addr, len, hash);
    1249                 :            : 
    1250         [ #  # ]:          0 :                 for (i = 0; i < 16; i++)
    1251                 :          0 :                         buf[pos + i] ^= hash[i];
    1252                 :            : 
    1253                 :          0 :                 pos += 16;
    1254                 :            :         }
    1255                 :            : 
    1256                 :          0 :         return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
    1257                 :            :                                    buf, buf_len);
    1258                 :            : }
    1259                 :            : 
    1260                 :            : 
    1261                 :       1493 : int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
    1262                 :            : {
    1263                 :       1493 :         struct radius_attr_hdr *attr = NULL, *tmp;
    1264                 :            :         size_t i, dlen;
    1265                 :            : 
    1266         [ +  + ]:       9569 :         for (i = 0; i < msg->attr_used; i++) {
    1267                 :       8520 :                 tmp = radius_get_attr_hdr(msg, i);
    1268         [ +  + ]:       8520 :                 if (tmp->type == type) {
    1269                 :        444 :                         attr = tmp;
    1270                 :        444 :                         break;
    1271                 :            :                 }
    1272                 :            :         }
    1273                 :            : 
    1274 [ +  + ][ -  + ]:       1493 :         if (!attr || attr->length < sizeof(*attr))
    1275                 :       1049 :                 return -1;
    1276                 :            : 
    1277                 :        444 :         dlen = attr->length - sizeof(*attr);
    1278         [ +  + ]:        444 :         if (buf)
    1279                 :        441 :                 os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
    1280                 :       1493 :         return dlen;
    1281                 :            : }
    1282                 :            : 
    1283                 :            : 
    1284                 :        164 : int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
    1285                 :            :                             size_t *len, const u8 *start)
    1286                 :            : {
    1287                 :            :         size_t i;
    1288                 :        164 :         struct radius_attr_hdr *attr = NULL, *tmp;
    1289                 :            : 
    1290         [ +  + ]:        820 :         for (i = 0; i < msg->attr_used; i++) {
    1291                 :        656 :                 tmp = radius_get_attr_hdr(msg, i);
    1292 [ #  # ][ -  + ]:        656 :                 if (tmp->type == type &&
    1293         [ #  # ]:          0 :                     (start == NULL || (u8 *) tmp > start)) {
    1294                 :          0 :                         attr = tmp;
    1295                 :          0 :                         break;
    1296                 :            :                 }
    1297                 :            :         }
    1298                 :            : 
    1299 [ -  + ][ #  # ]:        164 :         if (!attr || attr->length < sizeof(*attr))
    1300                 :        164 :                 return -1;
    1301                 :            : 
    1302                 :          0 :         *buf = (u8 *) (attr + 1);
    1303                 :          0 :         *len = attr->length - sizeof(*attr);
    1304                 :        164 :         return 0;
    1305                 :            : }
    1306                 :            : 
    1307                 :            : 
    1308                 :          0 : int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
    1309                 :            : {
    1310                 :            :         size_t i;
    1311                 :            :         int count;
    1312                 :            : 
    1313         [ #  # ]:          0 :         for (count = 0, i = 0; i < msg->attr_used; i++) {
    1314                 :          0 :                 struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
    1315 [ #  # ][ #  # ]:          0 :                 if (attr->type == type &&
    1316                 :          0 :                     attr->length >= sizeof(struct radius_attr_hdr) + min_len)
    1317                 :          0 :                         count++;
    1318                 :            :         }
    1319                 :            : 
    1320                 :          0 :         return count;
    1321                 :            : }
    1322                 :            : 
    1323                 :            : 
    1324                 :            : struct radius_tunnel_attrs {
    1325                 :            :         int tag_used;
    1326                 :            :         int type; /* Tunnel-Type */
    1327                 :            :         int medium_type; /* Tunnel-Medium-Type */
    1328                 :            :         int vlanid;
    1329                 :            : };
    1330                 :            : 
    1331                 :            : 
    1332                 :            : /**
    1333                 :            :  * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
    1334                 :            :  * @msg: RADIUS message
    1335                 :            :  * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
    1336                 :            :  */
    1337                 :          0 : int radius_msg_get_vlanid(struct radius_msg *msg)
    1338                 :            : {
    1339                 :            :         struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
    1340                 :            :         size_t i;
    1341                 :          0 :         struct radius_attr_hdr *attr = NULL;
    1342                 :            :         const u8 *data;
    1343                 :            :         char buf[10];
    1344                 :            :         size_t dlen;
    1345                 :            : 
    1346                 :          0 :         os_memset(&tunnel, 0, sizeof(tunnel));
    1347                 :            : 
    1348         [ #  # ]:          0 :         for (i = 0; i < msg->attr_used; i++) {
    1349                 :          0 :                 attr = radius_get_attr_hdr(msg, i);
    1350         [ #  # ]:          0 :                 if (attr->length < sizeof(*attr))
    1351                 :          0 :                         return -1;
    1352                 :          0 :                 data = (const u8 *) (attr + 1);
    1353                 :          0 :                 dlen = attr->length - sizeof(*attr);
    1354         [ #  # ]:          0 :                 if (attr->length < 3)
    1355                 :          0 :                         continue;
    1356         [ #  # ]:          0 :                 if (data[0] >= RADIUS_TUNNEL_TAGS)
    1357                 :          0 :                         tun = &tunnel[0];
    1358                 :            :                 else
    1359                 :          0 :                         tun = &tunnel[data[0]];
    1360                 :            : 
    1361   [ #  #  #  # ]:          0 :                 switch (attr->type) {
    1362                 :            :                 case RADIUS_ATTR_TUNNEL_TYPE:
    1363         [ #  # ]:          0 :                         if (attr->length != 6)
    1364                 :          0 :                                 break;
    1365                 :          0 :                         tun->tag_used++;
    1366                 :          0 :                         tun->type = WPA_GET_BE24(data + 1);
    1367                 :          0 :                         break;
    1368                 :            :                 case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
    1369         [ #  # ]:          0 :                         if (attr->length != 6)
    1370                 :          0 :                                 break;
    1371                 :          0 :                         tun->tag_used++;
    1372                 :          0 :                         tun->medium_type = WPA_GET_BE24(data + 1);
    1373                 :          0 :                         break;
    1374                 :            :                 case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
    1375         [ #  # ]:          0 :                         if (data[0] < RADIUS_TUNNEL_TAGS) {
    1376                 :          0 :                                 data++;
    1377                 :          0 :                                 dlen--;
    1378                 :            :                         }
    1379         [ #  # ]:          0 :                         if (dlen >= sizeof(buf))
    1380                 :          0 :                                 break;
    1381                 :          0 :                         os_memcpy(buf, data, dlen);
    1382                 :          0 :                         buf[dlen] = '\0';
    1383                 :          0 :                         tun->tag_used++;
    1384                 :          0 :                         tun->vlanid = atoi(buf);
    1385                 :          0 :                         break;
    1386                 :            :                 }
    1387                 :            :         }
    1388                 :            : 
    1389         [ #  # ]:          0 :         for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
    1390                 :          0 :                 tun = &tunnel[i];
    1391 [ #  # ][ #  # ]:          0 :                 if (tun->tag_used &&
    1392         [ #  # ]:          0 :                     tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
    1393         [ #  # ]:          0 :                     tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
    1394                 :          0 :                     tun->vlanid > 0)
    1395                 :          0 :                         return tun->vlanid;
    1396                 :            :         }
    1397                 :            : 
    1398                 :          0 :         return -1;
    1399                 :            : }
    1400                 :            : 
    1401                 :            : 
    1402                 :            : /**
    1403                 :            :  * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password
    1404                 :            :  * @msg: Received RADIUS message
    1405                 :            :  * @keylen: Length of returned password
    1406                 :            :  * @secret: RADIUS shared secret
    1407                 :            :  * @secret_len: Length of secret
    1408                 :            :  * @sent_msg: Sent RADIUS message
    1409                 :            :  * @n: Number of password attribute to return (starting with 0)
    1410                 :            :  * Returns: Pointer to n-th password (free with os_free) or %NULL
    1411                 :            :  */
    1412                 :          0 : char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
    1413                 :            :                                       const u8 *secret, size_t secret_len,
    1414                 :            :                                       struct radius_msg *sent_msg, size_t n)
    1415                 :            : {
    1416                 :          0 :         u8 *buf = NULL;
    1417                 :            :         size_t buflen;
    1418                 :            :         const u8 *salt;
    1419                 :            :         u8 *str;
    1420                 :            :         const u8 *addr[3];
    1421                 :            :         size_t len[3];
    1422                 :            :         u8 hash[16];
    1423                 :            :         u8 *pos;
    1424                 :          0 :         size_t i, j = 0;
    1425                 :            :         struct radius_attr_hdr *attr;
    1426                 :            :         const u8 *data;
    1427                 :            :         size_t dlen;
    1428                 :          0 :         const u8 *fdata = NULL; /* points to found item */
    1429                 :          0 :         size_t fdlen = -1;
    1430                 :          0 :         char *ret = NULL;
    1431                 :            : 
    1432                 :            :         /* find n-th valid Tunnel-Password attribute */
    1433         [ #  # ]:          0 :         for (i = 0; i < msg->attr_used; i++) {
    1434                 :          0 :                 attr = radius_get_attr_hdr(msg, i);
    1435 [ #  # ][ #  # ]:          0 :                 if (attr == NULL ||
    1436                 :          0 :                     attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) {
    1437                 :          0 :                         continue;
    1438                 :            :                 }
    1439         [ #  # ]:          0 :                 if (attr->length <= 5)
    1440                 :          0 :                         continue;
    1441                 :          0 :                 data = (const u8 *) (attr + 1);
    1442                 :          0 :                 dlen = attr->length - sizeof(*attr);
    1443 [ #  # ][ #  # ]:          0 :                 if (dlen <= 3 || dlen % 16 != 3)
    1444                 :          0 :                         continue;
    1445                 :          0 :                 j++;
    1446         [ #  # ]:          0 :                 if (j <= n)
    1447                 :          0 :                         continue;
    1448                 :            : 
    1449                 :          0 :                 fdata = data;
    1450                 :          0 :                 fdlen = dlen;
    1451                 :          0 :                 break;
    1452                 :            :         }
    1453         [ #  # ]:          0 :         if (fdata == NULL)
    1454                 :          0 :                 goto out;
    1455                 :            : 
    1456                 :            :         /* alloc writable memory for decryption */
    1457                 :          0 :         buf = os_malloc(fdlen);
    1458         [ #  # ]:          0 :         if (buf == NULL)
    1459                 :          0 :                 goto out;
    1460                 :          0 :         os_memcpy(buf, fdata, fdlen);
    1461                 :          0 :         buflen = fdlen;
    1462                 :            : 
    1463                 :            :         /* init pointers */
    1464                 :          0 :         salt = buf + 1;
    1465                 :          0 :         str = buf + 3;
    1466                 :            : 
    1467                 :            :         /* decrypt blocks */
    1468                 :          0 :         pos = buf + buflen - 16; /* last block */
    1469         [ #  # ]:          0 :         while (pos >= str + 16) { /* all but the first block */
    1470                 :          0 :                 addr[0] = secret;
    1471                 :          0 :                 len[0] = secret_len;
    1472                 :          0 :                 addr[1] = pos - 16;
    1473                 :          0 :                 len[1] = 16;
    1474                 :          0 :                 md5_vector(2, addr, len, hash);
    1475                 :            : 
    1476         [ #  # ]:          0 :                 for (i = 0; i < 16; i++)
    1477                 :          0 :                         pos[i] ^= hash[i];
    1478                 :            : 
    1479                 :          0 :                 pos -= 16;
    1480                 :            :         }
    1481                 :            : 
    1482                 :            :         /* decrypt first block */
    1483         [ #  # ]:          0 :         if (str != pos)
    1484                 :          0 :                 goto out;
    1485                 :          0 :         addr[0] = secret;
    1486                 :          0 :         len[0] = secret_len;
    1487                 :          0 :         addr[1] = sent_msg->hdr->authenticator;
    1488                 :          0 :         len[1] = 16;
    1489                 :          0 :         addr[2] = salt;
    1490                 :          0 :         len[2] = 2;
    1491                 :          0 :         md5_vector(3, addr, len, hash);
    1492                 :            : 
    1493         [ #  # ]:          0 :         for (i = 0; i < 16; i++)
    1494                 :          0 :                 pos[i] ^= hash[i];
    1495                 :            : 
    1496                 :            :         /* derive plaintext length from first subfield */
    1497                 :          0 :         *keylen = (unsigned char) str[0];
    1498         [ #  # ]:          0 :         if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) {
    1499                 :            :                 /* decryption error - invalid key length */
    1500                 :          0 :                 goto out;
    1501                 :            :         }
    1502         [ #  # ]:          0 :         if (*keylen == 0) {
    1503                 :            :                 /* empty password */
    1504                 :          0 :                 goto out;
    1505                 :            :         }
    1506                 :            : 
    1507                 :            :         /* copy passphrase into new buffer */
    1508                 :          0 :         ret = os_malloc(*keylen);
    1509         [ #  # ]:          0 :         if (ret)
    1510                 :          0 :                 os_memcpy(ret, str + 1, *keylen);
    1511                 :            : 
    1512                 :            : out:
    1513                 :            :         /* return new buffer */
    1514                 :          0 :         os_free(buf);
    1515                 :          0 :         return ret;
    1516                 :            : }
    1517                 :            : 
    1518                 :            : 
    1519                 :        191 : void radius_free_class(struct radius_class_data *c)
    1520                 :            : {
    1521                 :            :         size_t i;
    1522         [ -  + ]:        191 :         if (c == NULL)
    1523                 :        191 :                 return;
    1524         [ -  + ]:        191 :         for (i = 0; i < c->count; i++)
    1525                 :          0 :                 os_free(c->attr[i].data);
    1526                 :        191 :         os_free(c->attr);
    1527                 :        191 :         c->attr = NULL;
    1528                 :        191 :         c->count = 0;
    1529                 :            : }
    1530                 :            : 
    1531                 :            : 
    1532                 :         86 : int radius_copy_class(struct radius_class_data *dst,
    1533                 :            :                       const struct radius_class_data *src)
    1534                 :            : {
    1535                 :            :         size_t i;
    1536                 :            : 
    1537         [ +  - ]:         86 :         if (src->attr == NULL)
    1538                 :         86 :                 return 0;
    1539                 :            : 
    1540                 :          0 :         dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data));
    1541         [ #  # ]:          0 :         if (dst->attr == NULL)
    1542                 :          0 :                 return -1;
    1543                 :            : 
    1544                 :          0 :         dst->count = 0;
    1545                 :            : 
    1546         [ #  # ]:          0 :         for (i = 0; i < src->count; i++) {
    1547                 :          0 :                 dst->attr[i].data = os_malloc(src->attr[i].len);
    1548         [ #  # ]:          0 :                 if (dst->attr[i].data == NULL)
    1549                 :          0 :                         break;
    1550                 :          0 :                 dst->count++;
    1551                 :          0 :                 os_memcpy(dst->attr[i].data, src->attr[i].data,
    1552                 :            :                           src->attr[i].len);
    1553                 :          0 :                 dst->attr[i].len = src->attr[i].len;
    1554                 :            :         }
    1555                 :            : 
    1556                 :         86 :         return 0;
    1557                 :            : }
    1558                 :            : 
    1559                 :            : 
    1560                 :          0 : u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs)
    1561                 :            : {
    1562                 :            :         size_t i, j;
    1563                 :            :         struct radius_attr_hdr *attr;
    1564                 :            : 
    1565         [ #  # ]:          0 :         for (i = 0; i < msg->attr_used; i++) {
    1566                 :          0 :                 attr = radius_get_attr_hdr(msg, i);
    1567                 :            : 
    1568         [ #  # ]:          0 :                 for (j = 0; attrs[j]; j++) {
    1569         [ #  # ]:          0 :                         if (attr->type == attrs[j])
    1570                 :          0 :                                 break;
    1571                 :            :                 }
    1572                 :            : 
    1573         [ #  # ]:          0 :                 if (attrs[j] == 0)
    1574                 :          0 :                         return attr->type; /* unlisted attr */
    1575                 :            :         }
    1576                 :            : 
    1577                 :          0 :         return 0;
    1578                 :            : }

Generated by: LCOV version 1.9