implemented get|set_identifier() for eap_gtc_t
[strongswan.git] / src / libcharon / plugins / eap_gtc / eap_gtc.c
1 /*
2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "eap_gtc.h"
17
18 #include <daemon.h>
19 #include <library.h>
20 #include <crypto/hashers/hasher.h>
21
22 #include <security/pam_appl.h>
23
24 #define GTC_REQUEST_MSG "password"
25 #define GTC_PAM_SERVICE "login"
26
27 typedef struct private_eap_gtc_t private_eap_gtc_t;
28
29 /**
30 * Private data of an eap_gtc_t object.
31 */
32 struct private_eap_gtc_t {
33
34 /**
35 * Public authenticator_t interface.
36 */
37 eap_gtc_t public;
38
39 /**
40 * ID of the server
41 */
42 identification_t *server;
43
44 /**
45 * ID of the peer
46 */
47 identification_t *peer;
48
49 /**
50 * EAP message identififier
51 */
52 u_int8_t identifier;
53 };
54
55 typedef struct eap_gtc_header_t eap_gtc_header_t;
56
57 /**
58 * packed eap GTC header struct
59 */
60 struct eap_gtc_header_t {
61 /** EAP code (REQUEST/RESPONSE) */
62 u_int8_t code;
63 /** unique message identifier */
64 u_int8_t identifier;
65 /** length of whole message */
66 u_int16_t length;
67 /** EAP type */
68 u_int8_t type;
69 /** type data */
70 u_int8_t data[];
71 } __attribute__((__packed__));
72
73 METHOD(eap_method_t, initiate_peer, status_t,
74 private_eap_gtc_t *this, eap_payload_t **out)
75 {
76 /* peer never initiates */
77 return FAILED;
78 }
79
80 /**
81 * PAM conv callback function
82 */
83 static int auth_conv(int num_msg, const struct pam_message **msg,
84 struct pam_response **resp, char *password)
85 {
86 struct pam_response *response;
87
88 if (num_msg != 1)
89 {
90 return PAM_CONV_ERR;
91 }
92 response = malloc(sizeof(struct pam_response));
93 response->resp = strdup(password);
94 response->resp_retcode = 0;
95 *resp = response;
96 return PAM_SUCCESS;
97 }
98
99 /**
100 * Authenticate a username/password using PAM
101 */
102 static bool authenticate(char *service, char *user, char *password)
103 {
104 pam_handle_t *pamh = NULL;
105 static struct pam_conv conv;
106 int ret;
107
108 conv.conv = (void*)auth_conv;
109 conv.appdata_ptr = password;
110
111 ret = pam_start(service, user, &conv, &pamh);
112 if (ret != PAM_SUCCESS)
113 {
114 DBG1(DBG_IKE, "EAP-GTC pam_start failed: %s",
115 pam_strerror(pamh, ret));
116 return FALSE;
117 }
118 ret = pam_authenticate(pamh, 0);
119 if (ret == PAM_SUCCESS)
120 {
121 ret = pam_acct_mgmt(pamh, 0);
122 if (ret != PAM_SUCCESS)
123 {
124 DBG1(DBG_IKE, "EAP-GTC pam_acct_mgmt failed: %s",
125 pam_strerror(pamh, ret));
126 }
127 }
128 else
129 {
130 DBG1(DBG_IKE, "EAP-GTC pam_authenticate failed: %s",
131 pam_strerror(pamh, ret));
132 }
133 pam_end(pamh, ret);
134 return ret == PAM_SUCCESS;
135 }
136
137 METHOD(eap_method_t, initiate_server, status_t,
138 private_eap_gtc_t *this, eap_payload_t **out)
139 {
140 eap_gtc_header_t *req;
141 size_t len;
142
143 len = strlen(GTC_REQUEST_MSG);
144 req = alloca(sizeof(eap_gtc_header_t) + len);
145 req->length = htons(sizeof(eap_gtc_header_t) + len);
146 req->code = EAP_REQUEST;
147 req->identifier = this->identifier;
148 req->type = EAP_GTC;
149 memcpy(req->data, GTC_REQUEST_MSG, len);
150
151 *out = eap_payload_create_data(chunk_create((void*)req,
152 sizeof(eap_gtc_header_t) + len));
153 return NEED_MORE;
154 }
155
156 METHOD(eap_method_t, process_peer, status_t,
157 private_eap_gtc_t *this, eap_payload_t *in, eap_payload_t **out)
158 {
159 eap_gtc_header_t *res;
160 shared_key_t *shared;
161 chunk_t key;
162 size_t len;
163
164 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP,
165 this->peer, this->server);
166 if (shared == NULL)
167 {
168 DBG1(DBG_IKE, "no EAP key found for '%Y' - '%Y'",
169 this->peer, this->server);
170 return FAILED;
171 }
172 key = shared->get_key(shared);
173 len = key.len;
174
175 /* TODO: According to the draft we should "SASLprep" password, RFC4013. */
176
177 res = alloca(sizeof(eap_gtc_header_t) + len);
178 res->length = htons(sizeof(eap_gtc_header_t) + len);
179 res->code = EAP_RESPONSE;
180 res->identifier = in->get_identifier(in);
181 res->type = EAP_GTC;
182 memcpy(res->data, key.ptr, len);
183
184 shared->destroy(shared);
185
186 *out = eap_payload_create_data(chunk_create((void*)res,
187 sizeof(eap_gtc_header_t) + len));
188 return NEED_MORE;
189 }
190
191 METHOD(eap_method_t, process_server, status_t,
192 private_eap_gtc_t *this, eap_payload_t *in, eap_payload_t **out)
193 {
194 chunk_t data, encoding;
195 char *user, *password, *service, *pos;
196
197 data = chunk_skip(in->get_data(in), 5);
198 if (this->identifier != in->get_identifier(in) || !data.len)
199 {
200 DBG1(DBG_IKE, "received invalid EAP-GTC message");
201 return FAILED;
202 }
203
204 encoding = this->peer->get_encoding(this->peer);
205 /* if a RFC822_ADDR id is provided, we use the username part only */
206 pos = memchr(encoding.ptr, '@', encoding.len);
207 if (pos)
208 {
209 encoding.len = (u_char*)pos - encoding.ptr;
210 }
211 user = alloca(encoding.len + 1);
212 memcpy(user, encoding.ptr, encoding.len);
213 user[encoding.len] = '\0';
214
215 password = alloca(data.len + 1);
216 memcpy(password, data.ptr, data.len);
217 password[data.len] = '\0';
218
219 service = lib->settings->get_str(lib->settings,
220 "charon.plugins.eap-gtc.pam_service", GTC_PAM_SERVICE);
221
222 if (!authenticate(service, user, password))
223 {
224 return FAILED;
225 }
226 return SUCCESS;
227 }
228
229 METHOD(eap_method_t, get_type, eap_type_t,
230 private_eap_gtc_t *this, u_int32_t *vendor)
231 {
232 *vendor = 0;
233 return EAP_GTC;
234 }
235
236 METHOD(eap_method_t, get_msk, status_t,
237 private_eap_gtc_t *this, chunk_t *msk)
238 {
239 return FAILED;
240 }
241
242 METHOD(eap_method_t, get_identifier, u_int8_t,
243 private_eap_gtc_t *this)
244 {
245 return this->identifier;
246 }
247
248 METHOD(eap_method_t, set_identifier, void,
249 private_eap_gtc_t *this, u_int8_t identifier)
250 {
251 this->identifier = identifier;
252 }
253
254 METHOD(eap_method_t, is_mutual, bool,
255 private_eap_gtc_t *this)
256 {
257 return FALSE;
258 }
259
260 METHOD(eap_method_t, destroy, void,
261 private_eap_gtc_t *this)
262 {
263 this->peer->destroy(this->peer);
264 this->server->destroy(this->server);
265 free(this);
266 }
267
268 /**
269 * Generic constructor
270 */
271 static private_eap_gtc_t *eap_gtc_create_generic(identification_t *server,
272 identification_t *peer)
273 {
274 private_eap_gtc_t *this;
275
276 INIT(this,
277 .public = {
278 .eap_method_interface = {
279 .get_type = _get_type,
280 .is_mutual = _is_mutual,
281 .get_msk = _get_msk,
282 .get_identifier = _get_identifier,
283 .set_identifier = _set_identifier,
284 .destroy = _destroy,
285 },
286 },
287 .peer = peer->clone(peer),
288 .server = server->clone(server),
289 );
290
291 return this;
292 }
293
294 /*
295 * see header
296 */
297 eap_gtc_t *eap_gtc_create_server(identification_t *server, identification_t *peer)
298 {
299 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
300
301 this->public.eap_method_interface.initiate = _initiate_server;
302 this->public.eap_method_interface.process = _process_server;
303
304 /* generate a non-zero identifier */
305 do {
306 this->identifier = random();
307 } while (!this->identifier);
308
309 return &this->public;
310 }
311
312 /*
313 * see header
314 */
315 eap_gtc_t *eap_gtc_create_peer(identification_t *server, identification_t *peer)
316 {
317 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
318
319 this->public.eap_method_interface.initiate = _initiate_peer;
320 this->public.eap_method_interface.process = _process_peer;
321
322 return &this->public;
323 }
324