All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
eapsimlib.c
Go to the documentation of this file.
1 /*
2  * eapsimlib.c based upon draft-haverinen-pppext-eap-sim-11.txt.
3  *
4  * The development of the EAP/SIM support was funded by Internet Foundation
5  * Austria (http://www.nic.at/ipa).
6  *
7  * code common to EAP-SIM clients and to servers.
8  *
9  * Version: $Id: 3d6214e8b408649fb558bbb856617bbb8413cbba $
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  *
25  * Copyright 2000-2003,2006 The FreeRADIUS server project
26  * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
27  */
28 
29 /*
30  * EAP-SIM PACKET FORMAT
31  * ------- ------ ------
32  *
33  * EAP Request and Response Packet Format
34  * --- ------- --- -------- ------ ------
35  * 0 1 2 3
36  * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
37  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38  * | Code | Identifier | Length |
39  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40  * | Type | SIM-Type | SIM-Length | value ... |
41  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42  *
43  * with SIM-Type/SIM-Length/Value... repeating. SIM-Length is in units
44  * of 32 bits, and includes the Sim-Type/Sim-Length fields.
45  *
46  * The SIM-Type's are mapped to PW_EAP_SIM_BASE+Sim-type and
47  * unmapped by these functions.
48  *
49  */
50 
51 RCSID("$Id: 3d6214e8b408649fb558bbb856617bbb8413cbba $")
52 
53 #include <freeradius-devel/libradius.h>
54 #include "eap_types.h"
55 #include "eap_sim.h"
56 #include <freeradius-devel/sha1.h>
57 
58 /*
59  * given a radius request with many attributes in the EAP-SIM range, build
60  * them all into a single EAP-SIM body.
61  *
62  */
64 {
65  VALUE_PAIR *vp;
66  int encoded_size;
67  uint8_t *encodedmsg, *attr;
68  unsigned int id, eapcode;
69  uint8_t *macspace;
70  uint8_t const *append;
71  int appendlen;
72  unsigned char subtype;
73  vp_cursor_t cursor;
74 
75  macspace = NULL;
76  append = NULL;
77  appendlen = 0;
78 
79  /*
80  * encodedmsg is now an EAP-SIM message.
81  * it might be too big for putting into an EAP-Type-SIM
82  *
83  */
84  subtype = (vp = fr_pair_find_by_num(r->vps, 0, PW_EAP_SIM_SUBTYPE, TAG_ANY)) ?
85  vp->vp_integer : EAPSIM_START;
86 
87  id = (vp = fr_pair_find_by_num(r->vps, 0, PW_EAP_ID, TAG_ANY)) ?
88  vp->vp_integer : ((int)getpid() & 0xff);
89 
90  eapcode = (vp = fr_pair_find_by_num(r->vps, 0, PW_EAP_CODE, TAG_ANY)) ?
91  vp->vp_integer : PW_EAP_REQUEST;
92 
93  /*
94  * take a walk through the attribute list to see how much space
95  * that we need to encode all of this.
96  */
97  encoded_size = 0;
98  for (vp = fr_cursor_init(&cursor, &r->vps);
99  vp;
100  vp = fr_cursor_next(&cursor)) {
101  int roundedlen;
102  int vplen;
103 
104  if ((vp->da->attr < PW_EAP_SIM_BASE) || (vp->da->attr >= (PW_EAP_SIM_BASE + 256))) {
105  continue;
106  }
107 
108  vplen = vp->vp_length;
109 
110  /*
111  * the AT_MAC attribute is a bit different, when we get to this
112  * attribute, we pull the contents out, save it for later
113  * processing, set the size to 16 bytes (plus 2 bytes padding).
114  *
115  * At this point, we only care about the size.
116  */
117  if(vp->da->attr == PW_EAP_SIM_MAC) {
118  vplen = 18;
119  }
120 
121  /* round up to next multiple of 4, after taking in
122  * account the type and length bytes
123  */
124  roundedlen = (vplen + 2 + 3) & ~3;
125  encoded_size += roundedlen;
126  }
127 
128  if (ep->code != PW_EAP_SUCCESS) {
129  ep->code = eapcode;
130  }
131 
132  ep->id = (id & 0xff);
133  ep->type.num = PW_EAP_SIM;
134 
135  /*
136  * if no attributes were found, do very little.
137  *
138  */
139  if (encoded_size == 0) {
140  encodedmsg = talloc_array(ep, uint8_t, 3);
141  /* FIX: could be NULL */
142 
143  encodedmsg[0] = subtype;
144  encodedmsg[1] = 0;
145  encodedmsg[2] = 0;
146 
147  ep->type.length = 3;
148  ep->type.data = encodedmsg;
149 
150  return 0;
151  }
152 
153 
154  /*
155  * figured out the length, so allocate some space for the results.
156  *
157  * Note that we do not bother going through an "EAP" stage, which
158  * is a bit strange compared to the unmap, which expects to see
159  * an EAP-SIM virtual attributes.
160  *
161  * EAP is 1-code, 1-identifier, 2-length, 1-type = 5 overhead.
162  *
163  * SIM code adds a subtype, and 2 bytes of reserved = 3.
164  *
165  */
166  encoded_size += 3;
167  encodedmsg = talloc_array(ep, uint8_t, encoded_size);
168  if (!encodedmsg) {
169  return 0;
170  }
171  memset(encodedmsg, 0, encoded_size);
172 
173  /*
174  * now walk the attributes again, sticking them in.
175  *
176  * we go three bytes into the encoded message, because there are two
177  * bytes of reserved, and we will fill the "subtype" in later.
178  *
179  */
180  attr = encodedmsg+3;
181 
182  for (vp = fr_cursor_first(&cursor); vp; vp = fr_cursor_next(&cursor)) {
183  int roundedlen;
184 
185  if(vp->da->attr < PW_EAP_SIM_BASE ||
186  vp->da->attr >= PW_EAP_SIM_BASE + 256) {
187  continue;
188  }
189 
190  /*
191  * the AT_MAC attribute is a bit different, when we get to this
192  * attribute, we pull the contents out, save it for later
193  * processing, set the size to 16 bytes (plus 2 bytes padding).
194  *
195  * At this point, we put in zeros, and remember where the
196  * sixteen bytes go.
197  */
198  if(vp->da->attr == PW_EAP_SIM_MAC) {
199  roundedlen = 20;
200  memset(&attr[2], 0, 18);
201  macspace = &attr[4];
202  append = vp->vp_octets;
203  appendlen = vp->vp_length;
204  } else {
205  roundedlen = (vp->vp_length + 2 + 3) & ~3;
206  memset(attr, 0, roundedlen);
207  memcpy(&attr[2], vp->vp_strvalue, vp->vp_length);
208  }
209  attr[0] = vp->da->attr - PW_EAP_SIM_BASE;
210  attr[1] = roundedlen >> 2;
211 
212  attr += roundedlen;
213  }
214 
215  encodedmsg[0] = subtype;
216 
217  ep->type.length = encoded_size;
218  ep->type.data = encodedmsg;
219 
220  /*
221  * if macspace was set and we have a key,
222  * then we should calculate the HMAC-SHA1 of the resulting EAP-SIM
223  * packet, appended with the value of append.
224  */
225  vp = fr_pair_find_by_num(r->vps, 0, PW_EAP_SIM_KEY, TAG_ANY);
226  if(macspace != NULL && vp != NULL) {
227  unsigned char *buffer;
228  eap_packet_raw_t *hdr;
229  uint16_t hmaclen, total_length = 0;
230  unsigned char sha1digest[20];
231 
232  total_length = EAP_HEADER_LEN + 1 + encoded_size;
233  hmaclen = total_length + appendlen;
234  buffer = talloc_array(r, uint8_t, hmaclen);
235  hdr = (eap_packet_raw_t *) buffer;
236  if (!hdr) {
237  talloc_free(encodedmsg);
238  return 0;
239  }
240 
241  hdr->code = eapcode & 0xFF;
242  hdr->id = (id & 0xFF);
243  total_length = htons(total_length);
244  memcpy(hdr->length, &total_length, sizeof(total_length));
245 
246  hdr->data[0] = PW_EAP_SIM;
247 
248  /* copy the data */
249  memcpy(&hdr->data[1], encodedmsg, encoded_size);
250 
251  /* copy the nonce */
252  memcpy(&hdr->data[encoded_size+1], append, appendlen);
253 
254  /* HMAC it! */
255  fr_hmac_sha1(sha1digest, buffer, hmaclen, vp->vp_octets, vp->vp_length);
256 
257  /* done with the buffer, free it */
258  talloc_free(buffer);
259 
260  /* now copy the digest to where it belongs in the AT_MAC */
261  /* note that it is truncated to 128-bits */
262  memcpy(macspace, sha1digest, 16);
263  }
264 
265  /* if we had an AT_MAC and no key, then fail */
266  if ((macspace != NULL) && !vp) {
267  if (encodedmsg != NULL) {
268  talloc_free(encodedmsg);
269  }
270 
271  return 0;
272  }
273 
274  return 1;
275 }
276 
277 /*
278  * given a radius request with an EAP-SIM body, decode it into TLV pairs
279  *
280  * return value is true if it succeeded, false if there was something
281  * wrong and the packet should be discarded.
282  *
283  */
285  uint8_t *attr, unsigned int attrlen)
286 {
287  VALUE_PAIR *newvp;
288  int eapsim_attribute;
289  unsigned int eapsim_len;
290  int es_attribute_count;
291 
292  es_attribute_count = 0;
293 
294  /* big enough to have even a single attribute */
295  if (attrlen < 5) {
296  ERROR("eap: EAP-Sim attribute too short: %d < 5", attrlen);
297  return 0;
298  }
299 
300  newvp = fr_pair_afrom_num(r, 0, PW_EAP_SIM_SUBTYPE);
301  if (!newvp) {
302  return 0;
303  }
304 
305  newvp->vp_integer = attr[0];
306  newvp->vp_length = 1;
307  fr_pair_add(&(r->vps), newvp);
308 
309  attr += 3;
310  attrlen -= 3;
311 
312  /* now, loop processing each attribute that we find */
313  while (attrlen > 0) {
314  if (attrlen < 2) {
315  ERROR("eap: EAP-Sim attribute %d too short: %d < 2", es_attribute_count, attrlen);
316  return 0;
317  }
318 
319  eapsim_attribute = attr[0];
320  eapsim_len = attr[1] * 4;
321 
322  if (eapsim_len > attrlen) {
323  ERROR("eap: EAP-Sim attribute %d (no.%d) has length longer than data (%d > %d)",
324  eapsim_attribute, es_attribute_count, eapsim_len, attrlen);
325 
326  return 0;
327  }
328 
329  if (eapsim_len > MAX_STRING_LEN) {
330  eapsim_len = MAX_STRING_LEN;
331  }
332  if (eapsim_len < 2) {
333  ERROR("eap: EAP-Sim attribute %d (no.%d) has length too small", eapsim_attribute,
334  es_attribute_count);
335  return 0;
336  }
337 
338  newvp = fr_pair_afrom_num(r, 0, eapsim_attribute + PW_EAP_SIM_BASE);
339  fr_pair_value_memcpy(newvp, &attr[2], eapsim_len - 2);
340  fr_pair_add(&(r->vps), newvp);
341  newvp = NULL;
342 
343  /* advance pointers, decrement length */
344  attr += eapsim_len;
345  attrlen -= eapsim_len;
346  es_attribute_count++;
347  }
348  return 1;
349 }
350 
351 /*
352  * calculate the MAC for the EAP message, given the key.
353  * The "extra" will be appended to the EAP message and included in the
354  * HMAC.
355  *
356  */
357 int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_SIZE], uint8_t *extra, int extralen,
358  uint8_t calcmac[20])
359 {
360  int ret;
361  eap_packet_raw_t *e;
362  uint8_t *buffer;
363  int elen,len;
364  VALUE_PAIR *mac;
365 
366  mac = fr_pair_find_by_num(rvps, 0, PW_EAP_SIM_MAC, TAG_ANY);
367 
368  if(!mac || mac->vp_length != 18) {
369  /* can't check a packet with no AT_MAC attribute */
370  return 0;
371  }
372 
373  /* get original copy of EAP message, note that it was sanitized
374  * to have a valid length, which we depend upon.
375  */
376  e = eap_vp2packet(ctx, rvps);
377  if (!e) return 0;
378 
379  /* make copy big enough for everything */
380  elen = (e->length[0] * 256) + e->length[1];
381  len = elen + extralen;
382 
383  buffer = talloc_array(ctx, uint8_t, len);
384  if (!buffer) {
385  talloc_free(e);
386  return 0;
387  }
388 
389  memcpy(buffer, e, elen);
390  memcpy(buffer + elen, extra, extralen);
391 
392  /*
393  * now look for the AT_MAC attribute in the copy of the buffer
394  * and make sure that the checksum is zero.
395  *
396  */
397  {
398  uint8_t *attr;
399 
400  /* first attribute is 8 bytes into the EAP packet.
401  * 4 bytes for EAP, 1 for type, 1 for subtype, 2 reserved.
402  */
403  attr = buffer+8;
404  while(attr < (buffer+elen)) {
405  if (attr[0] == (PW_EAP_SIM_MAC - PW_EAP_SIM_BASE)) {
406  /* zero the data portion, after making sure
407  * the size is >=5. Maybe future versions.
408  * will use more bytes, so be liberal.
409  */
410  if(attr[1] < 5) {
411  ret = 0;
412  goto done;
413  }
414  memset(&attr[4], 0, (attr[1]-1)*4);
415  }
416  /* advance the pointer */
417  attr += attr[1]*4;
418  }
419  }
420 
421  /* now, HMAC-SHA1 it with the key. */
422  fr_hmac_sha1(calcmac, buffer, len, key, 16);
423 
424  ret = memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0 ? 1 : 0;
425  done:
426  talloc_free(e);
427  talloc_free(buffer);
428  return(ret);
429 }
430 
431 /*
432  * definitions changed to take a buffer for unknowns
433  * as this is more thread safe.
434  */
435 static char const *simstates[] = { "init", "start", NULL };
436 
438  char *statenamebuf,
439  int statenamebuflen)
440 {
441  if(state >= EAPSIM_CLIENT_MAXSTATES) {
442  snprintf(statenamebuf, statenamebuflen, "eapstate:%d", state);
443  return statenamebuf;
444  }
445 
446  return simstates[state];
447 }
448 
449 static char const *subtypes[] = { "subtype0", "subtype1", "subtype2", "subtype3",
450  "subtype4", "subtype5", "subtype6", "subtype7",
451  "subtype8", "subtype9",
452  "start",
453  "challenge",
454  "notification",
455  "reauth",
456  "client-error",
457  NULL };
458 
459 char const *sim_subtype2name(enum eapsim_subtype subtype, char *subtypenamebuf, int subtypenamebuflen)
460 {
461  if (subtype >= EAPSIM_MAX_SUBTYPE) {
462  snprintf(subtypenamebuf, subtypenamebuflen, "illegal-subtype:%d", subtype);
463 
464  return subtypenamebuf;
465  }
466 
467  return subtypes[subtype];
468 }
uint8_t id
Definition: eap_types.h:145
int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_SIZE], uint8_t *extra, int extralen, uint8_t calcmac[20])
Definition: eapsimlib.c:357
VALUE_PAIR * fr_cursor_first(vp_cursor_t *cursor)
Rewind cursor to the start of the list.
Definition: cursor.c:105
int unmap_eapsim_basictypes(RADIUS_PACKET *r, uint8_t *attr, unsigned int attrlen)
Definition: eapsimlib.c:284
VALUE_PAIR * fr_pair_afrom_num(TALLOC_CTX *ctx, unsigned int vendor, unsigned int attr)
Create a new valuepair.
Definition: pair.c:106
#define EAPSIM_AUTH_SIZE
Definition: eap_sim.h:84
eapsim_clientstates
Definition: eap_sim.h:42
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
uint8_t code
Definition: eap_types.h:144
eap_packet_raw_t * eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
Definition: eapcommon.c:297
#define EAP_HEADER_LEN
Definition: eap_types.h:35
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
eap_type_data_t type
Definition: eap_types.h:136
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
eap_type_t num
Definition: eap_types.h:122
uint8_t id
Definition: eap_types.h:134
Structure to represent packet format of eap on wire
Definition: eap_types.h:143
void fr_pair_add(VALUE_PAIR **head, VALUE_PAIR *vp)
Add a VP to the end of the list.
Definition: pair.c:659
unsigned int attr
Attribute number.
Definition: dict.h:79
static bool done
Definition: radclient.c:53
static char const * subtypes[]
Definition: eapsimlib.c:449
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
char const * sim_subtype2name(enum eapsim_subtype subtype, char *subtypenamebuf, int subtypenamebuflen)
Definition: eapsimlib.c:459
size_t length
Definition: eap_types.h:123
int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
Definition: eapsimlib.c:63
uint8_t length[2]
Definition: eap_types.h:146
uint8_t data[1]
Definition: eap_types.h:147
char const * sim_state2name(enum eapsim_clientstates state, char *statenamebuf, int statenamebuflen)
Definition: eapsimlib.c:437
unsigned int state
Definition: proto_bfd.c:200
#define TAG_ANY
Definition: pair.h:191
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
void fr_hmac_sha1(uint8_t digest[SHA1_DIGEST_LENGTH], uint8_t const *text, size_t text_len, uint8_t const *key, size_t key_len)
Calculate HMAC using SHA1.
Definition: hmacsha1.c:28
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
static char const * simstates[]
Definition: eapsimlib.c:435
Structure to hold EAP data.
Definition: eap_types.h:132
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
eapsim_subtype
Definition: eap_sim.h:33
#define MAX_STRING_LEN
Definition: libradius.h:120
#define RCSID(id)
Definition: build.h:135
static int r
Definition: rbmonkey.c:66
#define ERROR(fmt,...)
Definition: log.h:145
eap_code_t code
Definition: eap_types.h:133
void fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const *src, size_t len)
Copy data into an "octets" data type.
Definition: pair.c:1905
uint8_t * data
Definition: eap_types.h:124