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).
34 #include <freeradius-devel/ident.h>
37 #include "rlm_eap_pwd.h"
41 #define MSK_EMSK_LEN 64
42 #define MPPE_KEY_LEN 32
44 static CONF_PARSER pwd_module_config[] = {
45 { "group", PW_TYPE_INTEGER,
46 offsetof(EAP_PWD_CONF, group), NULL, "19"},
47 { "fragment_size", PW_TYPE_INTEGER,
48 offsetof(EAP_PWD_CONF, fragment_size), NULL, "1020"},
49 { "server_id", PW_TYPE_STRING_PTR,
50 offsetof(EAP_PWD_CONF, server_id), NULL, NULL },
51 { "virtual_server", PW_TYPE_STRING_PTR,
52 offsetof(EAP_PWD_CONF, virtual_server), NULL, NULL },
53 { NULL, -1, 0, NULL, NULL }
57 * stolen from rlm_eap_sim: Add value pair to reply
59 static void add_reply(VALUE_PAIR** vp,
60 const char* name, const uint8_t *value, size_t len)
62 VALUE_PAIR *reply_attr;
63 reply_attr = pairmake(name, "", T_OP_EQ);
66 "add_reply failed to create attribute %s: %s\n",
71 memcpy(reply_attr->vp_strvalue, value, len);
72 reply_attr->length = len;
73 pairadd(vp, reply_attr);
77 eap_pwd_detach (void *arg)
82 inst = (eap_pwd_t *) arg;
85 BN_CTX_free(inst->bnctx);
92 eap_pwd_attach (CONF_SECTION *cs, void **instance)
96 *instance = inst = talloc_zero(cs, eap_pwd_t);
99 inst->conf = talloc_zero(inst, EAP_PWD_CONF);
100 if (!inst->conf) return -1;
102 if (cf_section_parse(cs, inst->conf, pwd_module_config) < 0) {
106 if ((inst->bnctx = BN_CTX_new()) == NULL) {
107 radlog(L_ERR, "rlm_eap_pwd: failed to get BN context!");
115 free_session (void *data)
117 pwd_session_t *session = (pwd_session_t *)data;
119 if (session == NULL) {
120 radlog(L_ERR, "rlm_eap_pwd: freeing a NULL session...naughty, naughty");
123 BN_free(session->private_value);
124 BN_free(session->peer_scalar);
125 BN_free(session->my_scalar);
127 EC_POINT_free(session->my_element);
128 EC_POINT_free(session->peer_element);
129 EC_GROUP_free(session->group);
130 EC_POINT_free(session->pwe);
131 BN_free(session->order);
132 BN_free(session->prime);
138 send_pwd_request (pwd_session_t *sess, EAP_DS *eap_ds)
144 len = (sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr);
146 eap_ds->request->code = PW_EAP_REQUEST;
147 eap_ds->request->type.type = PW_EAP_PWD;
148 eap_ds->request->type.length = (len > sess->mtu) ? sess->mtu : len;
149 eap_ds->request->type.data = malloc(eap_ds->request->type.length);
150 memset(eap_ds->request->type.data, 0, eap_ds->request->type.length);
152 hdr = (pwd_hdr *)eap_ds->request->type.data;
153 switch (sess->state) {
154 case PWD_STATE_ID_REQ:
155 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_ID);
157 case PWD_STATE_COMMIT:
158 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_COMMIT);
160 case PWD_STATE_CONFIRM:
161 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_CONFIRM);
164 DEBUG2("pwd state is messed up! Cannot send request");
168 * are we fragmenting?
170 if ((int)((sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr)) > sess->mtu) {
171 EAP_PWD_SET_MORE_BIT(hdr);
172 if (sess->out_buf_pos == 0) {
174 * the first fragment, add the total length
176 EAP_PWD_SET_LENGTH_BIT(hdr);
177 totlen = ntohs(sess->out_buf_len);
178 memcpy(hdr->data, (char *)&totlen, sizeof(totlen));
179 memcpy(hdr->data + sizeof(uint16_t),
181 sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
182 sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
185 * an intermediate fragment
187 memcpy(hdr->data, sess->out_buf + sess->out_buf_pos, (sess->mtu - sizeof(pwd_hdr)));
188 sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr));
192 * either it's not a fragment or it's the last fragment.
193 * The out buffer isn't needed anymore though so get rid of it.
195 memcpy(hdr->data, sess->out_buf + sess->out_buf_pos,
196 (sess->out_buf_len - sess->out_buf_pos));
198 sess->out_buf = NULL;
199 sess->out_buf_pos = sess->out_buf_len = 0;
205 eap_pwd_initiate (void *type_data, EAP_HANDLER *handler)
207 pwd_session_t *pwd_session;
208 eap_pwd_t *inst = (eap_pwd_t *)type_data;
212 if (!inst || !handler) {
213 radlog(L_ERR, "rlm_eap_pwd: initiate, NULL data provided");
218 * make sure the server's been configured properly
220 if (inst->conf->server_id == NULL) {
221 radlog(L_ERR, "rlm_eap_pwd: server ID is not configured!");
224 switch (inst->conf->group) {
232 radlog(L_ERR, "rlm_eap_pwd: group is not supported!");
236 if ((pwd_session = (pwd_session_t *)malloc(sizeof(*pwd_session))) == NULL) {
237 radlog(L_ERR, "rlm_eap_pwd: initiate, out of memory (1)");
241 * set things up so they can be free'd reliably
243 pwd_session->group_num = inst->conf->group;
244 pwd_session->private_value = NULL;
245 pwd_session->peer_scalar = NULL;
246 pwd_session->my_scalar = NULL;
247 pwd_session->k = NULL;
248 pwd_session->my_element = NULL;
249 pwd_session->peer_element = NULL;
250 pwd_session->group = NULL;
251 pwd_session->pwe = NULL;
252 pwd_session->order = NULL;
253 pwd_session->prime = NULL;
256 * figure out the MTU (basically do what eap-tls does)
258 pwd_session->mtu = inst->conf->fragment_size;
259 vp = pairfind(handler->request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY);
260 if (vp && ((int)(vp->vp_integer - 9) < pwd_session->mtu)) {
262 * 9 = 4 (EAPOL header) + 4 (EAP header) + 1 (EAP type)
264 * the fragmentation code deals with the included length
265 * so we don't need to subtract that here.
267 pwd_session->mtu = vp->vp_integer - 9;
270 pwd_session->state = PWD_STATE_ID_REQ;
271 pwd_session->in_buf = NULL;
272 pwd_session->out_buf_pos = 0;
273 handler->opaque = pwd_session;
274 handler->free_opaque = free_session;
277 * construct an EAP-pwd-ID/Request
279 pwd_session->out_buf_len = sizeof(pwd_id_packet) + strlen(inst->conf->server_id);
280 if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
281 radlog(L_ERR, "rlm_eap_pwd: initiate, out of memory to send ID request");
284 memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
286 pack = (pwd_id_packet *)pwd_session->out_buf;
287 pack->group_num = htons(pwd_session->group_num);
288 pack->random_function = EAP_PWD_DEF_RAND_FUN;
289 pack->prf = EAP_PWD_DEF_PRF;
290 pwd_session->token = random();
291 memcpy(pack->token, (char *)&pwd_session->token, 4);
292 pack->prep = EAP_PWD_PREP_NONE;
293 strcpy(pack->identity, inst->conf->server_id);
295 handler->stage = AUTHENTICATE;
297 return send_pwd_request(pwd_session, handler->eap_ds);
301 eap_pwd_authenticate (void *arg, EAP_HANDLER *handler)
303 pwd_session_t *pwd_session;
306 EAP_PACKET *response;
307 REQUEST *request, *fake;
308 VALUE_PAIR *pw, **outvps, *vp;
311 eap_pwd_t *inst = (eap_pwd_t *)arg;
313 uint8_t exch, *buf, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN];
314 uint8_t peer_confirm[SHA256_DIGEST_LENGTH];
315 BIGNUM *x = NULL, *y = NULL;
317 if ((handler == NULL) ||
318 ((eap_ds = handler->eap_ds) == NULL) ||
322 pwd_session = (pwd_session_t *)handler->opaque;
323 request = handler->request;
324 response = handler->eap_ds->response;
325 hdr = (pwd_hdr *)response->type.data;
328 len = response->type.length - sizeof(pwd_hdr);
331 * see if we're fragmenting, if so continue until we're done
333 if (pwd_session->out_buf_pos) {
335 RDEBUG2("pwd got something more than an ACK for a fragment");
337 return send_pwd_request(pwd_session, eap_ds);
341 * the first fragment will have a total length, make a
342 * buffer to hold all the fragments
344 if (EAP_PWD_GET_LENGTH_BIT(hdr)) {
345 if (pwd_session->in_buf) {
346 RDEBUG2("pwd already alloced buffer for fragments");
349 pwd_session->in_buf_len = ntohs(buf[0] * 256 | buf[1]);
350 if ((pwd_session->in_buf = malloc(pwd_session->in_buf_len)) == NULL) {
351 RDEBUG2("pwd cannot malloc %d buffer to hold fragments",
352 pwd_session->in_buf_len);
355 memset(pwd_session->in_buf, 0, pwd_session->in_buf_len);
356 pwd_session->in_buf_pos = 0;
357 buf += sizeof(uint16_t);
358 len -= sizeof(uint16_t);
362 * all fragments, including the 1st will have the M(ore) bit set,
363 * buffer those fragments!
365 if (EAP_PWD_GET_MORE_BIT(hdr)) {
366 rad_assert(pwd_session->in_buf != NULL);
367 if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
368 RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent.");
371 memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
372 pwd_session->in_buf_pos += len;
375 * send back an ACK for this fragment
377 exch = EAP_PWD_GET_EXCHANGE(hdr);
378 eap_ds->request->code = PW_EAP_REQUEST;
379 eap_ds->request->type.type = PW_EAP_PWD;
380 eap_ds->request->type.length = sizeof(pwd_hdr);
381 if ((eap_ds->request->type.data = malloc(sizeof(pwd_hdr))) == NULL) {
382 radlog(L_ERR, "rlm_eap_pwd: fragment ACK, out of memory");
385 hdr = (pwd_hdr *)eap_ds->request->type.data;
386 EAP_PWD_SET_EXCHANGE(hdr, exch);
391 if (pwd_session->in_buf) {
393 * the last fragment...
395 if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
396 RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent.");
399 memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
400 buf = pwd_session->in_buf;
401 len = pwd_session->in_buf_len;
404 switch (pwd_session->state) {
405 case PWD_STATE_ID_REQ:
406 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_ID) {
407 RDEBUG2("pwd exchange is incorrect: not ID");
410 id = (pwd_id_packet *)buf;
411 if ((id->prf != EAP_PWD_DEF_PRF) ||
412 (id->random_function != EAP_PWD_DEF_RAND_FUN) ||
413 (id->prep != EAP_PWD_PREP_NONE) ||
414 (memcmp(id->token, (char *)&pwd_session->token, 4)) ||
415 (id->group_num != ntohs(pwd_session->group_num))) {
416 RDEBUG2("pwd id response is invalid");
420 * we've agreed on the ciphersuite, record it...
422 ptr = (uint8_t *)&pwd_session->ciphersuite;
423 memcpy(ptr, (char *)&id->group_num, sizeof(uint16_t));
424 ptr += sizeof(uint16_t);
425 *ptr = EAP_PWD_DEF_RAND_FUN;
426 ptr += sizeof(uint8_t);
427 *ptr = EAP_PWD_DEF_PRF;
429 pwd_session->peer_id_len = len - sizeof(pwd_id_packet);
430 if (pwd_session->peer_id_len >= sizeof(pwd_session->peer_id)) {
431 RDEBUG2("pwd id response is malformed");
434 memcpy(pwd_session->peer_id, id->identity,
435 pwd_session->peer_id_len);
436 pwd_session->peer_id[pwd_session->peer_id_len] = '\0';
439 * make fake request to get the password for the usable ID
441 if ((fake = request_alloc_fake(handler->request)) == NULL) {
442 RDEBUG("pwd unable to create fake request!");
445 if ((fake->username = pairmake("User-Name", "", T_OP_EQ)) == NULL) {
446 RDEBUG("pwd unanable to create value pair for username!");
450 memcpy(fake->username->vp_strvalue, pwd_session->peer_id,
451 pwd_session->peer_id_len);
452 fake->username->length = pwd_session->peer_id_len;
453 fake->username->vp_strvalue[fake->username->length] = 0;
455 if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
456 fake->server = vp->vp_strvalue;
458 } else if (inst->conf->virtual_server) {
459 fake->server = inst->conf->virtual_server;
461 } /* else fake->server == request->server */
463 if ((debug_flag > 0) && fr_log_fp) {
464 RDEBUG("Sending tunneled request");
466 debug_pair_list(fake->packet->vps);
468 fprintf(fr_log_fp, "server %s {\n",
469 (fake->server == NULL) ? "" : fake->server);
473 * Call authorization recursively, which will
476 module_authorize(0, fake);
479 * Note that we don't do *anything* with the reply
482 if ((debug_flag > 0) && fr_log_fp) {
483 fprintf(fr_log_fp, "} # server %s\n",
484 (fake->server == NULL) ? "" : fake->server);
486 RDEBUG("Got tunneled reply code %d", fake->reply->code);
488 debug_pair_list(fake->reply->vps);
491 if ((pw = pairfind(fake->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) {
492 DEBUG2("failed to find password for %s to do pwd authentication",
493 pwd_session->peer_id);
498 if (compute_password_element(pwd_session, pwd_session->group_num,
499 pw->data.strvalue, strlen(pw->data.strvalue),
500 inst->conf->server_id, strlen(inst->conf->server_id),
501 pwd_session->peer_id, strlen(pwd_session->peer_id),
502 &pwd_session->token)) {
503 DEBUG2("failed to obtain password element :-(");
510 * compute our scalar and element
512 if (compute_scalar_element(pwd_session, inst->bnctx)) {
513 DEBUG2("failed to compute server's scalar and element");
516 if (((x = BN_new()) == NULL) ||
517 ((y = BN_new()) == NULL)) {
518 DEBUG2("server point allocation failed");
522 * element is a point, get both coordinates: x and y
524 if (!EC_POINT_get_affine_coordinates_GFp(pwd_session->group,
525 pwd_session->my_element, x, y,
527 DEBUG2("server point assignment failed");
535 pwd_session->out_buf_len = BN_num_bytes(pwd_session->order) +
536 (2 * BN_num_bytes(pwd_session->prime));
537 if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
538 radlog(L_ERR, "rlm_eap_pwd: out of memory to send commit");
541 memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
543 ptr = pwd_session->out_buf;
544 offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(x);
545 BN_bn2bin(x, ptr + offset);
547 ptr += BN_num_bytes(pwd_session->prime);
548 offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(y);
549 BN_bn2bin(y, ptr + offset);
551 ptr += BN_num_bytes(pwd_session->prime);
552 offset = BN_num_bytes(pwd_session->order) - BN_num_bytes(pwd_session->my_scalar);
553 BN_bn2bin(pwd_session->my_scalar, ptr + offset);
555 pwd_session->state = PWD_STATE_COMMIT;
556 ret = send_pwd_request(pwd_session, eap_ds);
558 case PWD_STATE_COMMIT:
559 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_COMMIT) {
560 RDEBUG2("pwd exchange is incorrect: not commit!");
564 * process the peer's commit and generate the shared key, k
566 if (process_peer_commit(pwd_session, buf, inst->bnctx)) {
567 RDEBUG2("failed to process peer's commit");
572 * compute our confirm blob
574 if (compute_server_confirm(pwd_session, pwd_session->my_confirm, inst->bnctx)) {
575 radlog(L_ERR, "rlm_eap_pwd: failed to compute confirm!");
579 * construct a response...which is just our confirm blob
581 pwd_session->out_buf_len = SHA256_DIGEST_LENGTH;
582 if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
583 radlog(L_ERR, "rlm_eap_pwd: out of memory to send confirm");
586 memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
587 memcpy(pwd_session->out_buf, pwd_session->my_confirm, SHA256_DIGEST_LENGTH);
589 pwd_session->state = PWD_STATE_CONFIRM;
590 ret = send_pwd_request(pwd_session, eap_ds);
592 case PWD_STATE_CONFIRM:
593 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_CONFIRM) {
594 RDEBUG2("pwd exchange is incorrect: not commit!");
597 if (compute_peer_confirm(pwd_session, peer_confirm, inst->bnctx)) {
598 RDEBUG2("pwd exchange cannot compute peer's confirm");
601 if (memcmp(peer_confirm, buf, SHA256_DIGEST_LENGTH)) {
602 RDEBUG2("pwd exchange fails: peer confirm is incorrect!");
605 if (compute_keys(pwd_session, peer_confirm, msk, emsk)) {
606 RDEBUG2("pwd exchange cannot generate (E)MSK!");
609 eap_ds->request->code = PW_EAP_SUCCESS;
611 * return the MSK (in halves)
613 outvps = &handler->request->reply->vps;
614 add_reply(outvps, "MS-MPPE-Recv-Key", msk, MPPE_KEY_LEN);
615 add_reply(outvps, "MS-MPPE-Send-Key", msk+MPPE_KEY_LEN, MPPE_KEY_LEN);
619 RDEBUG2("unknown PWD state");
624 * we processed the buffered fragments, get rid of them
626 if (pwd_session->in_buf) {
627 free(pwd_session->in_buf);
628 pwd_session->in_buf = NULL;
635 EAP_TYPE rlm_eap_pwd = {
637 eap_pwd_attach, /* attach */
638 eap_pwd_initiate, /* initiate to a client */
639 NULL, /* no authorization */
640 eap_pwd_authenticate, /* pwd authentication */
641 eap_pwd_detach /* detach */