use username part of RFC822 IDs for PAM authentication
[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 "password"
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 = NULL;
109 static struct pam_conv conv;
110 int ret;
111
112 conv.conv = (void*)auth_conv;
113 conv.appdata_ptr = password;
114
115 ret = pam_start(service, user, &conv, &pamh);
116 if (ret != PAM_SUCCESS)
117 {
118 DBG1(DBG_IKE, "EAP-GTC pam_start failed: %s",
119 pam_strerror(pamh, ret));
120 return FALSE;
121 }
122 ret = pam_authenticate(pamh, 0);
123 if (ret != PAM_SUCCESS)
124 {
125 DBG1(DBG_IKE, "EAP-GTC pam_authenticate failed: %s",
126 pam_strerror(pamh, ret));
127 }
128 pam_end(pamh, ret);
129 return ret == PAM_SUCCESS;
130 }
131
132 /**
133 * Implementation of eap_method_t.initiate for the server
134 */
135 static status_t initiate_server(private_eap_gtc_t *this, eap_payload_t **out)
136 {
137 eap_gtc_header_t *req;
138 size_t len;
139
140 len = strlen(GTC_REQUEST_MSG);
141 req = alloca(sizeof(eap_gtc_header_t) + len);
142 req->length = htons(sizeof(eap_gtc_header_t) + len);
143 req->code = EAP_REQUEST;
144 req->identifier = this->identifier;
145 req->type = EAP_GTC;
146 memcpy(req->data, GTC_REQUEST_MSG, len);
147
148 *out = eap_payload_create_data(chunk_create((void*)req,
149 sizeof(eap_gtc_header_t) + len));
150 return NEED_MORE;
151 }
152
153 /**
154 * Implementation of eap_method_t.process for the peer
155 */
156 static status_t process_peer(private_eap_gtc_t *this,
157 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 = charon->credentials->get_shared(charon->credentials, SHARED_EAP,
165 this->peer, this->server);
166 if (shared == NULL)
167 {
168 DBG1(DBG_IKE, "no EAP key found for '%D' - '%D'",
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 /**
192 * Implementation of eap_method_t.process for the server
193 */
194 static status_t process_server(private_eap_gtc_t *this,
195 eap_payload_t *in, eap_payload_t **out)
196 {
197 chunk_t data, encoding;
198 char *user, *password, *service, *pos;
199
200 data = chunk_skip(in->get_data(in), 5);
201 if (this->identifier != in->get_identifier(in) || !data.len)
202 {
203 DBG1(DBG_IKE, "received invalid EAP-GTC message");
204 return FAILED;
205 }
206
207 encoding = this->peer->get_encoding(this->peer);
208 /* if a RFC822_ADDR id is provided, we use the username part only */
209 pos = memchr(encoding.ptr, '@', encoding.len);
210 if (pos)
211 {
212 encoding.len = (u_char*)pos - encoding.ptr;
213 }
214 user = alloca(encoding.len + 1);
215 memcpy(user, encoding.ptr, encoding.len);
216 user[encoding.len] = '\0';
217
218 password = alloca(data.len + 1);
219 memcpy(password, data.ptr, data.len);
220 password[data.len] = '\0';
221
222 service = lib->settings->get_str(lib->settings,
223 "charon.plugins.eap_gtc.pam_service", GTC_PAM_SERVICE);
224
225 if (!authenticate(service, user, password))
226 {
227 return FAILED;
228 }
229 return SUCCESS;
230 }
231
232 /**
233 * Implementation of eap_method_t.get_type.
234 */
235 static eap_type_t get_type(private_eap_gtc_t *this, u_int32_t *vendor)
236 {
237 *vendor = 0;
238 return EAP_GTC;
239 }
240
241 /**
242 * Implementation of eap_method_t.get_msk.
243 */
244 static status_t get_msk(private_eap_gtc_t *this, chunk_t *msk)
245 {
246 return FAILED;
247 }
248
249 /**
250 * Implementation of eap_method_t.is_mutual.
251 */
252 static bool is_mutual(private_eap_gtc_t *this)
253 {
254 return FALSE;
255 }
256
257 /**
258 * Implementation of eap_method_t.destroy.
259 */
260 static void destroy(private_eap_gtc_t *this)
261 {
262 this->peer->destroy(this->peer);
263 this->server->destroy(this->server);
264 free(this);
265 }
266
267 /**
268 * Generic constructor
269 */
270 static private_eap_gtc_t *eap_gtc_create_generic(identification_t *server,
271 identification_t *peer)
272 {
273 private_eap_gtc_t *this = malloc_thing(private_eap_gtc_t);
274
275 this->public.eap_method_interface.initiate = NULL;
276 this->public.eap_method_interface.process = NULL;
277 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
278 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
279 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
280 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
281
282 /* private data */
283 this->peer = peer->clone(peer);
284 this->server = server->clone(server);
285 this->identifier = 0;
286
287 return this;
288 }
289
290 /*
291 * see header
292 */
293 eap_gtc_t *eap_gtc_create_server(identification_t *server, identification_t *peer)
294 {
295 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
296
297 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
298 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
299
300 /* generate a non-zero identifier */
301 do {
302 this->identifier = random();
303 } while (!this->identifier);
304
305 return &this->public;
306 }
307
308 /*
309 * see header
310 */
311 eap_gtc_t *eap_gtc_create_peer(identification_t *server, identification_t *peer)
312 {
313 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
314
315 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
316 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
317
318 return &this->public;
319 }
320