90f0a5746c34bf77f282ecf4fadcbbc6c2f89521
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_fast / rlm_eap_fast.c
1 /*
2  * rlm_eap_fast.c  contains the interfaces that are called from eap
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2016 Alan DeKok <aland@freeradius.org>
21  * Copyright 2016 The FreeRADIUS server project
22  */
23
24 RCSID("$Id$")
25 USES_APPLE_DEPRECATED_API       /* OpenSSL API has been deprecated by Apple */
26
27
28 #include "eap_fast.h"
29 #include "eap_fast_crypto.h"
30
31
32 #include <freeradius-devel/md5.h>
33
34 /*
35  *      An instance of EAP-FAST
36  */
37 typedef struct rlm_eap_fast_t {
38         char const              *tls_conf_name;                         //!< Name of shared TLS config.
39         fr_tls_server_conf_t *tls_conf;
40
41         char const              *default_method_name;
42         int                     default_method;
43
44         char const              *virtual_server;                        //!< Virtual server to use for processing
45                                                                         //!< inner EAP method.
46         bool                    req_client_cert;                        //!< Whether we require a client cert
47                                                                         //!< in the outer tunnel.
48
49         int                     stage;                                  //!< Processing stage.
50
51         uint32_t pac_lifetime;                          //!< seconds to add to current time to describe PAC lifetime
52         char const              *authority_identity;                    //!< The identity we present in the EAP-TLS
53         uint8_t                 a_id[PAC_A_ID_LENGTH];                  //!< The identity we present in the EAP-TLS
54         char const              *pac_opaque_key;                        //!< The key used to encrypt PAC-Opaque
55         bool use_tunneled_reply;                //!< Use the reply attributes from the tunneled session in
56                                                 //!< the non-tunneled reply to the client.
57
58         bool copy_request_to_tunnel;            //!< Use SOME of the request attributes from outside of the
59 } rlm_eap_fast_t;
60
61
62 static CONF_PARSER module_config[] = {
63         { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_fast_t, tls_conf_name), NULL },
64
65         { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_fast_t, default_method_name), "mschapv2" },
66
67         { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_NOT_EMPTY, rlm_eap_fast_t, virtual_server) , NULL},
68
69         { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_fast_t, req_client_cert), "no" },
70
71         { "pac_lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_fast_t, pac_lifetime), "604800" },
72         { "authority_identity", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_eap_fast_t, authority_identity), NULL },
73         { "pac_opaque_key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_eap_fast_t, pac_opaque_key), NULL },
74         { "copy_request_to_tunnel", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_fast_t, copy_request_to_tunnel), "no" },
75
76         { "use_tunneled_reply", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_fast_t, use_tunneled_reply), "no" },
77
78         CONF_PARSER_TERMINATOR
79 };
80
81 /*
82  *      Attach the module.
83  */
84 static int mod_instantiate(CONF_SECTION *cs, void **instance)
85 {
86         rlm_eap_fast_t *inst;
87
88         *instance = inst = talloc_zero(cs, rlm_eap_fast_t);
89         if (!inst) return -1;
90
91         /*
92          *      Parse the configuration attributes.
93          */
94         if (cf_section_parse(cs, inst, module_config) < 0) {
95                 return -1;
96         }
97
98         if (!cf_section_sub_find_name2(main_config.config, "server", inst->virtual_server)) {
99                 ERROR("rlm_eap_fast.virtual_server: Unknown virtual server '%s'", inst->virtual_server);
100                 return -1;
101         }
102
103         inst->default_method = eap_name2type(inst->default_method_name);
104         if (!inst->default_method) {
105                 ERROR("rlm_eap_fast.default_provisioning_eap_type: "
106                           "Unknown EAP type %s",
107                                    inst->default_method_name);
108                 return -1;
109         }
110
111         /*
112          *      Read tls configuration, either from group given by 'tls'
113          *      option, or from the eap-tls configuration.
114          */
115         inst->tls_conf = eaptls_conf_parse(cs, "tls");
116
117         if (!inst->tls_conf) {
118                 ERROR("rlm_eap_fast.tls: Failed initializing SSL context");
119                 return -1;
120         }
121
122         if (talloc_array_length(inst->pac_opaque_key) - 1 != 32) {
123                 ERROR("rlm_eap_fast.pac_opaque_key: Must be 32 bytes long");
124                 return -1;
125         }
126
127         // FIXME TLSv1.2 uses a different PRF and SSL_export_keying_material("key expansion") is forbidden
128         if (!inst->tls_conf->disable_tlsv1_2) {
129                 ERROR("rlm_eap_fast.disable_tlsv1_2: require disable_tlsv1_2=yes");
130                 return -1;
131         }
132
133         if (!inst->pac_lifetime) {
134                 ERROR("rlm_eap_fast.pac_lifetime: must be non-zero");
135                 return -1;
136         }
137
138         rad_assert(PAC_A_ID_LENGTH == MD5_DIGEST_LENGTH);
139         FR_MD5_CTX ctx;
140         fr_md5_init(&ctx);
141         fr_md5_update(&ctx, inst->authority_identity, talloc_array_length(inst->authority_identity) - 1);
142         fr_md5_final(inst->a_id, &ctx);
143
144         return 0;
145 }
146
147 /** Allocate the FAST per-session data
148  *
149  */
150 static eap_fast_tunnel_t *eap_fast_alloc(TALLOC_CTX *ctx, rlm_eap_fast_t *inst)
151 {
152         eap_fast_tunnel_t *t = talloc_zero(ctx, eap_fast_tunnel_t);
153
154         t->mode = EAP_FAST_UNKNOWN;
155         t->stage = TLS_SESSION_HANDSHAKE;
156
157         t->default_method = inst->default_method;
158         t->copy_request_to_tunnel = inst->copy_request_to_tunnel;
159         t->use_tunneled_reply = inst->use_tunneled_reply;
160
161         t->pac_lifetime = inst->pac_lifetime;
162         t->authority_identity = inst->authority_identity;
163         t->a_id = inst->a_id;
164         t->pac_opaque_key = (const uint8_t *)inst->pac_opaque_key;
165
166         t->virtual_server = inst->virtual_server;
167
168         return t;
169 }
170
171 static void eap_fast_session_ticket(tls_session_t *tls_session, uint8_t *client_random,
172                                         uint8_t *server_random, uint8_t *secret, int *secret_len)
173 {
174         eap_fast_tunnel_t       *t = (eap_fast_tunnel_t *) tls_session->opaque;
175         uint8_t                 seed[2 * SSL3_RANDOM_SIZE];
176
177         rad_assert(t->pac.key);
178
179         memcpy(seed, server_random, SSL3_RANDOM_SIZE);
180         memcpy(&seed[SSL3_RANDOM_SIZE], client_random, SSL3_RANDOM_SIZE);
181
182         T_PRF(t->pac.key, PAC_KEY_LENGTH, "PAC to master secret label hash",
183                   seed, sizeof(seed), secret, SSL_MAX_MASTER_KEY_LENGTH);
184         *secret_len = SSL_MAX_MASTER_KEY_LENGTH;
185 }
186
187 // hostap:src/crypto/tls_openssl.c:tls_sess_sec_cb()
188 static int _session_secret(SSL *s, void *secret, int *secret_len,
189                            UNUSED STACK_OF(SSL_CIPHER) *peer_ciphers,
190                            UNUSED SSL_CIPHER **cipher, void *arg)
191 {
192         // FIXME enforce non-anon cipher
193
194         REQUEST         *request = (REQUEST *)SSL_get_ex_data(s, FR_TLS_EX_INDEX_REQUEST);
195         tls_session_t   *tls_session = arg;
196         eap_fast_tunnel_t       *t;
197
198         if (!tls_session) return 0;
199
200         t = (eap_fast_tunnel_t *) tls_session->opaque;
201
202         if (!t->pac.key) return 0;
203
204         RDEBUG("processing PAC-Opaque");
205
206 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
207         eap_fast_session_ticket(tls_session, s->s3->client_random, s->s3->server_random, secret, secret_len);
208 #else
209         uint8_t const client_random[SSL3_RANDOM_SIZE];
210         uint8_t const server_random[SSL3_RANDOM_SIZE];
211
212         SSL_get_client_random(s, client_random, sizeof(client_random));
213         SSL_get_server_random(s, server_random, sizeof(server_random));
214
215         eap_fast_session_ticket(tls_session, client_random, server_random, secret, secret_len);
216 #endif
217
218         memset(t->pac.key, 0, PAC_KEY_LENGTH);
219         talloc_free(t->pac.key);
220         t->pac.key = NULL;
221
222         return 1;
223 }
224
225 /*
226  * hints from hostap:src/crypto/tls_openssl.c:tls_session_ticket_ext_cb()
227  *
228  * N.B. we actually always tell OpenSSL we have digested the ticket so that
229  *      it does not cause a fail loop and enables us to update the PAC easily
230  *
231  */
232 static int _session_ticket(SSL *s, uint8_t const *data, int len, void *arg)
233 {
234         tls_session_t           *tls_session = arg;
235         REQUEST                 *request = (REQUEST *)SSL_get_ex_data(s, FR_TLS_EX_INDEX_REQUEST);
236         eap_fast_tunnel_t       *t;
237         VALUE_PAIR              *fast_vps;
238         vp_cursor_t             cursor;
239         DICT_ATTR const *fast_da;
240         char const              *errmsg;
241         int                     dlen, plen;
242         uint16_t                length;
243         eap_fast_attr_pac_opaque_t const        *opaque = (eap_fast_attr_pac_opaque_t const *) data;
244         eap_fast_attr_pac_opaque_t              opaque_plaintext;
245
246         if (!tls_session) return 0;
247
248         t = (eap_fast_tunnel_t *) tls_session->opaque;
249
250         RDEBUG("PAC provided via ClientHello SessionTicket extension");
251
252         if ((ntohs(opaque->hdr.type) & EAP_FAST_TLV_TYPE) != PAC_INFO_PAC_OPAQUE) {
253                 errmsg = "PAC is not of type Opaque";
254 error:
255                 RERROR("%s, sending alert to client", errmsg);
256                 /*
257                 if (tls_session_handshake_alert(request, tls_session, SSL3_AL_FATAL, SSL_AD_BAD_CERTIFICATE)) {
258                         RERROR("too many alerts");
259                         return 0;
260                 }
261                 */
262                 if (t->pac.key) talloc_free(t->pac.key);
263
264                 memset(&t->pac, 0, sizeof(t->pac));
265                 return 1;
266         }
267
268         /*
269          * we would like to use the length of the SessionTicket
270          * but Cisco hates everyone and sends a zero padding payload
271          * so we have to use the length in the PAC-Opaque header
272          */
273         length = ntohs(opaque->hdr.length);
274         if (len - sizeof(opaque->hdr) < length) {
275                 errmsg = "PAC has bad length in header";
276                 goto error;
277         }
278
279         if (length < PAC_A_ID_LENGTH + EVP_MAX_IV_LENGTH + EVP_GCM_TLS_TAG_LEN + 1) {
280                 errmsg = "PAC file too short";
281                 goto error;
282         }
283
284         if (memcmp(opaque->aad, t->a_id, PAC_A_ID_LENGTH)) {
285                 errmsg = "PAC has incorrect A_ID";
286                 goto error;
287         }
288
289         dlen = length - sizeof(opaque->aad) - sizeof(opaque->iv) - sizeof(opaque->tag);
290         plen = eap_fast_decrypt(opaque->data, dlen, opaque->aad, PAC_A_ID_LENGTH,
291                                         (uint8_t const *) opaque->tag, t->pac_opaque_key, opaque->iv,
292                                         (uint8_t *)&opaque_plaintext);
293         if (plen == -1) {
294                 errmsg = "PAC failed to decrypt";
295                 goto error;
296         }
297
298         fast_da = dict_attrbyname("EAP-FAST-PAC-Opaque-TLV");
299         rad_assert(fast_da != NULL);
300
301         fast_vps = eap_fast_fast2vp((REQUEST *)tls_session, s, (uint8_t *)&opaque_plaintext, plen, fast_da, NULL);
302         if (!fast_vps) return 0;
303
304         for (VALUE_PAIR *vp = fr_cursor_init(&cursor, &fast_vps); vp; vp = fr_cursor_next(&cursor)) {
305                 char *value;
306
307                 switch (vp->da->attr) {
308                 case PAC_INFO_PAC_TYPE:
309                         rad_assert(t->pac.type == 0);
310                         t->pac.type = vp->vp_integer;
311                         break;
312                 case PAC_INFO_PAC_LIFETIME:
313                         rad_assert(t->pac.expires == 0);
314                         t->pac.expires = vp->vp_integer;
315                         t->pac.expired = (vp->vp_integer <= time(NULL));
316                         break;
317                 case PAC_INFO_PAC_KEY:
318                         rad_assert(t->pac.key == NULL);
319                         rad_assert(vp->vp_length == PAC_KEY_LENGTH);
320                         t->pac.key = talloc_size(t, PAC_KEY_LENGTH);
321                         rad_assert(t->pac.key != NULL);
322                         memcpy(t->pac.key, vp->vp_octets, PAC_KEY_LENGTH);
323                         break;
324                 default:
325                         value = vp_aprints_value(tls_session, vp, '"');
326                         RERROR("unknown TLV: %s", value);
327                         talloc_free(value);
328                         errmsg = "unknown TLV";
329                         goto error;
330                 }
331         }
332
333         if (!t->pac.type) {
334                 errmsg = "PAC missing type TLV";
335                 goto error;
336         }
337
338         if (t->pac.type != PAC_TYPE_TUNNEL) {
339                 errmsg = "PAC is of not of tunnel type";
340                 goto error;
341         }
342
343         if (!t->pac.expires) {
344                 errmsg = "PAC missing lifetime TLV";
345                 goto error;
346         }
347
348         if (!t->pac.key) {
349                 errmsg = "PAC missing key TLV";
350                 goto error;
351         }
352
353         if (!SSL_set_session_secret_cb(tls_session->ssl, _session_secret, tls_session)) {
354                 RERROR("Failed setting SSL session secret callback");
355                 return 0;
356         }
357
358         return 1;
359 }
360
361
362 /*
363  *      Do authentication, by letting EAP-TLS do most of the work.
364  */
365 static int mod_process(void *arg, eap_handler_t *handler)
366 {
367         int rcode;
368         fr_tls_status_t status;
369         rlm_eap_fast_t *inst = (rlm_eap_fast_t *) arg;
370         tls_session_t *tls_session = (tls_session_t *) handler->opaque;
371         eap_fast_tunnel_t *t = (eap_fast_tunnel_t *) tls_session->opaque;
372         REQUEST *request = handler->request;
373
374         RDEBUG2("Authenticate");
375
376         /*
377          *      We need FAST data associated with the session, so
378          *      allocate it here, if it wasn't already alloacted.
379          */
380         if (!t) t = tls_session->opaque = eap_fast_alloc(tls_session, inst);
381
382         /*
383          *      Process TLS layer until done.
384          */
385         status = eaptls_process(handler);
386         if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
387                 REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
388         } else {
389                 RDEBUG2("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
390         }
391
392         switch (status) {
393         /*
394          *      EAP-TLS handshake was successful, tell the
395          *      client to keep talking.
396          *
397          *      If this was EAP-TLS, we would just return
398          *      an EAP-TLS-Success packet here.
399          */
400         case FR_TLS_SUCCESS:
401                 tls_handshake_send(request, tls_session);
402                 rad_assert(t != NULL);
403                 break;
404
405         /*
406          *      The TLS code is still working on the TLS
407          *      exchange, and it's a valid TLS request.
408          *      do nothing.
409          */
410         case FR_TLS_HANDLED:
411                 return 1;
412
413         /*
414          *      Handshake is done, proceed with decoding tunneled
415          *      data.
416          */
417         case FR_TLS_OK:
418                 break;
419
420         /*
421          *      Anything else: fail.
422          */
423         default:
424                 return 0;
425         }
426
427         /*
428          *      Session is established, proceed with decoding
429          *      tunneled data.
430          */
431         RDEBUG2("Session established.  Proceeding to decode tunneled attributes");
432
433         /*
434          *      Process the FAST portion of the request.
435          */
436         rcode = eap_fast_process(handler, tls_session);
437
438         switch (rcode) {
439         case PW_CODE_ACCESS_REJECT:
440                 RDEBUG("Reject");
441                 eaptls_fail(handler, 0);
442                 return 0;
443
444                 /*
445                  *      Access-Challenge, continue tunneled conversation.
446                  */
447         case PW_CODE_ACCESS_CHALLENGE:
448                 RDEBUG("Challenge");
449                 tls_handshake_send(request, tls_session);
450                 eaptls_request(handler->eap_ds, tls_session);
451                 return 1;
452
453                 /*
454                  *      Success: Automatically return MPPE keys.
455                  */
456         case PW_CODE_ACCESS_ACCEPT:
457                 RDEBUG("Note the missing PRF label warning below is harmless, ignore it");
458                 if (t->accept_vps) {
459                         RDEBUG2("Using saved attributes from the original Access-Accept");
460                         rdebug_pair_list(L_DBG_LVL_2, request, t->accept_vps, NULL);
461                         fr_pair_list_mcopy_by_num(handler->request->reply,
462                                   &handler->request->reply->vps,
463                                   &t->accept_vps, 0, 0, TAG_ANY);
464                 } else if (t->use_tunneled_reply) {
465                         RDEBUG2("No saved attributes in the original Access-Accept");
466                 }
467                 return eaptls_success(handler, 0);
468
469                 /*
470                  *      No response packet, MUST be proxying it.
471                  *      The main EAP module will take care of discovering
472                  *      that the request now has a "proxy" packet, and
473                  *      will proxy it, rather than returning an EAP packet.
474                  */
475         case PW_CODE_STATUS_CLIENT:
476 #ifdef WITH_PROXY
477                 rad_assert(handler->request->proxy != NULL);
478 #endif
479                 return 1;
480
481         default:
482                 break;
483         }
484
485         /*
486          *      Something we don't understand: Reject it.
487          */
488         eaptls_fail(handler, 0);
489         return 0;
490 }
491
492 static int eap_fast_tls_start(EAP_DS * eap_ds,tls_session_t *tls_session)
493 {
494         EAPTLS_PACKET   reply;
495
496         reply.code = FR_TLS_START;
497         reply.length = TLS_HEADER_LEN + 1 + tls_session->clean_in.used;/*flags*/
498
499         reply.flags = tls_session->peap_flag;
500         reply.flags = SET_START(reply.flags);
501
502         reply.data = tls_session->clean_in.data;
503         reply.dlen = tls_session->clean_in.used;
504
505         eaptls_compose(eap_ds, &reply);
506
507         return 1;
508 }
509
510
511 /*
512  *      Send an initial eap-tls request to the peer, using the libeap functions.
513  */
514 static int mod_session_init(void *type_arg, eap_handler_t *handler)
515 {
516         int                     rcode;
517         tls_session_t           *tls_session;
518         rlm_eap_fast_t          *inst;
519         VALUE_PAIR              *vp;
520         bool                    client_cert;
521         REQUEST                 *request = handler->request;
522
523         inst = type_arg;
524
525         handler->tls = true;
526
527         /*
528          *      EAP-TLS-Require-Client-Cert attribute will override
529          *      the require_client_cert configuration option.
530          */
531         vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
532         if (vp) {
533                 client_cert = vp->vp_integer ? true : false;
534         } else {
535                 client_cert = inst->req_client_cert;
536         }
537         handler->opaque = tls_session = eaptls_session(handler, inst->tls_conf, client_cert);
538
539         if (!tls_session) return 0;
540
541         /*
542          *      Push TLV of authority_identity into tls_record
543          *      call eap_tls_compose() with args
544          *
545          *      RFC 4851 section 4.1.1
546          *      N.B. mandatory/reserved flags are not applicable here
547          */
548         eap_fast_tlv_append(tls_session, PAC_INFO_A_ID, false, PAC_A_ID_LENGTH, inst->a_id);
549         tls_session->peap_flag = EAP_FAST_VERSION;
550         tls_session->length_flag = false;
551         rcode = eap_fast_tls_start(handler->eap_ds, tls_session);
552
553         if (rcode < 0) {
554                 talloc_free(tls_session);
555                 return 0;
556         }
557
558         tls_session->record_init(&tls_session->clean_in);
559
560         if (!SSL_set_session_ticket_ext_cb(tls_session->ssl, _session_ticket, tls_session)) {
561                 RERROR("Failed setting SSL session ticket callback");
562                 return 0;
563         }
564
565         handler->stage = PROCESS;
566
567         return 1;
568 }
569
570
571 /*
572  *      The module name should be the only globally exported symbol.
573  *      That is, everything else should be 'static'.
574  */
575 extern rlm_eap_module_t rlm_eap_fast;
576 rlm_eap_module_t rlm_eap_fast = {
577         .name           = "eap_fast",
578         .instantiate    = mod_instantiate,      /* Create new submodule instance */
579         .session_init   = mod_session_init,     /* Initialise a new EAP session */
580         .process        = mod_process           /* Process next round of EAP method */
581 };