2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2010
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 3 of the License, or
9 (at your option) any later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "system/network.h"
22 #include "../util/tevent_unix.h"
23 #include "../lib/tsocket/tsocket.h"
24 #include "../lib/tsocket/tsocket_internal.h"
25 #include "lib/tls/tls.h"
26 #include "gnutls/gnutls.h"
30 static const struct tstream_context_ops tstream_tls_ops;
33 struct tstream_context *plain_stream;
36 gnutls_session tls_session;
39 gnutls_certificate_credentials xcred;
41 struct tevent_context *current_ev;
49 static void tstream_tls_schedule_retry(struct tstream_context *tls,
50 struct tevent_context *ev)
54 static void tstream_tls_retry(struct tstream_context *tls)
59 static void tstream_tls_push_done(struct tevent_req *subreq);
61 static ssize_t tstream_tls_push_function(gnutls_transport_ptr ptr,
62 const void *buf, size_t size)
64 struct tstream_context *tls = talloc_get_type_abort(ptr,
65 struct tstream_context);
66 struct tstream_tls *tlss = tstream_context_data(tls, struct tstream_tls);
67 struct tevent_req *subreq;
69 if (tlss->push.iov.iov_base) {
74 tlss->push.iov.iov_base = tlss->push.buffer;
75 tlss->push.iov.iov_len = MIN(size, sizeof(tlss->push.buffer));
77 memcpy(tlss->push.buffer, buf, tlss->push.iov.iov_len);
79 subreq = tstream_writev_send(tlss,
87 tevent_req_set_callback(subreq, tstream_tls_push_done, tls);
89 return tlss->push.iov.iov_len;
92 static void tstream_tls_push_done(struct tevent_req *subreq)
94 struct tstream_context *tls = tevent_req_callback_data(subreq,
95 struct tstream_context);
96 struct tstream_tls *tlss = tstream_context_data(tls, struct tstream_tls);
100 ZERO_STRUCT(tlss->push.iov);
102 ret = tstream_writev_recv(subreq, &perrno);
105 tlss->plain_errno = perrno;
106 tstream_tls_retry(tls);
110 tstream_tls_retry(tls);
113 static void tstream_tls_pull_done(struct tevent_req *subreq);
115 static ssize_t tstream_tls_pull_function(gnutls_transport_ptr ptr,
116 void *buf, size_t size)
118 struct tstream_context *tls = talloc_get_type_abort(ptr,
119 struct tstream_context);
120 struct tstream_tls *tlss = tstream_context_data(tls, struct tstream_tls);
121 struct tevent_req *subreq;
123 if (tlss->pull.iov.iov_base) {
126 n = MIN(tlss->pull.iov.iov_len, size);
127 memcpy(buf, tlss->pull.iov.iov_base, n);
129 tlss->pull.iov.iov_len -= n;
130 if (tlss->pull.iov.iov_len == 0) {
131 tlss->pull.iov.iov_base = NULL;
141 tlss->pull.iov.iov_base = tlss->pull.buffer;
142 tlss->pull.iov.iov_len = MIN(size, sizeof(tlss->pull.buffer));
144 subreq = tstream_readv_send(tlss,
148 if (subreq == NULL) {
152 tevent_req_set_callback(subreq, tstream_tls_pull_done, tls);
158 static void tstream_tls_pull_done(struct tevent_req *subreq)
160 struct tstream_context *tls = tevent_req_callback_data(subreq,
161 struct tstream_context);
162 struct tstream_tls *tlss = tstream_context_data(tls, struct tstream_tls);
166 ret = tstream_readv_recv(subreq, &perrno);
169 tlss->plain_errno = perrno;
170 tstream_tls_retry(tls);
174 tstream_tls_retry(tls);
177 static int tstream_tls_destructor(struct tstream_tls *tlss)
180 gnutls_certificate_free_credentials(tlss->xcred);
183 if (tlss->tls_session) {
184 gnutls_deinit(tlss->tls_session);
185 tlss->tls_session = NULL;
190 static ssize_t tstream_tls_pending_bytes(struct tstream_context *stream)
192 struct tstream_tls *tlss = tstream_context_data(stream,
196 if (!tlss->plain_stream) {
201 if (!tlss->tls_session) {
202 ret = tstream_pending_bytes(tlss->plain_stream);
206 ret = gnutls_record_check_pending(tlss->tls_session);
208 /* TODO: better mapping */
216 struct tstream_tls_readv_state {
220 static void tstream_tls_readv_plain_handler(struct tevent_req *subreq);
222 static struct tevent_req *tstream_tls_readv_send(TALLOC_CTX *mem_ctx,
223 struct tevent_context *ev,
224 struct tstream_context *stream,
225 struct iovec *vector,
228 struct tevent_req *req;
229 struct tstream_tls_readv_state *state;
230 struct tstream_tls *tlss = tstream_context_data(stream, struct tstream_tls);
231 struct tevent_req *subreq;
233 req = tevent_req_create(mem_ctx, &state,
234 struct tstream_tls_readv_state);
241 if (!tlss->plain_stream) {
242 tevent_req_error(req, ENOTCONN);
243 return tevent_req_post(req, ev);
246 if (!tlss->tls_session) {
247 subreq = tstream_readv_send(state,
252 if (tevent_req_nomem(subreq,req)) {
253 return tevent_req_post(req, ev);
255 tevent_req_set_callback(subreq,
256 tstream_tls_readv_plain_handler,
263 tevent_req_error(req, ENOSYS);
264 return tevent_req_post(req, ev);
267 static void tstream_tls_readv_plain_handler(struct tevent_req *subreq)
269 struct tevent_req *req = tevent_req_callback_data(subreq,
271 struct tstream_tls_readv_state *state = tevent_req_data(req,
272 struct tstream_tls_readv_state);
276 ret = tstream_readv_recv(subreq, &sys_errno);
279 tevent_req_error(req, sys_errno);
285 tevent_req_done(req);
288 static int tstream_tls_readv_recv(struct tevent_req *req,
291 struct tstream_tls_readv_state *state = tevent_req_data(req,
292 struct tstream_tls_readv_state);
295 ret = tsocket_simple_int_recv(req, perrno);
300 tevent_req_received(req);
304 struct tstream_tls_writev_state {
308 static void tstream_tls_writev_plain_handler(struct tevent_req *subreq);
310 static struct tevent_req *tstream_tls_writev_send(TALLOC_CTX *mem_ctx,
311 struct tevent_context *ev,
312 struct tstream_context *stream,
313 const struct iovec *vector,
316 struct tevent_req *req;
317 struct tstream_tls_writev_state *state;
318 struct tstream_tls *tlss = tstream_context_data(stream, struct tstream_tls);
319 struct tevent_req *subreq;
321 req = tevent_req_create(mem_ctx, &state,
322 struct tstream_tls_writev_state);
329 if (!tlss->plain_stream) {
330 tevent_req_error(req, ENOTCONN);
331 return tevent_req_post(req, ev);
334 if (!tlss->tls_session) {
335 subreq = tstream_writev_send(state,
340 if (tevent_req_nomem(subreq, req)) {
341 return tevent_req_post(req, ev);
343 tevent_req_set_callback(subreq, tstream_tls_writev_plain_handler, req);
349 tevent_req_error(req, ENOSYS);
350 return tevent_req_post(req, ev);
353 static void tstream_tls_writev_plain_handler(struct tevent_req *subreq)
355 struct tevent_req *req = tevent_req_callback_data(subreq,
357 struct tstream_tls_writev_state *state = tevent_req_data(req,
358 struct tstream_tls_writev_state);
362 ret = tstream_writev_recv(subreq, &sys_errno);
365 tevent_req_error(req, sys_errno);
371 tevent_req_done(req);
374 static int tstream_tls_writev_recv(struct tevent_req *req,
377 struct tstream_tls_writev_state *state = tevent_req_data(req,
378 struct tstream_tls_writev_state);
381 ret = tsocket_simple_int_recv(req, perrno);
386 tevent_req_received(req);
390 struct tstream_tls_disconnect_state {
394 static struct tevent_req *tstream_tls_disconnect_send(TALLOC_CTX *mem_ctx,
395 struct tevent_context *ev,
396 struct tstream_context *stream)
398 struct tstream_tls *tlss = tstream_context_data(stream, struct tstream_tls);
399 struct tevent_req *req;
400 struct tstream_tls_disconnect_state *state;
402 req = tevent_req_create(mem_ctx, &state,
403 struct tstream_tls_disconnect_state);
408 if (!tlss->plain_stream) {
409 tevent_req_error(req, ENOTCONN);
410 return tevent_req_post(req, ev);
413 if (!tlss->tls_session) {
415 * The caller is responsible to do the real disconnect
416 * on the plain stream!
418 tlss->plain_stream = NULL;
419 tevent_req_done(req);
420 return tevent_req_post(req, ev);
424 tevent_req_error(req, ENOSYS);
425 return tevent_req_post(req, ev);
428 static int tstream_tls_disconnect_recv(struct tevent_req *req,
433 ret = tsocket_simple_int_recv(req, perrno);
435 tevent_req_received(req);
439 static const struct tstream_context_ops tstream_tls_ops = {
442 .pending_bytes = tstream_tls_pending_bytes,
444 .readv_send = tstream_tls_readv_send,
445 .readv_recv = tstream_tls_readv_recv,
447 .writev_send = tstream_tls_writev_send,
448 .writev_recv = tstream_tls_writev_recv,
450 .disconnect_send = tstream_tls_disconnect_send,
451 .disconnect_recv = tstream_tls_disconnect_recv,
454 struct tstream_tls_params {
456 gnutls_certificate_credentials xcred;
459 struct tstream_tls_connect_state {
461 struct tevent_context *ev;
463 struct tstream_context *tls_stream;
466 struct tevent_req *_tstream_tls_connect_send(TALLOC_CTX *mem_ctx,
467 struct tevent_context *ev,
468 struct tstream_context *plain_stream,
469 struct tstream_tls_params *tls_params,
470 const char *location)
472 struct tevent_req *req;
473 struct tstream_tls_connect_state *state;
474 struct tstream_tls *tlss;
476 static const int cert_type_priority[] = {
482 req = tevent_req_create(mem_ctx, &state,
483 struct tstream_tls_connect_state);
488 state->caller.ev = ev;
490 state->tls_stream = tstream_context_create(state,
495 if (tevent_req_nomem(state->tls_stream, req)) {
496 return tevent_req_post(req, ev);
499 talloc_set_destructor(tlss, tstream_tls_destructor);
501 tlss->plain_stream = plain_stream;
503 gnutls_global_init();
505 ret = gnutls_certificate_allocate_credentials(&tlss->xcred);
506 if (tevent_req_error(req, ret)) {
507 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
508 return tevent_req_post(req, ev);
511 gnutls_certificate_set_x509_trust_file(tlss->xcred,
513 GNUTLS_X509_FMT_PEM);
515 ret = gnutls_init(&tlss->tls_session, GNUTLS_CLIENT);
516 if (tevent_req_error(req, ret)) {
517 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
518 return tevent_req_post(req, ev);
521 ret = gnutls_set_default_priority(tlss->tls_session);
522 if (tevent_req_error(req, ret)) {
523 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
524 return tevent_req_post(req, ev);
527 gnutls_certificate_type_set_priority(tlss->tls_session, cert_type_priority);
529 ret = gnutls_credentials_set(tlss->tls_session, GNUTLS_CRD_CERTIFICATE, tlss->xcred);
530 if (tevent_req_error(req, ret)) {
531 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
532 return tevent_req_post(req, ev);
535 gnutls_transport_set_ptr(tlss->tls_session, (gnutls_transport_ptr)state->tls_stream);
536 gnutls_transport_set_pull_function(tlss->tls_session,
537 (gnutls_pull_func)tstream_tls_pull_function);
538 gnutls_transport_set_push_function(tlss->tls_session,
539 (gnutls_push_func)tstream_tls_push_function);
540 gnutls_transport_set_lowat(tlss->tls_session, 0);
542 //tstream_tls_prepare_operation(tls, state->caller.ev);
543 ret = gnutls_handshake(tlss->tls_session);
544 if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
545 tstream_tls_schedule_retry(state->tls_stream, state->caller.ev);
548 if (tevent_req_error(req, ret)) {
549 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
550 return tevent_req_post(req, ev);
553 return tevent_req_post(req, ev);
556 int tstream_tls_connect_recv(struct tevent_req *req,
559 struct tstream_context **tls_stream)
561 struct tstream_tls_connect_state *state =
563 struct tstream_tls_connect_state);
565 if (tevent_req_is_unix_error(req, perrno)) {
566 tevent_req_received(req);
570 *tls_stream = talloc_move(mem_ctx, &state->tls_stream);
571 tevent_req_received(req);
575 struct tstream_tls_accept_state {
577 struct tevent_context *ev;
579 struct tstream_context *tls_stream;
582 struct tevent_req *_tstream_tls_accept_send(TALLOC_CTX *mem_ctx,
583 struct tevent_context *ev,
584 struct tstream_context *plain_stream,
585 struct tstream_tls_params *tls_params,
586 const char *location)
588 struct tevent_req *req;
589 struct tstream_tls_accept_state *state;
590 struct tstream_tls *tlss;
593 req = tevent_req_create(mem_ctx, &state,
594 struct tstream_tls_accept_state);
599 state->caller.ev = ev;
601 state->tls_stream = tstream_context_create(state,
606 if (tevent_req_nomem(state->tls_stream, req)) {
607 return tevent_req_post(req, ev);
610 talloc_set_destructor(tlss, tstream_tls_destructor);
612 tlss->plain_stream = plain_stream;
614 gnutls_global_init();
616 ret = gnutls_init(&tlss->tls_session, GNUTLS_SERVER);
617 if (tevent_req_error(req, ret)) {
618 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
619 return tevent_req_post(req, ev);
622 ret = gnutls_set_default_priority(tlss->tls_session);
623 if (tevent_req_error(req, ret)) {
624 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
625 return tevent_req_post(req, ev);
628 ret = gnutls_credentials_set(tlss->tls_session, GNUTLS_CRD_CERTIFICATE,
630 if (tevent_req_error(req, ret)) {
631 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
632 return tevent_req_post(req, ev);
635 gnutls_certificate_server_set_request(tlss->tls_session,
636 GNUTLS_CERT_REQUEST);
637 gnutls_dh_set_prime_bits(tlss->tls_session, DH_BITS);
639 gnutls_transport_set_ptr(tlss->tls_session, (gnutls_transport_ptr)state->tls_stream);
640 gnutls_transport_set_pull_function(tlss->tls_session,
641 (gnutls_pull_func)tstream_tls_pull_function);
642 gnutls_transport_set_push_function(tlss->tls_session,
643 (gnutls_push_func)tstream_tls_push_function);
644 gnutls_transport_set_lowat(tlss->tls_session, 0);
646 //tstream_tls_prepare_operation(tls, state->caller.ev);
647 ret = gnutls_handshake(tlss->tls_session);
648 if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
649 tstream_tls_schedule_retry(state->tls_stream, state->caller.ev);
652 if (tevent_req_error(req, ret)) {
653 DEBUG(0,("TLS %s - %s\n", __location__, gnutls_strerror(ret)));
654 return tevent_req_post(req, ev);
657 return tevent_req_post(req, ev);
660 int tstream_tls_accept_recv(struct tevent_req *req,
663 struct tstream_context **tls_stream)
665 struct tstream_tls_accept_state *state =
667 struct tstream_tls_accept_state);
669 if (tevent_req_is_unix_error(req, perrno)) {
670 tevent_req_received(req);
674 *tls_stream = talloc_move(mem_ctx, &state->tls_stream);
675 tevent_req_received(req);