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