📄 tls.c
字号:
/* Unix SMB/CIFS implementation. transport layer security handling code Copyright (C) Andrew Tridgell 2004-2005 Copyright (C) Stefan Metzmacher 2004 Copyright (C) Andrew Bartlett 2006 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.*/#include "includes.h"#include "lib/events/events.h"#include "lib/socket/socket.h"#include "lib/tls/tls.h"#include "param/param.h"#if ENABLE_GNUTLS#include "gnutls/gnutls.h"#define DH_BITS 1024#if defined(HAVE_GNUTLS_DATUM) && !defined(HAVE_GNUTLS_DATUM_T)typedef gnutls_datum gnutls_datum_t;#endif/* hold persistent tls data */struct tls_params { gnutls_certificate_credentials x509_cred; gnutls_dh_params dh_params; bool tls_enabled;};#endif/* hold per connection tls data */struct tls_context { struct socket_context *socket; struct fd_event *fde; bool tls_enabled;#if ENABLE_GNUTLS gnutls_session session; bool done_handshake; bool have_first_byte; uint8_t first_byte; bool tls_detect; const char *plain_chars; bool output_pending; gnutls_certificate_credentials xcred; bool interrupted;#endif};bool tls_enabled(struct socket_context *sock){ struct tls_context *tls; if (!sock) { return false; } if (strcmp(sock->backend_name, "tls") != 0) { return false; } tls = talloc_get_type(sock->private_data, struct tls_context); if (!tls) { return false; } return tls->tls_enabled;}#if ENABLE_GNUTLSstatic const struct socket_ops tls_socket_ops;static NTSTATUS tls_socket_init(struct socket_context *sock){ switch (sock->type) { case SOCKET_TYPE_STREAM: break; default: return NT_STATUS_INVALID_PARAMETER; } sock->backend_name = "tls"; return NT_STATUS_OK;}#define TLSCHECK(call) do { \ ret = call; \ if (ret < 0) { \ DEBUG(0,("TLS %s - %s\n", #call, gnutls_strerror(ret))); \ goto failed; \ } \} while (0)/* callback for reading from a socket*/static ssize_t tls_pull(gnutls_transport_ptr ptr, void *buf, size_t size){ struct tls_context *tls = talloc_get_type(ptr, struct tls_context); NTSTATUS status; size_t nread; if (tls->have_first_byte) { *(uint8_t *)buf = tls->first_byte; tls->have_first_byte = false; return 1; } status = socket_recv(tls->socket, buf, size, &nread); if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { return 0; } if (NT_STATUS_IS_ERR(status)) { EVENT_FD_NOT_READABLE(tls->fde); EVENT_FD_NOT_WRITEABLE(tls->fde); errno = EBADF; return -1; } if (!NT_STATUS_IS_OK(status)) { EVENT_FD_READABLE(tls->fde); errno = EAGAIN; return -1; } if (tls->output_pending) { EVENT_FD_WRITEABLE(tls->fde); } if (size != nread) { EVENT_FD_READABLE(tls->fde); } return nread;}/* callback for writing to a socket*/static ssize_t tls_push(gnutls_transport_ptr ptr, const void *buf, size_t size){ struct tls_context *tls = talloc_get_type(ptr, struct tls_context); NTSTATUS status; size_t nwritten; DATA_BLOB b; if (!tls->tls_enabled) { return size; } b.data = discard_const(buf); b.length = size; status = socket_send(tls->socket, &b, &nwritten); if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { errno = EAGAIN; return -1; } if (!NT_STATUS_IS_OK(status)) { EVENT_FD_WRITEABLE(tls->fde); return -1; } if (size != nwritten) { EVENT_FD_WRITEABLE(tls->fde); } return nwritten;}/* destroy a tls session */static int tls_destructor(struct tls_context *tls){ int ret; ret = gnutls_bye(tls->session, GNUTLS_SHUT_WR); if (ret < 0) { DEBUG(0,("TLS gnutls_bye failed - %s\n", gnutls_strerror(ret))); } return 0;}/* possibly continue the handshake process*/static NTSTATUS tls_handshake(struct tls_context *tls){ int ret; if (tls->done_handshake) { return NT_STATUS_OK; } ret = gnutls_handshake(tls->session); if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { if (gnutls_record_get_direction(tls->session) == 1) { EVENT_FD_WRITEABLE(tls->fde); } return STATUS_MORE_ENTRIES; } if (ret < 0) { DEBUG(0,("TLS gnutls_handshake failed - %s\n", gnutls_strerror(ret))); return NT_STATUS_UNEXPECTED_NETWORK_ERROR; } tls->done_handshake = true; return NT_STATUS_OK;}/* possibly continue an interrupted operation*/static NTSTATUS tls_interrupted(struct tls_context *tls){ int ret; if (!tls->interrupted) { return NT_STATUS_OK; } if (gnutls_record_get_direction(tls->session) == 1) { ret = gnutls_record_send(tls->session, NULL, 0); } else { ret = gnutls_record_recv(tls->session, NULL, 0); } if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { return STATUS_MORE_ENTRIES; } tls->interrupted = false; return NT_STATUS_OK;}/* see how many bytes are pending on the connection*/static NTSTATUS tls_socket_pending(struct socket_context *sock, size_t *npending){ struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context); if (!tls->tls_enabled || tls->tls_detect) { return socket_pending(tls->socket, npending); } *npending = gnutls_record_check_pending(tls->session); if (*npending == 0) { NTSTATUS status = socket_pending(tls->socket, npending); if (*npending == 0) { /* seems to be a gnutls bug */ (*npending) = 100; } return status; } return NT_STATUS_OK;}/* receive data either by tls or normal socket_recv*/static NTSTATUS tls_socket_recv(struct socket_context *sock, void *buf, size_t wantlen, size_t *nread){ int ret; NTSTATUS status; struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context); if (tls->tls_enabled && tls->tls_detect) { status = socket_recv(tls->socket, &tls->first_byte, 1, nread); NT_STATUS_NOT_OK_RETURN(status); if (*nread == 0) return NT_STATUS_OK; tls->tls_detect = false; /* look for the first byte of a valid HTTP operation */ if (strchr(tls->plain_chars, tls->first_byte)) { /* not a tls link */ tls->tls_enabled = false; *(uint8_t *)buf = tls->first_byte; return NT_STATUS_OK; } tls->have_first_byte = true; } if (!tls->tls_enabled) { return socket_recv(tls->socket, buf, wantlen, nread); } status = tls_handshake(tls); NT_STATUS_NOT_OK_RETURN(status); status = tls_interrupted(tls); NT_STATUS_NOT_OK_RETURN(status); ret = gnutls_record_recv(tls->session, buf, wantlen); if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { if (gnutls_record_get_direction(tls->session) == 1) { EVENT_FD_WRITEABLE(tls->fde); } tls->interrupted = true; return STATUS_MORE_ENTRIES; } if (ret < 0) { return NT_STATUS_UNEXPECTED_NETWORK_ERROR; } *nread = ret; return NT_STATUS_OK;}/* send data either by tls or normal socket_recv*/static NTSTATUS tls_socket_send(struct socket_context *sock, const DATA_BLOB *blob, size_t *sendlen){ NTSTATUS status; int ret; struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context); if (!tls->tls_enabled) { return socket_send(tls->socket, blob, sendlen); } status = tls_handshake(tls); NT_STATUS_NOT_OK_RETURN(status); status = tls_interrupted(tls); NT_STATUS_NOT_OK_RETURN(status); ret = gnutls_record_send(tls->session, blob->data, blob->length); if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { if (gnutls_record_get_direction(tls->session) == 1) { EVENT_FD_WRITEABLE(tls->fde); } tls->interrupted = true; return STATUS_MORE_ENTRIES; } if (ret < 0) { DEBUG(0,("gnutls_record_send of %d failed - %s\n", (int)blob->length, gnutls_strerror(ret))); return NT_STATUS_UNEXPECTED_NETWORK_ERROR; } *sendlen = ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -