Annotation of embedaddon/strongswan/src/libtls/tls_eap.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (C) 2010 Martin Willi
! 4: * Copyright (C) 2010 revosec AG
! 5: *
! 6: * This program is free software; you can redistribute it and/or modify it
! 7: * under the terms of the GNU General Public License as published by the
! 8: * Free Software Foundation; either version 2 of the License, or (at your
! 9: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 10: *
! 11: * This program is distributed in the hope that it will be useful, but
! 12: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 13: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 14: * for more details.
! 15: */
! 16:
! 17: #include "tls_eap.h"
! 18:
! 19: #include "tls.h"
! 20:
! 21: #include <utils/debug.h>
! 22: #include <library.h>
! 23:
! 24: /**
! 25: * Size limit for a TLS message allowing for worst-case protection overhead
! 26: * according to section 6.2.3. "Payload Protection" of RFC 5246 TLS 1.2
! 27: */
! 28: #define TLS_MAX_MESSAGE_LEN 4 * (TLS_MAX_FRAGMENT_LEN + 2048)
! 29:
! 30: typedef struct private_tls_eap_t private_tls_eap_t;
! 31:
! 32: /**
! 33: * Private data of an tls_eap_t object.
! 34: */
! 35: struct private_tls_eap_t {
! 36:
! 37: /**
! 38: * Public tls_eap_t interface.
! 39: */
! 40: tls_eap_t public;
! 41:
! 42: /**
! 43: * Type of EAP method, EAP-TLS, EAP-TTLS, or EAP-TNC
! 44: */
! 45: eap_type_t type;
! 46:
! 47: /**
! 48: * Current value of EAP identifier
! 49: */
! 50: uint8_t identifier;
! 51:
! 52: /**
! 53: * TLS stack
! 54: */
! 55: tls_t *tls;
! 56:
! 57: /**
! 58: * Role
! 59: */
! 60: bool is_server;
! 61:
! 62: /**
! 63: * Supported version of the EAP tunnel protocol
! 64: */
! 65: uint8_t supported_version;
! 66:
! 67: /**
! 68: * If FALSE include the total length of an EAP message
! 69: * in the first fragment of fragmented messages only.
! 70: * If TRUE also include the length in non-fragmented messages.
! 71: */
! 72: bool include_length;
! 73:
! 74: /**
! 75: * First fragment of a multi-fragment record?
! 76: */
! 77: bool first_fragment;
! 78:
! 79: /**
! 80: * Maximum size of an outgoing EAP-TLS fragment
! 81: */
! 82: size_t frag_size;
! 83:
! 84: /**
! 85: * Number of EAP messages/fragments processed so far
! 86: */
! 87: int processed;
! 88:
! 89: /**
! 90: * Maximum number of processed EAP messages/fragments
! 91: */
! 92: int max_msg_count;
! 93: };
! 94:
! 95: /**
! 96: * Flags of an EAP-TLS/TTLS/TNC message
! 97: */
! 98: typedef enum {
! 99: EAP_TLS_LENGTH = (1<<7), /* shared with EAP-TTLS/TNC/PEAP */
! 100: EAP_TLS_MORE_FRAGS = (1<<6), /* shared with EAP-TTLS/TNC/PEAP */
! 101: EAP_TLS_START = (1<<5), /* shared with EAP-TTLS/TNC/PEAP */
! 102: EAP_TTLS_VERSION = (0x07), /* shared with EAP-TNC/PEAP/PT-EAP */
! 103: EAP_PT_START = (1<<7) /* PT-EAP only */
! 104: } eap_tls_flags_t;
! 105:
! 106: #define EAP_TTLS_SUPPORTED_VERSION 0
! 107: #define EAP_TNC_SUPPORTED_VERSION 1
! 108: #define EAP_PEAP_SUPPORTED_VERSION 0
! 109: #define EAP_PT_EAP_SUPPORTED_VERSION 1
! 110:
! 111: /**
! 112: * EAP-TLS/TTLS packet format
! 113: */
! 114: typedef struct __attribute__((packed)) {
! 115: uint8_t code;
! 116: uint8_t identifier;
! 117: uint16_t length;
! 118: uint8_t type;
! 119: uint8_t flags;
! 120: } eap_tls_packet_t;
! 121:
! 122: METHOD(tls_eap_t, initiate, status_t,
! 123: private_tls_eap_t *this, chunk_t *out)
! 124: {
! 125: if (this->is_server)
! 126: {
! 127: eap_tls_packet_t pkt = {
! 128: .type = this->type,
! 129: .code = EAP_REQUEST,
! 130: .flags = this->supported_version
! 131: };
! 132: switch (this->type)
! 133: {
! 134: case EAP_TLS:
! 135: case EAP_TTLS:
! 136: case EAP_TNC:
! 137: case EAP_PEAP:
! 138: pkt.flags |= EAP_TLS_START;
! 139: break;
! 140: case EAP_PT_EAP:
! 141: pkt.flags |= EAP_PT_START;
! 142: break;
! 143: default:
! 144: break;
! 145: }
! 146: htoun16(&pkt.length, sizeof(eap_tls_packet_t));
! 147: pkt.identifier = this->identifier;
! 148:
! 149: *out = chunk_clone(chunk_from_thing(pkt));
! 150: DBG2(DBG_TLS, "sending %N start packet (%u bytes)",
! 151: eap_type_names, this->type, sizeof(eap_tls_packet_t));
! 152: DBG3(DBG_TLS, "%B", out);
! 153: return NEED_MORE;
! 154: }
! 155: return FAILED;
! 156: }
! 157:
! 158: /**
! 159: * Process a received packet
! 160: */
! 161: static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt)
! 162: {
! 163: uint8_t version;
! 164: uint16_t pkt_len;
! 165: uint32_t msg_len;
! 166: size_t msg_len_offset = 0;
! 167:
! 168: /* EAP-TLS doesn't have a version field */
! 169: if (this->type != EAP_TLS)
! 170: {
! 171: version = pkt->flags & EAP_TTLS_VERSION;
! 172: if (version != this->supported_version)
! 173: {
! 174: DBG1(DBG_TLS, "received %N packet with unsupported version v%u",
! 175: eap_type_names, this->type, version);
! 176: return FAILED;
! 177: }
! 178: }
! 179: pkt_len = untoh16(&pkt->length);
! 180:
! 181: if (this->type != EAP_PT_EAP && (pkt->flags & EAP_TLS_LENGTH))
! 182: {
! 183: if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len))
! 184: {
! 185: DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type);
! 186: return FAILED;
! 187: }
! 188: msg_len = untoh32(pkt + 1);
! 189: if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) ||
! 190: msg_len > TLS_MAX_MESSAGE_LEN)
! 191: {
! 192: DBG1(DBG_TLS, "invalid %N packet length (%u bytes)", eap_type_names,
! 193: this->type, msg_len);
! 194: return FAILED;
! 195: }
! 196: msg_len_offset = sizeof(msg_len);
! 197: }
! 198:
! 199: return this->tls->process(this->tls, (char*)(pkt + 1) + msg_len_offset,
! 200: pkt_len - sizeof(eap_tls_packet_t) - msg_len_offset);
! 201: }
! 202:
! 203: /**
! 204: * Build a packet to send
! 205: */
! 206: static status_t build_pkt(private_tls_eap_t *this, chunk_t *out)
! 207: {
! 208: char buf[this->frag_size];
! 209: eap_tls_packet_t *pkt;
! 210: size_t len, reclen, msg_len_offset;
! 211: status_t status;
! 212: char *kind;
! 213:
! 214: if (this->is_server)
! 215: {
! 216: this->identifier++;
! 217: }
! 218: pkt = (eap_tls_packet_t*)buf;
! 219: pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE;
! 220: pkt->identifier = this->identifier;
! 221: pkt->type = this->type;
! 222: pkt->flags = this->supported_version;
! 223:
! 224: if (this->first_fragment)
! 225: {
! 226: len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(uint32_t);
! 227: msg_len_offset = sizeof(uint32_t);
! 228: }
! 229: else
! 230: {
! 231: len = sizeof(buf) - sizeof(eap_tls_packet_t);
! 232: msg_len_offset = 0;
! 233: }
! 234: status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) +
! 235: msg_len_offset, &len, &reclen);
! 236:
! 237: switch (status)
! 238: {
! 239: case NEED_MORE:
! 240: pkt->flags |= EAP_TLS_MORE_FRAGS;
! 241: kind = "further fragment";
! 242: if (this->first_fragment)
! 243: {
! 244: pkt->flags |= EAP_TLS_LENGTH;
! 245: this->first_fragment = FALSE;
! 246: kind = "first fragment";
! 247: }
! 248: break;
! 249: case ALREADY_DONE:
! 250: if (this->first_fragment)
! 251: {
! 252: if (this->include_length)
! 253: {
! 254: pkt->flags |= EAP_TLS_LENGTH;
! 255: }
! 256: kind = "packet";
! 257: }
! 258: else if (this->type != EAP_TNC && this->type != EAP_PT_EAP)
! 259: {
! 260: this->first_fragment = TRUE;
! 261: kind = "final fragment";
! 262: }
! 263: else
! 264: {
! 265: kind = "packet";
! 266: }
! 267: break;
! 268: default:
! 269: return status;
! 270: }
! 271: if (reclen)
! 272: {
! 273: if (pkt->flags & EAP_TLS_LENGTH)
! 274: {
! 275: htoun32(pkt + 1, reclen);
! 276: len += sizeof(uint32_t);
! 277: pkt->flags |= EAP_TLS_LENGTH;
! 278: }
! 279: else
! 280: {
! 281: /* get rid of the reserved length field */
! 282: memmove(buf + sizeof(eap_tls_packet_t),
! 283: buf + sizeof(eap_tls_packet_t) + sizeof(uint32_t), len);
! 284: }
! 285: }
! 286: len += sizeof(eap_tls_packet_t);
! 287: htoun16(&pkt->length, len);
! 288: *out = chunk_clone(chunk_create(buf, len));
! 289: DBG2(DBG_TLS, "sending %N %s (%u bytes)",
! 290: eap_type_names, this->type, kind, len);
! 291: DBG3(DBG_TLS, "%B", out);
! 292: return NEED_MORE;
! 293: }
! 294:
! 295: /**
! 296: * Send an ack to request next fragment
! 297: */
! 298: static chunk_t create_ack(private_tls_eap_t *this)
! 299: {
! 300: eap_tls_packet_t pkt = {
! 301: .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE,
! 302: .type = this->type,
! 303: };
! 304:
! 305: if (this->is_server)
! 306: {
! 307: this->identifier++;
! 308: }
! 309: pkt.identifier = this->identifier;
! 310: htoun16(&pkt.length, sizeof(pkt));
! 311:
! 312: switch (this->type)
! 313: {
! 314: case EAP_TTLS:
! 315: pkt.flags |= EAP_TTLS_SUPPORTED_VERSION;
! 316: break;
! 317: case EAP_TNC:
! 318: pkt.flags |= EAP_TNC_SUPPORTED_VERSION;
! 319: break;
! 320: case EAP_PEAP:
! 321: pkt.flags |= EAP_PEAP_SUPPORTED_VERSION;
! 322: break;
! 323: default:
! 324: break;
! 325: }
! 326: DBG2(DBG_TLS, "sending %N acknowledgement packet",
! 327: eap_type_names, this->type);
! 328: return chunk_clone(chunk_from_thing(pkt));
! 329: }
! 330:
! 331: METHOD(tls_eap_t, process, status_t,
! 332: private_tls_eap_t *this, chunk_t in, chunk_t *out)
! 333: {
! 334: eap_tls_packet_t *pkt;
! 335: status_t status;
! 336:
! 337: if (this->max_msg_count && ++this->processed > this->max_msg_count)
! 338: {
! 339: DBG1(DBG_TLS, "%N packet count exceeded (%d > %d)",
! 340: eap_type_names, this->type,
! 341: this->processed, this->max_msg_count);
! 342: return FAILED;
! 343: }
! 344:
! 345: pkt = (eap_tls_packet_t*)in.ptr;
! 346: if (in.len < sizeof(eap_tls_packet_t) || untoh16(&pkt->length) != in.len)
! 347: {
! 348: DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type);
! 349: return FAILED;
! 350: }
! 351:
! 352: /* update EAP identifier */
! 353: if (!this->is_server)
! 354: {
! 355: this->identifier = pkt->identifier;
! 356: }
! 357: DBG3(DBG_TLS, "%N payload %B", eap_type_names, this->type, &in);
! 358:
! 359: if ((this->type == EAP_PT_EAP && (pkt->flags & EAP_PT_START)) ||
! 360: (pkt->flags & EAP_TLS_START))
! 361: {
! 362: if (this->type == EAP_TTLS || this->type == EAP_TNC ||
! 363: this->type == EAP_PEAP || this->type == EAP_PT_EAP)
! 364: {
! 365: DBG1(DBG_TLS, "%N version is v%u", eap_type_names, this->type,
! 366: pkt->flags & EAP_TTLS_VERSION);
! 367: }
! 368: }
! 369: else
! 370: {
! 371: if (in.len == sizeof(eap_tls_packet_t))
! 372: {
! 373: DBG2(DBG_TLS, "received %N acknowledgement packet",
! 374: eap_type_names, this->type);
! 375: status = build_pkt(this, out);
! 376: if (status == INVALID_STATE && this->tls->is_complete(this->tls))
! 377: {
! 378: return SUCCESS;
! 379: }
! 380: return status;
! 381: }
! 382: status = process_pkt(this, pkt);
! 383: switch (status)
! 384: {
! 385: case NEED_MORE:
! 386: break;
! 387: case SUCCESS:
! 388: return this->tls->is_complete(this->tls) ? SUCCESS : FAILED;
! 389: default:
! 390: return status;
! 391: }
! 392: }
! 393: status = build_pkt(this, out);
! 394: switch (status)
! 395: {
! 396: case INVALID_STATE:
! 397: *out = create_ack(this);
! 398: return NEED_MORE;
! 399: case FAILED:
! 400: if (!this->is_server)
! 401: {
! 402: *out = create_ack(this);
! 403: return NEED_MORE;
! 404: }
! 405: return FAILED;
! 406: default:
! 407: return status;
! 408: }
! 409: }
! 410:
! 411: METHOD(tls_eap_t, get_msk, chunk_t,
! 412: private_tls_eap_t *this)
! 413: {
! 414: return this->tls->get_eap_msk(this->tls);
! 415: }
! 416:
! 417: METHOD(tls_eap_t, get_identifier, uint8_t,
! 418: private_tls_eap_t *this)
! 419: {
! 420: return this->identifier;
! 421: }
! 422:
! 423: METHOD(tls_eap_t, set_identifier, void,
! 424: private_tls_eap_t *this, uint8_t identifier)
! 425: {
! 426: this->identifier = identifier;
! 427: }
! 428:
! 429: METHOD(tls_eap_t, get_auth, auth_cfg_t*,
! 430: private_tls_eap_t *this)
! 431: {
! 432: return this->tls->get_auth(this->tls);
! 433: }
! 434:
! 435: METHOD(tls_eap_t, destroy, void,
! 436: private_tls_eap_t *this)
! 437: {
! 438: this->tls->destroy(this->tls);
! 439: free(this);
! 440: }
! 441:
! 442: /**
! 443: * See header
! 444: */
! 445: tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size,
! 446: int max_msg_count, bool include_length)
! 447: {
! 448: private_tls_eap_t *this;
! 449:
! 450: if (!tls)
! 451: {
! 452: return NULL;
! 453: }
! 454:
! 455: INIT(this,
! 456: .public = {
! 457: .initiate = _initiate,
! 458: .process = _process,
! 459: .get_msk = _get_msk,
! 460: .get_identifier = _get_identifier,
! 461: .set_identifier = _set_identifier,
! 462: .get_auth = _get_auth,
! 463: .destroy = _destroy,
! 464: },
! 465: .type = type,
! 466: .is_server = tls->is_server(tls),
! 467: .first_fragment = (type != EAP_TNC && type != EAP_PT_EAP),
! 468: .frag_size = frag_size,
! 469: .max_msg_count = max_msg_count,
! 470: .include_length = include_length,
! 471: .tls = tls,
! 472: );
! 473:
! 474: switch (type)
! 475: {
! 476: case EAP_TTLS:
! 477: this->supported_version = EAP_TTLS_SUPPORTED_VERSION;
! 478: break;
! 479: case EAP_TNC:
! 480: this->supported_version = EAP_TNC_SUPPORTED_VERSION;
! 481: break;
! 482: case EAP_PEAP:
! 483: this->supported_version = EAP_PEAP_SUPPORTED_VERSION;
! 484: break;
! 485: case EAP_PT_EAP:
! 486: this->supported_version = EAP_PT_EAP_SUPPORTED_VERSION;
! 487: break;
! 488: default:
! 489: break;
! 490: }
! 491:
! 492: if (this->is_server)
! 493: {
! 494: do
! 495: { /* start with non-zero random identifier */
! 496: this->identifier = random();
! 497: }
! 498: while (!this->identifier);
! 499: }
! 500:
! 501: return &this->public;
! 502: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>