1214501Srpaulo/*
2214501Srpaulo * hostapd / EAP-GTC (RFC 3748)
3214501Srpaulo * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5214501Srpaulo * This program is free software; you can redistribute it and/or modify
6214501Srpaulo * it under the terms of the GNU General Public License version 2 as
7214501Srpaulo * published by the Free Software Foundation.
8214501Srpaulo *
9214501Srpaulo * Alternatively, this software may be distributed under the terms of BSD
10214501Srpaulo * license.
11214501Srpaulo *
12214501Srpaulo * See README and COPYING for more details.
13214501Srpaulo */
14214501Srpaulo
15214501Srpaulo#include "includes.h"
16214501Srpaulo
17214501Srpaulo#include "common.h"
18214501Srpaulo#include "eap_i.h"
19214501Srpaulo
20214501Srpaulo
21214501Srpaulostruct eap_gtc_data {
22214501Srpaulo	enum { CONTINUE, SUCCESS, FAILURE } state;
23214501Srpaulo	int prefix;
24214501Srpaulo};
25214501Srpaulo
26214501Srpaulo
27214501Srpaulostatic void * eap_gtc_init(struct eap_sm *sm)
28214501Srpaulo{
29214501Srpaulo	struct eap_gtc_data *data;
30214501Srpaulo
31214501Srpaulo	data = os_zalloc(sizeof(*data));
32214501Srpaulo	if (data == NULL)
33214501Srpaulo		return NULL;
34214501Srpaulo	data->state = CONTINUE;
35214501Srpaulo
36214501Srpaulo#ifdef EAP_SERVER_FAST
37214501Srpaulo	if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
38214501Srpaulo	    sm->m->method == EAP_TYPE_FAST) {
39214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
40214501Srpaulo			   "with challenge/response");
41214501Srpaulo		data->prefix = 1;
42214501Srpaulo	}
43214501Srpaulo#endif /* EAP_SERVER_FAST */
44214501Srpaulo
45214501Srpaulo	return data;
46214501Srpaulo}
47214501Srpaulo
48214501Srpaulo
49214501Srpaulostatic void eap_gtc_reset(struct eap_sm *sm, void *priv)
50214501Srpaulo{
51214501Srpaulo	struct eap_gtc_data *data = priv;
52214501Srpaulo	os_free(data);
53214501Srpaulo}
54214501Srpaulo
55214501Srpaulo
56214501Srpaulostatic struct wpabuf * eap_gtc_buildReq(struct eap_sm *sm, void *priv, u8 id)
57214501Srpaulo{
58214501Srpaulo	struct eap_gtc_data *data = priv;
59214501Srpaulo	struct wpabuf *req;
60214501Srpaulo	char *msg;
61214501Srpaulo	size_t msg_len;
62214501Srpaulo
63214501Srpaulo	msg = data->prefix ? "CHALLENGE=Password" : "Password";
64214501Srpaulo
65214501Srpaulo	msg_len = os_strlen(msg);
66214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, msg_len,
67214501Srpaulo			    EAP_CODE_REQUEST, id);
68214501Srpaulo	if (req == NULL) {
69214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for "
70214501Srpaulo			   "request");
71214501Srpaulo		data->state = FAILURE;
72214501Srpaulo		return NULL;
73214501Srpaulo	}
74214501Srpaulo
75214501Srpaulo	wpabuf_put_data(req, msg, msg_len);
76214501Srpaulo
77214501Srpaulo	data->state = CONTINUE;
78214501Srpaulo
79214501Srpaulo	return req;
80214501Srpaulo}
81214501Srpaulo
82214501Srpaulo
83214501Srpaulostatic Boolean eap_gtc_check(struct eap_sm *sm, void *priv,
84214501Srpaulo			     struct wpabuf *respData)
85214501Srpaulo{
86214501Srpaulo	const u8 *pos;
87214501Srpaulo	size_t len;
88214501Srpaulo
89214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &len);
90214501Srpaulo	if (pos == NULL || len < 1) {
91214501Srpaulo		wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame");
92214501Srpaulo		return TRUE;
93214501Srpaulo	}
94214501Srpaulo
95214501Srpaulo	return FALSE;
96214501Srpaulo}
97214501Srpaulo
98214501Srpaulo
99214501Srpaulostatic void eap_gtc_process(struct eap_sm *sm, void *priv,
100214501Srpaulo			    struct wpabuf *respData)
101214501Srpaulo{
102214501Srpaulo	struct eap_gtc_data *data = priv;
103214501Srpaulo	const u8 *pos;
104214501Srpaulo	size_t rlen;
105214501Srpaulo
106214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &rlen);
107214501Srpaulo	if (pos == NULL || rlen < 1)
108214501Srpaulo		return; /* Should not happen - frame already validated */
109214501Srpaulo
110214501Srpaulo	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen);
111214501Srpaulo
112214501Srpaulo#ifdef EAP_SERVER_FAST
113214501Srpaulo	if (data->prefix) {
114214501Srpaulo		const u8 *pos2, *end;
115214501Srpaulo		/* "RESPONSE=<user>\0<password>" */
116214501Srpaulo		if (rlen < 10) {
117214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-GTC: Too short response "
118214501Srpaulo				   "for EAP-FAST prefix");
119214501Srpaulo			data->state = FAILURE;
120214501Srpaulo			return;
121214501Srpaulo		}
122214501Srpaulo
123214501Srpaulo		end = pos + rlen;
124214501Srpaulo		pos += 9;
125214501Srpaulo		pos2 = pos;
126214501Srpaulo		while (pos2 < end && *pos2)
127214501Srpaulo			pos2++;
128214501Srpaulo		if (pos2 == end) {
129214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-GTC: No password in "
130214501Srpaulo				   "response to EAP-FAST prefix");
131214501Srpaulo			data->state = FAILURE;
132214501Srpaulo			return;
133214501Srpaulo		}
134214501Srpaulo
135214501Srpaulo		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Response user",
136214501Srpaulo				  pos, pos2 - pos);
137214501Srpaulo		if (sm->identity && sm->require_identity_match &&
138214501Srpaulo		    (pos2 - pos != (int) sm->identity_len ||
139214501Srpaulo		     os_memcmp(pos, sm->identity, sm->identity_len))) {
140214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-GTC: Phase 2 Identity did "
141214501Srpaulo				   "not match with required Identity");
142214501Srpaulo			wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Expected "
143214501Srpaulo					  "identity",
144214501Srpaulo					  sm->identity, sm->identity_len);
145214501Srpaulo			data->state = FAILURE;
146214501Srpaulo			return;
147214501Srpaulo		} else {
148214501Srpaulo			os_free(sm->identity);
149214501Srpaulo			sm->identity_len = pos2 - pos;
150214501Srpaulo			sm->identity = os_malloc(sm->identity_len);
151214501Srpaulo			if (sm->identity == NULL) {
152214501Srpaulo				data->state = FAILURE;
153214501Srpaulo				return;
154214501Srpaulo			}
155214501Srpaulo			os_memcpy(sm->identity, pos, sm->identity_len);
156214501Srpaulo		}
157214501Srpaulo
158214501Srpaulo		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
159214501Srpaulo			wpa_hexdump_ascii(MSG_DEBUG, "EAP-GTC: Phase2 "
160214501Srpaulo					  "Identity not found in the user "
161214501Srpaulo					  "database",
162214501Srpaulo					  sm->identity, sm->identity_len);
163214501Srpaulo			data->state = FAILURE;
164214501Srpaulo			return;
165214501Srpaulo		}
166214501Srpaulo
167214501Srpaulo		pos = pos2 + 1;
168214501Srpaulo		rlen = end - pos;
169214501Srpaulo		wpa_hexdump_ascii_key(MSG_MSGDUMP,
170214501Srpaulo				      "EAP-GTC: Response password",
171214501Srpaulo				      pos, rlen);
172214501Srpaulo	}
173214501Srpaulo#endif /* EAP_SERVER_FAST */
174214501Srpaulo
175214501Srpaulo	if (sm->user == NULL || sm->user->password == NULL ||
176214501Srpaulo	    sm->user->password_hash) {
177214501Srpaulo		wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not "
178214501Srpaulo			   "configured");
179214501Srpaulo		data->state = FAILURE;
180214501Srpaulo		return;
181214501Srpaulo	}
182214501Srpaulo
183214501Srpaulo	if (rlen != sm->user->password_len ||
184214501Srpaulo	    os_memcmp(pos, sm->user->password, rlen) != 0) {
185214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
186214501Srpaulo		data->state = FAILURE;
187214501Srpaulo	} else {
188214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success");
189214501Srpaulo		data->state = SUCCESS;
190214501Srpaulo	}
191214501Srpaulo}
192214501Srpaulo
193214501Srpaulo
194214501Srpaulostatic Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv)
195214501Srpaulo{
196214501Srpaulo	struct eap_gtc_data *data = priv;
197214501Srpaulo	return data->state != CONTINUE;
198214501Srpaulo}
199214501Srpaulo
200214501Srpaulo
201214501Srpaulostatic Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv)
202214501Srpaulo{
203214501Srpaulo	struct eap_gtc_data *data = priv;
204214501Srpaulo	return data->state == SUCCESS;
205214501Srpaulo}
206214501Srpaulo
207214501Srpaulo
208214501Srpauloint eap_server_gtc_register(void)
209214501Srpaulo{
210214501Srpaulo	struct eap_method *eap;
211214501Srpaulo	int ret;
212214501Srpaulo
213214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
214214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
215214501Srpaulo	if (eap == NULL)
216214501Srpaulo		return -1;
217214501Srpaulo
218214501Srpaulo	eap->init = eap_gtc_init;
219214501Srpaulo	eap->reset = eap_gtc_reset;
220214501Srpaulo	eap->buildReq = eap_gtc_buildReq;
221214501Srpaulo	eap->check = eap_gtc_check;
222214501Srpaulo	eap->process = eap_gtc_process;
223214501Srpaulo	eap->isDone = eap_gtc_isDone;
224214501Srpaulo	eap->isSuccess = eap_gtc_isSuccess;
225214501Srpaulo
226214501Srpaulo	ret = eap_server_method_register(eap);
227214501Srpaulo	if (ret)
228214501Srpaulo		eap_server_method_free(eap);
229214501Srpaulo	return ret;
230214501Srpaulo}
231