Home | History | Annotate | Download | only in wpa_supplicant
      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