📄 be-secure.c
字号:
/*------------------------------------------------------------------------- * * be-secure.c * functions related to setting up a secure connection to the frontend. * Secure connections are expected to provide confidentiality, * message integrity and endpoint authentication. * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.59.2.2 2006/05/12 22:44:43 tgl Exp $ * * Since the server static private key ($DataDir/server.key) * will normally be stored unencrypted so that the database * backend can restart automatically, it is important that * we select an algorithm that continues to provide confidentiality * even if the attacker has the server's private key. Empheral * DH (EDH) keys provide this, and in fact provide Perfect Forward * Secrecy (PFS) except for situations where the session can * be hijacked during a periodic handshake/renegotiation. * Even that backdoor can be closed if client certificates * are used (since the imposter will be unable to successfully * complete renegotiation). * * N.B., the static private key should still be protected to * the largest extent possible, to minimize the risk of * impersonations. * * Another benefit of EDH is that it allows the backend and * clients to use DSA keys. DSA keys can only provide digital * signatures, not encryption, and are often acceptable in * jurisdictions where RSA keys are unacceptable. * * The downside to EDH is that it makes it impossible to * use ssldump(1) if there's a problem establishing an SSL * session. In this case you'll need to temporarily disable * EDH by commenting out the callback. * * ... * * Because the risk of cryptanalysis increases as large * amounts of data are sent with the same session key, the * session keys are periodically renegotiated. * * PATCH LEVEL * milestone 1: fix basic coding errors * [*] existing SSL code pulled out of existing files. * [*] SSL_get_error() after SSL_read() and SSL_write(), * SSL_shutdown(), default to TLSv1. * * milestone 2: provide endpoint authentication (server) * [*] client verifies server cert * [*] client verifies server hostname * * milestone 3: improve confidentially, support perfect forward secrecy * [ ] use 'random' file, read from '/dev/urandom?' * [*] emphermal DH keys, default values * [*] periodic renegotiation * [*] private key permissions * * milestone 4: provide endpoint authentication (client) * [*] server verifies client certificates * * milestone 5: provide informational callbacks * [*] provide informational callbacks * * other changes * [ ] tcp-wrappers * [ ] more informative psql * *------------------------------------------------------------------------- */#include "postgres.h"#include <sys/stat.h>#include <signal.h>#include <fcntl.h>#include <ctype.h>#include <sys/socket.h>#include <unistd.h>#include <netdb.h>#include <netinet/in.h>#ifdef HAVE_NETINET_TCP_H#include <netinet/tcp.h>#include <arpa/inet.h>#endif#ifdef USE_SSL#include <openssl/ssl.h>#include <openssl/dh.h>#endif#include "libpq/libpq.h"#include "miscadmin.h"#include "tcop/tcopprot.h"#ifdef USE_SSL#define ROOT_CERT_FILE "root.crt"#define SERVER_CERT_FILE "server.crt"#define SERVER_PRIVATE_KEY_FILE "server.key"static DH *load_dh_file(int keylength);static DH *load_dh_buffer(const char *, size_t);static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);static int verify_cb(int, X509_STORE_CTX *);static void info_cb(const SSL *ssl, int type, int args);static int initialize_SSL(void);static void destroy_SSL(void);static int open_server_SSL(Port *);static void close_SSL(Port *);static const char *SSLerrmessage(void);#endif#ifdef USE_SSL/* * How much data can be sent across a secure connection * (total in both directions) before we require renegotiation. */#define RENEGOTIATION_LIMIT (512 * 1024 * 1024)static SSL_CTX *SSL_context = NULL;#endif/* ------------------------------------------------------------ *//* Hardcoded values *//* ------------------------------------------------------------ *//* * Hardcoded DH parameters, used in empheral DH keying. * As discussed above, EDH protects the confidentiality of * sessions even if the static private key is compromised, * so we are *highly* motivated to ensure that we can use * EDH even if the DBA... or an attacker... deletes the * $DataDir/dh*.pem files. * * We could refuse SSL connections unless a good DH parameter * file exists, but some clients may quietly renegotiate an * unsecured connection without fully informing the user. * Very uncool. * * Alternately, the backend could attempt to load these files * on startup if SSL is enabled - and refuse to start if any * do not exist - but this would tend to piss off DBAs. * * If you want to create your own hardcoded DH parameters * for fun and profit, review "Assigned Number for SKIP * Protocols" (http://www.skip-vpn.org/spec/numbers.html) * for suggestions. */#ifdef USE_SSLstatic const char file_dh512[] ="-----BEGIN DH PARAMETERS-----\n\MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\-----END DH PARAMETERS-----\n";static const char file_dh1024[] ="-----BEGIN DH PARAMETERS-----\n\MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\-----END DH PARAMETERS-----\n";static const char file_dh2048[] ="-----BEGIN DH PARAMETERS-----\n\MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\-----END DH PARAMETERS-----\n";static const char file_dh4096[] ="-----BEGIN DH PARAMETERS-----\n\MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\-----END DH PARAMETERS-----\n";#endif/* ------------------------------------------------------------ *//* Procedures common to all secure sessions *//* ------------------------------------------------------------ *//* * Initialize global context */intsecure_initialize(void){ int r = 0;#ifdef USE_SSL r = initialize_SSL();#endif return r;}/* * Destroy global context */voidsecure_destroy(void){#ifdef USE_SSL destroy_SSL();#endif}/* * Attempt to negotiate secure session. */intsecure_open_server(Port *port){ int r = 0;#ifdef USE_SSL r = open_server_SSL(port);#endif return r;}/* * Close secure session. */voidsecure_close(Port *port){#ifdef USE_SSL if (port->ssl) close_SSL(port);#endif}/* * Read data from a secure connection. */ssize_tsecure_read(Port *port, void *ptr, size_t len){ ssize_t n;#ifdef USE_SSL if (port->ssl) { int err;rloop: n = SSL_read(port->ssl, ptr, len); err = SSL_get_error(port->ssl, n); switch (err) { case SSL_ERROR_NONE: port->count += n; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE:#ifdef WIN32 pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl), (err == SSL_ERROR_WANT_READ) ? FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE);#endif goto rloop; case SSL_ERROR_SYSCALL: if (n == -1) ereport(COMMERROR, (errcode_for_socket_access(), errmsg("SSL SYSCALL error: %m"))); else { ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL SYSCALL error: EOF detected"))); errno = ECONNRESET; n = -1; } break; case SSL_ERROR_SSL: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL error: %s", SSLerrmessage()))); /* fall through */ case SSL_ERROR_ZERO_RETURN: errno = ECONNRESET; n = -1; break; default: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unrecognized SSL error code: %d", err))); n = -1; break; } } else#endif { prepare_for_client_read(); n = recv(port->sock, ptr, len, 0); client_read_ended(); } return n;}/* * Write data to a secure connection. */ssize_tsecure_write(Port *port, void *ptr, size_t len){ ssize_t n;#ifdef USE_SSL if (port->ssl) { int err; if (port->count > RENEGOTIATION_LIMIT) { SSL_set_session_id_context(port->ssl, (void *) &SSL_context, sizeof(SSL_context)); if (SSL_renegotiate(port->ssl) <= 0) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL renegotiation failure"))); if (SSL_do_handshake(port->ssl) <= 0) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL renegotiation failure"))); if (port->ssl->state != SSL_ST_OK) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL failed to send renegotiation request"))); port->ssl->state |= SSL_ST_ACCEPT; SSL_do_handshake(port->ssl); if (port->ssl->state != SSL_ST_OK) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL renegotiation failure"))); port->count = 0; }wloop: n = SSL_write(port->ssl, ptr, len); err = SSL_get_error(port->ssl, n); switch (err) { case SSL_ERROR_NONE: port->count += n; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE:#ifdef WIN32 pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl), (err == SSL_ERROR_WANT_READ) ? FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE);#endif goto wloop; case SSL_ERROR_SYSCALL: if (n == -1) ereport(COMMERROR, (errcode_for_socket_access(), errmsg("SSL SYSCALL error: %m"))); else { ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL SYSCALL error: EOF detected"))); errno = ECONNRESET; n = -1; } break; case SSL_ERROR_SSL: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL error: %s", SSLerrmessage()))); /* fall through */ case SSL_ERROR_ZERO_RETURN: errno = ECONNRESET; n = -1; break; default: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unrecognized SSL error code: %d", err))); n = -1; break; } } else#endif n = send(port->sock, ptr, len, 0); return n;}/* ------------------------------------------------------------ *//* SSL specific code *//* ------------------------------------------------------------ */#ifdef USE_SSL/* * Private substitute BIO: this wraps the SSL library's standard socket BIO * so that we can enable and disable interrupts just while calling recv(). * We cannot have interrupts occurring while the bulk of openssl runs, * because it uses malloc() and possibly other non-reentrant libc facilities. * * As of openssl 0.9.7, we can use the reasonably clean method of interposing * a wrapper around the standard socket BIO's sock_read() method. This relies * on the fact that sock_read() doesn't call anything non-reentrant, in fact * not much of anything at all except recv(). If this ever changes we'd * probably need to duplicate the code of sock_read() in order to push the * interrupt enable/disable down yet another level. */static bool my_bio_initialized = false;static BIO_METHOD my_bio_methods;static int (*std_sock_read) (BIO *h, char *buf, int size);static intmy_sock_read(BIO *h, char *buf, int size){ int res; prepare_for_client_read(); res = std_sock_read(h, buf, size); client_read_ended(); return res;}static BIO_METHOD *my_BIO_s_socket(void){ if (!my_bio_initialized) { memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD)); std_sock_read = my_bio_methods.bread; my_bio_methods.bread = my_sock_read; my_bio_initialized = true; } return &my_bio_methods;}/* This should exactly match openssl's SSL_set_fd except for using my BIO */static intmy_SSL_set_fd(SSL *s, int fd){ int ret = 0; BIO *bio = NULL; bio = BIO_new(my_BIO_s_socket()); if (bio == NULL) { SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB); goto err; } BIO_set_fd(bio, fd, BIO_NOCLOSE); SSL_set_bio(s, bio, bio);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -