📄 ssl.c
字号:
/* * Part of Very Secure FTPd * Licence: GPL v2. Note that this code interfaces with with the OpenSSL * libraries, so please read LICENSE where I give explicit permission to link * against the OpenSSL libraries. * Author: Chris Evans * ssl.c * * Routines to handle a SSL/TLS-based implementation of RFC 2228, i.e. * encryption. */#include "ssl.h"#include "session.h"#include "ftpcodes.h"#include "ftpcmdio.h"#include "defs.h"#include "str.h"#include "sysutil.h"#include "tunables.h"#include "utility.h"#include "builddefs.h"#include "logging.h"#ifdef VSF_BUILD_SSL#include <openssl/ssl.h>#include <openssl/err.h>#include <openssl/rand.h>#include <openssl/bio.h>#include <errno.h>static char* get_ssl_error();static SSL* get_ssl(struct vsf_session* p_sess, int fd);static int ssl_session_init(struct vsf_session* p_sess);static void setup_bio_callbacks();static long bio_callback( BIO* p_bio, int oper, const char* p_arg, int argi, long argl, long retval);static int ssl_verify_callback(int verify_ok, X509_STORE_CTX* p_ctx);static int ssl_cert_digest( SSL* p_ssl, struct vsf_session* p_sess, struct mystr* p_str);static void maybe_log_shutdown_state(struct vsf_session* p_sess);static void maybe_log_ssl_error_state(struct vsf_session* p_sess, int ret);static int ssl_inited;static struct mystr debug_str;voidssl_init(struct vsf_session* p_sess){ if (!ssl_inited) { SSL_CTX* p_ctx; long options; int verify_option = 0; SSL_library_init(); p_ctx = SSL_CTX_new(SSLv23_server_method()); if (p_ctx == NULL) { die("SSL: could not allocate SSL context"); } options = SSL_OP_ALL; if (!tunable_sslv2) { options |= SSL_OP_NO_SSLv2; } if (!tunable_sslv3) { options |= SSL_OP_NO_SSLv3; } if (!tunable_tlsv1) { options |= SSL_OP_NO_TLSv1; } SSL_CTX_set_options(p_ctx, options); if (tunable_rsa_cert_file) { const char* p_key = tunable_rsa_private_key_file; if (!p_key) { p_key = tunable_rsa_cert_file; } if (SSL_CTX_use_certificate_chain_file(p_ctx, tunable_rsa_cert_file) != 1) { die("SSL: cannot load RSA certificate"); } if (SSL_CTX_use_PrivateKey_file(p_ctx, p_key, X509_FILETYPE_PEM) != 1) { die("SSL: cannot load RSA private key"); } } if (tunable_dsa_cert_file) { const char* p_key = tunable_dsa_private_key_file; if (!p_key) { p_key = tunable_dsa_cert_file; } if (SSL_CTX_use_certificate_chain_file(p_ctx, tunable_dsa_cert_file) != 1) { die("SSL: cannot load DSA certificate"); } if (SSL_CTX_use_PrivateKey_file(p_ctx, p_key, X509_FILETYPE_PEM) != 1) { die("SSL: cannot load DSA private key"); } } if (tunable_ssl_ciphers && SSL_CTX_set_cipher_list(p_ctx, tunable_ssl_ciphers) != 1) { die("SSL: could not set cipher list"); } if (RAND_status() != 1) { die("SSL: RNG is not seeded"); } if (tunable_ssl_request_cert) { verify_option |= SSL_VERIFY_PEER; } if (tunable_require_cert) { verify_option |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; } if (verify_option) { SSL_CTX_set_verify(p_ctx, verify_option, ssl_verify_callback); if (tunable_ca_certs_file) { if (!SSL_CTX_load_verify_locations(p_ctx, tunable_ca_certs_file, NULL)) { die("SSL: could not load verify file"); } } } p_sess->p_ssl_ctx = p_ctx; ssl_inited = 1; }}voidhandle_auth(struct vsf_session* p_sess){ str_upper(&p_sess->ftp_arg_str); if (str_equal_text(&p_sess->ftp_arg_str, "TLS") || str_equal_text(&p_sess->ftp_arg_str, "TLS-C") || str_equal_text(&p_sess->ftp_arg_str, "SSL") || str_equal_text(&p_sess->ftp_arg_str, "TLS-P")) { vsf_cmdio_write(p_sess, FTP_AUTHOK, "Proceed with negotiation."); if (!ssl_session_init(p_sess)) { struct mystr err_str = INIT_MYSTR; str_alloc_text(&err_str, "Negotiation failed: "); /* Technically, we shouldn't leak such detailed error messages. */ str_append_text(&err_str, get_ssl_error()); vsf_cmdio_write_str(p_sess, FTP_TLS_FAIL, &err_str); vsf_sysutil_exit(0); } p_sess->control_use_ssl = 1; if (str_equal_text(&p_sess->ftp_arg_str, "SSL") || str_equal_text(&p_sess->ftp_arg_str, "TLS-P")) { p_sess->data_use_ssl = 1; } } else { vsf_cmdio_write(p_sess, FTP_BADAUTH, "Unknown AUTH type."); }}voidhandle_pbsz(struct vsf_session* p_sess){ if (!p_sess->control_use_ssl) { vsf_cmdio_write(p_sess, FTP_BADPBSZ, "PBSZ needs a secure connection."); } else { vsf_cmdio_write(p_sess, FTP_PBSZOK, "PBSZ set to 0."); }}voidhandle_prot(struct vsf_session* p_sess){ str_upper(&p_sess->ftp_arg_str); if (!p_sess->control_use_ssl) { vsf_cmdio_write(p_sess, FTP_BADPROT, "PROT needs a secure connection."); } else if (str_equal_text(&p_sess->ftp_arg_str, "C")) { p_sess->data_use_ssl = 0; vsf_cmdio_write(p_sess, FTP_PROTOK, "PROT now Clear."); } else if (str_equal_text(&p_sess->ftp_arg_str, "P")) { p_sess->data_use_ssl = 1; vsf_cmdio_write(p_sess, FTP_PROTOK, "PROT now Private."); } else if (str_equal_text(&p_sess->ftp_arg_str, "S") || str_equal_text(&p_sess->ftp_arg_str, "E")) { vsf_cmdio_write(p_sess, FTP_NOHANDLEPROT, "PROT not supported."); } else { vsf_cmdio_write(p_sess, FTP_NOSUCHPROT, "PROT not recognized."); }}voidssl_getline(const struct vsf_session* p_sess, struct mystr* p_str, char end_char, char* p_buf, unsigned int buflen){ char* p_buf_start; if (buflen == 0) { return; } p_buf_start = p_buf; p_buf[buflen - 1] = end_char; buflen--; while (1) { /* Should I use SSL_peek here? Won't lack of it break pipelining? (SSL * clients seem to work just fine, mind you, so maybe pipelining is banned * over SSL connections. Also note that OpenSSL didn't always have * SSL_peek). */ int retval = SSL_read(p_sess->p_control_ssl, p_buf, buflen); if (retval <= 0) { die("SSL_read"); } p_buf[retval] = end_char; buflen -= retval; if (p_buf[retval - 1] == end_char || buflen == 0) { break; } p_buf += retval; } str_alloc_alt_term(p_str, p_buf_start, end_char);}intssl_read(struct vsf_session* p_sess, char* p_buf, unsigned int len){ int retval; int err; SSL* p_ssl = p_sess->p_data_ssl; do { retval = SSL_read(p_ssl, p_buf, len); err = SSL_get_error(p_ssl, retval); } while (retval < 0 && (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)); // If we hit an EOF, make sure it was from the peer, not injected by the // attacker. if (retval == 0 && SSL_get_shutdown(p_ssl) != SSL_RECEIVED_SHUTDOWN) { str_alloc_text(&debug_str, "Connection terminated without SSL shutdown " "- buggy client?"); vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str); if (tunable_strict_ssl_read_eof) { return -1; } } return retval;}intssl_write(void* p_ssl, const char* p_buf, unsigned int len){ int retval; int err; do { retval = SSL_write((SSL*) p_ssl, p_buf, len); err = SSL_get_error((SSL*) p_ssl, retval); } while (retval < 0 && (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)); return retval;}intssl_write_str(void* p_ssl, const struct mystr* p_str){ unsigned int len = str_getlen(p_str); int ret = SSL_write((SSL*) p_ssl, str_getbuf(p_str), len); if ((unsigned int) ret != len) { return -1; } return 0;}static voidmaybe_log_shutdown_state(struct vsf_session* p_sess){ if (tunable_debug_ssl) { int ret = SSL_get_shutdown(p_sess->p_data_ssl); str_alloc_text(&debug_str, "SSL shutdown state is: "); if (ret == 0) { str_append_text(&debug_str, "NONE"); } else if (ret == SSL_SENT_SHUTDOWN) { str_append_text(&debug_str, "SSL_SENT_SHUTDOWN"); } else if (ret == SSL_RECEIVED_SHUTDOWN) { str_append_text(&debug_str, "SSL_RECEIVED_SHUTDOWN"); } else { str_append_ulong(&debug_str, ret); } vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str); }}static voidmaybe_log_ssl_error_state(struct vsf_session* p_sess, int ret){ if (tunable_debug_ssl) { str_alloc_text(&debug_str, "SSL ret: "); str_append_ulong(&debug_str, ret); str_append_text(&debug_str, ", SSL error: "); str_append_text(&debug_str, get_ssl_error()); str_append_text(&debug_str, ", errno: "); str_append_ulong(&debug_str, errno); vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str); }}intssl_data_close(struct vsf_session* p_sess){ int success = 1; SSL* p_ssl = p_sess->p_data_ssl; if (p_ssl)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -