6 * Rustam Kovhaev <rkovhaev@gmail.com>
9 * 1 - Outer EAP, where TLS session gets established
10 * 2 - Inner EAP, where inside TLS session with EAP MSCHAPV2 auth, or any
13 * And so protocols encapsulation looks like this:
14 * Outer EAP -> TLS -> Inner EAP -> MSCHAPV2
15 * PEAP can compress an inner EAP packet prior to encapsulating it within
16 * the Data field of a PEAP packet by removing its Code, Identifier,
17 * and Length fields, and Microsoft PEAP server/client always does that
19 * Current implementation does not support:
21 * b) Inner EAP fragmentation
22 * c) Any other auth other than MSCHAPV2
30 #include <openssl/opensslv.h>
31 #include <openssl/ssl.h>
32 #include <openssl/hmac.h>
33 #include <openssl/rand.h>
34 #include <openssl/err.h>
51 u_char ipmk[PEAP_TLV_IPMK_LEN];
52 u_char tk[PEAP_TLV_TK_LEN];
53 u_char nonce[PEAP_TLV_NONCE_LEN];
56 static struct peap_state *psm;
57 static int peap_phase;
58 extern bool tls_verify_cert;
61 static void ssl_init()
63 #if OPENSSL_VERSION_NUMBER < 0x10100000L
65 SSL_load_error_strings();
71 * K = Key, S = Seed, LEN = output length
72 * PRF+(K, S, LEN) = T1 | T2 | ... |Tn
74 * T1 = HMAC-SHA1 (K, S | 0x01 | 0x00 | 0x00)
75 * T2 = HMAC-SHA1 (K, T1 | S | 0x02 | 0x00 | 0x00)
77 * Tn = HMAC-SHA1 (K, Tn-1 | S | n | 0x00 | 0x00)
78 * As shown, PRF+ is computed in iterations. The number of iterations (n)
79 * depends on the output length (LEN).
81 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)
85 size_t max_iter, i, j, k;
88 max_iter = (pfr_len + SHA_HASH_LEN - 1) / SHA_HASH_LEN;
89 buf = malloc(seed_len + max_iter * SHA_HASH_LEN);
92 hash = malloc(pfr_len + SHA_HASH_LEN);
96 for (i = 0; i < max_iter; i++) {
102 for (k = 0; k < seed_len; k++)
103 buf[j + k] = seed[k];
111 if (!HMAC(EVP_sha1(), key, key_len, buf, pos, (hash + i * SHA_HASH_LEN), &len))
112 fatal("HMAC() failed");
113 for (j = 0; j < SHA_HASH_LEN; j++)
114 buf[j] = hash[i * SHA_HASH_LEN + j];
116 BCOPY(hash, out_buf, pfr_len);
121 static void generate_cmk(u_char *tempkey, u_char *nonce, u_char *tlv_response_out, int client)
123 const char *label = PEAP_TLV_IPMK_SEED_LABEL;
124 u_char data_tlv[PEAP_TLV_DATA_LEN] = {0};
125 u_char isk[PEAP_TLV_ISK_LEN] = {0};
126 u_char ipmkseed[PEAP_TLV_IPMKSEED_LEN] = {0};
127 u_char cmk[PEAP_TLV_CMK_LEN] = {0};
128 u_char buf[PEAP_TLV_CMK_LEN + PEAP_TLV_IPMK_LEN] = {0};
129 u_char compound_mac[PEAP_TLV_COMP_MAC_LEN] = {0};
133 info("PEAP CB: generate compound mac");
134 /* format outgoing CB TLV response packet */
135 data_tlv[1] = PEAP_TLV_TYPE;
136 data_tlv[3] = PEAP_TLV_LENGTH_FIELD;
138 data_tlv[7] = PEAP_TLV_SUBTYPE_RESPONSE;
140 data_tlv[7] = PEAP_TLV_SUBTYPE_REQUEST;
141 BCOPY(nonce, (data_tlv + PEAP_TLV_HEADERLEN), PEAP_TLV_NONCE_LEN);
142 data_tlv[60] = EAPT_PEAP;
144 BCOPY(mppe_send_key, isk, MPPE_MAX_KEY_LEN);
145 BCOPY(mppe_recv_key, isk + MPPE_MAX_KEY_LEN, MPPE_MAX_KEY_LEN);
147 BCOPY(label, ipmkseed, strlen(label));
148 BCOPY(isk, ipmkseed + strlen(label), PEAP_TLV_ISK_LEN);
149 peap_prfplus(ipmkseed, PEAP_TLV_IPMKSEED_LEN,
150 tempkey, PEAP_TLV_TEMPKEY_LEN, buf, PEAP_TLV_CMK_LEN + PEAP_TLV_IPMK_LEN);
152 BCOPY(buf, psm->ipmk, PEAP_TLV_IPMK_LEN);
153 BCOPY(buf + PEAP_TLV_IPMK_LEN, cmk, PEAP_TLV_CMK_LEN);
154 if (!HMAC(EVP_sha1(), cmk, PEAP_TLV_CMK_LEN, data_tlv, PEAP_TLV_DATA_LEN, compound_mac, &len))
155 fatal("HMAC() failed");
156 BCOPY(compound_mac, data_tlv + PEAP_TLV_HEADERLEN + PEAP_TLV_NONCE_LEN, PEAP_TLV_COMP_MAC_LEN);
157 /* do not copy last byte to response packet */
158 BCOPY(data_tlv, tlv_response_out, PEAP_TLV_DATA_LEN - 1);
161 static void verify_compound_mac(u_char *in_buf)
163 u_char nonce[PEAP_TLV_NONCE_LEN] = {0};
164 u_char out_buf[PEAP_TLV_LEN] = {0};
166 BCOPY(in_buf, nonce, PEAP_TLV_NONCE_LEN);
167 generate_cmk(psm->tk, nonce, out_buf, 0);
168 if (memcmp((in_buf + PEAP_TLV_NONCE_LEN), (out_buf + PEAP_TLV_HEADERLEN + PEAP_TLV_NONCE_LEN), PEAP_TLV_CMK_LEN))
169 fatal("server's CMK does not match client's CMK, potential MiTM");
172 static void generate_mppe_keys(void)
174 const char *label = PEAP_TLV_CSK_SEED_LABEL;
175 u_char csk[PEAP_TLV_CSK_LEN] = {0};
179 info("PEAP CB: generate mppe keys");
181 len++; /* CSK requires NULL byte in seed */
182 peap_prfplus((u_char *)label, len, psm->ipmk, PEAP_TLV_IPMK_LEN, csk, PEAP_TLV_CSK_LEN);
185 * The first 64 bytes of the CSK are split into two MPPE keys, as follows.
187 * +-----------------------+------------------------+
188 * | First 32 bytes of CSK | Second 32 bytes of CSK |
189 * +-----------------------+------------------------+
190 * | MS-MPPE-Send-Key | MS-MPPE-Recv-Key |
191 * +-----------------------+------------------------+
193 BCOPY(csk, mppe_send_key, MPPE_MAX_KEY_LEN);
194 BCOPY(csk + 32, mppe_recv_key, MPPE_MAX_KEY_LEN);
197 static void dump(u_char *buf, int len)
201 dbglog("len: %d bytes", len);
202 for (i = 0; i < len; i++)
203 printf("%02x ", buf[i]);
207 static void peap_ack(eap_state *esp, u_char id)
211 outp = outpacket_buf;
212 MAKEHEADER(outp, PPP_EAP);
213 PUTCHAR(EAP_RESPONSE, outp);
215 esp->es_client.ea_id = id;
216 PUTSHORT(PEAP_HEADERLEN, outp);
217 PUTCHAR(EAPT_PEAP, outp);
218 PUTCHAR(PEAP_FLAGS_ACK, outp);
219 output(esp->es_unit, outpacket_buf, PPP_HDRLEN + PEAP_HEADERLEN);
222 static void peap_response(eap_state *esp, u_char id, u_char *buf, int len)
227 outp = outpacket_buf;
228 MAKEHEADER(outp, PPP_EAP);
229 PUTCHAR(EAP_RESPONSE, outp);
231 esp->es_client.ea_id = id;
233 if (peap_phase == PEAP_PHASE_1)
234 peap_len = PEAP_HEADERLEN + PEAP_FRAGMENT_LENGTH_FIELD + len;
236 peap_len = PEAP_HEADERLEN + len;
238 PUTSHORT(peap_len, outp);
239 PUTCHAR(EAPT_PEAP, outp);
241 if (peap_phase == PEAP_PHASE_1) {
242 PUTCHAR(PEAP_L_FLAG_SET, outp);
245 PUTCHAR(PEAP_NO_FLAGS, outp);
247 BCOPY(buf, outp, len);
248 output(esp->es_unit, outpacket_buf, PPP_HDRLEN + peap_len);
251 void do_inner_eap(u_char *in_buf, int in_len, eap_state *esp, int id,
252 char *rhostname, u_char *out_buf, int *out_len)
255 dump(in_buf, in_len);
262 if (*in_buf == EAPT_IDENTITY && in_len == 1) {
263 PUTCHAR(EAPT_IDENTITY, outp);
265 BCOPY(esp->es_client.ea_name, outp,
266 esp->es_client.ea_namelen);
267 used += esp->es_client.ea_namelen;
268 } else if (*(in_buf + EAP_HEADERLEN) == PEAP_CAPABILITIES_TYPE &&
269 in_len == (EAP_HEADERLEN + PEAP_CAPABILITIES_LEN)) {
270 /* use original packet as template for response */
271 BCOPY(in_buf, outp, EAP_HEADERLEN + PEAP_CAPABILITIES_LEN);
272 PUTCHAR(EAP_RESPONSE, outp);
274 /* change last byte to 0 to disable fragmentation */
275 *(outp + PEAP_CAPABILITIES_LEN + 1) = 0x00;
276 used = EAP_HEADERLEN + PEAP_CAPABILITIES_LEN;
277 } else if (*in_buf == EAPT_TLS && in_len == 2) {
278 /* send NAK to EAP_TLS request */
279 PUTCHAR(EAPT_NAK, outp);
281 PUTCHAR(EAPT_MSCHAPV2, outp);
283 } else if (*in_buf == EAPT_MSCHAPV2 && *(in_buf + 1) == CHAP_CHALLENGE) {
286 char secret[MAXSECRETLEN + 1];
289 u_char response[MS_CHAP2_RESPONSE_LEN];
290 u_char auth_response[MS_AUTH_RESPONSE_LENGTH + 1];
292 u_char rchallenge[MS_CHAP2_PEER_CHAL_LEN];
294 user = esp->es_client.ea_name;
295 user_len = esp->es_client.ea_namelen;
296 chap_id = *(in_buf + 2);
297 BCOPY((in_buf + 6), rchallenge, MS_CHAP2_PEER_CHAL_LEN);
298 if (!get_secret(esp->es_unit, esp->es_client.ea_name,
299 rhostname, secret, &secret_len, 0))
300 fatal("Can't read password file");
301 /* MSCHAPV2 response */
302 ChapMS2(rchallenge, NULL, esp->es_client.ea_name,
303 secret, secret_len, response, auth_response, MS_CHAP2_AUTHENTICATEE);
304 PUTCHAR(EAPT_MSCHAPV2, outp);
305 PUTCHAR(CHAP_RESPONSE, outp);
306 PUTCHAR(chap_id, outp);
308 PUTCHAR(5 + user_len + MS_CHAP2_RESPONSE_LEN, outp);
309 PUTCHAR(MS_CHAP2_RESPONSE_LEN, outp)
310 BCOPY(response, outp, MS_CHAP2_RESPONSE_LEN);
311 outp = outp + MS_CHAP2_RESPONSE_LEN;
312 BCOPY(user, outp, user_len);
313 used = 5 + user_len + MS_CHAP2_RESPONSE_LEN + 1;
314 } else if (*in_buf == EAPT_MSCHAPV2 && *(in_buf + 1) == CHAP_SUCCESS) {
315 PUTCHAR(EAPT_MSCHAPV2, outp);
317 PUTCHAR(CHAP_SUCCESS, outp);
319 auth_peer_success(esp->es_unit, PPP_CHAP, CHAP_MICROSOFT_V2,
320 esp->es_server.ea_peer, esp->es_server.ea_peerlen);
321 } else if (*(in_buf + EAP_HEADERLEN + PEAP_TLV_HEADERLEN) == PEAP_TLV_TYPE &&
322 in_len == PEAP_TLV_LEN) {
323 /* PEAP TLV message, do cryptobinding */
324 SSL_export_keying_material(psm->ssl, psm->tk, PEAP_TLV_TK_LEN,
325 PEAP_TLV_TK_SEED_LABEL, strlen(PEAP_TLV_TK_SEED_LABEL), NULL, 0, 0);
326 /* verify server's CMK */
327 verify_compound_mac(in_buf + EAP_HEADERLEN + PEAP_TLV_RESULT_LEN + PEAP_TLV_HEADERLEN);
328 /* generate client's CMK with new nonce */
329 PUTCHAR(EAP_RESPONSE, outp);
331 PUTSHORT(PEAP_TLV_LEN, outp);
332 BCOPY(in_buf + EAP_HEADERLEN, outp, PEAP_TLV_RESULT_LEN);
333 outp = outp + PEAP_TLV_RESULT_LEN;
334 RAND_bytes(psm->nonce, PEAP_TLV_NONCE_LEN);
335 generate_cmk(psm->tk, psm->nonce, outp, 1);
337 generate_mppe_keys();
340 /* send compressed EAP NAK for any unknown packet */
341 PUTCHAR(EAPT_NAK, outp);
346 dump(psm->out_buf, used);
350 void allocate_buffers(void)
352 const SSL_METHOD *method;
354 psm = malloc(sizeof(*psm));
356 novm("peap psm struct");
357 psm->in_buf = malloc(TLS_RECORD_MAX_SIZE);
359 novm("peap tls buffer");
360 psm->out_buf = malloc(TLS_RECORD_MAX_SIZE);
362 novm("peap tls buffer");
363 method = TLS_method();
365 novm("TLS_method() failed");
366 psm->ctx = SSL_CTX_new(method);
368 novm("SSL_CTX_new() failed");
370 if (!tls_verify_cert)
371 SSL_CTX_set_verify(psm->ctx, SSL_VERIFY_NONE, NULL);
373 SSL_CTX_set_verify(psm->ctx, SSL_VERIFY_PEER, NULL);
374 info("PEAP: SSL certificate validation is %s", tls_verify_cert ? "enabled" : "disabled");
376 SSL_CTX_set_options(psm->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
378 psm->out_bio = BIO_new(BIO_s_mem());
379 psm->in_bio = BIO_new(BIO_s_mem());
380 BIO_set_mem_eof_return(psm->out_bio, -1);
381 BIO_set_mem_eof_return(psm->in_bio, -1);
382 psm->ssl = SSL_new(psm->ctx);
383 SSL_set_bio(psm->ssl, psm->in_bio, psm->out_bio);
384 SSL_set_connect_state(psm->ssl);
385 peap_phase = PEAP_PHASE_1;
388 void peap_process(eap_state *esp, u_char id, u_char *inp, int len, char *rhostname)
396 if (esp->es_client.ea_id == id) {
397 info("PEAP: retransmits are not supported..");
402 case PEAP_S_FLAG_SET:
405 info("PEAP: S bit is set, starting PEAP phase 1");
406 ret = SSL_do_handshake(psm->ssl);
408 ret = SSL_get_error(psm->ssl, ret);
409 if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
410 fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
413 psm->read = BIO_read(psm->out_bio, psm->out_buf, TLS_RECORD_MAX_SIZE);
414 peap_response(esp, id, psm->out_buf, psm->read);
417 case PEAP_LM_FLAG_SET:
419 info("PEAP TLS: LM bits are set, need to get more TLS fragments");
420 inp = inp + PEAP_FRAGMENT_LENGTH_FIELD + PEAP_FLAGS_FIELD;
421 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FRAGMENT_LENGTH_FIELD - PEAP_FLAGS_FIELD);
425 case PEAP_M_FLAG_SET:
427 info("PEAP TLS: M bit is set, need to get more TLS fragments");
428 inp = inp + PEAP_FLAGS_FIELD;
429 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
433 case PEAP_L_FLAG_SET:
435 if (*inp == PEAP_L_FLAG_SET) {
437 info("PEAP TLS: L bit is set");
438 inp = inp + PEAP_FRAGMENT_LENGTH_FIELD + PEAP_FLAGS_FIELD;
439 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FRAGMENT_LENGTH_FIELD - PEAP_FLAGS_FIELD);
442 info("PEAP TLS: all bits are off");
443 inp = inp + PEAP_FLAGS_FIELD;
444 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
447 if (peap_phase == PEAP_PHASE_1) {
449 info("PEAP TLS: continue handshake");
450 ret = SSL_do_handshake(psm->ssl);
452 ret = SSL_get_error(psm->ssl, ret);
453 if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
454 fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
456 if (SSL_is_init_finished(psm->ssl))
457 peap_phase = PEAP_PHASE_2;
458 if (BIO_ctrl_pending(psm->out_bio) == 0) {
463 psm->read = BIO_read(psm->out_bio, psm->out_buf,
464 TLS_RECORD_MAX_SIZE);
465 peap_response(esp, id, psm->out_buf, psm->read);
468 psm->read = SSL_read(psm->ssl, psm->in_buf,
469 TLS_RECORD_MAX_SIZE);
470 out_len = TLS_RECORD_MAX_SIZE;
471 do_inner_eap(psm->in_buf, psm->read, esp, id, rhostname,
472 psm->out_buf, &out_len);
473 psm->written = SSL_write(psm->ssl, psm->out_buf, out_len);
474 psm->read = BIO_read(psm->out_bio, psm->out_buf,
475 TLS_RECORD_MAX_SIZE);
476 peap_response(esp, id, psm->out_buf, psm->read);