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)
81 inst = (eap_pwd_t *) arg;
84 BN_CTX_free(inst->bnctx);
91 eap_pwd_attach (CONF_SECTION *cs, void **instance)
95 *instance = inst = talloc_zero(cs, eap_pwd_t);
98 inst->conf = talloc_zero(inst, EAP_PWD_CONF);
99 if (!inst->conf) return -1;
101 if (cf_section_parse(cs, inst->conf, pwd_module_config) < 0) {
105 if ((inst->bnctx = BN_CTX_new()) == NULL) {
106 radlog(L_ERR, "rlm_eap_pwd: failed to get BN context!");
114 free_session (void *data)
116 pwd_session_t *session = (pwd_session_t *)data;
118 if (session == NULL) {
119 radlog(L_ERR, "rlm_eap_pwd: freeing a NULL session...naughty, naughty");
122 BN_free(session->private_value);
123 BN_free(session->peer_scalar);
124 BN_free(session->my_scalar);
126 EC_POINT_free(session->my_element);
127 EC_POINT_free(session->peer_element);
128 EC_GROUP_free(session->group);
129 EC_POINT_free(session->pwe);
130 BN_free(session->order);
131 BN_free(session->prime);
137 send_pwd_request (pwd_session_t *sess, EAP_DS *eap_ds)
143 len = (sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr);
145 eap_ds->request->code = PW_EAP_REQUEST;
146 eap_ds->request->type.type = PW_EAP_PWD;
147 eap_ds->request->type.length = (len > sess->mtu) ? sess->mtu : len;
148 eap_ds->request->type.data = malloc(eap_ds->request->type.length);
149 memset(eap_ds->request->type.data, 0, eap_ds->request->type.length);
151 hdr = (pwd_hdr *)eap_ds->request->type.data;
152 switch (sess->state) {
153 case PWD_STATE_ID_REQ:
154 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_ID);
156 case PWD_STATE_COMMIT:
157 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_COMMIT);
159 case PWD_STATE_CONFIRM:
160 EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_CONFIRM);
163 DEBUG2("pwd state is messed up! Cannot send request");
167 * are we fragmenting?
169 if ((int)((sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr)) > sess->mtu) {
170 EAP_PWD_SET_MORE_BIT(hdr);
171 if (sess->out_buf_pos == 0) {
173 * the first fragment, add the total length
175 EAP_PWD_SET_LENGTH_BIT(hdr);
176 totlen = ntohs(sess->out_buf_len);
177 memcpy(hdr->data, (char *)&totlen, sizeof(totlen));
178 memcpy(hdr->data + sizeof(uint16_t),
180 sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
181 sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
184 * an intermediate fragment
186 memcpy(hdr->data, sess->out_buf + sess->out_buf_pos, (sess->mtu - sizeof(pwd_hdr)));
187 sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr));
191 * either it's not a fragment or it's the last fragment.
192 * The out buffer isn't needed anymore though so get rid of it.
194 memcpy(hdr->data, sess->out_buf + sess->out_buf_pos,
195 (sess->out_buf_len - sess->out_buf_pos));
197 sess->out_buf = NULL;
198 sess->out_buf_pos = sess->out_buf_len = 0;
204 eap_pwd_initiate (void *type_data, EAP_HANDLER *handler)
206 pwd_session_t *pwd_session;
207 eap_pwd_t *inst = (eap_pwd_t *)type_data;
211 if (!inst || !handler) {
212 radlog(L_ERR, "rlm_eap_pwd: initiate, NULL data provided");
217 * make sure the server's been configured properly
219 if (inst->conf->server_id == NULL) {
220 radlog(L_ERR, "rlm_eap_pwd: server ID is not configured!");
223 switch (inst->conf->group) {
231 radlog(L_ERR, "rlm_eap_pwd: group is not supported!");
235 if ((pwd_session = (pwd_session_t *)malloc(sizeof(*pwd_session))) == NULL) {
236 radlog(L_ERR, "rlm_eap_pwd: initiate, out of memory (1)");
240 * set things up so they can be free'd reliably
242 pwd_session->group_num = inst->conf->group;
243 pwd_session->private_value = NULL;
244 pwd_session->peer_scalar = NULL;
245 pwd_session->my_scalar = NULL;
246 pwd_session->k = NULL;
247 pwd_session->my_element = NULL;
248 pwd_session->peer_element = NULL;
249 pwd_session->group = NULL;
250 pwd_session->pwe = NULL;
251 pwd_session->order = NULL;
252 pwd_session->prime = NULL;
255 * figure out the MTU (basically do what eap-tls does)
257 pwd_session->mtu = inst->conf->fragment_size;
258 vp = pairfind(handler->request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY);
259 if (vp && ((int)(vp->vp_integer - 9) < pwd_session->mtu)) {
261 * 9 = 4 (EAPOL header) + 4 (EAP header) + 1 (EAP type)
263 * the fragmentation code deals with the included length
264 * so we don't need to subtract that here.
266 pwd_session->mtu = vp->vp_integer - 9;
269 pwd_session->state = PWD_STATE_ID_REQ;
270 pwd_session->in_buf = NULL;
271 pwd_session->out_buf_pos = 0;
272 handler->opaque = pwd_session;
273 handler->free_opaque = free_session;
276 * construct an EAP-pwd-ID/Request
278 pwd_session->out_buf_len = sizeof(pwd_id_packet) + strlen(inst->conf->server_id);
279 if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
280 radlog(L_ERR, "rlm_eap_pwd: initiate, out of memory to send ID request");
283 memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
285 pack = (pwd_id_packet *)pwd_session->out_buf;
286 pack->group_num = htons(pwd_session->group_num);
287 pack->random_function = EAP_PWD_DEF_RAND_FUN;
288 pack->prf = EAP_PWD_DEF_PRF;
289 pwd_session->token = random();
290 memcpy(pack->token, (char *)&pwd_session->token, 4);
291 pack->prep = EAP_PWD_PREP_NONE;
292 strcpy(pack->identity, inst->conf->server_id);
294 handler->stage = AUTHENTICATE;
296 return send_pwd_request(pwd_session, handler->eap_ds);
300 eap_pwd_authenticate (void *arg, EAP_HANDLER *handler)
302 pwd_session_t *pwd_session;
305 EAP_PACKET *response;
306 REQUEST *request, *fake;
307 VALUE_PAIR *pw, **outvps, *vp;
310 eap_pwd_t *inst = (eap_pwd_t *)arg;
312 uint8_t exch, *buf, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN];
313 uint8_t peer_confirm[SHA256_DIGEST_LENGTH];
314 BIGNUM *x = NULL, *y = NULL;
316 if ((handler == NULL) ||
317 ((eap_ds = handler->eap_ds) == NULL) ||
321 pwd_session = (pwd_session_t *)handler->opaque;
322 request = handler->request;
323 response = handler->eap_ds->response;
324 hdr = (pwd_hdr *)response->type.data;
327 len = response->type.length - sizeof(pwd_hdr);
330 * see if we're fragmenting, if so continue until we're done
332 if (pwd_session->out_buf_pos) {
334 RDEBUG2("pwd got something more than an ACK for a fragment");
336 return send_pwd_request(pwd_session, eap_ds);
340 * the first fragment will have a total length, make a
341 * buffer to hold all the fragments
343 if (EAP_PWD_GET_LENGTH_BIT(hdr)) {
344 if (pwd_session->in_buf) {
345 RDEBUG2("pwd already alloced buffer for fragments");
348 pwd_session->in_buf_len = ntohs(buf[0] * 256 | buf[1]);
349 if ((pwd_session->in_buf = malloc(pwd_session->in_buf_len)) == NULL) {
350 RDEBUG2("pwd cannot malloc %d buffer to hold fragments",
351 pwd_session->in_buf_len);
354 memset(pwd_session->in_buf, 0, pwd_session->in_buf_len);
355 pwd_session->in_buf_pos = 0;
356 buf += sizeof(uint16_t);
357 len -= sizeof(uint16_t);
361 * all fragments, including the 1st will have the M(ore) bit set,
362 * buffer those fragments!
364 if (EAP_PWD_GET_MORE_BIT(hdr)) {
365 rad_assert(pwd_session->in_buf != NULL);
366 if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
367 RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent.");
370 memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
371 pwd_session->in_buf_pos += len;
374 * send back an ACK for this fragment
376 exch = EAP_PWD_GET_EXCHANGE(hdr);
377 eap_ds->request->code = PW_EAP_REQUEST;
378 eap_ds->request->type.type = PW_EAP_PWD;
379 eap_ds->request->type.length = sizeof(pwd_hdr);
380 if ((eap_ds->request->type.data = malloc(sizeof(pwd_hdr))) == NULL) {
381 radlog(L_ERR, "rlm_eap_pwd: fragment ACK, out of memory");
384 hdr = (pwd_hdr *)eap_ds->request->type.data;
385 EAP_PWD_SET_EXCHANGE(hdr, exch);
390 if (pwd_session->in_buf) {
392 * the last fragment...
394 if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
395 RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent.");
398 memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
399 buf = pwd_session->in_buf;
400 len = pwd_session->in_buf_len;
403 switch (pwd_session->state) {
404 case PWD_STATE_ID_REQ:
405 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_ID) {
406 RDEBUG2("pwd exchange is incorrect: not ID");
409 id = (pwd_id_packet *)buf;
410 if ((id->prf != EAP_PWD_DEF_PRF) ||
411 (id->random_function != EAP_PWD_DEF_RAND_FUN) ||
412 (id->prep != EAP_PWD_PREP_NONE) ||
413 (memcmp(id->token, (char *)&pwd_session->token, 4)) ||
414 (id->group_num != ntohs(pwd_session->group_num))) {
415 RDEBUG2("pwd id response is invalid");
419 * we've agreed on the ciphersuite, record it...
421 ptr = (uint8_t *)&pwd_session->ciphersuite;
422 memcpy(ptr, (char *)&id->group_num, sizeof(uint16_t));
423 ptr += sizeof(uint16_t);
424 *ptr = EAP_PWD_DEF_RAND_FUN;
425 ptr += sizeof(uint8_t);
426 *ptr = EAP_PWD_DEF_PRF;
428 pwd_session->peer_id_len = len - sizeof(pwd_id_packet);
429 if (pwd_session->peer_id_len >= sizeof(pwd_session->peer_id)) {
430 RDEBUG2("pwd id response is malformed");
433 memcpy(pwd_session->peer_id, id->identity,
434 pwd_session->peer_id_len);
435 pwd_session->peer_id[pwd_session->peer_id_len] = '\0';
438 * make fake request to get the password for the usable ID
440 if ((fake = request_alloc_fake(handler->request)) == NULL) {
441 RDEBUG("pwd unable to create fake request!");
444 if ((fake->username = pairmake("User-Name", "", T_OP_EQ)) == NULL) {
445 RDEBUG("pwd unanable to create value pair for username!");
449 memcpy(fake->username->vp_strvalue, pwd_session->peer_id,
450 pwd_session->peer_id_len);
451 fake->username->length = pwd_session->peer_id_len;
452 fake->username->vp_strvalue[fake->username->length] = 0;
454 if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
455 fake->server = vp->vp_strvalue;
457 } else if (inst->conf->virtual_server) {
458 fake->server = inst->conf->virtual_server;
460 } /* else fake->server == request->server */
462 if ((debug_flag > 0) && fr_log_fp) {
463 RDEBUG("Sending tunneled request");
465 debug_pair_list(fake->packet->vps);
467 fprintf(fr_log_fp, "server %s {\n",
468 (fake->server == NULL) ? "" : fake->server);
472 * Call authorization recursively, which will
475 module_authorize(0, fake);
478 * Note that we don't do *anything* with the reply
481 if ((debug_flag > 0) && fr_log_fp) {
482 fprintf(fr_log_fp, "} # server %s\n",
483 (fake->server == NULL) ? "" : fake->server);
485 RDEBUG("Got tunneled reply code %d", fake->reply->code);
487 debug_pair_list(fake->reply->vps);
490 if ((pw = pairfind(fake->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) {
491 DEBUG2("failed to find password for %s to do pwd authentication",
492 pwd_session->peer_id);
497 if (compute_password_element(pwd_session, pwd_session->group_num,
498 pw->data.strvalue, strlen(pw->data.strvalue),
499 inst->conf->server_id, strlen(inst->conf->server_id),
500 pwd_session->peer_id, strlen(pwd_session->peer_id),
501 &pwd_session->token)) {
502 DEBUG2("failed to obtain password element :-(");
509 * compute our scalar and element
511 if (compute_scalar_element(pwd_session, inst->bnctx)) {
512 DEBUG2("failed to compute server's scalar and element");
515 if (((x = BN_new()) == NULL) ||
516 ((y = BN_new()) == NULL)) {
517 DEBUG2("server point allocation failed");
521 * element is a point, get both coordinates: x and y
523 if (!EC_POINT_get_affine_coordinates_GFp(pwd_session->group,
524 pwd_session->my_element, x, y,
526 DEBUG2("server point assignment failed");
534 pwd_session->out_buf_len = BN_num_bytes(pwd_session->order) +
535 (2 * BN_num_bytes(pwd_session->prime));
536 if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
537 radlog(L_ERR, "rlm_eap_pwd: out of memory to send commit");
540 memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
542 ptr = pwd_session->out_buf;
543 offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(x);
544 BN_bn2bin(x, ptr + offset);
546 ptr += BN_num_bytes(pwd_session->prime);
547 offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(y);
548 BN_bn2bin(y, ptr + offset);
550 ptr += BN_num_bytes(pwd_session->prime);
551 offset = BN_num_bytes(pwd_session->order) - BN_num_bytes(pwd_session->my_scalar);
552 BN_bn2bin(pwd_session->my_scalar, ptr + offset);
554 pwd_session->state = PWD_STATE_COMMIT;
555 ret = send_pwd_request(pwd_session, eap_ds);
557 case PWD_STATE_COMMIT:
558 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_COMMIT) {
559 RDEBUG2("pwd exchange is incorrect: not commit!");
563 * process the peer's commit and generate the shared key, k
565 if (process_peer_commit(pwd_session, buf, inst->bnctx)) {
566 RDEBUG2("failed to process peer's commit");
571 * compute our confirm blob
573 if (compute_server_confirm(pwd_session, pwd_session->my_confirm, inst->bnctx)) {
574 radlog(L_ERR, "rlm_eap_pwd: failed to compute confirm!");
578 * construct a response...which is just our confirm blob
580 pwd_session->out_buf_len = SHA256_DIGEST_LENGTH;
581 if ((pwd_session->out_buf = malloc(pwd_session->out_buf_len)) == NULL) {
582 radlog(L_ERR, "rlm_eap_pwd: out of memory to send confirm");
585 memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
586 memcpy(pwd_session->out_buf, pwd_session->my_confirm, SHA256_DIGEST_LENGTH);
588 pwd_session->state = PWD_STATE_CONFIRM;
589 ret = send_pwd_request(pwd_session, eap_ds);
591 case PWD_STATE_CONFIRM:
592 if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_CONFIRM) {
593 RDEBUG2("pwd exchange is incorrect: not commit!");
596 if (compute_peer_confirm(pwd_session, peer_confirm, inst->bnctx)) {
597 RDEBUG2("pwd exchange cannot compute peer's confirm");
600 if (memcmp(peer_confirm, buf, SHA256_DIGEST_LENGTH)) {
601 RDEBUG2("pwd exchange fails: peer confirm is incorrect!");
604 if (compute_keys(pwd_session, peer_confirm, msk, emsk)) {
605 RDEBUG2("pwd exchange cannot generate (E)MSK!");
608 eap_ds->request->code = PW_EAP_SUCCESS;
610 * return the MSK (in halves)
612 outvps = &handler->request->reply->vps;
613 add_reply(outvps, "MS-MPPE-Recv-Key", msk, MPPE_KEY_LEN);
614 add_reply(outvps, "MS-MPPE-Send-Key", msk+MPPE_KEY_LEN, MPPE_KEY_LEN);
618 RDEBUG2("unknown PWD state");
623 * we processed the buffered fragments, get rid of them
625 if (pwd_session->in_buf) {
626 free(pwd_session->in_buf);
627 pwd_session->in_buf = NULL;
634 EAP_TYPE rlm_eap_pwd = {
636 eap_pwd_attach, /* attach */
637 eap_pwd_initiate, /* initiate to a client */
638 NULL, /* no authorization */
639 eap_pwd_authenticate, /* pwd authentication */
640 eap_pwd_detach /* detach */