Merge pull request #15 from pobept/dictionary-redback
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_tls / rlm_eap_tls.c
1 /*
2  * rlm_eap_tls.c  contains the interfaces 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 2001  hereUare Communications, Inc. <raghud@hereuare.com>
21  * Copyright 2003  Alan DeKok <aland@freeradius.org>
22  * Copyright 2006  The FreeRADIUS server project
23  *
24  */
25
26 #include <freeradius-devel/ident.h>
27 RCSID("$Id$")
28
29 #include <freeradius-devel/autoconf.h>
30
31 #ifdef HAVE_OPENSSL_RAND_H
32 #include <openssl/rand.h>
33 #endif
34
35 #ifdef HAVE_OPENSSL_EVP_H
36 #include <openssl/evp.h>
37 #endif
38
39 #include "rlm_eap_tls.h"
40 #include "config.h"
41
42 #ifdef HAVE_SYS_STAT_H
43 #include <sys/stat.h>
44 #endif
45
46 /*
47  *      Detach the EAP-TLS module.
48  */
49 static int eaptls_detach(void *arg)
50 {
51         rlm_eap_tls_t *inst = (rlm_eap_tls_t *) arg;
52
53         free(inst);
54
55         return 0;
56 }
57
58
59 /*
60  *      Attach the EAP-TLS module.
61  */
62 static int eaptls_attach(CONF_SECTION *cs, void **instance)
63 {
64         rlm_eap_tls_t           *inst;
65
66         /*
67          *      Parse the config file & get all the configured values
68          */
69         inst = rad_malloc(sizeof(*inst));
70         if (!inst) {
71                 radlog(L_ERR, "rlm_eap_tls: out of memory");
72                 return -1;
73         }
74         memset(inst, 0, sizeof(*inst));
75
76         if (cf_section_parse(cs, inst, module_config) < 0) {
77                 eaptls_detach(inst);
78                 return -1;
79         }
80
81         inst->tls_conf = eaptls_conf_parse(cs, "tls");
82
83         if (!inst->tls_conf) {
84                 radlog(L_ERR, "rlm_eap_tls: Failed initializing SSL context");
85                 eaptls_detach(inst);
86                 return -1;
87         }
88
89         *instance = inst;
90
91         return 0;
92 }
93
94
95 /*
96  *      Send an initial eap-tls request to the peer, using the libeap functions.
97  */
98 static int eaptls_initiate(void *type_arg, EAP_HANDLER *handler)
99 {
100         int             status;
101         tls_session_t   *ssn;
102         rlm_eap_tls_t   *inst;
103         REQUEST         *request = handler->request;
104
105         inst = type_arg;
106
107         handler->tls = TRUE;
108         handler->finished = FALSE;
109
110         /*
111          *      EAP-TLS always requires a client certificate.
112          */
113         ssn = eaptls_session(inst->tls_conf, handler, TRUE);
114         if (!ssn) {
115                 return 0;
116         }
117
118         handler->opaque = ((void *)ssn);
119         handler->free_opaque = session_free;
120
121         /*
122          *      Set up type-specific information.
123          */
124         ssn->prf_label = "client EAP encryption";
125
126         /*
127          *      TLS session initialization is over.  Now handle TLS
128          *      related handshaking or application data.
129          */
130         status = eaptls_start(handler->eap_ds, ssn->peap_flag);
131         RDEBUG2("Start returned %d", status);
132         if (status == 0) {
133                 return 0;
134         }
135
136         /*
137          *      The next stage to process the packet.
138          */
139         handler->stage = AUTHENTICATE;
140
141         return 1;
142 }
143
144 /*
145  *      Do authentication, by letting EAP-TLS do most of the work.
146  */
147 static int eaptls_authenticate(void *type_arg, EAP_HANDLER *handler)
148 {
149         fr_tls_status_t status;
150         tls_session_t *tls_session = (tls_session_t *) handler->opaque;
151         REQUEST *request = handler->request;
152         rlm_eap_tls_t *inst;
153
154         inst = type_arg;
155
156         RDEBUG2("Authenticate");
157
158         status = eaptls_process(handler);
159         RDEBUG2("eaptls_process returned %d\n", status);
160         switch (status) {
161                 /*
162                  *      EAP-TLS handshake was successful, return an
163                  *      EAP-TLS-Success packet here.
164                  *
165                  *      If a virtual server was configured, check that
166                  *      it accepts the certificates, too.
167                  */
168         case FR_TLS_SUCCESS:
169                 if (inst->virtual_server) {
170                         VALUE_PAIR *vp;
171                         REQUEST *fake;
172
173                         /* create a fake request */
174                         fake = request_alloc_fake(request);
175                         rad_assert(fake->packet->vps == NULL);
176
177                         fake->packet->vps = paircopy(request->packet->vps);
178
179                         /* set the virtual server to use */
180                         if ((vp = pairfind(request->config_items,
181                                            PW_VIRTUAL_SERVER, 0)) != NULL) {
182                                 fake->server = vp->vp_strvalue;
183                         } else {
184                                 fake->server = inst->virtual_server;
185                         }
186
187                         RDEBUG("Processing EAP-TLS Certificate check:");
188                         debug_pair_list(fake->packet->vps);
189
190                         RDEBUG("server %s {", fake->server);
191
192                         rad_authenticate(fake);
193
194                         RDEBUG("} # server %s", fake->server);
195
196                         /* copy the reply vps back to our reply */
197                         pairadd(&request->reply->vps, fake->reply->vps);
198                         fake->reply->vps = NULL;
199
200                         /* reject if virtual server didn't return accept */
201                         if (fake->reply->code != PW_AUTHENTICATION_ACK) {
202                                 RDEBUG2("Certifictes were rejected by the virtual server");
203                                 request_free(&fake);
204                                 eaptls_fail(handler, 0);
205                                 return 0;
206                         }
207
208                         request_free(&fake);
209                         /* success */
210                 }
211                 break;
212
213                 /*
214                  *      The TLS code is still working on the TLS
215                  *      exchange, and it's a valid TLS request.
216                  *      do nothing.
217                  */
218         case FR_TLS_HANDLED:
219                 return 1;
220
221                 /*
222                  *      Handshake is done, proceed with decoding tunneled
223                  *      data.
224                  */
225         case FR_TLS_OK:
226                 RDEBUG2("Received unexpected tunneled data after successful handshake.");
227 #ifndef NDEBUG
228                 if ((debug_flag > 2) && fr_log_fp) {
229                         unsigned int i;
230                         unsigned int data_len;
231                         unsigned char buffer[1024];
232
233                         data_len = (tls_session->record_minus)(&tls_session->dirty_in,
234                                                 buffer, sizeof(buffer));
235                         log_debug("  Tunneled data (%u bytes)\n", data_len);
236                         for (i = 0; i < data_len; i++) {
237                                 if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, "  %x: ", i);
238                                 if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
239
240                                 fprintf(fr_log_fp, "%02x ", buffer[i]);
241                         }
242                         fprintf(fr_log_fp, "\n");
243                 }
244 #endif
245
246                 eaptls_fail(handler, 0);
247                 return 0;
248                 break;
249
250                 /*
251                  *      Anything else: fail.
252                  *
253                  *      Also, remove the session from the cache so that
254                  *      the client can't re-use it.
255                  */
256         default:
257                 tls_fail(tls_session);
258
259                 return 0;
260         }
261
262         /*
263          *      Success: Automatically return MPPE keys.
264          */
265         return eaptls_success(handler, 0);
266 }
267
268 /*
269  *      The module name should be the only globally exported symbol.
270  *      That is, everything else should be 'static'.
271  */
272 EAP_TYPE rlm_eap_tls = {
273         "eap_tls",
274         eaptls_attach,                  /* attach */
275         eaptls_initiate,                /* Start the initial request */
276         NULL,                           /* authorization */
277         eaptls_authenticate,            /* authentication */
278         eaptls_detach                   /* detach */
279 };