r7048: added auto-generation of TLS self-signed certificates if none exist already
[sfrench/samba-autobuild/.git] / source4 / web_server / tls.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    transport layer security handling code
5
6    Copyright (C) Andrew Tridgell 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "smbd/service_task.h"
25 #include "smbd/service_stream.h"
26 #include "web_server/web_server.h"
27 #include "lib/events/events.h"
28 #include "system/network.h"
29
30 #if HAVE_LIBGNUTLS
31 #include "gnutls/gnutls.h"
32
33 #define DH_BITS 1024
34
35 /* hold per connection tls data */
36 struct tls_session {
37         gnutls_session session;
38         BOOL done_handshake;
39 };
40
41 /* hold persistent tls data */
42 struct tls_data {
43         gnutls_certificate_credentials x509_cred;
44         gnutls_dh_params dh_params;
45 };
46
47 /*
48   callback for reading from a socket
49 */
50 static ssize_t tls_pull(gnutls_transport_ptr ptr, void *buf, size_t size)
51 {
52         struct websrv_context *web = talloc_get_type(ptr, struct websrv_context);
53         NTSTATUS status;
54         size_t nread;
55         
56         if (web->input.tls_first_char) {
57                 *(uint8_t *)buf = web->input.first_byte;
58                 web->input.tls_first_char = False;
59                 return 1;
60         }
61
62         status = socket_recv(web->conn->socket, buf, size, &nread, 0);
63         if (!NT_STATUS_IS_OK(status)) {
64                 EVENT_FD_READABLE(web->conn->event.fde);
65                 EVENT_FD_NOT_WRITEABLE(web->conn->event.fde);
66                 return -1;
67         }
68         if (web->output.output_pending) {
69                 EVENT_FD_WRITEABLE(web->conn->event.fde);
70         }
71         if (size != nread) {
72                 EVENT_FD_READABLE(web->conn->event.fde);
73         }
74         return nread;
75 }
76
77 /*
78   callback for writing to a socket
79 */
80 static ssize_t tls_push(gnutls_transport_ptr ptr, const void *buf, size_t size)
81 {
82         struct websrv_context *web = talloc_get_type(ptr, struct websrv_context);
83         NTSTATUS status;
84         size_t nwritten;
85         DATA_BLOB b;
86
87         if (web->tls_session == NULL) {
88                 return size;
89         }
90
91         b.data = discard_const(buf);
92         b.length = size;
93
94         status = socket_send(web->conn->socket, &b, &nwritten, 0);
95         if (!NT_STATUS_IS_OK(status)) {
96                 EVENT_FD_WRITEABLE(web->conn->event.fde);
97                 return -1;
98         }
99         if (size != nwritten) {
100                 EVENT_FD_WRITEABLE(web->conn->event.fde);
101         }
102         return nwritten;
103 }
104
105 /*
106   destroy a tls session
107  */
108 static int tls_destructor(void *ptr)
109 {
110         struct tls_session *tls_session = talloc_get_type(ptr, struct tls_session);
111         int ret;
112         ret = gnutls_bye(tls_session->session, GNUTLS_SHUT_WR);
113         if (ret < 0) {
114                 DEBUG(0,("TLS gnutls_bye failed - %s\n", gnutls_strerror(ret)));
115         }
116         return 0;
117 }
118
119
120 /*
121   possibly continue the handshake process
122 */
123 static NTSTATUS tls_handshake(struct tls_session *tls_session)
124 {
125         int ret;
126
127         if (tls_session->done_handshake) {
128                 return NT_STATUS_OK;
129         }
130         
131         ret = gnutls_handshake(tls_session->session);
132         if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
133                 return STATUS_MORE_ENTRIES;
134         }
135         if (ret < 0) {
136                 DEBUG(0,("TLS gnutls_handshake failed - %s\n", gnutls_strerror(ret)));
137                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
138         }
139         tls_session->done_handshake = True;
140         return NT_STATUS_OK;
141 }
142
143
144 /*
145   receive data either by tls or normal socket_recv
146 */
147 NTSTATUS tls_socket_recv(struct websrv_context *web, void *buf, size_t wantlen, 
148                          size_t *nread)
149 {
150         int ret;
151         NTSTATUS status;
152         struct tls_session *tls_session = talloc_get_type(web->tls_session, 
153                                                           struct tls_session);
154
155         if (web->tls_session != NULL && web->input.tls_detect) {
156                 status = socket_recv(web->conn->socket, &web->input.first_byte, 
157                                      1, nread, 0);
158                 NT_STATUS_NOT_OK_RETURN(status);
159                 if (*nread == 0) return NT_STATUS_OK;
160                 web->input.tls_detect = False;
161                 /* look for the first byte of a valid HTTP operation */
162                 if (strchr("GPHO", web->input.first_byte)) {
163                         /* not a tls link */
164                         web->tls_session = NULL;
165                         talloc_free(tls_session);
166                         *(uint8_t *)buf = web->input.first_byte;
167                         return NT_STATUS_OK;
168                 }
169                 web->input.tls_first_char = True;
170         }
171
172         if (web->tls_session == NULL) {
173                 return socket_recv(web->conn->socket, buf, wantlen, nread, 0);
174         }
175
176         status = tls_handshake(tls_session);
177         NT_STATUS_NOT_OK_RETURN(status);
178
179         ret = gnutls_record_recv(tls_session->session, buf, wantlen);
180         if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
181                 return STATUS_MORE_ENTRIES;
182         }
183         if (ret < 0) {
184                 DEBUG(0,("gnutls_record_recv failed - %s\n", gnutls_strerror(ret)));
185                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
186         }
187         *nread = ret;
188         return NT_STATUS_OK;
189 }
190
191
192 /*
193   send data either by tls or normal socket_recv
194 */
195 NTSTATUS tls_socket_send(struct websrv_context *web, const DATA_BLOB *blob, 
196                          size_t *sendlen)
197 {
198         NTSTATUS status;
199         int ret;
200         struct tls_session *tls_session = talloc_get_type(web->tls_session, 
201                                                           struct tls_session);
202
203         if (web->tls_session == NULL) {
204                 return socket_send(web->conn->socket, blob, sendlen, 0);
205         }
206
207         status = tls_handshake(tls_session);
208         NT_STATUS_NOT_OK_RETURN(status);
209
210         ret = gnutls_record_send(tls_session->session, blob->data, blob->length);
211         if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
212                 return STATUS_MORE_ENTRIES;
213         }
214         if (ret < 0) {
215                 DEBUG(0,("gnutls_record_send failed - %s\n", gnutls_strerror(ret)));
216                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
217         }
218         *sendlen = ret;
219         return NT_STATUS_OK;
220 }
221
222
223 /*
224   initialise global tls state
225 */
226 void tls_initialise(struct task_server *task)
227 {
228         struct esp_data *edata = talloc_get_type(task->private, struct esp_data);
229         struct tls_data *tls;
230         int ret;
231         const char *keyfile = lp_web_keyfile();
232         const char *certfile = lp_web_certfile();
233         const char *cafile = lp_web_cafile();
234         const char *crlfile = lp_web_crlfile();
235
236         if (!lp_web_tls() || keyfile == NULL || *keyfile == 0) {
237                 return;
238         }
239
240         tls = talloc_zero(edata, struct tls_data);
241         edata->tls_data = tls;
242
243         if (!file_exist(cafile)) {
244                 tls_cert_generate(tls, keyfile, certfile, cafile);
245         }
246
247         ret = gnutls_global_init();
248         if (ret < 0) goto init_failed;
249
250         gnutls_certificate_allocate_credentials(&tls->x509_cred);
251         if (ret < 0) goto init_failed;
252
253         if (cafile && *cafile) {
254                 ret = gnutls_certificate_set_x509_trust_file(tls->x509_cred, cafile, 
255                                                              GNUTLS_X509_FMT_PEM);      
256                 if (ret < 0) {
257                         DEBUG(0,("TLS failed to initialise cafile %s\n", cafile));
258                         goto init_failed;
259                 }
260         }
261
262         if (crlfile && *crlfile) {
263                 ret = gnutls_certificate_set_x509_crl_file(tls->x509_cred, 
264                                                            crlfile, 
265                                                            GNUTLS_X509_FMT_PEM);
266                 if (ret < 0) {
267                         DEBUG(0,("TLS failed to initialise crlfile %s\n", crlfile));
268                         goto init_failed;
269                 }
270         }
271         
272         ret = gnutls_certificate_set_x509_key_file(tls->x509_cred, 
273                                                    certfile, keyfile,
274                                                    GNUTLS_X509_FMT_PEM);
275         if (ret < 0) {
276                 DEBUG(0,("TLS failed to initialise certfile %s and keyfile %s\n", 
277                          certfile, keyfile));
278                 goto init_failed;
279         }
280         
281         ret = gnutls_dh_params_init(&tls->dh_params);
282         if (ret < 0) goto init_failed;
283
284         ret = gnutls_dh_params_generate2(tls->dh_params, DH_BITS);
285         if (ret < 0) goto init_failed;
286
287         gnutls_certificate_set_dh_params(tls->x509_cred, tls->dh_params);
288         return;
289
290 init_failed:
291         DEBUG(0,("GNUTLS failed to initialise - %s\n", gnutls_strerror(ret)));
292         talloc_free(tls);
293         edata->tls_data = NULL;
294 }
295
296
297 /*
298   setup for a new connection
299 */
300 NTSTATUS tls_init_connection(struct websrv_context *web)
301 {
302         struct esp_data *edata = talloc_get_type(web->task->private, struct esp_data);
303         struct tls_data *tls_data = talloc_get_type(edata->tls_data, struct tls_data);
304         struct tls_session *tls_session;
305         int ret;
306
307         if (edata->tls_data == NULL) {
308                 web->tls_session = NULL;
309                 return NT_STATUS_OK;
310         }
311
312 #define TLSCHECK(call) do { \
313         ret = call; \
314         if (ret < 0) { \
315                 DEBUG(0,("TLS %s - %s\n", #call, gnutls_strerror(ret))); \
316                 goto failed; \
317         } \
318 } while (0)
319
320         tls_session = talloc_zero(web, struct tls_session);
321         web->tls_session = tls_session;
322
323         TLSCHECK(gnutls_init(&tls_session->session, GNUTLS_SERVER));
324
325         talloc_set_destructor(tls_session, tls_destructor);
326
327         TLSCHECK(gnutls_set_default_priority(tls_session->session));
328         TLSCHECK(gnutls_credentials_set(tls_session->session, GNUTLS_CRD_CERTIFICATE, tls_data->x509_cred));
329         gnutls_certificate_server_set_request(tls_session->session, GNUTLS_CERT_REQUEST);
330         gnutls_dh_set_prime_bits(tls_session->session, DH_BITS);
331         gnutls_transport_set_ptr(tls_session->session, (gnutls_transport_ptr)web);
332         gnutls_transport_set_pull_function(tls_session->session, (gnutls_pull_func)tls_pull);
333         gnutls_transport_set_push_function(tls_session->session, (gnutls_push_func)tls_push);
334         gnutls_transport_set_lowat(tls_session->session, 0);
335
336         web->input.tls_detect = True;
337         
338         return NT_STATUS_OK;
339
340 failed:
341         DEBUG(0,("TLS init connection failed - %s\n", gnutls_strerror(ret)));
342         web->tls_session = NULL;
343         talloc_free(tls_session);
344         return NT_STATUS_OK;
345 }
346
347
348 #else
349
350 /* for systems without tls we just map the tls socket calls to the
351    normal socket calls */
352 NTSTATUS tls_socket_recv(struct websrv_context *web, void *buf, size_t wantlen, 
353                          size_t *nread)
354 {
355         return socket_recv(web->conn->socket, buf, wantlen, nread, 0);
356 }
357
358 NTSTATUS tls_socket_send(struct websrv_context *web, const DATA_BLOB *blob, 
359                          size_t *sendlen)
360 {
361         return socket_send(web->conn->socket, blob, sendlen, 0);
362 }
363
364 NTSTATUS tls_init_connection(struct websrv_context *web)
365 {
366         web->tls_session = NULL;
367         return NT_STATUS_OK;
368 }
369
370 void tls_initialise(struct task_server *task)
371 {
372         struct esp_data *edata = talloc_get_type(task->private, struct esp_data);
373         edata->tls_data = NULL;
374 }
375
376 #endif