check user account validity after 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 ret = pam_acct_mgmt(pamh, 0);
126 if (ret != PAM_SUCCESS)
127 {
128 DBG1(DBG_IKE, "EAP-GTC pam_acct_mgmt failed: %s",
129 pam_strerror(pamh, ret));
130 }
131 }
132 else
133 {
134 DBG1(DBG_IKE, "EAP-GTC pam_authenticate failed: %s",
135 pam_strerror(pamh, ret));
136 }
137 pam_end(pamh, ret);
138 return ret == PAM_SUCCESS;
139 }
140
141 /**
142 * Implementation of eap_method_t.initiate for the server
143 */
144 static status_t initiate_server(private_eap_gtc_t *this, eap_payload_t **out)
145 {
146 eap_gtc_header_t *req;
147 size_t len;
148
149 len = strlen(GTC_REQUEST_MSG);
150 req = alloca(sizeof(eap_gtc_header_t) + len);
151 req->length = htons(sizeof(eap_gtc_header_t) + len);
152 req->code = EAP_REQUEST;
153 req->identifier = this->identifier;
154 req->type = EAP_GTC;
155 memcpy(req->data, GTC_REQUEST_MSG, len);
156
157 *out = eap_payload_create_data(chunk_create((void*)req,
158 sizeof(eap_gtc_header_t) + len));
159 return NEED_MORE;
160 }
161
162 /**
163 * Implementation of eap_method_t.process for the peer
164 */
165 static status_t process_peer(private_eap_gtc_t *this,
166 eap_payload_t *in, eap_payload_t **out)
167 {
168 eap_gtc_header_t *res;
169 shared_key_t *shared;
170 chunk_t key;
171 size_t len;
172
173 shared = charon->credentials->get_shared(charon->credentials, SHARED_EAP,
174 this->peer, this->server);
175 if (shared == NULL)
176 {
177 DBG1(DBG_IKE, "no EAP key found for '%D' - '%D'",
178 this->peer, this->server);
179 return FAILED;
180 }
181 key = shared->get_key(shared);
182 len = key.len;
183
184 /* TODO: According to the draft we should "SASLprep" password, RFC4013. */
185
186 res = alloca(sizeof(eap_gtc_header_t) + len);
187 res->length = htons(sizeof(eap_gtc_header_t) + len);
188 res->code = EAP_RESPONSE;
189 res->identifier = in->get_identifier(in);
190 res->type = EAP_GTC;
191 memcpy(res->data, key.ptr, len);
192
193 shared->destroy(shared);
194
195 *out = eap_payload_create_data(chunk_create((void*)res,
196 sizeof(eap_gtc_header_t) + len));
197 return NEED_MORE;
198 }
199
200 /**
201 * Implementation of eap_method_t.process for the server
202 */
203 static status_t process_server(private_eap_gtc_t *this,
204 eap_payload_t *in, eap_payload_t **out)
205 {
206 chunk_t data, encoding;
207 char *user, *password, *service, *pos;
208
209 data = chunk_skip(in->get_data(in), 5);
210 if (this->identifier != in->get_identifier(in) || !data.len)
211 {
212 DBG1(DBG_IKE, "received invalid EAP-GTC message");
213 return FAILED;
214 }
215
216 encoding = this->peer->get_encoding(this->peer);
217 /* if a RFC822_ADDR id is provided, we use the username part only */
218 pos = memchr(encoding.ptr, '@', encoding.len);
219 if (pos)
220 {
221 encoding.len = (u_char*)pos - encoding.ptr;
222 }
223 user = alloca(encoding.len + 1);
224 memcpy(user, encoding.ptr, encoding.len);
225 user[encoding.len] = '\0';
226
227 password = alloca(data.len + 1);
228 memcpy(password, data.ptr, data.len);
229 password[data.len] = '\0';
230
231 service = lib->settings->get_str(lib->settings,
232 "charon.plugins.eap_gtc.pam_service", GTC_PAM_SERVICE);
233
234 if (!authenticate(service, user, password))
235 {
236 return FAILED;
237 }
238 return SUCCESS;
239 }
240
241 /**
242 * Implementation of eap_method_t.get_type.
243 */
244 static eap_type_t get_type(private_eap_gtc_t *this, u_int32_t *vendor)
245 {
246 *vendor = 0;
247 return EAP_GTC;
248 }
249
250 /**
251 * Implementation of eap_method_t.get_msk.
252 */
253 static status_t get_msk(private_eap_gtc_t *this, chunk_t *msk)
254 {
255 return FAILED;
256 }
257
258 /**
259 * Implementation of eap_method_t.is_mutual.
260 */
261 static bool is_mutual(private_eap_gtc_t *this)
262 {
263 return FALSE;
264 }
265
266 /**
267 * Implementation of eap_method_t.destroy.
268 */
269 static void destroy(private_eap_gtc_t *this)
270 {
271 this->peer->destroy(this->peer);
272 this->server->destroy(this->server);
273 free(this);
274 }
275
276 /**
277 * Generic constructor
278 */
279 static private_eap_gtc_t *eap_gtc_create_generic(identification_t *server,
280 identification_t *peer)
281 {
282 private_eap_gtc_t *this = malloc_thing(private_eap_gtc_t);
283
284 this->public.eap_method_interface.initiate = NULL;
285 this->public.eap_method_interface.process = NULL;
286 this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
287 this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
288 this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
289 this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
290
291 /* private data */
292 this->peer = peer->clone(peer);
293 this->server = server->clone(server);
294 this->identifier = 0;
295
296 return this;
297 }
298
299 /*
300 * see header
301 */
302 eap_gtc_t *eap_gtc_create_server(identification_t *server, identification_t *peer)
303 {
304 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
305
306 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
307 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
308
309 /* generate a non-zero identifier */
310 do {
311 this->identifier = random();
312 } while (!this->identifier);
313
314 return &this->public;
315 }
316
317 /*
318 * see header
319 */
320 eap_gtc_t *eap_gtc_create_peer(identification_t *server, identification_t *peer)
321 {
322 private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
323
324 this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
325 this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
326
327 return &this->public;
328 }
329