LCOV - code coverage report
Current view: top level - src/eap_peer - eap_fast_pac.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1475438200 Lines: 491 515 95.3 %
Date: 2016-10-02 Functions: 26 26 100.0 %

          Line data    Source code
       1             : /*
       2             :  * EAP peer method: EAP-FAST PAC file processing
       3             :  * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
       4             :  *
       5             :  * This software may be distributed under the terms of the BSD license.
       6             :  * See README for more details.
       7             :  */
       8             : 
       9             : #include "includes.h"
      10             : 
      11             : #include "common.h"
      12             : #include "eap_config.h"
      13             : #include "eap_i.h"
      14             : #include "eap_fast_pac.h"
      15             : 
      16             : /* TODO: encrypt PAC-Key in the PAC file */
      17             : 
      18             : 
      19             : /* Text data format */
      20             : static const char *pac_file_hdr =
      21             :         "wpa_supplicant EAP-FAST PAC file - version 1";
      22             : 
      23             : /*
      24             :  * Binary data format
      25             :  * 4-octet magic value: 6A E4 92 0C
      26             :  * 2-octet version (big endian)
      27             :  * <version specific data>
      28             :  *
      29             :  * version=0:
      30             :  * Sequence of PAC entries:
      31             :  *   2-octet PAC-Type (big endian)
      32             :  *   32-octet PAC-Key
      33             :  *   2-octet PAC-Opaque length (big endian)
      34             :  *   <variable len> PAC-Opaque data (length bytes)
      35             :  *   2-octet PAC-Info length (big endian)
      36             :  *   <variable len> PAC-Info data (length bytes)
      37             :  */
      38             : 
      39             : #define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
      40             : #define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
      41             : 
      42             : 
      43             : /**
      44             :  * eap_fast_free_pac - Free PAC data
      45             :  * @pac: Pointer to the PAC entry
      46             :  *
      47             :  * Note that the PAC entry must not be in a list since this function does not
      48             :  * remove the list links.
      49             :  */
      50         142 : void eap_fast_free_pac(struct eap_fast_pac *pac)
      51             : {
      52         142 :         os_free(pac->pac_opaque);
      53         142 :         os_free(pac->pac_info);
      54         142 :         os_free(pac->a_id);
      55         142 :         os_free(pac->i_id);
      56         142 :         os_free(pac->a_id_info);
      57         142 :         os_free(pac);
      58         142 : }
      59             : 
      60             : 
      61             : /**
      62             :  * eap_fast_get_pac - Get a PAC entry based on A-ID
      63             :  * @pac_root: Pointer to root of the PAC list
      64             :  * @a_id: A-ID to search for
      65             :  * @a_id_len: Length of A-ID
      66             :  * @pac_type: PAC-Type to search for
      67             :  * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
      68             :  */
      69         232 : struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
      70             :                                        const u8 *a_id, size_t a_id_len,
      71             :                                        u16 pac_type)
      72             : {
      73         232 :         struct eap_fast_pac *pac = pac_root;
      74             : 
      75         508 :         while (pac) {
      76         135 :                 if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
      77          53 :                     os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
      78          38 :                         return pac;
      79             :                 }
      80          44 :                 pac = pac->next;
      81             :         }
      82         194 :         return NULL;
      83             : }
      84             : 
      85             : 
      86          52 : static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
      87             :                                 struct eap_fast_pac **pac_current,
      88             :                                 const u8 *a_id, size_t a_id_len, u16 pac_type)
      89             : {
      90             :         struct eap_fast_pac *pac, *prev;
      91             : 
      92          52 :         pac = *pac_root;
      93          52 :         prev = NULL;
      94             : 
      95         126 :         while (pac) {
      96          43 :                 if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
      97          18 :                     os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
      98           3 :                         if (prev == NULL)
      99           1 :                                 *pac_root = pac->next;
     100             :                         else
     101           2 :                                 prev->next = pac->next;
     102           3 :                         if (*pac_current == pac)
     103           3 :                                 *pac_current = NULL;
     104           3 :                         eap_fast_free_pac(pac);
     105           3 :                         break;
     106             :                 }
     107          22 :                 prev = pac;
     108          22 :                 pac = pac->next;
     109             :         }
     110          52 : }
     111             : 
     112             : 
     113         251 : static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
     114             :                              const u8 *src, size_t src_len)
     115             : {
     116         251 :         if (src) {
     117         250 :                 *dst = os_malloc(src_len);
     118         250 :                 if (*dst == NULL)
     119           1 :                         return -1;
     120         249 :                 os_memcpy(*dst, src, src_len);
     121         249 :                 *dst_len = src_len;
     122             :         }
     123         250 :         return 0;
     124             : }
     125             : 
     126             : 
     127             : /**
     128             :  * eap_fast_add_pac - Add a copy of a PAC entry to a list
     129             :  * @pac_root: Pointer to PAC list root pointer
     130             :  * @pac_current: Pointer to the current PAC pointer
     131             :  * @entry: New entry to clone and add to the list
     132             :  * Returns: 0 on success, -1 on failure
     133             :  *
     134             :  * This function makes a clone of the given PAC entry and adds this copied
     135             :  * entry to the list (pac_root). If an old entry for the same A-ID is found,
     136             :  * it will be removed from the PAC list and in this case, pac_current entry
     137             :  * is set to %NULL if it was the removed entry.
     138             :  */
     139          52 : int eap_fast_add_pac(struct eap_fast_pac **pac_root,
     140             :                      struct eap_fast_pac **pac_current,
     141             :                      struct eap_fast_pac *entry)
     142             : {
     143             :         struct eap_fast_pac *pac;
     144             : 
     145          52 :         if (entry == NULL || entry->a_id == NULL)
     146           0 :                 return -1;
     147             : 
     148             :         /* Remove a possible old entry for the matching A-ID. */
     149         104 :         eap_fast_remove_pac(pac_root, pac_current,
     150         104 :                             entry->a_id, entry->a_id_len, entry->pac_type);
     151             : 
     152             :         /* Allocate a new entry and add it to the list of PACs. */
     153          52 :         pac = os_zalloc(sizeof(*pac));
     154          52 :         if (pac == NULL)
     155           1 :                 return -1;
     156             : 
     157          51 :         pac->pac_type = entry->pac_type;
     158          51 :         os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
     159         102 :         if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
     160         101 :                               entry->pac_opaque, entry->pac_opaque_len) < 0 ||
     161         100 :             eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
     162         100 :                               entry->pac_info, entry->pac_info_len) < 0 ||
     163         100 :             eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
     164         100 :                               entry->a_id, entry->a_id_len) < 0 ||
     165         100 :             eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
     166         100 :                               entry->i_id, entry->i_id_len) < 0 ||
     167         100 :             eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
     168          50 :                               entry->a_id_info, entry->a_id_info_len) < 0) {
     169           1 :                 eap_fast_free_pac(pac);
     170           1 :                 return -1;
     171             :         }
     172             : 
     173          50 :         pac->next = *pac_root;
     174          50 :         *pac_root = pac;
     175             : 
     176          50 :         return 0;
     177             : }
     178             : 
     179             : 
     180             : struct eap_fast_read_ctx {
     181             :         FILE *f;
     182             :         const char *pos;
     183             :         const char *end;
     184             :         int line;
     185             :         char *buf;
     186             :         size_t buf_len;
     187             : };
     188             : 
     189         715 : static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
     190             : {
     191             :         char *pos;
     192             : 
     193         715 :         rc->line++;
     194         715 :         if (rc->f) {
     195          26 :                 if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
     196           2 :                         return -1;
     197             :         } else {
     198             :                 const char *l_end;
     199             :                 size_t len;
     200         689 :                 if (rc->pos >= rc->end)
     201          71 :                         return -1;
     202         618 :                 l_end = rc->pos;
     203       24875 :                 while (l_end < rc->end && *l_end != '\n')
     204       23639 :                         l_end++;
     205         618 :                 len = l_end - rc->pos;
     206         618 :                 if (len >= rc->buf_len)
     207           0 :                         len = rc->buf_len - 1;
     208         618 :                 os_memcpy(rc->buf, rc->pos, len);
     209         618 :                 rc->buf[len] = '\0';
     210         618 :                 rc->pos = l_end + 1;
     211             :         }
     212             : 
     213         642 :         rc->buf[rc->buf_len - 1] = '\0';
     214         642 :         pos = rc->buf;
     215       25923 :         while (*pos != '\0') {
     216       24663 :                 if (*pos == '\n' || *pos == '\r') {
     217          24 :                         *pos = '\0';
     218          24 :                         break;
     219             :                 }
     220       24639 :                 pos++;
     221             :         }
     222             : 
     223         642 :         pos = os_strchr(rc->buf, '=');
     224         642 :         if (pos)
     225         459 :                 *pos++ = '\0';
     226         642 :         *value = pos;
     227             : 
     228         642 :         return 0;
     229             : }
     230             : 
     231             : 
     232         266 : static u8 * eap_fast_parse_hex(const char *value, size_t *len)
     233             : {
     234             :         int hlen;
     235             :         u8 *buf;
     236             : 
     237         266 :         if (value == NULL)
     238           0 :                 return NULL;
     239         266 :         hlen = os_strlen(value);
     240         266 :         if (hlen & 1)
     241           5 :                 return NULL;
     242         261 :         *len = hlen / 2;
     243         261 :         buf = os_malloc(*len);
     244         261 :         if (buf == NULL)
     245           3 :                 return NULL;
     246         258 :         if (hexstr2bin(value, buf, *len)) {
     247           1 :                 os_free(buf);
     248           1 :                 return NULL;
     249             :         }
     250         257 :         return buf;
     251             : }
     252             : 
     253             : 
     254         131 : static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
     255             :                                   struct eap_fast_read_ctx *rc)
     256             : {
     257         131 :         os_memset(rc, 0, sizeof(*rc));
     258             : 
     259         131 :         rc->buf_len = 2048;
     260         131 :         rc->buf = os_malloc(rc->buf_len);
     261         131 :         if (rc->buf == NULL)
     262           1 :                 return -1;
     263             : 
     264         130 :         if (os_strncmp(pac_file, "blob://", 7) == 0) {
     265             :                 const struct wpa_config_blob *blob;
     266         127 :                 blob = eap_get_config_blob(sm, pac_file + 7);
     267         127 :                 if (blob == NULL) {
     268          40 :                         wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
     269             :                                    "assume no PAC entries have been "
     270             :                                    "provisioned", pac_file + 7);
     271          40 :                         os_free(rc->buf);
     272          40 :                         return -1;
     273             :                 }
     274          87 :                 rc->pos = (char *) blob->data;
     275          87 :                 rc->end = (char *) blob->data + blob->len;
     276             :         } else {
     277           3 :                 rc->f = fopen(pac_file, "rb");
     278           3 :                 if (rc->f == NULL) {
     279           1 :                         wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
     280             :                                    "assume no PAC entries have been "
     281             :                                    "provisioned", pac_file);
     282           1 :                         os_free(rc->buf);
     283           1 :                         return -1;
     284             :                 }
     285             :         }
     286             : 
     287          89 :         return 0;
     288             : }
     289             : 
     290             : 
     291          89 : static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
     292             : {
     293          89 :         os_free(rc->buf);
     294          89 :         if (rc->f)
     295           2 :                 fclose(rc->f);
     296          89 : }
     297             : 
     298             : 
     299          70 : static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
     300             : {
     301          70 :         if (*pac)
     302           1 :                 return "START line without END";
     303             : 
     304          69 :         *pac = os_zalloc(sizeof(struct eap_fast_pac));
     305          69 :         if (*pac == NULL)
     306           1 :                 return "No memory for PAC entry";
     307          68 :         (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
     308          68 :         return NULL;
     309             : }
     310             : 
     311             : 
     312          56 : static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
     313             :                                        struct eap_fast_pac **pac)
     314             : {
     315          56 :         if (*pac == NULL)
     316           1 :                 return "END line without START";
     317          55 :         if (*pac_root) {
     318          17 :                 struct eap_fast_pac *end = *pac_root;
     319          36 :                 while (end->next)
     320           2 :                         end = end->next;
     321          17 :                 end->next = *pac;
     322             :         } else
     323          38 :                 *pac_root = *pac;
     324             : 
     325          55 :         *pac = NULL;
     326          55 :         return NULL;
     327             : }
     328             : 
     329             : 
     330          56 : static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
     331             :                                             char *pos)
     332             : {
     333          56 :         if (!pos)
     334           1 :                 return "Cannot parse pac type";
     335          55 :         pac->pac_type = atoi(pos);
     336          56 :         if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
     337           2 :             pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
     338           1 :             pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
     339           1 :                 return "Unrecognized PAC-Type";
     340             : 
     341          54 :         return NULL;
     342             : }
     343             : 
     344             : 
     345          56 : static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
     346             : {
     347             :         u8 *key;
     348             :         size_t key_len;
     349             : 
     350          56 :         key = eap_fast_parse_hex(pos, &key_len);
     351          56 :         if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
     352           3 :                 os_free(key);
     353           3 :                 return "Invalid PAC-Key";
     354             :         }
     355             : 
     356          53 :         os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
     357          53 :         os_free(key);
     358             : 
     359          53 :         return NULL;
     360             : }
     361             : 
     362             : 
     363          54 : static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
     364             :                                               char *pos)
     365             : {
     366          54 :         os_free(pac->pac_opaque);
     367          54 :         pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
     368          54 :         if (pac->pac_opaque == NULL)
     369           2 :                 return "Invalid PAC-Opaque";
     370          52 :         return NULL;
     371             : }
     372             : 
     373             : 
     374          53 : static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
     375             : {
     376          53 :         os_free(pac->a_id);
     377          53 :         pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
     378          53 :         if (pac->a_id == NULL)
     379           2 :                 return "Invalid A-ID";
     380          51 :         return NULL;
     381             : }
     382             : 
     383             : 
     384          51 : static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
     385             : {
     386          51 :         os_free(pac->i_id);
     387          51 :         pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
     388          51 :         if (pac->i_id == NULL)
     389           1 :                 return "Invalid I-ID";
     390          50 :         return NULL;
     391             : }
     392             : 
     393             : 
     394          52 : static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
     395             :                                              char *pos)
     396             : {
     397          52 :         os_free(pac->a_id_info);
     398          52 :         pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
     399          52 :         if (pac->a_id_info == NULL)
     400           1 :                 return "Invalid A-ID-Info";
     401          51 :         return NULL;
     402             : }
     403             : 
     404             : 
     405             : /**
     406             :  * eap_fast_load_pac - Load PAC entries (text format)
     407             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     408             :  * @pac_root: Pointer to root of the PAC list (to be filled)
     409             :  * @pac_file: Name of the PAC file/blob to load
     410             :  * Returns: 0 on success, -1 on failure
     411             :  */
     412         131 : int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
     413             :                       const char *pac_file)
     414             : {
     415             :         struct eap_fast_read_ctx rc;
     416         131 :         struct eap_fast_pac *pac = NULL;
     417         131 :         int count = 0;
     418             :         char *pos;
     419         131 :         const char *err = NULL;
     420             : 
     421         131 :         if (pac_file == NULL)
     422           0 :                 return -1;
     423             : 
     424         131 :         if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
     425          42 :                 return 0;
     426             : 
     427          89 :         if (eap_fast_read_line(&rc, &pos) < 0) {
     428             :                 /* empty file - assume it is fine to overwrite */
     429          34 :                 eap_fast_deinit_pac_data(&rc);
     430          34 :                 return 0;
     431             :         }
     432          55 :         if (os_strcmp(pac_file_hdr, rc.buf) != 0)
     433           1 :                 err = "Unrecognized header line";
     434             : 
     435         697 :         while (!err && eap_fast_read_line(&rc, &pos) == 0) {
     436         587 :                 if (os_strcmp(rc.buf, "START") == 0)
     437          70 :                         err = eap_fast_parse_start(&pac);
     438         517 :                 else if (os_strcmp(rc.buf, "END") == 0) {
     439          56 :                         err = eap_fast_parse_end(pac_root, &pac);
     440          56 :                         count++;
     441         461 :                 } else if (!pac)
     442           1 :                         err = "Unexpected line outside START/END block";
     443         460 :                 else if (os_strcmp(rc.buf, "PAC-Type") == 0)
     444          56 :                         err = eap_fast_parse_pac_type(pac, pos);
     445         404 :                 else if (os_strcmp(rc.buf, "PAC-Key") == 0)
     446          56 :                         err = eap_fast_parse_pac_key(pac, pos);
     447         348 :                 else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
     448          54 :                         err = eap_fast_parse_pac_opaque(pac, pos);
     449         294 :                 else if (os_strcmp(rc.buf, "A-ID") == 0)
     450          53 :                         err = eap_fast_parse_a_id(pac, pos);
     451         241 :                 else if (os_strcmp(rc.buf, "I-ID") == 0)
     452          51 :                         err = eap_fast_parse_i_id(pac, pos);
     453         190 :                 else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
     454          52 :                         err = eap_fast_parse_a_id_info(pac, pos);
     455             :         }
     456             : 
     457          55 :         if (pac) {
     458          13 :                 if (!err)
     459           1 :                         err = "PAC block not terminated with END";
     460          13 :                 eap_fast_free_pac(pac);
     461             :         }
     462             : 
     463          55 :         eap_fast_deinit_pac_data(&rc);
     464             : 
     465          55 :         if (err) {
     466          17 :                 wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
     467             :                            err, pac_file, rc.line);
     468          17 :                 return -1;
     469             :         }
     470             : 
     471          38 :         wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
     472             :                    count, pac_file);
     473             : 
     474          38 :         return 0;
     475             : }
     476             : 
     477             : 
     478         324 : static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
     479             :                            const char *field, const u8 *data,
     480             :                            size_t len, int txt)
     481             : {
     482             :         size_t i, need;
     483             :         int ret;
     484             :         char *end;
     485             : 
     486         324 :         if (data == NULL || buf == NULL || *buf == NULL ||
     487         288 :             pos == NULL || *pos == NULL || *pos < *buf)
     488          36 :                 return;
     489             : 
     490         288 :         need = os_strlen(field) + len * 2 + 30;
     491         288 :         if (txt)
     492          97 :                 need += os_strlen(field) + len + 20;
     493             : 
     494         288 :         if (*pos - *buf + need > *buf_len) {
     495           4 :                 char *nbuf = os_realloc(*buf, *buf_len + need);
     496           4 :                 if (nbuf == NULL) {
     497           1 :                         os_free(*buf);
     498           1 :                         *buf = NULL;
     499           1 :                         return;
     500             :                 }
     501           3 :                 *pos = nbuf + (*pos - *buf);
     502           3 :                 *buf = nbuf;
     503           3 :                 *buf_len += need;
     504             :         }
     505         287 :         end = *buf + *buf_len;
     506             : 
     507         287 :         ret = os_snprintf(*pos, end - *pos, "%s=", field);
     508         287 :         if (os_snprintf_error(end - *pos, ret))
     509           0 :                 return;
     510         287 :         *pos += ret;
     511         287 :         *pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
     512         287 :         ret = os_snprintf(*pos, end - *pos, "\n");
     513         287 :         if (os_snprintf_error(end - *pos, ret))
     514           0 :                 return;
     515         287 :         *pos += ret;
     516             : 
     517         287 :         if (txt) {
     518          97 :                 ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
     519          97 :                 if (os_snprintf_error(end - *pos, ret))
     520           0 :                         return;
     521          97 :                 *pos += ret;
     522         936 :                 for (i = 0; i < len; i++) {
     523         839 :                         ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
     524         839 :                         if (os_snprintf_error(end - *pos, ret))
     525           0 :                                 return;
     526         839 :                         *pos += ret;
     527             :                 }
     528          97 :                 ret = os_snprintf(*pos, end - *pos, "\n");
     529          97 :                 if (os_snprintf_error(end - *pos, ret))
     530           0 :                         return;
     531          97 :                 *pos += ret;
     532             :         }
     533             : }
     534             : 
     535             : 
     536          49 : static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
     537             :                               char *buf, size_t len)
     538             : {
     539          49 :         if (os_strncmp(pac_file, "blob://", 7) == 0) {
     540             :                 struct wpa_config_blob *blob;
     541          47 :                 blob = os_zalloc(sizeof(*blob));
     542          47 :                 if (blob == NULL)
     543           2 :                         return -1;
     544          45 :                 blob->data = (u8 *) buf;
     545          45 :                 blob->len = len;
     546          45 :                 buf = NULL;
     547          45 :                 blob->name = os_strdup(pac_file + 7);
     548          45 :                 if (blob->name == NULL) {
     549           2 :                         os_free(blob);
     550           2 :                         return -1;
     551             :                 }
     552          43 :                 eap_set_config_blob(sm, blob);
     553             :         } else {
     554             :                 FILE *f;
     555           2 :                 f = fopen(pac_file, "wb");
     556           2 :                 if (f == NULL) {
     557           0 :                         wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
     558             :                                    "file '%s' for writing", pac_file);
     559           0 :                         return -1;
     560             :                 }
     561           2 :                 if (fwrite(buf, 1, len, f) != len) {
     562           0 :                         wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
     563             :                                    "PACs into '%s'", pac_file);
     564           0 :                         fclose(f);
     565           0 :                         return -1;
     566             :                 }
     567           2 :                 os_free(buf);
     568           2 :                 fclose(f);
     569             :         }
     570             : 
     571          45 :         return 0;
     572             : }
     573             : 
     574             : 
     575          54 : static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
     576             :                                  char **pos, size_t *buf_len)
     577             : {
     578             :         int ret;
     579             : 
     580          54 :         ret = os_snprintf(*pos, *buf + *buf_len - *pos,
     581          54 :                           "START\nPAC-Type=%d\n", pac->pac_type);
     582          54 :         if (os_snprintf_error(*buf + *buf_len - *pos, ret))
     583           0 :                 return -1;
     584             : 
     585          54 :         *pos += ret;
     586          54 :         eap_fast_write(buf, pos, buf_len, "PAC-Key",
     587          54 :                        pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
     588         108 :         eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
     589          54 :                        pac->pac_opaque, pac->pac_opaque_len, 0);
     590         108 :         eap_fast_write(buf, pos, buf_len, "PAC-Info",
     591          54 :                        pac->pac_info, pac->pac_info_len, 0);
     592         108 :         eap_fast_write(buf, pos, buf_len, "A-ID",
     593          54 :                        pac->a_id, pac->a_id_len, 0);
     594         108 :         eap_fast_write(buf, pos, buf_len, "I-ID",
     595          54 :                        pac->i_id, pac->i_id_len, 1);
     596         108 :         eap_fast_write(buf, pos, buf_len, "A-ID-Info",
     597          54 :                        pac->a_id_info, pac->a_id_info_len, 1);
     598          54 :         if (*buf == NULL) {
     599           1 :                 wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
     600             :                            "data");
     601           1 :                 return -1;
     602             :         }
     603          53 :         ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
     604          53 :         if (os_snprintf_error(*buf + *buf_len - *pos, ret))
     605           0 :                 return -1;
     606          53 :         *pos += ret;
     607             : 
     608          53 :         return 0;
     609             : }
     610             : 
     611             : 
     612             : /**
     613             :  * eap_fast_save_pac - Save PAC entries (text format)
     614             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     615             :  * @pac_root: Root of the PAC list
     616             :  * @pac_file: Name of the PAC file/blob
     617             :  * Returns: 0 on success, -1 on failure
     618             :  */
     619          41 : int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
     620             :                       const char *pac_file)
     621             : {
     622             :         struct eap_fast_pac *pac;
     623          41 :         int ret, count = 0;
     624             :         char *buf, *pos;
     625             :         size_t buf_len;
     626             : 
     627          41 :         if (pac_file == NULL)
     628           0 :                 return -1;
     629             : 
     630          41 :         buf_len = 1024;
     631          41 :         pos = buf = os_malloc(buf_len);
     632          41 :         if (buf == NULL)
     633           1 :                 return -1;
     634             : 
     635          40 :         ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
     636          40 :         if (os_snprintf_error(buf + buf_len - pos, ret)) {
     637           0 :                 os_free(buf);
     638           0 :                 return -1;
     639             :         }
     640          40 :         pos += ret;
     641             : 
     642          40 :         pac = pac_root;
     643         133 :         while (pac) {
     644          54 :                 if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
     645           1 :                         os_free(buf);
     646           1 :                         return -1;
     647             :                 }
     648          53 :                 count++;
     649          53 :                 pac = pac->next;
     650             :         }
     651             : 
     652          39 :         if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
     653           2 :                 os_free(buf);
     654           2 :                 return -1;
     655             :         }
     656             : 
     657          37 :         wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
     658             :                    count, pac_file);
     659             : 
     660          37 :         return 0;
     661             : }
     662             : 
     663             : 
     664             : /**
     665             :  * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
     666             :  * @pac_root: Root of the PAC list
     667             :  * @max_len: Maximum length of the list (>= 1)
     668             :  * Returns: Number of PAC entries removed
     669             :  */
     670         188 : size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
     671             :                                   size_t max_len)
     672             : {
     673             :         struct eap_fast_pac *pac, *prev;
     674             :         size_t count;
     675             : 
     676         188 :         pac = pac_root;
     677         188 :         prev = NULL;
     678         188 :         count = 0;
     679             : 
     680         517 :         while (pac) {
     681         144 :                 count++;
     682         144 :                 if (count > max_len)
     683           3 :                         break;
     684         141 :                 prev = pac;
     685         141 :                 pac = pac->next;
     686             :         }
     687             : 
     688         188 :         if (count <= max_len || prev == NULL)
     689         185 :                 return 0;
     690             : 
     691           3 :         count = 0;
     692           3 :         prev->next = NULL;
     693             : 
     694           9 :         while (pac) {
     695           3 :                 prev = pac;
     696           3 :                 pac = pac->next;
     697           3 :                 eap_fast_free_pac(prev);
     698           3 :                 count++;
     699             :         }
     700             : 
     701           3 :         return count;
     702             : }
     703             : 
     704             : 
     705          18 : static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
     706             : {
     707             :         u8 *pos, *end;
     708             :         u16 type, len;
     709             : 
     710          18 :         pos = pac->pac_info;
     711          18 :         end = pos + pac->pac_info_len;
     712             : 
     713          97 :         while (end - pos > 4) {
     714          65 :                 type = WPA_GET_BE16(pos);
     715          65 :                 pos += 2;
     716          65 :                 len = WPA_GET_BE16(pos);
     717          65 :                 pos += 2;
     718          65 :                 if (len > (unsigned int) (end - pos))
     719           2 :                         break;
     720             : 
     721          63 :                 if (type == PAC_TYPE_A_ID) {
     722          16 :                         os_free(pac->a_id);
     723          16 :                         pac->a_id = os_malloc(len);
     724          16 :                         if (pac->a_id == NULL)
     725           1 :                                 break;
     726          15 :                         os_memcpy(pac->a_id, pos, len);
     727          15 :                         pac->a_id_len = len;
     728             :                 }
     729             : 
     730          62 :                 if (type == PAC_TYPE_A_ID_INFO) {
     731          14 :                         os_free(pac->a_id_info);
     732          14 :                         pac->a_id_info = os_malloc(len);
     733          14 :                         if (pac->a_id_info == NULL)
     734           1 :                                 break;
     735          13 :                         os_memcpy(pac->a_id_info, pos, len);
     736          13 :                         pac->a_id_info_len = len;
     737             :                 }
     738             : 
     739          61 :                 pos += len;
     740             :         }
     741          18 : }
     742             : 
     743             : 
     744             : /**
     745             :  * eap_fast_load_pac_bin - Load PAC entries (binary format)
     746             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     747             :  * @pac_root: Pointer to root of the PAC list (to be filled)
     748             :  * @pac_file: Name of the PAC file/blob to load
     749             :  * Returns: 0 on success, -1 on failure
     750             :  */
     751          33 : int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
     752             :                           const char *pac_file)
     753             : {
     754          33 :         const struct wpa_config_blob *blob = NULL;
     755             :         u8 *buf, *end, *pos;
     756          33 :         size_t len, count = 0;
     757             :         struct eap_fast_pac *pac, *prev;
     758             : 
     759          33 :         *pac_root = NULL;
     760             : 
     761          33 :         if (pac_file == NULL)
     762           0 :                 return -1;
     763             : 
     764          33 :         if (os_strncmp(pac_file, "blob://", 7) == 0) {
     765          30 :                 blob = eap_get_config_blob(sm, pac_file + 7);
     766          30 :                 if (blob == NULL) {
     767           1 :                         wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
     768             :                                    "assume no PAC entries have been "
     769             :                                    "provisioned", pac_file + 7);
     770           1 :                         return 0;
     771             :                 }
     772          29 :                 buf = blob->data;
     773          29 :                 len = blob->len;
     774             :         } else {
     775           3 :                 buf = (u8 *) os_readfile(pac_file, &len);
     776           3 :                 if (buf == NULL) {
     777           1 :                         wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
     778             :                                    "assume no PAC entries have been "
     779             :                                    "provisioned", pac_file);
     780           1 :                         return 0;
     781             :                 }
     782             :         }
     783             : 
     784          31 :         if (len == 0) {
     785           6 :                 if (blob == NULL)
     786           0 :                         os_free(buf);
     787           6 :                 return 0;
     788             :         }
     789             : 
     790          48 :         if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
     791          23 :             WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
     792           3 :                 wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
     793             :                            pac_file);
     794           3 :                 if (blob == NULL)
     795           0 :                         os_free(buf);
     796           3 :                 return -1;
     797             :         }
     798             : 
     799          22 :         pac = prev = NULL;
     800          22 :         pos = buf + 6;
     801          22 :         end = buf + len;
     802          62 :         while (pos < end) {
     803             :                 u16 val;
     804             : 
     805          26 :                 if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) {
     806           2 :                         pac = NULL;
     807           2 :                         goto parse_fail;
     808             :                 }
     809             : 
     810          24 :                 pac = os_zalloc(sizeof(*pac));
     811          24 :                 if (pac == NULL)
     812           1 :                         goto parse_fail;
     813             : 
     814          23 :                 pac->pac_type = WPA_GET_BE16(pos);
     815          23 :                 pos += 2;
     816          23 :                 os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
     817          23 :                 pos += EAP_FAST_PAC_KEY_LEN;
     818          23 :                 val = WPA_GET_BE16(pos);
     819          23 :                 pos += 2;
     820          23 :                 if (val > end - pos)
     821           1 :                         goto parse_fail;
     822          22 :                 pac->pac_opaque_len = val;
     823          22 :                 pac->pac_opaque = os_malloc(pac->pac_opaque_len);
     824          22 :                 if (pac->pac_opaque == NULL)
     825           1 :                         goto parse_fail;
     826          21 :                 os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
     827          21 :                 pos += pac->pac_opaque_len;
     828          21 :                 if (2 > end - pos)
     829           1 :                         goto parse_fail;
     830          20 :                 val = WPA_GET_BE16(pos);
     831          20 :                 pos += 2;
     832          20 :                 if (val > end - pos)
     833           1 :                         goto parse_fail;
     834          19 :                 pac->pac_info_len = val;
     835          19 :                 pac->pac_info = os_malloc(pac->pac_info_len);
     836          19 :                 if (pac->pac_info == NULL)
     837           1 :                         goto parse_fail;
     838          18 :                 os_memcpy(pac->pac_info, pos, pac->pac_info_len);
     839          18 :                 pos += pac->pac_info_len;
     840          18 :                 eap_fast_pac_get_a_id(pac);
     841             : 
     842          18 :                 count++;
     843          18 :                 if (prev)
     844           3 :                         prev->next = pac;
     845             :                 else
     846          15 :                         *pac_root = pac;
     847          18 :                 prev = pac;
     848             :         }
     849             : 
     850          14 :         if (blob == NULL)
     851           2 :                 os_free(buf);
     852             : 
     853          14 :         wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
     854             :                    (unsigned long) count, pac_file);
     855             : 
     856          14 :         return 0;
     857             : 
     858             : parse_fail:
     859           8 :         wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
     860             :                    pac_file);
     861           8 :         if (blob == NULL)
     862           0 :                 os_free(buf);
     863           8 :         if (pac)
     864           5 :                 eap_fast_free_pac(pac);
     865           8 :         return -1;
     866             : }
     867             : 
     868             : 
     869             : /**
     870             :  * eap_fast_save_pac_bin - Save PAC entries (binary format)
     871             :  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
     872             :  * @pac_root: Root of the PAC list
     873             :  * @pac_file: Name of the PAC file/blob
     874             :  * Returns: 0 on success, -1 on failure
     875             :  */
     876          11 : int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
     877             :                           const char *pac_file)
     878             : {
     879          11 :         size_t len, count = 0;
     880             :         struct eap_fast_pac *pac;
     881             :         u8 *buf, *pos;
     882             : 
     883          11 :         len = 6;
     884          11 :         pac = pac_root;
     885          36 :         while (pac) {
     886          28 :                 if (pac->pac_opaque_len > 65535 ||
     887          14 :                     pac->pac_info_len > 65535)
     888           0 :                         return -1;
     889          28 :                 len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
     890          14 :                         2 + pac->pac_info_len;
     891          14 :                 pac = pac->next;
     892             :         }
     893             : 
     894          11 :         buf = os_malloc(len);
     895          11 :         if (buf == NULL)
     896           1 :                 return -1;
     897             : 
     898          10 :         pos = buf;
     899          10 :         WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
     900          10 :         pos += 4;
     901          10 :         WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
     902          10 :         pos += 2;
     903             : 
     904          10 :         pac = pac_root;
     905          33 :         while (pac) {
     906          13 :                 WPA_PUT_BE16(pos, pac->pac_type);
     907          13 :                 pos += 2;
     908          13 :                 os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
     909          13 :                 pos += EAP_FAST_PAC_KEY_LEN;
     910          13 :                 WPA_PUT_BE16(pos, pac->pac_opaque_len);
     911          13 :                 pos += 2;
     912          13 :                 os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
     913          13 :                 pos += pac->pac_opaque_len;
     914          13 :                 WPA_PUT_BE16(pos, pac->pac_info_len);
     915          13 :                 pos += 2;
     916          13 :                 os_memcpy(pos, pac->pac_info, pac->pac_info_len);
     917          13 :                 pos += pac->pac_info_len;
     918             : 
     919          13 :                 pac = pac->next;
     920          13 :                 count++;
     921             :         }
     922             : 
     923          10 :         if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
     924           2 :                 os_free(buf);
     925           2 :                 return -1;
     926             :         }
     927             : 
     928           8 :         wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
     929             :                    "(bin)", (unsigned long) count, pac_file);
     930             : 
     931           8 :         return 0;
     932             : }

Generated by: LCOV version 1.10