Moved credential manager to libstrongswan
[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 /**
74 * Implementation of eap_method_t.initiate for the peer
75 */
76 static status_t initiate_peer(private_eap_gtc_t *this, eap_payload_t **out)
77 {
78 /* peer never initiates */
79 return FAILED;
80 }
81
82 /**
83 * PAM conv callback function
84 */
85 static int auth_conv(int num_msg, const struct pam_message **msg,
86 struct pam_response **resp, char *password)
87 {
88 struct pam_response *response;
89
90 if (num_msg != 1)
91 {
92 return PAM_CONV_ERR;
93 }
94 response = malloc(sizeof(struct pam_response));
95 response->resp = strdup(password);
96 response->resp_retcode = 0;
97 *resp = response;
98 return PAM_SUCCESS;
99 }
100
101 /**
102 * Authenticate a username/password using PAM
103 */
104 static bool authenticate(char *service, char *user, char *password)
105 {
106 pam_handle_t *pamh = NULL;
107 static struct pam_conv conv;
108 int ret;
109
110 conv.conv = (void*)auth_conv;
111 conv.appdata_ptr = password;
112
113 ret = pam_start(service, user, &conv, &pamh);
114 if (ret != PAM_SUCCESS)
115 {
116 DBG1(DBG_IKE, "EAP-GTC pam_start failed: %s",
117 pam_strerror(pamh, ret));
118 return FALSE;
119 }
120 ret = pam_authenticate(pamh, 0);
121 if (ret == PAM_SUCCESS)
122 {
123 ret = pam_acct_mgmt(pamh, 0);
124 if (ret != PAM_SUCCESS)
125 {
126 DBG1(DBG_IKE, "EAP-GTC pam_acct_mgmt failed: %s",
127 pam_strerror(pamh, ret));
128 }
129 }
130 else
131 {
132 DBG1(DBG_IKE, "EAP-GTC pam_authenticate failed: %s",
133 pam_strerror(pamh, ret));
134 }
135 pam_end(pamh, ret);
136 return ret == PAM_SUCCESS;
137 }
138
139 /**
140 * Implementation of eap_method_t.initiate for the server
141 */
142 static status_t initiate_server(private_eap_gtc_t *this, eap_payload_t **out)
143 {
144 eap_gtc_header_t *req;
145 size_t len;
146
147 len = strlen(GTC_REQUEST_MSG);
148 req = alloca(sizeof(eap_gtc_header_t) + len);
149 req->length = htons(sizeof(eap_gtc_header_t) + len);
150 req->code = EAP_REQUEST;
151 req->identifier = this->identifier;
152 req->type = EAP_GTC;
153 memcpy(req->data, GTC_REQUEST_MSG, len);
154
155 *out = eap_payload_create_data(chunk_create((void*)req,
156 sizeof(eap_gtc_header_t) + len));
157 return NEED_MORE;
158 }
159
160 /**
161 * Implementation of eap_method_t.process for the peer
162 */
163 static status_t process_peer(private_eap_gtc_t *this,
164 eap_payload_t *in, eap_payload_t **out)
165 {
166 eap_gtc_header_t *res;
167 shared_key_t *shared;
168 chunk_t key;
169 size_t len;
170
171 shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP,
172 this->peer, this->server);
173 if (shared == NULL)
174 {
175 DBG1(DBG_IKE, "no EAP key found for '%Y' - '%Y'",
176 this->peer, this->server);
177 return FAILED;
178 }
179 key = shared->get_key(shared);
180 len = key.len;
181
182 /* TODO: According to the draft we should "SASLprep" password, RFC4013. */
183
184 res = alloca(sizeof(eap_gtc_header_t) + len);
185 res->length = htons(sizeof(eap_gtc_header_t) + len);
186 res->code = EAP_RESPONSE;
187 res->identifier = in->get_identifier(in);
188 res->type = EAP_GTC;
189 memcpy(res->data, key.ptr, len);
190
191 shared->destroy(shared);
192
193 *out = eap_payload_create_data(chunk_create((void*)res,
194 sizeof(eap_gtc_header_t) + len));
195 return NEED_MORE;
196 }
197
198 /**
199 * Implementation of eap_method_t.process for the server
200 */
201 static status_t process_server(private_eap_gtc_t *this,
202 eap_payload_t *in, eap_payload_t **out)
203 {
204 chunk_t data, encoding;
205 char *user, *password, *service, *pos;
206
207 data = chunk_skip(in->get_data(in), 5);
208 if (this->identifier != in->get_identifier(in) || !data.len)
209 {
210 DBG1(DBG_IKE, "received invalid EAP-GTC message");
211 return FAILED;
212 }
213
214 encoding = this->peer->get_encoding(this->peer);
215 /* if a RFC822_ADDR id is provided, we use the username part only */
216 pos = memchr(encoding.ptr, '@', encoding.len);
217 if (pos)
218 {
219 encoding.len = (u_char*)pos - encoding.ptr;
220 }
221 user = alloca(encoding.len + 1);
222 memcpy(user, encoding.ptr, encoding.len);
223 user[encoding.len] = '\0';
224
225 password = alloca(data.len + 1);
226 memcpy(password, data.ptr, data.len);
227 password[data.len] = '\0';
228
229 service = lib->settings->get_str(lib->settings,
230 "charon.plugins.eap-gtc.pam_service", GTC_PAM_SERVICE);
231
232 if (!authenticate(service, user, password))
233 {
234 return FAILED;
235 }
236 return SUCCESS;
237 }
238
239 /**
240 * Implementation of eap_method_t.get_type.
241 */
242 static eap_type_t get_type(private_eap_gtc_t *this, u_int32_t *vendor)
243 {
244 *vendor = 0;
245 return EAP_GTC;
246 }
247
248 /**
249 * Implementation of eap_method_t.get_msk.
250 */
251 static status_t get_msk(private_eap_gtc_t *this, chunk_t *msk)
252 {
253 return FAILED;
254 }
255
256 /**
257 * Implementation of eap_method_t.is_mutual.
258 */
259 static bool is_mutual(private_eap_gtc_t *this)
260 {
261 return FALSE;
262 }
263
264 /**
265 * Implementation of eap_method_t.destroy.
266 */
267 static void destroy(private_eap_gtc_t *this)
268 {
269 this->peer->destroy(this->peer);
270 this->server->destroy(this->server);
271 free(this);
272 }
273
274 /**
275 * Generic constructor
276 */
277 static private_eap_gtc_t *eap_gtc_create_generic(identification_t *server,
278 identification_t *peer)
279 {
280 private_eap_gtc_t *this = malloc_thing(private_eap_gtc_t);
281
282 this->public.eap_method_interface.initiate = NULL;
283 this->public.eap_method_interface.process = NULL;
284 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
285 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
286 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
287 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
288
289 /* private data */
290 this->peer = peer->clone(peer);
291 this->server = server->clone(server);
292 this->identifier = 0;
293
294 return this;
295 }
296
297 /*
298 * see header
299 */
300 eap_gtc_t *eap_gtc_create_server(identification_t *server, identification_t *peer)
301 {
302 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
303
304 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
305 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
306
307 /* generate a non-zero identifier */
308 do {
309 this->identifier = random();
310 } while (!this->identifier);
311
312 return &this->public;
313 }
314
315 /*
316 * see header
317 */
318 eap_gtc_t *eap_gtc_create_peer(identification_t *server, identification_t *peer)
319 {
320 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
321
322 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
323 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
324
325 return &this->public;
326 }
327