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

Generated by: LCOV version 1.10