more info in times struct
[platform/upstream/openconnect.git] / dtls.c
1 /*
2  * Open AnyConnect (SSL + DTLS) client
3  *
4  * © 2008 David Woodhouse <dwmw2@infradead.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software
7  * for any purpose with or without fee is hereby granted, provided
8  * that the above copyright notice and this permission notice appear
9  * in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
16  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
17  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netdb.h>
25 #include <unistd.h>
26 #include <openssl/err.h>
27 #include <fcntl.h>
28
29 #include "anyconnect.h"
30
31 /*
32  * The master-secret is generated randomly by the client. The server
33  * responds with a DTLS Session-ID. These, done over the HTTPS
34  * connection, are enough to 'resume' a DTLS session, bypassing all
35  * the normal setup of a normal DTLS connection.
36  *
37  * Cisco use a version of the protocol which predates RFC4347, but
38  * isn't quite the same as the pre-RFC version of the protocol which
39  * was in OpenSSL 0.9.8e -- it includes backports of some later
40  * OpenSSL patches.
41  *
42  * The openssl/ directory of this source tree should contain both a 
43  * small patch against OpenSSL 0.9.8e to make it support Cisco's 
44  * snapshot of the protocol, and a larger patch against newer OpenSSL
45  * which gives us an option to use the old protocol again.
46  *
47  * Cisco's server also seems to respond to the official version of the
48  * protocol, with a change in the ChangeCipherSpec packet which implies
49  * that it does know the difference and isn't just repeating the version
50  * number seen in the ClientHello. But although I can make the handshake
51  * complete by hacking tls1_mac() to use the _old_ protocol version
52  * number when calculating the MAC, the server still seems to be ignoring
53  * my subsequent data packets.
54  */   
55
56 static unsigned char nybble(unsigned char n)
57 {
58         if      (n >= '0' && n <= '9') return n - '0';
59         else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
60         else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
61         return 0;
62 }
63
64 static unsigned char hex(const char *data)
65 {
66         return (nybble(data[0]) << 4) | nybble(data[1]);
67 }
68
69 static int connect_dtls_socket(struct anyconnect_info *vpninfo, SSL **ret_ssl,
70                                int *ret_fd)
71 {
72         SSL_METHOD *dtls_method;
73         SSL_CTX *dtls_ctx;
74         SSL_SESSION *dtls_session;
75         SSL_CIPHER *https_cipher;
76         SSL *dtls_ssl;
77         BIO *dtls_bio;
78         int dtls_fd;
79         int ret;
80
81         dtls_fd = socket(vpninfo->peer_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
82         if (dtls_fd < 0) {
83                 perror("Open UDP socket for DTLS:");
84                 return -EINVAL;
85         }
86         
87         if (connect(dtls_fd, vpninfo->peer_addr, vpninfo->peer_addrlen)) {
88                 perror("UDP (DTLS) connect:\n");
89                 close(dtls_fd);
90                 return -EINVAL;
91         }
92
93         fcntl(dtls_fd, F_SETFD, FD_CLOEXEC);
94
95         dtls_method = DTLSv1_client_method();
96         dtls_ctx = SSL_CTX_new(dtls_method);
97         SSL_CTX_set_read_ahead(dtls_ctx, 1);
98         https_cipher = SSL_get_current_cipher(vpninfo->https_ssl);
99
100         dtls_ssl = SSL_new(dtls_ctx);
101         SSL_set_connect_state(dtls_ssl);
102         SSL_set_cipher_list(dtls_ssl, SSL_CIPHER_get_name(https_cipher));
103
104         /* We're going to "resume" a session which never existed. Fake it... */
105         dtls_session = SSL_SESSION_new();
106
107         dtls_session->ssl_version = 0x0100; // DTLS1_BAD_VER
108
109         dtls_session->master_key_length = sizeof(vpninfo->dtls_secret);
110         memcpy(dtls_session->master_key, vpninfo->dtls_secret,
111                sizeof(vpninfo->dtls_secret));
112
113         dtls_session->session_id_length = sizeof(vpninfo->dtls_session_id);
114         memcpy(dtls_session->session_id, vpninfo->dtls_session_id,
115                sizeof(vpninfo->dtls_session_id));
116
117         dtls_session->cipher = https_cipher;
118         dtls_session->cipher_id = https_cipher->id;
119
120         /* Having faked a session, add it to the CTX and the SSL */
121         if (!SSL_set_session(dtls_ssl, dtls_session)) {
122                 printf("SSL_set_session() failed with old protocol version 0x%x\n",
123                        dtls_session->ssl_version);
124                 printf("Your OpenSSL may lack Cisco compatibility support\n");
125                 printf("See http://rt.openssl.org/Ticket/Display.html?id=1751\n");
126                 printf("Use the --no-dtls command line option to avoid this message\n");
127                 return -EINVAL;
128         }
129         if (!SSL_CTX_add_session(dtls_ctx, dtls_session))
130                 printf("SSL_CTX_add_session() failed\n");
131
132
133         /* Go Go Go! */
134         dtls_bio = BIO_new_socket(dtls_fd, BIO_NOCLOSE);
135         SSL_set_bio(dtls_ssl, dtls_bio, dtls_bio);
136
137         /* XXX Cargo cult programming. Other DTLS code does this, and it might
138            avoid http://rt.openssl.org/Ticket/Display.html?id=1703 */
139         BIO_ctrl(dtls_bio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL);
140
141 #ifndef SSL_OP_CISCO_ANYCONNECT
142 #define SSL_OP_CISCO_ANYCONNECT 0x8000
143 #endif
144         SSL_set_options(dtls_ssl, SSL_OP_CISCO_ANYCONNECT);
145         ret = SSL_do_handshake(dtls_ssl);
146         
147         if (ret != 1) {
148                 fprintf(stderr, "DTLS connection returned %d\n", ret);
149                 if (ret < 0)
150                         fprintf(stderr, "DTLS handshake error: %d\n", SSL_get_error(dtls_ssl, ret));
151                 ERR_print_errors_fp(stderr);
152                 SSL_free(dtls_ssl);
153                 SSL_CTX_free(dtls_ctx);
154                 close(dtls_fd);
155                 return -EINVAL;
156         }
157
158         BIO_set_nbio(SSL_get_rbio(dtls_ssl),1);
159         BIO_set_nbio(SSL_get_wbio(dtls_ssl),1);
160
161         fcntl(dtls_fd, F_SETFL, fcntl(dtls_fd, F_GETFL) | O_NONBLOCK);
162
163         *ret_fd = dtls_fd;
164         *ret_ssl = dtls_ssl;
165
166         return 0;
167 }
168
169 static int dtls_rekey(struct anyconnect_info *vpninfo)
170 {
171         SSL *dtls_ssl;
172         int dtls_fd;
173
174         /* To rekey, we just 'resume' the session again */
175         if (connect_dtls_socket(vpninfo, &dtls_ssl, &dtls_fd))
176                 return -EINVAL;
177
178         vpninfo->pfds[vpninfo->dtls_pfd].fd = dtls_fd;
179
180         SSL_free(vpninfo->dtls_ssl);
181         close(vpninfo->dtls_fd);
182
183         vpninfo->dtls_ssl = dtls_ssl;
184         vpninfo->dtls_fd = dtls_fd;
185
186         return 0;
187 }
188
189 int setup_dtls(struct anyconnect_info *vpninfo)
190 {
191         struct vpn_option *dtls_opt = vpninfo->dtls_options;
192         int sessid_found = 0;
193         int dtls_port = 0;
194         int i;
195
196         while (dtls_opt) {
197                 if (verbose)
198                         printf("DTLS option %s : %s\n", dtls_opt->option, dtls_opt->value);
199
200                 if (!strcmp(dtls_opt->option, "X-DTLS-Session-ID")) {
201                         if (strlen(dtls_opt->value) != 64) {
202                                 fprintf(stderr, "X-DTLS-Session-ID not 64 characters\n");
203                                 fprintf(stderr, "Is: %s\n", dtls_opt->value);
204                                 return -EINVAL;
205                         }
206                         for (i = 0; i < 64; i += 2)
207                                 vpninfo->dtls_session_id[i/2] = hex(dtls_opt->value + i);
208                         sessid_found = 1;
209                 } else if (!strcmp(dtls_opt->option + 7, "Port")) {
210                         dtls_port = atol(dtls_opt->value);
211                 } else if (!strcmp(dtls_opt->option + 7, "Keepalive")) {
212                         vpninfo->dtls_times.keepalive = atol(dtls_opt->value);
213                 } else if (!strcmp(dtls_opt->option + 7, "DPD")) {
214                         vpninfo->dtls_times.dpd = atol(dtls_opt->value);
215                 } else if (!strcmp(dtls_opt->option + 7, "Rekey-Time")) {
216                         vpninfo->dtls_times.rekey = atol(dtls_opt->value);
217                 }
218                         
219                 dtls_opt = dtls_opt->next;
220         }
221         if (!sessid_found || !dtls_port)
222                 return -EINVAL;
223
224         if (vpninfo->peer_addr->sa_family == AF_INET) {
225                 struct sockaddr_in *sin = (void *)vpninfo->peer_addr;
226                 sin->sin_port = htons(dtls_port);
227         } else if (vpninfo->peer_addr->sa_family == AF_INET6) {
228                 struct sockaddr_in6 *sin = (void *)vpninfo->peer_addr;
229                 sin->sin6_port = htons(dtls_port);
230         } else {
231                 fprintf(stderr, "Unknown protocol family %d. Cannot do DTLS\n",
232                         vpninfo->peer_addr->sa_family);
233                 return -EINVAL;
234         }
235
236         if (connect_dtls_socket(vpninfo, &vpninfo->dtls_ssl, &vpninfo->dtls_fd))
237                 return -EINVAL;
238
239         vpninfo->dtls_pfd = vpn_add_pollfd(vpninfo, vpninfo->dtls_fd,
240                                            POLLIN|POLLHUP|POLLERR);
241         vpninfo->dtls_times.last_rekey = vpninfo->dtls_times.last_rx =
242                 vpninfo->dtls_times.last_tx = time(NULL);
243
244         if (verbose)
245                 printf("DTLS connected. DPD %d, Keepalive %d\n",
246                        vpninfo->dtls_times.dpd, vpninfo->dtls_times.keepalive);
247
248         return 0;
249 }
250
251 int dtls_mainloop(struct anyconnect_info *vpninfo, int *timeout)
252 {
253         unsigned char buf[2000];
254         int len;
255         int work_done = 0;
256
257         while ( (len = SSL_read(vpninfo->dtls_ssl, buf, sizeof(buf))) > 0 ) {
258                 if (verbose)
259                         printf("Received DTLS packet 0x%02x of %d bytes\n",
260                                len, buf[0]);
261
262                 vpninfo->dtls_times.last_rx = time(NULL);
263
264                 switch(buf[0]) {
265                 case 0:
266                         queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf+1, len-1);
267                         work_done = 1;
268                         break;
269
270                 case 4: /* DPD response */
271                         if (verbose)
272                                 printf("Got DTLS DPD response\n");
273                         break;
274
275                 default:
276                         fprintf(stderr, "Unknown DTLS packet type %02x\n", buf[0]);
277                         vpninfo->quit_reason = "Unknown packet received";
278                         return 1;
279                 }
280         }
281         while (vpninfo->outgoing_queue) {
282                 struct pkt *this = vpninfo->outgoing_queue;
283                 int ret;
284
285                 vpninfo->outgoing_queue = this->next;
286
287                 /* One byte of header */
288                 this->hdr[7] = AC_PKT_DATA;
289                 
290                 ret = SSL_write(vpninfo->dtls_ssl, &this->hdr[7], this->len + 1);
291                 /* There's not a lot we can do if the write fails. If the link is
292                    really dead, DPD will kick in and we should fall back to SSL,
293                    if that's still working */
294                 vpninfo->dtls_times.last_tx = time(NULL);
295                 if (verbose) {
296                         printf("Sent DTLS packet of %d bytes; SSL_write() returned %d\n",
297                                this->len, ret);
298                 }
299         }
300
301         /* DPD is bidirectional -- PKT 3 out, PKT 4 back */
302         if (vpninfo->dtls_times.dpd) {
303                 time_t now = time(NULL);
304                 time_t due = vpninfo->dtls_times.last_rx + vpninfo->dtls_times.dpd;
305                 time_t overdue = vpninfo->dtls_times.last_rx + (2 * vpninfo->dtls_times.dpd);
306
307                 /* If we already have DPD outstanding, don't flood */
308                 if (vpninfo->dtls_times.last_dpd > vpninfo->dtls_times.last_rx) {
309                         if (verbose) {
310                                 printf("DTLS DPD outstanding. Will kill in %ld seconds\n",
311                                        overdue - now);
312                         }
313                         due = vpninfo->dtls_times.last_dpd + vpninfo->dtls_times.dpd;
314                 }
315                 if (now > overdue) {
316                         fprintf(stderr, "DTLS Dead Peer Detection detected dead peer!\n");
317                         /* Fall back to SSL */
318                         SSL_free(vpninfo->dtls_ssl);
319                         close(vpninfo->dtls_fd);
320                         vpninfo->pfds[vpninfo->dtls_pfd].fd = -1;
321                         vpninfo->dtls_ssl = NULL;
322                         vpninfo->dtls_fd = -1;
323                         return 1;
324                 }
325                 if (now >= due) {
326                         static unsigned char dtls_dpd_pkt[1] = { 3 };
327                         /* Haven't heard anything from the other end for a while.
328                            Check if it's still there */
329                         /* FIXME: If isn't, we should act on that */
330                         SSL_write(vpninfo->dtls_ssl, dtls_dpd_pkt, 1);
331                         vpninfo->dtls_times.last_dpd = vpninfo->dtls_times.last_tx = now;
332
333                         due = now + vpninfo->dtls_times.dpd;
334                         if (verbose)
335                                 printf("Sent DTLS DPD\n");
336                 }
337
338                 if (verbose && due < overdue)
339                         printf("Next DTLS DPD due in %ld seconds\n", (due - now));
340                 if (*timeout > (due - now) * 1000)
341                         *timeout = (due - now) * 1000;
342         }
343
344         /* Keepalive is just client -> server */
345         if (vpninfo->dtls_times.keepalive) {
346                 time_t now = time(NULL);
347                 time_t due = vpninfo->dtls_times.last_tx + vpninfo->dtls_times.keepalive;
348
349                 if (now >= due) {
350                         static unsigned char dtls_keepalive_pkt[1] = { 7 };
351
352                         /* Send something (which is discarded), to keep
353                            the connection alive. */
354                         SSL_write(vpninfo->dtls_ssl, dtls_keepalive_pkt, 1);
355                         vpninfo->dtls_times.last_tx = now;
356
357                         due = now + vpninfo->dtls_times.keepalive;
358                         if (verbose)
359                                 printf("Sent DTLS Keepalive\n");
360                 }
361
362                 if (verbose)
363                         printf("Next DTLS Keepalive due in %ld seconds\n", (due - now));
364                 if (*timeout > (due - now) * 1000)
365                         *timeout = (due - now) * 1000;
366         }
367
368         if (vpninfo->dtls_times.rekey) {
369                 time_t now = time(NULL);
370                 time_t due = vpninfo->dtls_times.last_rekey + vpninfo->dtls_times.rekey;
371
372                 if (now >= due) {
373                         if (verbose)
374                                 printf("DTLS rekey due\n");
375                         if (dtls_rekey(vpninfo)) {
376                                 fprintf(stderr, "DTLS rekey failed\n");
377                                 /* Fall back to SSL */
378                                 SSL_free(vpninfo->dtls_ssl);
379                                 close(vpninfo->dtls_fd);
380                                 vpninfo->pfds[vpninfo->dtls_pfd].fd = -1;
381                                 vpninfo->dtls_ssl = NULL;
382                                 vpninfo->dtls_fd = -1;
383                                 return 1;
384                         }
385                         vpninfo->dtls_times.last_rekey = time(NULL);
386                         due = vpninfo->dtls_times.last_rekey + vpninfo->dtls_times.rekey;
387                 }
388                 if (verbose)
389                         printf("Next DTLS rekey due in %ld seconds\n", (due - now));
390                 if (*timeout > (due - now) * 1000)
391                         *timeout = (due - now) * 1000;
392         }
393
394         return work_done;
395 }
396
397