2 * Copyright (c) Dan Harkins, 2012
4 * Copyright holder grants permission for redistribution and use in source
5 * and binary forms, with or without modification, provided that the
6 * following conditions are met:
7 * 1. Redistribution of source code must retain the above copyright
8 * notice, this list of conditions, and the following disclaimer
10 * 2. Redistribution in binary form must retain the above copyright
11 * notice, this list of conditions, and the following disclaimer
12 * in the documentation and/or other materials provided with the
15 * "DISCLAIMER OF LIABILITY
17 * THIS SOFTWARE IS PROVIDED BY DAN HARKINS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INDUSTRIAL LOUNGE BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * This license and distribution terms cannot be changed. In other words,
30 * this code cannot simply be copied and put under a different distribution
31 * license (including the GNU public license).
35 USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
37 #include "rlm_eap_pwd.h"
41 #define MPPE_KEY_LEN 32
42 #define MSK_EMSK_LEN (2*MPPE_KEY_LEN)
44 static CONF_PARSER pwd_module_config[] = {
45 { "group", FR_CONF_OFFSET(PW_TYPE_INTEGER, eap_pwd_t, group), "19" },
46 { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, eap_pwd_t, fragment_size), "1020" },
47 { "server_id", FR_CONF_OFFSET(PW_TYPE_STRING, eap_pwd_t, server_id), NULL },
48 { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, eap_pwd_t, virtual_server), NULL },
49 CONF_PARSER_TERMINATOR
52 static int mod_detach (void *arg)
56 inst = (eap_pwd_t *) arg;
58 if (inst->bnctx) BN_CTX_free(inst->bnctx);
63 static int mod_instantiate (CONF_SECTION *cs, void **instance)
67 *instance = inst = talloc_zero(cs, eap_pwd_t);
70 if (cf_section_parse(cs, inst, pwd_module_config) < 0) {
74 if (inst->fragment_size < 100) {
75 cf_log_err_cs(cs, "Fragment size is too small");
79 if ((inst->bnctx = BN_CTX_new()) == NULL) {
80 cf_log_err_cs(cs, "Failed to get BN context");
87 static int _free_pwd_session (pwd_session_t *session)
89 BN_clear_free(session->private_value);
90 BN_clear_free(session->peer_scalar);
91 BN_clear_free(session->my_scalar);
92 BN_clear_free(session->k);
93 EC_POINT_clear_free(session->my_element);
94 EC_POINT_clear_free(session->peer_element);
95 EC_GROUP_free(session->group);
96 EC_POINT_clear_free(session->pwe);
97 BN_clear_free(session->order);
98 BN_clear_free(session->prime);
103 static int send_pwd_request (pwd_session_t *session, EAP_DS *eap_ds)
109 len = (session->out_len - session->out_pos) + sizeof(pwd_hdr);
111 eap_ds->request->code = PW_EAP_REQUEST;
112 eap_ds->request->type.num = PW_EAP_PWD;
113 eap_ds->request->type.length = (len > session->mtu) ? session->mtu : len;
114 eap_ds->request->type.data = talloc_zero_array(eap_ds->request, uint8_t, eap_ds->request->type.length);
115 hdr = (pwd_hdr *)eap_ds->request->type.data;
117 switch (session->state) {
118 case PWD_STATE_ID_REQ:
119 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_ID);
122 case PWD_STATE_COMMIT:
123 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_COMMIT);
126 case PWD_STATE_CONFIRM:
127 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_CONFIRM);
131 ERROR("rlm_eap_pwd: PWD state is invalid. Can't send request");
135 * are we fragmenting?
137 if (((session->out_len - session->out_pos) + sizeof(pwd_hdr)) > session->mtu) {
138 EAP_PWD_SET_MORE_BIT(hdr);
139 if (session->out_pos == 0) {
141 * the first fragment, add the total length
143 EAP_PWD_SET_LENGTH_BIT(hdr);
144 totlen = ntohs(session->out_len);
145 memcpy(hdr->data, (char *)&totlen, sizeof(totlen));
146 memcpy(hdr->data + sizeof(uint16_t),
148 session->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
149 session->out_pos += (session->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
152 * an intermediate fragment
154 memcpy(hdr->data, session->out + session->out_pos, (session->mtu - sizeof(pwd_hdr)));
155 session->out_pos += (session->mtu - sizeof(pwd_hdr));
159 * either it's not a fragment or it's the last fragment.
160 * The out buffer isn't needed anymore though so get rid of it.
162 memcpy(hdr->data, session->out + session->out_pos,
163 (session->out_len - session->out_pos));
164 talloc_free(session->out);
166 session->out_pos = session->out_len = 0;
171 static int mod_session_init (void *instance, eap_handler_t *handler)
173 pwd_session_t *session;
174 eap_pwd_t *inst = (eap_pwd_t *)instance;
176 pwd_id_packet_t *packet;
178 if (!inst || !handler) {
179 ERROR("rlm_eap_pwd: Initiate, NULL data provided");
184 * make sure the server's been configured properly
186 if (!inst->server_id) {
187 ERROR("rlm_eap_pwd: Server ID is not configured");
190 switch (inst->group) {
199 ERROR("rlm_eap_pwd: Group is not supported");
203 if ((session = talloc_zero(handler, pwd_session_t)) == NULL) return 0;
204 talloc_set_destructor(session, _free_pwd_session);
206 * set things up so they can be free'd reliably
208 session->group_num = inst->group;
209 session->private_value = NULL;
210 session->peer_scalar = NULL;
211 session->my_scalar = NULL;
213 session->my_element = NULL;
214 session->peer_element = NULL;
215 session->group = NULL;
217 session->order = NULL;
218 session->prime = NULL;
221 * The admin can dynamically change the MTU.
223 session->mtu = inst->fragment_size;
224 vp = fr_pair_find_by_num(handler->request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY);
227 * session->mtu is *our* MTU. We need to subtract off the EAP
230 * 9 = 4 (EAPOL header) + 4 (EAP header) + 1 (EAP type)
232 * The fragmentation code deals with the included length
233 * so we don't need to subtract that here.
235 if (vp && (vp->vp_integer > 100) && (vp->vp_integer < session->mtu)) {
236 session->mtu = vp->vp_integer - 9;
239 session->state = PWD_STATE_ID_REQ;
241 session->out_pos = 0;
242 handler->opaque = session;
245 * construct an EAP-pwd-ID/Request
247 session->out_len = sizeof(pwd_id_packet_t) + strlen(inst->server_id);
248 if ((session->out = talloc_zero_array(session, uint8_t, session->out_len)) == NULL) {
252 packet = (pwd_id_packet_t *)session->out;
253 packet->group_num = htons(session->group_num);
254 packet->random_function = EAP_PWD_DEF_RAND_FUN;
255 packet->prf = EAP_PWD_DEF_PRF;
256 session->token = fr_rand();
257 memcpy(packet->token, (char *)&session->token, 4);
258 packet->prep = EAP_PWD_PREP_NONE;
259 memcpy(packet->identity, inst->server_id, session->out_len - sizeof(pwd_id_packet_t) );
261 handler->stage = PROCESS;
263 return send_pwd_request(session, handler->eap_ds);
266 static int mod_process(void *arg, eap_handler_t *handler)
268 pwd_session_t *session;
270 pwd_id_packet_t *packet;
271 eap_packet_t *response;
272 REQUEST *request, *fake;
277 eap_pwd_t *inst = (eap_pwd_t *)arg;
279 uint8_t exch, *in, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN];
280 uint8_t peer_confirm[SHA256_DIGEST_LENGTH];
281 BIGNUM *x = NULL, *y = NULL;
284 if (!handler || ((eap_ds = handler->eap_ds) == NULL) || !inst) return 0;
286 session = (pwd_session_t *)handler->opaque;
287 request = handler->request;
288 response = handler->eap_ds->response;
289 hdr = (pwd_hdr *)response->type.data;
292 * The header must be at least one byte.
294 if (!hdr || (response->type.length < sizeof(pwd_hdr))) {
295 RDEBUG("Packet with insufficient data");
300 in_len = response->type.length - sizeof(pwd_hdr);
303 * see if we're fragmenting, if so continue until we're done
305 if (session->out_pos) {
306 if (in_len) RDEBUG2("pwd got something more than an ACK for a fragment");
308 return send_pwd_request(session, eap_ds);
312 * the first fragment will have a total length, make a
313 * buffer to hold all the fragments
315 if (EAP_PWD_GET_LENGTH_BIT(hdr)) {
317 RDEBUG2("pwd already alloced buffer for fragments");
322 RDEBUG("Invalid packet: length bit set, but no length field");
326 session->in_len = ntohs(in[0] * 256 | in[1]);
327 if ((session->in = talloc_zero_array(session, uint8_t, session->in_len)) == NULL) {
328 RDEBUG2("pwd cannot allocate %zd buffer to hold fragments",
332 memset(session->in, 0, session->in_len);
334 in += sizeof(uint16_t);
335 in_len -= sizeof(uint16_t);
339 * all fragments, including the 1st will have the M(ore) bit set,
340 * buffer those fragments!
342 if (EAP_PWD_GET_MORE_BIT(hdr)) {
343 rad_assert(session->in != NULL);
345 if ((session->in_pos + in_len) > session->in_len) {
346 RDEBUG2("Fragment overflows packet.");
350 memcpy(session->in + session->in_pos, in, in_len);
351 session->in_pos += in_len;
354 * send back an ACK for this fragment
356 exch = EAP_PWD_GET_EXCHANGE(hdr);
357 eap_ds->request->code = PW_EAP_REQUEST;
358 eap_ds->request->type.num = PW_EAP_PWD;
359 eap_ds->request->type.length = sizeof(pwd_hdr);
360 if ((eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, sizeof(pwd_hdr))) == NULL) {
363 hdr = (pwd_hdr *)eap_ds->request->type.data;
364 EAP_PWD_SET_EXCHANGE(hdr, exch);
371 * the last fragment...
373 if ((session->in_pos + in_len) > session->in_len) {
374 RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent");
377 memcpy(session->in + session->in_pos, in, in_len);
379 in_len = session->in_len;
382 switch (session->state) {
383 case PWD_STATE_ID_REQ:
384 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_ID) {
385 RDEBUG2("pwd exchange is incorrect: not ID");
389 packet = (pwd_id_packet_t *) in;
390 if (in_len < sizeof(packet)) {
391 RDEBUG("Packet is too small (%zd < %zd).", in_len, sizeof(packet));
395 if ((packet->prf != EAP_PWD_DEF_PRF) ||
396 (packet->random_function != EAP_PWD_DEF_RAND_FUN) ||
397 (packet->prep != EAP_PWD_PREP_NONE) ||
398 (CRYPTO_memcmp(packet->token, &session->token, 4)) ||
399 (packet->group_num != ntohs(session->group_num))) {
400 RDEBUG2("pwd id response is invalid");
404 * we've agreed on the ciphersuite, record it...
406 ptr = (uint8_t *)&session->ciphersuite;
407 memcpy(ptr, (char *)&packet->group_num, sizeof(uint16_t));
408 ptr += sizeof(uint16_t);
409 *ptr = EAP_PWD_DEF_RAND_FUN;
410 ptr += sizeof(uint8_t);
411 *ptr = EAP_PWD_DEF_PRF;
413 session->peer_id_len = in_len - sizeof(pwd_id_packet_t);
414 if (session->peer_id_len >= sizeof(session->peer_id)) {
415 RDEBUG2("pwd id response is malformed");
419 memcpy(session->peer_id, packet->identity, session->peer_id_len);
420 session->peer_id[session->peer_id_len] = '\0';
423 * make fake request to get the password for the usable ID
425 if ((fake = request_alloc_fake(handler->request)) == NULL) {
426 RDEBUG("pwd unable to create fake request!");
429 fake->username = pair_make_request("User-Name", NULL, T_OP_EQ);
430 if (!fake->username) {
431 RDEBUG("pwd unanable to create value pair for username!");
435 fake->username->vp_length = session->peer_id_len;
436 fake->username->vp_strvalue = p = talloc_array(fake->username, char, fake->username->vp_length + 1);
437 memcpy(p, session->peer_id, session->peer_id_len);
438 p[fake->username->vp_length] = '\0';
440 fr_pair_add(&fake->packet->vps, fake->username);
442 if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
443 fake->server = vp->vp_strvalue;
444 } else if (inst->virtual_server) {
445 fake->server = inst->virtual_server;
446 } /* else fake->server == request->server */
448 RDEBUG("Sending tunneled request");
449 rdebug_pair_list(L_DBG_LVL_1, request, fake->packet->vps, NULL);
452 RDEBUG("server %s {", fake->server);
458 * Call authorization recursively, which will
462 process_authorize(0, fake);
466 * Note that we don't do *anything* with the reply
470 RDEBUG("} # server %s", fake->server);
475 RDEBUG("Got tunneled reply code %d", fake->reply->code);
476 rdebug_pair_list(L_DBG_LVL_1, request, fake->reply->vps, NULL);
478 if ((pw = fr_pair_find_by_num(fake->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) {
479 DEBUG2("failed to find password for %s to do pwd authentication",
485 if (compute_password_element(session, session->group_num,
486 pw->data.strvalue, strlen(pw->data.strvalue),
487 inst->server_id, strlen(inst->server_id),
488 session->peer_id, strlen(session->peer_id),
490 DEBUG2("failed to obtain password element");
497 * compute our scalar and element
499 if (compute_scalar_element(session, inst->bnctx)) {
500 DEBUG2("failed to compute server's scalar and element");
504 if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
505 DEBUG2("server point allocation failed");
510 * element is a point, get both coordinates: x and y
512 if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->my_element, x, y,
514 DEBUG2("server point assignment failed");
523 session->out_len = BN_num_bytes(session->order) + (2 * BN_num_bytes(session->prime));
524 if ((session->out = talloc_array(session, uint8_t, session->out_len)) == NULL) {
527 memset(session->out, 0, session->out_len);
530 offset = BN_num_bytes(session->prime) - BN_num_bytes(x);
531 BN_bn2bin(x, ptr + offset);
533 ptr += BN_num_bytes(session->prime);
534 offset = BN_num_bytes(session->prime) - BN_num_bytes(y);
535 BN_bn2bin(y, ptr + offset);
537 ptr += BN_num_bytes(session->prime);
538 offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar);
539 BN_bn2bin(session->my_scalar, ptr + offset);
541 session->state = PWD_STATE_COMMIT;
542 ret = send_pwd_request(session, eap_ds);
545 case PWD_STATE_COMMIT:
546 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_COMMIT) {
547 RDEBUG2("pwd exchange is incorrect: not commit!");
552 * process the peer's commit and generate the shared key, k
554 if (process_peer_commit(session, in, in_len, inst->bnctx)) {
555 RDEBUG2("failed to process peer's commit");
560 * compute our confirm blob
562 if (compute_server_confirm(session, session->my_confirm, inst->bnctx)) {
563 ERROR("rlm_eap_pwd: failed to compute confirm!");
568 * construct a response...which is just our confirm blob
570 session->out_len = SHA256_DIGEST_LENGTH;
571 if ((session->out = talloc_array(session, uint8_t, session->out_len)) == NULL) {
575 memset(session->out, 0, session->out_len);
576 memcpy(session->out, session->my_confirm, SHA256_DIGEST_LENGTH);
578 session->state = PWD_STATE_CONFIRM;
579 ret = send_pwd_request(session, eap_ds);
582 case PWD_STATE_CONFIRM:
583 if (in_len < SHA256_DIGEST_LENGTH) {
584 RDEBUG("Peer confirm is too short (%zd < %d)",
585 in_len, SHA256_DIGEST_LENGTH);
589 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_CONFIRM) {
590 RDEBUG2("pwd exchange is incorrect: not commit!");
593 if (compute_peer_confirm(session, peer_confirm, inst->bnctx)) {
594 RDEBUG2("pwd exchange cannot compute peer's confirm");
597 if (CRYPTO_memcmp(peer_confirm, in, SHA256_DIGEST_LENGTH)) {
598 RDEBUG2("pwd exchange fails: peer confirm is incorrect!");
601 if (compute_keys(session, peer_confirm, msk, emsk)) {
602 RDEBUG2("pwd exchange cannot generate (E)MSK!");
605 eap_ds->request->code = PW_EAP_SUCCESS;
607 * return the MSK (in halves)
609 eap_add_reply(handler->request, "MS-MPPE-Recv-Key", msk, MPPE_KEY_LEN);
610 eap_add_reply(handler->request, "MS-MPPE-Send-Key", msk + MPPE_KEY_LEN, MPPE_KEY_LEN);
616 RDEBUG2("unknown PWD state");
621 * we processed the buffered fragments, get rid of them
624 talloc_free(session->in);
631 extern rlm_eap_module_t rlm_eap_pwd;
632 rlm_eap_module_t rlm_eap_pwd = {
634 .instantiate = mod_instantiate, /* Create new submodule instance */
635 .session_init = mod_session_init, /* Create the initial request */
636 .process = mod_process, /* Process next round of EAP method */
637 .detach = mod_detach /* Destroy the submodule instance */