]> git.ozlabs.org Git - ppp.git/blob - pppd/peap.c
pppd man page: Update header to refer to pppd 2.5.x
[ppp.git] / pppd / peap.c
1 /*
2  * Copyright (c) 2011 Rustam Kovhaev. All rights reserved.
3  * Copyright (c) 2021 Eivind Næss. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name(s) of the authors of this software must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission.
20  *
21  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
22  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
23  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
24  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
26  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
27  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28  *
29  * NOTES:
30  *
31  * PEAP has 2 phases,
32  * 1 - Outer EAP, where TLS session gets established
33  * 2 - Inner EAP, where inside TLS session with EAP MSCHAPV2 auth, or any other auth
34  *
35  * And so protocols encapsulation looks like this:
36  * Outer EAP -> TLS -> Inner EAP -> MSCHAPV2
37  * PEAP can compress an inner EAP packet prior to encapsulating it within
38  * the Data field of a PEAP packet by removing its Code, Identifier,
39  * and Length fields, and Microsoft PEAP server/client always does that
40  *
41  * Current implementation does not support:
42  * a) Fast reconnect
43  * b) Inner EAP fragmentation
44  * c) Any other auth other than MSCHAPV2
45  *
46  * For details on the PEAP protocol, look to Microsoft:
47  *    https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-peap
48  */
49
50 #ifdef HAVE_CONFIG_H
51 #include "config.h"
52 #endif
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <openssl/opensslv.h>
59 #include <openssl/ssl.h>
60 #include <openssl/hmac.h>
61 #include <openssl/rand.h>
62 #include <openssl/err.h>
63
64 #include "pppd-private.h"
65 #include "eap.h"
66 #include "tls.h"
67 #include "chap.h"
68 #include "chap_ms.h"
69 #include "mppe.h"
70 #include "peap.h"
71
72 #ifdef UNIT_TEST
73 #define novm(x)
74 #endif
75
76 struct peap_state {
77         SSL_CTX *ctx;
78         SSL *ssl;
79         BIO *in_bio;
80         BIO *out_bio;
81
82         int phase;
83         int written, read;
84         u_char *in_buf;
85         u_char *out_buf;
86
87         u_char ipmk[PEAP_TLV_IPMK_LEN];
88         u_char tk[PEAP_TLV_TK_LEN];
89         u_char nonce[PEAP_TLV_NONCE_LEN];
90         struct tls_info *info;
91 #ifdef PPP_WITH_CHAPMS
92         struct chap_digest_type *chap;
93 #endif
94 };
95
96 /*
97  * K = Key, S = Seed, LEN = output length
98  * PRF+(K, S, LEN) = T1 | T2 | ... |Tn
99  * Where:
100  * T1 = HMAC-SHA1 (K, S | 0x01 | 0x00 | 0x00)
101  * T2 = HMAC-SHA1 (K, T1 | S | 0x02 | 0x00 | 0x00)
102  * ...
103  * Tn = HMAC-SHA1 (K, Tn-1 | S | n | 0x00 | 0x00)
104  * As shown, PRF+ is computed in iterations. The number of iterations (n)
105  * depends on the output length (LEN).
106  */
107 static void peap_prfplus(u_char *seed, size_t seed_len, u_char *key, size_t key_len, u_char *out_buf, size_t pfr_len)
108 {
109         int pos;
110         u_char *buf, *hash;
111         size_t max_iter, i, j, k;
112         u_int len;
113
114         max_iter = (pfr_len + SHA_DIGEST_LENGTH - 1) / SHA_DIGEST_LENGTH;
115         buf = malloc(seed_len + max_iter * SHA_DIGEST_LENGTH);
116         if (!buf)
117                 novm("pfr buffer");
118         hash = malloc(pfr_len + SHA_DIGEST_LENGTH);
119         if (!hash)
120                 novm("hash buffer");
121
122         for (i = 0; i < max_iter; i++) {
123                 j = 0;
124                 k = 0;
125
126                 if (i > 0)
127                         j = SHA_DIGEST_LENGTH;
128                 for (k = 0; k < seed_len; k++)
129                         buf[j + k] = seed[k];
130                 pos = j + k;
131                 buf[pos] = i + 1;
132                 pos++;
133                 buf[pos] = 0x00;
134                 pos++;
135                 buf[pos] = 0x00;
136                 pos++;
137                 if (!HMAC(EVP_sha1(), key, key_len, buf, pos, (hash + i * SHA_DIGEST_LENGTH), &len))
138                         fatal("HMAC() failed");
139                 for (j = 0; j < SHA_DIGEST_LENGTH; j++)
140                         buf[j] = hash[i * SHA_DIGEST_LENGTH + j];
141         }
142         BCOPY(hash, out_buf, pfr_len);
143         free(hash);
144         free(buf);
145 }
146
147 static void generate_cmk(u_char *ipmk, u_char *tempkey, u_char *nonce, u_char *tlv_response_out, int client)
148 {
149         const char *label = PEAP_TLV_IPMK_SEED_LABEL;
150         u_char data_tlv[PEAP_TLV_DATA_LEN] = {0};
151         u_char isk[PEAP_TLV_ISK_LEN] = {0};
152         u_char ipmkseed[PEAP_TLV_IPMKSEED_LEN] = {0};
153         u_char cmk[PEAP_TLV_CMK_LEN] = {0};
154         u_char buf[PEAP_TLV_CMK_LEN + PEAP_TLV_IPMK_LEN] = {0};
155         u_char compound_mac[PEAP_TLV_COMP_MAC_LEN] = {0};
156         u_int len;
157
158         /* format outgoing CB TLV response packet */
159         data_tlv[1] = PEAP_TLV_TYPE;
160         data_tlv[3] = PEAP_TLV_LENGTH_FIELD;
161         if (client)
162                 data_tlv[7] = PEAP_TLV_SUBTYPE_RESPONSE;
163         else
164                 data_tlv[7] = PEAP_TLV_SUBTYPE_REQUEST;
165         BCOPY(nonce, (data_tlv + PEAP_TLV_HEADERLEN), PEAP_TLV_NONCE_LEN);
166         data_tlv[60] = EAPT_PEAP;
167
168 #ifdef PPP_WITH_MPPE
169         mppe_get_send_key(isk, MPPE_MAX_KEY_LEN);
170         mppe_get_recv_key(isk + MPPE_MAX_KEY_LEN, MPPE_MAX_KEY_LEN);
171 #endif
172
173         BCOPY(label, ipmkseed, strlen(label));
174         BCOPY(isk, ipmkseed + strlen(label), PEAP_TLV_ISK_LEN);
175         peap_prfplus(ipmkseed, PEAP_TLV_IPMKSEED_LEN,
176                         tempkey, PEAP_TLV_TEMPKEY_LEN, buf, PEAP_TLV_CMK_LEN + PEAP_TLV_IPMK_LEN);
177
178         BCOPY(buf, ipmk, PEAP_TLV_IPMK_LEN);
179         BCOPY(buf + PEAP_TLV_IPMK_LEN, cmk, PEAP_TLV_CMK_LEN);
180         if (!HMAC(EVP_sha1(), cmk, PEAP_TLV_CMK_LEN, data_tlv, PEAP_TLV_DATA_LEN, compound_mac, &len))
181                 fatal("HMAC() failed");
182         BCOPY(compound_mac, data_tlv + PEAP_TLV_HEADERLEN + PEAP_TLV_NONCE_LEN, PEAP_TLV_COMP_MAC_LEN);
183         /* do not copy last byte to response packet */
184         BCOPY(data_tlv, tlv_response_out, PEAP_TLV_DATA_LEN - 1);
185 }
186
187 static void verify_compound_mac(struct peap_state *psm, u_char *in_buf)
188 {
189         u_char nonce[PEAP_TLV_NONCE_LEN] = {0};
190         u_char out_buf[PEAP_TLV_LEN] = {0};
191
192         BCOPY(in_buf, nonce, PEAP_TLV_NONCE_LEN);
193         generate_cmk(psm->ipmk, psm->tk, nonce, out_buf, 0);
194         if (memcmp((in_buf + PEAP_TLV_NONCE_LEN), (out_buf + PEAP_TLV_HEADERLEN + PEAP_TLV_NONCE_LEN), PEAP_TLV_CMK_LEN))
195                         fatal("server's CMK does not match client's CMK, potential MiTM");
196 }
197
198 #ifdef PPP_WITH_MPPE
199 #define PEAP_MPPE_KEY_LEN 32
200
201 static void generate_mppe_keys(u_char *ipmk, int client)
202 {
203         const char *label = PEAP_TLV_CSK_SEED_LABEL;
204         u_char csk[PEAP_TLV_CSK_LEN] = {0};
205         size_t len;
206
207         dbglog("PEAP CB: generate mppe keys");
208         len = strlen(label);
209         len++; /* CSK requires NULL byte in seed */
210         peap_prfplus((u_char *)label, len, ipmk, PEAP_TLV_IPMK_LEN, csk, PEAP_TLV_CSK_LEN);
211
212         /*
213          * The first 64 bytes of the CSK are split into two MPPE keys, as follows.
214          *
215          * +-----------------------+------------------------+
216          * | First 32 bytes of CSK | Second 32 bytes of CSK |
217          * +-----------------------+------------------------+
218          * | MS-MPPE-Send-Key      | MS-MPPE-Recv-Key       |
219          * +-----------------------+------------------------+
220          */
221         if (client) {
222                 mppe_set_keys(csk, csk + PEAP_MPPE_KEY_LEN, PEAP_MPPE_KEY_LEN);
223         } else {
224                 mppe_set_keys(csk + PEAP_MPPE_KEY_LEN, csk, PEAP_MPPE_KEY_LEN);
225         }
226 }
227
228 #endif
229
230 #ifndef UNIT_TEST
231
232 static void peap_ack(eap_state *esp, u_char id)
233 {
234         u_char *outp;
235
236         outp = outpacket_buf;
237         MAKEHEADER(outp, PPP_EAP);
238         PUTCHAR(EAP_RESPONSE, outp);
239         PUTCHAR(id, outp);
240         esp->es_client.ea_id = id;
241         PUTSHORT(PEAP_HEADERLEN, outp);
242         PUTCHAR(EAPT_PEAP, outp);
243         PUTCHAR(PEAP_FLAGS_ACK, outp);
244         output(esp->es_unit, outpacket_buf, PPP_HDRLEN + PEAP_HEADERLEN);
245 }
246
247 static void peap_response(eap_state *esp, u_char id, u_char *buf, int len)
248 {
249         struct peap_state *psm = esp->ea_peap;
250         u_char *outp;
251         int peap_len;
252
253         outp = outpacket_buf;
254         MAKEHEADER(outp, PPP_EAP);
255         PUTCHAR(EAP_RESPONSE, outp);
256         PUTCHAR(id, outp);
257         esp->es_client.ea_id = id;
258
259         if (psm->phase == PEAP_PHASE_1)
260                 peap_len = PEAP_HEADERLEN + PEAP_FRAGMENT_LENGTH_FIELD + len;
261         else
262                 peap_len = PEAP_HEADERLEN + len;
263
264         PUTSHORT(peap_len, outp);
265         PUTCHAR(EAPT_PEAP, outp);
266
267         if (psm->phase == PEAP_PHASE_1) {
268                 PUTCHAR(PEAP_L_FLAG_SET, outp);
269                 PUTLONG(len, outp);
270         } else
271                 PUTCHAR(PEAP_NO_FLAGS, outp);
272
273         BCOPY(buf, outp, len);
274         output(esp->es_unit, outpacket_buf, PPP_HDRLEN + peap_len);
275 }
276
277 void peap_do_inner_eap(u_char *in_buf, int in_len, eap_state *esp, int id,
278                 u_char *out_buf, int *out_len)
279 {
280         struct peap_state *psm = esp->ea_peap;
281         int used = 0;
282         int typenum;
283         int secret_len;
284         char secret[MAXSECRETLEN + 1];
285         char rhostname[MAXWORDLEN];
286         u_char *outp = out_buf;
287
288         dbglog("PEAP: EAP (in): %.*B", in_len, in_buf);
289
290         if (*(in_buf + EAP_HEADERLEN) == PEAP_CAPABILITIES_TYPE &&
291                         in_len  == (EAP_HEADERLEN + PEAP_CAPABILITIES_LEN)) {
292                 /* use original packet as template for response */
293                 BCOPY(in_buf, outp, EAP_HEADERLEN + PEAP_CAPABILITIES_LEN);
294                 PUTCHAR(EAP_RESPONSE, outp);
295                 PUTCHAR(id, outp);
296                 /* change last byte to 0 to disable fragmentation */
297                 *(outp + PEAP_CAPABILITIES_LEN + 1) = 0x00;
298                 used = EAP_HEADERLEN + PEAP_CAPABILITIES_LEN;
299                 goto done;
300         }
301         if (*(in_buf + EAP_HEADERLEN + PEAP_TLV_HEADERLEN) == PEAP_TLV_TYPE &&
302                         in_len == PEAP_TLV_LEN) {
303                 /* PEAP TLV message, do cryptobinding */
304                 SSL_export_keying_material(psm->ssl, psm->tk, PEAP_TLV_TK_LEN,
305                                 PEAP_TLV_TK_SEED_LABEL, strlen(PEAP_TLV_TK_SEED_LABEL), NULL, 0, 0);
306                 /* verify server's CMK */
307                 verify_compound_mac(psm, in_buf + EAP_HEADERLEN + PEAP_TLV_RESULT_LEN + PEAP_TLV_HEADERLEN);
308                 /* generate client's CMK with new nonce */
309                 PUTCHAR(EAP_RESPONSE, outp);
310                 PUTCHAR(id, outp);
311                 PUTSHORT(PEAP_TLV_LEN, outp);
312                 BCOPY(in_buf + EAP_HEADERLEN, outp, PEAP_TLV_RESULT_LEN);
313                 outp = outp + PEAP_TLV_RESULT_LEN;
314                 RAND_bytes(psm->nonce, PEAP_TLV_NONCE_LEN);
315                 generate_cmk(psm->ipmk, psm->tk, psm->nonce, outp, 1);
316 #ifdef PPP_WITH_MPPE
317                 /* set mppe keys */
318                 generate_mppe_keys(psm->ipmk, 1);
319 #endif
320                 used = PEAP_TLV_LEN;
321                 goto done;
322         }
323
324         GETCHAR(typenum, in_buf);
325         in_len--;
326
327         switch (typenum) {
328         case EAPT_IDENTITY:
329                 /* Respond with our identity to the peer */
330                 PUTCHAR(EAPT_IDENTITY, outp);
331                 BCOPY(esp->es_client.ea_name, outp,
332                                 esp->es_client.ea_namelen);
333                 used += (esp->es_client.ea_namelen + 1);
334                 break;
335
336         case EAPT_TLS:
337                 /* Send NAK to EAP_TLS request */
338                 PUTCHAR(EAPT_NAK, outp);
339                 PUTCHAR(EAPT_MSCHAPV2, outp);
340                 used += 2;
341                 break;
342
343 #if PPP_WITH_CHAPMS
344         case EAPT_MSCHAPV2: {
345
346                 // Must have at least 4 more bytes to process CHAP header
347                 if (in_len < 4) {
348                         error("PEAP: received invalid MSCHAPv2 packet, too short");
349                         break;
350                 }
351
352                 u_char opcode;
353                 GETCHAR(opcode, in_buf);
354
355                 u_char chap_id;
356                 GETCHAR(chap_id, in_buf);
357
358                 short mssize;
359                 GETSHORT(mssize, in_buf);
360
361                 // Validate the CHAP packet (including header)
362                 if (in_len != mssize) {
363                         error("PEAP: received invalid MSCHAPv2 packet, invalid length");
364                         break;
365                 }
366                 in_len -= 4;
367
368                 switch (opcode) {
369                 case CHAP_CHALLENGE: {
370
371                         u_char *challenge = in_buf;     // VLEN + VALUE
372                         u_char vsize;
373
374                         GETCHAR(vsize, in_buf);
375                         in_len -= 1;
376
377                         if (vsize != MS_CHAP2_PEER_CHAL_LEN || in_len < MS_CHAP2_PEER_CHAL_LEN) {
378                                 error("PEAP: received invalid MSCHAPv2 packet, invalid value-length: %d", vsize);
379                                 goto done;
380                         }
381
382                         INCPTR(MS_CHAP2_PEER_CHAL_LEN, in_buf);
383                         in_len -= MS_CHAP2_PEER_CHAL_LEN;
384
385                         // Copy the provided remote host name
386                         rhostname[0] = '\0';
387                         if (in_len > 0) {
388                                 if (in_len >= sizeof(rhostname)) {
389                                         dbglog("PEAP: trimming really long peer name down");
390                                         in_len = sizeof(rhostname) - 1;
391                                 }
392                                 BCOPY(in_buf, rhostname, in_len);
393                                 rhostname[in_len] = '\0';
394                         }
395
396                         // In case the remote doesn't give us his name, or user explictly specified remotename is config
397                         if (explicit_remote || (remote_name[0] != '\0' && in_len == 0))
398                                 strlcpy(rhostname, remote_name, sizeof(rhostname));
399
400                         // Get the scrert for authenticating ourselves with the specified host
401                         if (get_secret(esp->es_unit, esp->es_client.ea_name,
402                                                 rhostname, secret, &secret_len, 0)) {
403
404                                 u_char response[MS_CHAP2_RESPONSE_LEN+1];
405                                 u_char user_len = esp->es_client.ea_namelen;
406                                 char *user = esp->es_client.ea_name;
407
408                                 psm->chap->make_response(response, chap_id, user,
409                                                 challenge, secret, secret_len, NULL);
410
411                                 PUTCHAR(EAPT_MSCHAPV2, outp);
412                                 PUTCHAR(CHAP_RESPONSE, outp);
413                                 PUTCHAR(chap_id, outp);
414                                 PUTCHAR(0, outp);
415                                 PUTCHAR(5 + user_len + MS_CHAP2_RESPONSE_LEN, outp);
416                                 BCOPY(response, outp, MS_CHAP2_RESPONSE_LEN+1); // VLEN + VALUE
417                                 INCPTR(MS_CHAP2_RESPONSE_LEN+1, outp);
418                                 BCOPY(user, outp, user_len);
419                                 used = 5 + user_len + MS_CHAP2_RESPONSE_LEN + 1;
420
421                         } else {
422                                 dbglog("PEAP: no CHAP secret for auth to %q", rhostname);
423                                 PUTCHAR(EAPT_NAK, outp);
424                                 ++used;
425                         }
426                         break;
427                 }
428                 case CHAP_SUCCESS: {
429
430                         u_char status = CHAP_FAILURE;
431                         if (psm->chap->check_success(chap_id, in_buf, in_len)) {
432                                 info("Chap authentication succeeded! %.*v", in_len, in_buf);
433                                 status = CHAP_SUCCESS;
434                         }
435
436                         PUTCHAR(EAPT_MSCHAPV2, outp);
437                         PUTCHAR(status, outp);
438                         used += 2;
439                         break;
440                 }
441                 case CHAP_FAILURE: {
442
443                         u_char status = CHAP_FAILURE;
444                         psm->chap->handle_failure(in_buf, in_len);
445                         PUTCHAR(EAPT_MSCHAPV2, outp);
446                         PUTCHAR(status, outp);
447                         used += 2;
448                         break;
449                 }
450                 default:
451                         break;
452                 }
453                 break;
454         }       // EAPT_MSCHAPv2
455 #endif
456         default:
457
458                 /* send compressed EAP NAK for any unknown packet */
459                 PUTCHAR(EAPT_NAK, outp);
460                 ++used;
461         }
462
463 done:
464
465         dbglog("PEAP: EAP (out): %.*B", used, psm->out_buf);
466         *out_len = used;
467 }
468
469 int peap_init(struct peap_state **ctx, const char *rhostname)
470 {
471         const SSL_METHOD *method;
472
473         if (!ctx)
474                 return -1;
475
476         tls_init();
477
478         struct peap_state *psm = malloc(sizeof(*psm));
479         if (!psm)
480                 novm("peap psm struct");
481         psm->in_buf = malloc(TLS_RECORD_MAX_SIZE);
482         if (!psm->in_buf)
483                 novm("peap tls buffer");
484         psm->out_buf = malloc(TLS_RECORD_MAX_SIZE);
485         if (!psm->out_buf)
486                 novm("peap tls buffer");
487         method = tls_method();
488         if (!method)
489                 novm("TLS_method() failed");
490         psm->ctx = SSL_CTX_new(method);
491         if (!psm->ctx)
492                 novm("SSL_CTX_new() failed");
493
494         /* Configure the default options */
495         tls_set_opts(psm->ctx);
496
497         /* Configure the max TLS version */
498         tls_set_version(psm->ctx, max_tls_version);
499
500         /* Configure the peer certificate callback */
501         tls_set_verify(psm->ctx, 5);
502
503         /* Configure CA locations */
504         if (tls_set_ca(psm->ctx, ca_path, cacert_file)) {
505                 fatal("Could not set CA verify locations");
506         }
507
508         /* Configure CRL check (if any) */
509         if (tls_set_crl(psm->ctx, crl_dir, crl_file)) {
510                 fatal("Could not set CRL verify locations");
511         }
512
513         psm->out_bio = BIO_new(BIO_s_mem());
514         psm->in_bio = BIO_new(BIO_s_mem());
515         BIO_set_mem_eof_return(psm->out_bio, -1);
516         BIO_set_mem_eof_return(psm->in_bio, -1);
517         psm->ssl = SSL_new(psm->ctx);
518         SSL_set_bio(psm->ssl, psm->in_bio, psm->out_bio);
519         SSL_set_connect_state(psm->ssl);
520         psm->phase = PEAP_PHASE_1;
521         tls_set_verify_info(psm->ssl, explicit_remote ? rhostname : NULL, NULL, 1, &psm->info);
522         psm->chap = chap_find_digest(CHAP_MICROSOFT_V2);
523         *ctx = psm;
524         return 0;
525 }
526
527 void peap_finish(struct peap_state **psm) {
528
529         if (psm && *psm) {
530                 struct peap_state *tmp = *psm;
531
532                 if (tmp->ssl)
533                         SSL_free(tmp->ssl);
534
535                 if (tmp->ctx)
536                         SSL_CTX_free(tmp->ctx);
537
538                 if (tmp->info)
539                         tls_free_verify_info(&tmp->info);
540
541                 // NOTE: BIO and memory is freed as a part of SSL_free()
542
543                 free(*psm);
544                 *psm = NULL;
545         }
546 }
547
548 int peap_process(eap_state *esp, u_char id, u_char *inp, int len)
549 {
550         int ret;
551         int out_len;
552
553         struct peap_state *psm = esp->ea_peap;
554
555         if (esp->es_client.ea_id == id) {
556                 info("PEAP: retransmits are not supported..");
557                 return -1;
558         }
559
560         switch (*inp) {
561         case PEAP_S_FLAG_SET:
562                 dbglog("PEAP: S bit is set, starting PEAP phase 1");
563                 ret = SSL_do_handshake(psm->ssl);
564                 if (ret != 1) {
565                         ret = SSL_get_error(psm->ssl, ret);
566                         if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
567                                 fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
568
569                 }
570                 psm->read = BIO_read(psm->out_bio, psm->out_buf, TLS_RECORD_MAX_SIZE);
571                 peap_response(esp, id, psm->out_buf, psm->read);
572                 break;
573
574         case PEAP_LM_FLAG_SET:
575                 dbglog("PEAP TLS: LM bits are set, need to get more TLS fragments");
576                 inp = inp + PEAP_FRAGMENT_LENGTH_FIELD + PEAP_FLAGS_FIELD;
577                 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FRAGMENT_LENGTH_FIELD - PEAP_FLAGS_FIELD);
578                 peap_ack(esp, id);
579                 break;
580
581         case PEAP_M_FLAG_SET:
582                 dbglog("PEAP TLS: M bit is set, need to get more TLS fragments");
583                 inp = inp + PEAP_FLAGS_FIELD;
584                 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
585                 peap_ack(esp, id);
586                 break;
587
588         case PEAP_L_FLAG_SET:
589         case PEAP_NO_FLAGS:
590                 if (*inp == PEAP_L_FLAG_SET) {
591                         dbglog("PEAP TLS: L bit is set");
592                         inp = inp + PEAP_FRAGMENT_LENGTH_FIELD + PEAP_FLAGS_FIELD;
593                         psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FRAGMENT_LENGTH_FIELD - PEAP_FLAGS_FIELD);
594                 } else {
595                         dbglog("PEAP TLS: all bits are off");
596                         inp = inp + PEAP_FLAGS_FIELD;
597                         psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
598                 }
599
600                 if (psm->phase == PEAP_PHASE_1) {
601                         dbglog("PEAP TLS: continue handshake");
602                         ret = SSL_do_handshake(psm->ssl);
603                         if (ret != 1) {
604                                 ret = SSL_get_error(psm->ssl, ret);
605                                 if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
606                                         fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
607                         }
608                         if (SSL_is_init_finished(psm->ssl))
609                                 psm->phase = PEAP_PHASE_2;
610                         if (BIO_ctrl_pending(psm->out_bio) == 0) {
611                                 peap_ack(esp, id);
612                                 break;
613                         }
614                         psm->read = 0;
615                         psm->read = BIO_read(psm->out_bio, psm->out_buf,
616                                         TLS_RECORD_MAX_SIZE);
617                         peap_response(esp, id, psm->out_buf, psm->read);
618                         break;
619                 }
620                 psm->read = SSL_read(psm->ssl, psm->in_buf,
621                                 TLS_RECORD_MAX_SIZE);
622                 out_len = TLS_RECORD_MAX_SIZE;
623                 peap_do_inner_eap(psm->in_buf, psm->read, esp, id,
624                                 psm->out_buf, &out_len);
625                 if (out_len > 0) {
626                         psm->written = SSL_write(psm->ssl, psm->out_buf, out_len);
627                         psm->read = BIO_read(psm->out_bio, psm->out_buf,
628                                 TLS_RECORD_MAX_SIZE);
629                         peap_response(esp, id, psm->out_buf, psm->read);
630                 }
631                 break;
632         }
633         return 0;
634 }
635
636 #else
637
638 u_char outpacket_buf[255];
639 int debug = 1;
640 int error_count = 0;
641 int unsuccess = 0;
642
643 /**
644  * Using the example in MS-PEAP, section 4.4.1.
645  *      see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-peap/5308642b-90c9-4cc4-beec-fb367325c0f9
646  */
647 int test_cmk(u_char *ipmk) {
648         u_char nonce[PEAP_TLV_NONCE_LEN] = {
649                 0x6C, 0x6B, 0xA3, 0x87, 0x84, 0x23, 0x74, 0x57,
650                 0xCC, 0xC9, 0x0B, 0x1A, 0x90, 0x8C, 0xBD, 0xF4,
651                 0x71, 0x1B, 0x69, 0x99, 0x4D, 0x0C, 0xFE, 0x8D,
652                 0x3D, 0xB4, 0x4E, 0xCB, 0xCD, 0xAD, 0x37, 0xE9
653         };
654
655         u_char tmpkey[PEAP_TLV_TEMPKEY_LEN] = {
656                 0x73, 0x8B, 0xB5, 0xF4, 0x62, 0xD5, 0x8E, 0x7E,
657                 0xD8, 0x44, 0xE1, 0xF0, 0x0D, 0x0E, 0xBE, 0x50,
658                 0xC5, 0x0A, 0x20, 0x50, 0xDE, 0x11, 0x99, 0x77,
659                 0x10, 0xD6, 0x5F, 0x45, 0xFB, 0x5F, 0xBA, 0xB7,
660                 0xE3, 0x18, 0x1E, 0x92, 0x4F, 0x42, 0x97, 0x38,
661                 // 0xDE, 0x40, 0xC8, 0x46, 0xCD, 0xF5, 0x0B, 0xCB,
662                 // 0xF9, 0xCE, 0xDB, 0x1E, 0x85, 0x1D, 0x22, 0x52,
663                 // 0x45, 0x3B, 0xDF, 0x63
664         };
665
666         u_char expected[60] = {
667                 0x00, 0x0C, 0x00, 0x38, 0x00, 0x00, 0x00, 0x01,
668                 0x6C, 0x6B, 0xA3, 0x87, 0x84, 0x23, 0x74, 0x57,
669                 0xCC, 0xC9, 0x0B, 0x1A, 0x90, 0x8C, 0xBD, 0xF4,
670                 0x71, 0x1B, 0x69, 0x99, 0x4D, 0x0C, 0xFE, 0x8D,
671                 0x3D, 0xB4, 0x4E, 0xCB, 0xCD, 0xAD, 0x37, 0xE9,
672                 0x42, 0xE0, 0x86, 0x07, 0x1D, 0x1C, 0x8B, 0x8C,
673                 0x8E, 0x45, 0x8F, 0x70, 0x21, 0xF0, 0x6A, 0x6E,
674                 0xAB, 0x16, 0xB6, 0x46
675         };
676
677         u_char inner_mppe_keys[32] = {
678                 0x67, 0x3E, 0x96, 0x14, 0x01, 0xBE, 0xFB, 0xA5,
679                 0x60, 0x71, 0x7B, 0x3B, 0x5D, 0xDD, 0x40, 0x38,
680                 0x65, 0x67, 0xF9, 0xF4, 0x16, 0xFD, 0x3E, 0x9D,
681                 0xFC, 0x71, 0x16, 0x3B, 0xDF, 0xF2, 0xFA, 0x95
682         };
683
684         u_char response[60] = {};
685
686         // Set the inner MPPE keys (e.g. from CHAPv2)
687         mppe_set_keys(inner_mppe_keys, inner_mppe_keys + 16, 16);
688
689         // Generate and compare the response
690         generate_cmk(ipmk, tmpkey, nonce, response, 1);
691         if (memcmp(expected, response, sizeof(response)) != 0) {
692                 dbglog("Failed CMK key generation\n");
693                 dbglog("%.*B", sizeof(response), response);
694                 dbglog("%.*B", sizeof(expected), expected);
695                 return -1;
696         }
697
698         return 0;
699 }
700
701 int test_mppe(u_char *ipmk) {
702         u_char outer_mppe_send_key[MPPE_MAX_KEY_SIZE] = {
703                 0x6A, 0x02, 0xD7, 0x82, 0x20, 0x1B, 0xC7, 0x13,
704                 0x8B, 0xF8, 0xEF, 0xF7, 0x33, 0xB4, 0x96, 0x97,
705                 0x0D, 0x7C, 0xAB, 0x30, 0x0A, 0xC9, 0x57, 0x72,
706                 0x78, 0xE1, 0xDD, 0xD5, 0xAE, 0xF7, 0x66, 0x97
707         };
708
709         u_char outer_mppe_recv_key[MPPE_MAX_KEY_SIZE] = {
710                 0x17, 0x52, 0xD4, 0xE5, 0x84, 0xA1, 0xC8, 0x95,
711                 0x03, 0x9B, 0x4D, 0x05, 0xE3, 0xBC, 0x9A, 0x84,
712                 0x84, 0xDD, 0xC2, 0xAA, 0x6E, 0x2C, 0xE1, 0x62,
713                 0x76, 0x5C, 0x40, 0x68, 0xBF, 0xF6, 0x5A, 0x45
714         };
715
716         u_char result[MPPE_MAX_KEY_SIZE];
717         int len;
718
719         mppe_clear_keys();
720
721         generate_mppe_keys(ipmk, 1);
722
723         len = mppe_get_recv_key(result, sizeof(result));
724         if (len != sizeof(result)) {
725                 dbglog("Invalid length of resulting MPPE recv key");
726                 return -1;
727         }
728
729         if (memcmp(result, outer_mppe_recv_key, len) != 0) {
730                 dbglog("Invalid result for outer mppe recv key");
731                 return -1;
732         }
733
734         len = mppe_get_send_key(result, sizeof(result));
735         if (len != sizeof(result)) {
736                 dbglog("Invalid length of resulting MPPE send key");
737                 return -1;
738         }
739
740         if (memcmp(result, outer_mppe_send_key, len) != 0) {
741                 dbglog("Invalid result for outer mppe send key");
742                 return -1;
743         }
744
745         return 0;
746 }
747
748 int main(int argc, char *argv[])
749 {
750          u_char ipmk[PEAP_TLV_IPMK_LEN] = {
751                 0x3A, 0x91, 0x1C, 0x25, 0x54, 0x73, 0xE8, 0x3E,
752                 0x9A, 0x0C, 0xC3, 0x33, 0xAE, 0x1F, 0x8A, 0x35,
753                 0xCD, 0xC7, 0x41, 0x63, 0xE7, 0xF6, 0x0F, 0x6C,
754                 0x65, 0xEF, 0x71, 0xC2, 0x64, 0x42, 0xAA, 0xAC,
755                 0xA2, 0xB6, 0xF1, 0xEB, 0x4F, 0x25, 0xEC, 0xA3,
756         };
757         int ret = -1;
758
759         ret = test_cmk(ipmk);
760         if (ret != 0) {
761                 return -1;
762         }
763
764         ret = test_mppe(ipmk);
765         if (ret != 0) {
766                 return -1;
767         }
768
769         return 0;
770 }
771
772 #endif