1 /* 2 * EAP peer method: EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt) 3 * Copyright (c) 2004-2008, Jouni Malinen <j (at) w1.fi> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15 #include "includes.h" 16 17 #include "common.h" 18 #include "eap_i.h" 19 #include "eap_tlv.h" 20 21 22 /** 23 * eap_tlv_build_nak - Build EAP-TLV NAK message 24 * @id: EAP identifier for the header 25 * @nak_type: TLV type (EAP_TLV_*) 26 * @resp_len: Buffer for returning the response length 27 * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure 28 * 29 * This funtion builds an EAP-TLV NAK message. The caller is responsible for 30 * freeing the returned buffer. 31 */ 32 u8 * eap_tlv_build_nak(int id, u16 nak_type, size_t *resp_len) 33 { 34 struct eap_hdr *hdr; 35 u8 *pos; 36 37 hdr = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, resp_len, 38 10, EAP_CODE_RESPONSE, id, &pos); 39 if (hdr == NULL) 40 return NULL; 41 42 *pos++ = 0x80; /* Mandatory */ 43 *pos++ = EAP_TLV_NAK_TLV; 44 /* Length */ 45 *pos++ = 0; 46 *pos++ = 6; 47 /* Vendor-Id */ 48 *pos++ = 0; 49 *pos++ = 0; 50 *pos++ = 0; 51 *pos++ = 0; 52 /* NAK-Type */ 53 WPA_PUT_BE16(pos, nak_type); 54 55 return (u8 *) hdr; 56 } 57 58 59 /** 60 * eap_tlv_build_result - Build EAP-TLV Result message 61 * @id: EAP identifier for the header 62 * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) 63 * @resp_len: Buffer for returning the response length 64 * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure 65 * 66 * This funtion builds an EAP-TLV Result message. The caller is responsible for 67 * freeing the returned buffer. 68 */ 69 u8 * eap_tlv_build_result(int id, u16 status, size_t *resp_len) 70 { 71 struct eap_hdr *hdr; 72 u8 *pos; 73 74 hdr = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, resp_len, 75 6, EAP_CODE_RESPONSE, id, &pos); 76 if (hdr == NULL) 77 return NULL; 78 79 *pos++ = 0x80; /* Mandatory */ 80 *pos++ = EAP_TLV_RESULT_TLV; 81 /* Length */ 82 *pos++ = 0; 83 *pos++ = 2; 84 /* Status */ 85 WPA_PUT_BE16(pos, status); 86 87 return (u8 *) hdr; 88 } 89 90 91 /** 92 * eap_tlv_process - Process a received EAP-TLV message and generate a response 93 * @sm: Pointer to EAP state machine allocated with eap_sm_init() 94 * @ret: Return values from EAP request validation and processing 95 * @hdr: EAP-TLV request to be processed. The caller must have validated that 96 * the buffer is large enough to contain full request (hdr->length bytes) and 97 * that the EAP type is EAP_TYPE_TLV. 98 * @resp: Buffer to return a pointer to the allocated response message. This 99 * field should be initialized to %NULL before the call. The value will be 100 * updated if a response message is generated. The caller is responsible for 101 * freeing the allocated message. 102 * @resp_len: Buffer for returning the response length 103 * Returns: 0 on success, -1 on failure 104 */ 105 int eap_tlv_process(struct eap_sm *sm, struct eap_method_ret *ret, 106 const struct eap_hdr *hdr, u8 **resp, size_t *resp_len, 107 int force_failure) 108 { 109 size_t left, tlv_len; 110 const u8 *pos; 111 const u8 *result_tlv = NULL; 112 size_t result_tlv_len = 0; 113 int tlv_type, mandatory; 114 115 /* Parse TLVs */ 116 left = be_to_host16(hdr->length) - sizeof(struct eap_hdr) - 1; 117 pos = (const u8 *) (hdr + 1); 118 pos++; 119 wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); 120 while (left >= 4) { 121 mandatory = !!(pos[0] & 0x80); 122 tlv_type = WPA_GET_BE16(pos) & 0x3fff; 123 pos += 2; 124 tlv_len = WPA_GET_BE16(pos); 125 pos += 2; 126 left -= 4; 127 if (tlv_len > left) { 128 wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " 129 "(tlv_len=%lu left=%lu)", 130 (unsigned long) tlv_len, 131 (unsigned long) left); 132 return -1; 133 } 134 switch (tlv_type) { 135 case EAP_TLV_RESULT_TLV: 136 result_tlv = pos; 137 result_tlv_len = tlv_len; 138 break; 139 default: 140 wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " 141 "%d%s", tlv_type, 142 mandatory ? " (mandatory)" : ""); 143 if (mandatory) { 144 /* NAK TLV and ignore all TLVs in this packet. 145 */ 146 *resp = eap_tlv_build_nak(hdr->identifier, 147 tlv_type, resp_len); 148 return *resp == NULL ? -1 : 0; 149 } 150 /* Ignore this TLV, but process other TLVs */ 151 break; 152 } 153 154 pos += tlv_len; 155 left -= tlv_len; 156 } 157 if (left) { 158 wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " 159 "Request (left=%lu)", (unsigned long) left); 160 return -1; 161 } 162 163 /* Process supported TLVs */ 164 if (result_tlv) { 165 int status, resp_status; 166 wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", 167 result_tlv, result_tlv_len); 168 if (result_tlv_len < 2) { 169 wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " 170 "(len=%lu)", 171 (unsigned long) result_tlv_len); 172 return -1; 173 } 174 status = WPA_GET_BE16(result_tlv); 175 if (status == EAP_TLV_RESULT_SUCCESS) { 176 wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " 177 "- EAP-TLV/Phase2 Completed"); 178 if (force_failure) { 179 wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure" 180 " - force failed Phase 2"); 181 resp_status = EAP_TLV_RESULT_FAILURE; 182 ret->decision = DECISION_FAIL; 183 } else { 184 resp_status = EAP_TLV_RESULT_SUCCESS; 185 ret->decision = DECISION_UNCOND_SUCC; 186 } 187 } else if (status == EAP_TLV_RESULT_FAILURE) { 188 wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); 189 resp_status = EAP_TLV_RESULT_FAILURE; 190 ret->decision = DECISION_FAIL; 191 } else { 192 wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " 193 "Status %d", status); 194 resp_status = EAP_TLV_RESULT_FAILURE; 195 ret->decision = DECISION_FAIL; 196 } 197 ret->methodState = METHOD_DONE; 198 199 *resp = eap_tlv_build_result(hdr->identifier, resp_status, 200 resp_len); 201 } 202 203 return 0; 204 } 205