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