Add support for xlat'd challenge in rlm_eap_gtc
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_gtc / rlm_eap_gtc.c
1 /*
2  * rlm_eap_gtc.c    Handles that are called from eap
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2003,2006  The FreeRADIUS server project
21  */
22
23 #include <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include <freeradius-devel/autoconf.h>
27
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #include "eap.h"
32
33 #include <freeradius-devel/rad_assert.h>
34
35 /*
36  *      EAP-GTC is just ASCII data carried inside of the EAP session.
37  *      The length of the data is indicated by the encapsulating EAP
38  *      protocol.
39  */
40 typedef struct rlm_eap_gtc_t {
41         const char      *challenge;
42         const char      *auth_type_name;
43         int             auth_type;
44 } rlm_eap_gtc_t;
45
46 static CONF_PARSER module_config[] = {
47         { "challenge", PW_TYPE_STRING_PTR,
48           offsetof(rlm_eap_gtc_t, challenge), NULL, "Password: " },
49
50         { "auth_type", PW_TYPE_STRING_PTR,
51           offsetof(rlm_eap_gtc_t, auth_type_name), NULL, "PAP" },
52
53         { NULL, -1, 0, NULL, NULL }           /* end the list */
54 };
55
56
57 /*
58  *      Detach the module.
59  */
60 static int gtc_detach(void *arg)
61 {
62         rlm_eap_gtc_t *inst = (rlm_eap_gtc_t *) arg;
63
64
65         free(inst);
66
67         return 0;
68 }
69
70 /*
71  *      Attach the module.
72  */
73 static int gtc_attach(CONF_SECTION *cs, void **instance)
74 {
75         rlm_eap_gtc_t   *inst;
76         DICT_VALUE      *dval;
77
78         inst = malloc(sizeof(*inst));
79         if (!inst) {
80                 radlog(L_ERR, "rlm_eap_gtc: out of memory");
81                 return -1;
82         }
83         memset(inst, 0, sizeof(*inst));
84
85         /*
86          *      Parse the configuration attributes.
87          */
88         if (cf_section_parse(cs, inst, module_config) < 0) {
89                 gtc_detach(inst);
90                 return -1;
91         }
92
93         dval = dict_valbyname(PW_AUTH_TYPE, 0, inst->auth_type_name);
94         if (!dval) {
95                 radlog(L_ERR, "rlm_eap_gtc: Unknown Auth-Type %s",
96                        inst->auth_type_name);
97                 gtc_detach(inst);
98                 return -1;
99         }
100
101         inst->auth_type = dval->value;
102
103         *instance = inst;
104
105         return 0;
106 }
107
108 /*
109  *      Initiate the EAP-GTC session by sending a challenge to the peer.
110  */
111 static int gtc_initiate(void *type_data, EAP_HANDLER *handler)
112 {
113         char challenge_str[1024];
114         int length;
115         EAP_DS *eap_ds = handler->eap_ds;
116         rlm_eap_gtc_t *inst = (rlm_eap_gtc_t *) type_data;
117
118         if (!radius_xlat(challenge_str, sizeof(challenge_str), inst->challenge, handler->request, NULL)) {
119                 radlog(L_ERR, "rlm_eap_gtc: xlat failed.", inst->challenge);
120                 return 0;
121         }
122
123         length = strlen(challenge_str);
124
125         /*
126          *      We're sending a request...
127          */
128         eap_ds->request->code = PW_EAP_REQUEST;
129
130         eap_ds->request->type.data = malloc(length);
131         if (eap_ds->request->type.data == NULL) {
132                 radlog(L_ERR, "rlm_eap_gtc: out of memory");
133                 return 0;
134         }
135
136         memcpy(eap_ds->request->type.data, challenge_str, length);
137         eap_ds->request->type.length = length;
138
139         /*
140          *      We don't need to authorize the user at this point.
141          *
142          *      We also don't need to keep the challenge, as it's
143          *      stored in 'handler->eap_ds', which will be given back
144          *      to us...
145          */
146         handler->stage = AUTHENTICATE;
147
148         return 1;
149 }
150
151
152 /*
153  *      Authenticate a previously sent challenge.
154  */
155 static int gtc_authenticate(void *type_data, EAP_HANDLER *handler)
156 {
157         VALUE_PAIR *vp;
158         EAP_DS *eap_ds = handler->eap_ds;
159         rlm_eap_gtc_t *inst = (rlm_eap_gtc_t *) type_data;
160
161         /*
162          *      Get the Cleartext-Password for this user.
163          */
164         rad_assert(handler->request != NULL);
165         rad_assert(handler->stage == AUTHENTICATE);
166
167         /*
168          *      Sanity check the response.  We need at least one byte
169          *      of data.
170          */
171         if (eap_ds->response->length <= 4) {
172                 radlog(L_ERR, "rlm_eap_gtc: corrupted data");
173                 eap_ds->request->code = PW_EAP_FAILURE;
174                 return 0;
175         }
176
177 #if 0
178         if ((debug_flag > 2) && fr_log_fp) {
179                 int i;
180
181                 for (i = 0; i < eap_ds->response->length - 4; i++) {
182                         if ((i & 0x0f) == 0) fprintf(fr_log_fp, "%d: ", i);
183
184                         fprintf(fr_log_fp, "%02x ", eap_ds->response->type.data[i]);
185
186                         if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
187                 }
188         }
189 #endif
190
191         /*
192          *      Handle passwords here.
193          */
194         if (inst->auth_type == PW_AUTHTYPE_LOCAL) {
195                 /*
196                  *      For now, do clear-text password authentication.
197                  */
198                 vp = pairfind(handler->request->config_items, PW_CLEARTEXT_PASSWORD, 0);
199                 if (!vp) {
200                         DEBUG2("  rlm_eap_gtc: ERROR: Cleartext-Password is required for authentication.");
201                         eap_ds->request->code = PW_EAP_FAILURE;
202                         return 0;
203                 }
204
205                 if (eap_ds->response->type.length != vp->length) {
206                   DEBUG2("  rlm_eap_gtc: ERROR: Passwords are of different length. %d %d", (int) eap_ds->response->type.length, (int) vp->length);
207                         eap_ds->request->code = PW_EAP_FAILURE;
208                         return 0;
209                 }
210
211                 if (memcmp(eap_ds->response->type.data,
212                            vp->vp_strvalue, vp->length) != 0) {
213                         DEBUG2("  rlm_eap_gtc: ERROR: Passwords are different");
214                         eap_ds->request->code = PW_EAP_FAILURE;
215                         return 0;
216                 }
217
218                 /*
219                  *      EAP packets can be ~64k long maximum, and
220                  *      we don't like that.
221                  */
222         } else if (eap_ds->response->type.length <= 128) {
223                 int rcode;
224
225                 /*
226                  *      If there was a User-Password in the request,
227                  *      why the heck are they using EAP-GTC?
228                  */
229                 pairdelete(&handler->request->packet->vps, PW_USER_PASSWORD, 0);
230
231                 vp = pairmake("User-Password", "", T_OP_EQ);
232                 if (!vp) {
233                         radlog(L_ERR, "rlm_eap_gtc: out of memory");
234                         return 0;
235                 }
236                 vp->length = eap_ds->response->type.length;
237                 memcpy(vp->vp_strvalue, eap_ds->response->type.data, vp->length);
238                 vp->vp_strvalue[vp->length] = 0;
239
240                 /*
241                  *      Add the password to the request, and allow
242                  *      another module to do the work of authenticating it.
243                  */
244                 pairadd(&handler->request->packet->vps, vp);
245                 handler->request->password = vp;
246
247                 /*
248                  *      This is a wild & crazy hack.
249                  */
250                 rcode = module_authenticate(inst->auth_type, handler->request);
251                 if (rcode != RLM_MODULE_OK) {
252                         eap_ds->request->code = PW_EAP_FAILURE;
253                         return 0;
254                 }
255
256         } else {
257                 radlog(L_ERR, "rlm_eap_gtc: Response is too large to understand");
258                 eap_ds->request->code = PW_EAP_FAILURE;
259                 return 0;
260
261         }
262
263         DEBUG2("  rlm_eap_gtc: Everything is OK.");
264
265         eap_ds->request->code = PW_EAP_SUCCESS;
266
267         return 1;
268 }
269
270 /*
271  *      The module name should be the only globally exported symbol.
272  *      That is, everything else should be 'static'.
273  */
274 EAP_TYPE rlm_eap_gtc = {
275         "eap_gtc",
276         gtc_attach,                     /* attach */
277         gtc_initiate,                   /* Start the initial request */
278         NULL,                           /* authorization */
279         gtc_authenticate,               /* authentication */
280         gtc_detach                      /* detach */
281 };