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