a (incomplete) implementation of draft-sheffer-ikev2-gtc-00.txt using PAM
[strongswan.git] / src / charon / 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 * $Id: eap_gtc.c 3806 2008-04-15 05:56:35Z martin $
16 */
17
18 #include "eap_gtc.h"
19
20 #include <daemon.h>
21 #include <library.h>
22 #include <crypto/hashers/hasher.h>
23
24 #include <security/pam_appl.h>
25
26 #define GTC_REQUEST_MSG "login"
27 #define GTC_PAM_SERVICE "login"
28
29 typedef struct private_eap_gtc_t private_eap_gtc_t;
30
31 /**
32 * Private data of an eap_gtc_t object.
33 */
34 struct private_eap_gtc_t {
35
36 /**
37 * Public authenticator_t interface.
38 */
39 eap_gtc_t public;
40
41 /**
42 * ID of the server
43 */
44 identification_t *server;
45
46 /**
47 * ID of the peer
48 */
49 identification_t *peer;
50
51 /**
52 * EAP message identififier
53 */
54 u_int8_t identifier;
55 };
56
57 typedef struct eap_gtc_header_t eap_gtc_header_t;
58
59 /**
60 * packed eap GTC header struct
61 */
62 struct eap_gtc_header_t {
63 /** EAP code (REQUEST/RESPONSE) */
64 u_int8_t code;
65 /** unique message identifier */
66 u_int8_t identifier;
67 /** length of whole message */
68 u_int16_t length;
69 /** EAP type */
70 u_int8_t type;
71 /** type data */
72 u_int8_t data[];
73 } __attribute__((__packed__));
74
75 /**
76 * Implementation of eap_method_t.initiate for the peer
77 */
78 static status_t initiate_peer(private_eap_gtc_t *this, eap_payload_t **out)
79 {
80 /* peer never initiates */
81 return FAILED;
82 }
83
84 /**
85 * PAM conv callback function
86 */
87 static int auth_conv(int num_msg, const struct pam_message **msg,
88 struct pam_response **resp, char *password)
89 {
90 struct pam_response *response;
91
92 if (num_msg != 1)
93 {
94 return PAM_CONV_ERR;
95 }
96 response = malloc(sizeof(struct pam_response));
97 response->resp = strdup(password);
98 response->resp_retcode = 0;
99 *resp = response;
100 return PAM_SUCCESS;
101 }
102
103 /**
104 * Authenticate a username/password using PAM
105 */
106 static bool authenticate(char *service, char *user, char *password)
107 {
108 pam_handle_t *pamh;
109 static struct pam_conv conv;
110 int ret;
111
112 conv.conv = (void*)auth_conv;
113 conv.appdata_ptr = password;
114
115 if (pam_start(service, user, &conv, &pamh) != PAM_SUCCESS)
116 {
117 return FALSE;
118 }
119 ret = pam_authenticate(pamh, 0);
120 pam_end(pamh, ret);
121 return ret == PAM_SUCCESS;
122 }
123
124 /**
125 * Implementation of eap_method_t.initiate for the server
126 */
127 static status_t initiate_server(private_eap_gtc_t *this, eap_payload_t **out)
128 {
129 eap_gtc_header_t *req;
130 size_t len;
131
132 len = strlen(GTC_REQUEST_MSG);
133 req = alloca(sizeof(eap_gtc_header_t) + len);
134 req->length = htons(sizeof(eap_gtc_header_t) + len);
135 req->code = EAP_REQUEST;
136 req->identifier = this->identifier;
137 req->type = EAP_GTC;
138 memcpy(req->data, GTC_REQUEST_MSG, len);
139
140 *out = eap_payload_create_data(chunk_create((void*)req,
141 sizeof(eap_gtc_header_t) + len));
142 return NEED_MORE;
143 }
144
145 /**
146 * Implementation of eap_method_t.process for the peer
147 */
148 static status_t process_peer(private_eap_gtc_t *this,
149 eap_payload_t *in, eap_payload_t **out)
150 {
151 eap_gtc_header_t *res;
152 shared_key_t *shared;
153 chunk_t key;
154 size_t len;
155
156 shared = charon->credentials->get_shared(charon->credentials, SHARED_EAP,
157 this->server, this->peer);
158 if (shared == NULL)
159 {
160 DBG1(DBG_IKE, "no EAP key found for '%D' - '%D'",
161 this->server, this->peer);
162 return FAILED;
163 }
164 key = shared->get_key(shared);
165 len = key.len;
166
167 res = alloca(sizeof(eap_gtc_header_t) + len);
168 res->length = htons(sizeof(eap_gtc_header_t) + len);
169 res->code = EAP_RESPONSE;
170 res->identifier = in->get_identifier(in);
171 res->type = EAP_GTC;
172 memcpy(res->data, key.ptr, len);
173
174 shared->destroy(shared);
175
176 *out = eap_payload_create_data(chunk_create((void*)res,
177 sizeof(eap_gtc_header_t) + len));
178 return NEED_MORE;
179 }
180
181 /**
182 * Implementation of eap_method_t.process for the server
183 */
184 static status_t process_server(private_eap_gtc_t *this,
185 eap_payload_t *in, eap_payload_t **out)
186 {
187 chunk_t data, encoding;
188 char *user, *password, *service;
189
190 data = chunk_skip(in->get_data(in), 5);
191 if (this->identifier != in->get_identifier(in) || !data.len)
192 {
193 DBG1(DBG_IKE, "received invalid EAP-GTC message");
194 return FAILED;
195 }
196
197 encoding = this->peer->get_encoding(this->peer);
198 user = alloca(encoding.len + 1);
199 memcpy(user, encoding.ptr, encoding.len);
200 user[encoding.len] = '\0';
201
202 password = alloca(data.len + 1);
203 memcpy(password, data.ptr, data.len);
204 password[data.len] = '\0';
205
206 service = lib->settings->get_str(lib->settings,
207 "charon.plugins.eap_gtc.pam_service", GTC_PAM_SERVICE);
208
209 /* TODO: According to the draft we should "SASLprep" username and
210 * passwords... RFC4013 */
211 if (!authenticate(service, user, password))
212 {
213 DBG1(DBG_IKE, "EAP-GTC PAM authentication failed");
214 return FAILED;
215 }
216 return SUCCESS;
217 }
218
219 /**
220 * Implementation of eap_method_t.get_type.
221 */
222 static eap_type_t get_type(private_eap_gtc_t *this, u_int32_t *vendor)
223 {
224 *vendor = 0;
225 return EAP_GTC;
226 }
227
228 /**
229 * Implementation of eap_method_t.get_msk.
230 */
231 static status_t get_msk(private_eap_gtc_t *this, chunk_t *msk)
232 {
233 return FAILED;
234 }
235
236 /**
237 * Implementation of eap_method_t.is_mutual.
238 */
239 static bool is_mutual(private_eap_gtc_t *this)
240 {
241 return FALSE;
242 }
243
244 /**
245 * Implementation of eap_method_t.destroy.
246 */
247 static void destroy(private_eap_gtc_t *this)
248 {
249 free(this);
250 }
251
252 /**
253 * Generic constructor
254 */
255 static private_eap_gtc_t *eap_gtc_create_generic(identification_t *server,
256 identification_t *peer)
257 {
258 private_eap_gtc_t *this = malloc_thing(private_eap_gtc_t);
259
260 this->public.eap_method_interface.initiate = NULL;
261 this->public.eap_method_interface.process = NULL;
262 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
263 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
264 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
265 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
266
267 /* private data */
268 this->peer = peer;
269 this->server = server;
270 this->identifier = random();
271
272 return this;
273 }
274
275 /*
276 * see header
277 */
278 eap_gtc_t *eap_gtc_create_server(identification_t *server, identification_t *peer)
279 {
280 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
281
282 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
283 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
284
285 return &this->public;
286 }
287
288 /*
289 * see header
290 */
291 eap_gtc_t *eap_gtc_create_peer(identification_t *server, identification_t *peer)
292 {
293 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
294
295 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
296 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
297
298 return &this->public;
299 }
300