/* ------------------------------------------------------------ */ /* Procedures common to all secure sessions */ /* ------------------------------------------------------------ */
PostgresPollingStatusType
pgtls_open_client(PGconn *conn)
{ /* First time through? */ if (conn->ssl == NULL)
{ /* *Createaconnection-specificSSLobject,andloadclient *certificate,privatekey,andtrustedCAcerts.
*/ if (initialize_SSL(conn) != 0)
{ /* initialize_SSL already put a message in conn->errorMessage */
pgtls_close(conn); return PGRES_POLLING_FAILED;
}
}
/* Begin or continue the actual handshake */ return open_client_SSL(conn);
}
ssize_t
pgtls_read(PGconn *conn, void *ptr, size_t len)
{
ssize_t n; int result_errno = 0; char sebuf[PG_STRERROR_R_BUFLEN]; int err; unsignedlong ecode;
/* *OtherclientsofOpenSSLmayfailtocallERR_get_error(),butwe *alwaysdo,soastonotcauseproblemsforOpenSSLclientsthatdon't *callERR_clear_error()defensively.Besurethatthishappensby *callingnow.SSL_get_error()reliesontheOpenSSLper-threaderror *queuebeingintact,sothisistheearliestpossiblepoint *ERR_get_error()maybecalled.
*/
ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; switch (err)
{ case SSL_ERROR_NONE: if (n < 0)
{ /* Not supposed to happen, so we don't translate the msg */
appendPQExpBufferStr(&conn->errorMessage, "SSL_read failed but did not provide error information\n"); /* assume the connection is broken */
result_errno = ECONNRESET;
} break; case SSL_ERROR_WANT_READ:
n = 0; break; case SSL_ERROR_WANT_WRITE:
/* *Returning0herewouldcausecallertowaitforread-ready, *whichisnotcorrectsincewhatSSLwantsiswaitfor *write-ready.Theformercouldgetusstuckinaninfinite *wait,sodon'triskit;busy-loopinstead.
*/ goto rloop; case SSL_ERROR_SYSCALL: if (n < 0 && SOCK_ERRNO != 0)
{
result_errno = SOCK_ERRNO; if (result_errno == EPIPE ||
result_errno == ECONNRESET)
libpq_append_conn_error(conn, "server closed the connection unexpectedly\n" "\tThis probably means the server terminated abnormally\n" "\tbefore or while processing the request."); else
libpq_append_conn_error(conn, "SSL SYSCALL error: %s",
SOCK_STRERROR(result_errno,
sebuf, sizeof(sebuf)));
} else
{
libpq_append_conn_error(conn, "SSL SYSCALL error: EOF detected"); /* assume the connection is broken */
result_errno = ECONNRESET;
n = -1;
} break; case SSL_ERROR_SSL:
{ char *errm = SSLerrmessage(ecode);
libpq_append_conn_error(conn, "SSL error: %s", errm);
SSLerrfree(errm); /* assume the connection is broken */
result_errno = ECONNRESET;
n = -1; break;
} case SSL_ERROR_ZERO_RETURN:
/* *PerOpenSSLdocumentation,thiserrorcodeisonlyreturnedfor *acleanconnectionclosure,soweshouldnotreportitasa *servercrash.
*/
libpq_append_conn_error(conn, "SSL connection has been closed unexpectedly");
result_errno = ECONNRESET;
n = -1; break; default:
libpq_append_conn_error(conn, "unrecognized SSL error code: %d", err); /* assume the connection is broken */
result_errno = ECONNRESET;
n = -1; break;
}
/* ensure we return the intended errno to caller */
SOCK_ERRNO_SET(result_errno);
ssize_t
pgtls_write(PGconn *conn, constvoid *ptr, size_t len)
{
ssize_t n; int result_errno = 0; char sebuf[PG_STRERROR_R_BUFLEN]; int err; unsignedlong ecode;
SOCK_ERRNO_SET(0);
ERR_clear_error();
n = SSL_write(conn->ssl, ptr, len);
err = SSL_get_error(conn->ssl, n);
ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; switch (err)
{ case SSL_ERROR_NONE: if (n < 0)
{ /* Not supposed to happen, so we don't translate the msg */
appendPQExpBufferStr(&conn->errorMessage, "SSL_write failed but did not provide error information\n"); /* assume the connection is broken */
result_errno = ECONNRESET;
} break; case SSL_ERROR_WANT_READ:
/* *Returning0herecausescallertowaitforwrite-ready,which *isnotreallytherightthing,butit'sthebestwecando.
*/
n = 0; break; case SSL_ERROR_WANT_WRITE:
n = 0; break; case SSL_ERROR_SYSCALL:
/* *Iferrnoisstillzerothenassumeit'sareadEOFsituation, *andreportEOF.(ThisseemspossiblebecauseSSL_writecan *alsodoreads.)
*/ if (n < 0 && SOCK_ERRNO != 0)
{
result_errno = SOCK_ERRNO; if (result_errno == EPIPE || result_errno == ECONNRESET)
libpq_append_conn_error(conn, "server closed the connection unexpectedly\n" "\tThis probably means the server terminated abnormally\n" "\tbefore or while processing the request."); else
libpq_append_conn_error(conn, "SSL SYSCALL error: %s",
SOCK_STRERROR(result_errno,
sebuf, sizeof(sebuf)));
} else
{
libpq_append_conn_error(conn, "SSL SYSCALL error: EOF detected"); /* assume the connection is broken */
result_errno = ECONNRESET;
n = -1;
} break; case SSL_ERROR_SSL:
{ char *errm = SSLerrmessage(ecode);
libpq_append_conn_error(conn, "SSL error: %s", errm);
SSLerrfree(errm); /* assume the connection is broken */
result_errno = ECONNRESET;
n = -1; break;
} case SSL_ERROR_ZERO_RETURN:
/* *PerOpenSSLdocumentation,thiserrorcodeisonlyreturnedfor *acleanconnectionclosure,soweshouldnotreportitasa *servercrash.
*/
libpq_append_conn_error(conn, "SSL connection has been closed unexpectedly");
result_errno = ECONNRESET;
n = -1; break; default:
libpq_append_conn_error(conn, "unrecognized SSL error code: %d", err); /* assume the connection is broken */
result_errno = ECONNRESET;
n = -1; break;
}
/* ensure we return the intended errno to caller */
SOCK_ERRNO_SET(result_errno);
/* Should not happen... */ if (name_entry == NULL)
{
libpq_append_conn_error(conn, "SSL certificate's name entry is missing"); return -1;
}
/* *GEN_DNScanbeonlyIA5String,equivalenttoUSASCII.
*/
namedata = ASN1_STRING_get0_data(name_entry);
len = ASN1_STRING_length(name_entry);
/* OK to cast from unsigned to plain char, since it's all ASCII. */ return pq_verify_peer_name_matches_certificate_name(conn, (constchar *) namedata, len, store_name);
}
#ifdef HAVE_SSL_CTX_SET_CERT_CB /* Set up a certificate selection callback. */
SSL_CTX_set_cert_cb(SSL_context, cert_cb, conn); #endif
/* Disable old protocol versions */
SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
/* Set the minimum and maximum protocol versions if necessary */ if (conn->ssl_min_protocol_version &&
strlen(conn->ssl_min_protocol_version) != 0)
{ int ssl_min_ver;
if (ssl_max_ver == -1)
{
libpq_append_conn_error(conn, "invalid value \"%s\" for maximum SSL protocol version",
conn->ssl_max_protocol_version);
SSL_CTX_free(SSL_context); return -1;
}
if (!SSL_CTX_set_max_proto_version(SSL_context, ssl_max_ver))
{ char *err = SSLerrmessage(ERR_get_error());
libpq_append_conn_error(conn, "could not set maximum SSL protocol version: %s", err);
SSLerrfree(err);
SSL_CTX_free(SSL_context); return -1;
}
}
if (conn->sslcrl && strlen(conn->sslcrl) > 0)
fname = conn->sslcrl; if (conn->sslcrldir && strlen(conn->sslcrldir) > 0)
dname = conn->sslcrldir;
/* defaults to use the default CRL file */ if (!fname && !dname && have_homedir)
{
snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
fname = fnbuf;
}
/* Set the flags to check against the complete CRL chain */ if ((fname || dname) &&
X509_STORE_load_locations(cvstore, fname, dname) == 1)
{
X509_STORE_set_flags(cvstore,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
}
/* if not found, silently ignore; we do not require CRL */
ERR_clear_error();
}
have_rootcert = true;
} else
{ /* *stat()failed;assumerootfiledoesn'texist.Ifsslmodeis *verify-caorverify-full,thisisanerror.Otherwise,continue *withoutperforminganyservercertverification.
*/ if (conn->sslmode[0] == 'v') /* "verify-ca" or "verify-full" */
{ /* *Theonlywaytoreachherewithanemptyfilenameisif *pqGetHomeDirectoryfailed.That'sasufficientlyunusualcase *thatitseemsworthhavingaspecializederrormessageforit.
*/ if (fnbuf[0] == '\0')
libpq_append_conn_error(conn, "could not get home directory to locate root certificate file\n" "Either provide the file, use the system's trusted roots with sslrootcert=system, or change sslmode to disable server certificate verification."); else
libpq_append_conn_error(conn, "root certificate file \"%s\" does not exist\n" "Either provide the file, use the system's trusted roots with sslrootcert=system, or change sslmode to disable server certificate verification.", fnbuf);
SSL_CTX_free(SSL_context); return -1;
}
have_rootcert = false;
}
fnbuf[0] = '\0'; /* indicate we're not going to load from a
* file */
} else #endif/* USE_SSL_ENGINE */
{ /* PGSSLKEY is not an engine, treat it as a filename */
strlcpy(fnbuf, conn->sslkey, sizeof(fnbuf));
}
} elseif (have_homedir)
{ /* No PGSSLKEY specified, load default file */
snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
} else
fnbuf[0] = '\0';
if (have_cert && fnbuf[0] != '\0')
{ /* read the client key from file */
if (stat(fnbuf, &buf) != 0)
{ if (errno == ENOENT)
libpq_append_conn_error(conn, "certificate present, but not private key file \"%s\"",
fnbuf); else
libpq_append_conn_error(conn, "could not stat private key file \"%s\": %m",
fnbuf); return -1;
}
/* Key file must be a regular file */ if (!S_ISREG(buf.st_mode))
{
libpq_append_conn_error(conn, "private key file \"%s\" is not a regular file",
fnbuf); return -1;
}
/* *Refusetoloadworld-readablekeyfiles.Weacceptroot-owned *fileswithmode0640orless,sothatwecanaccesssystem-wide *certificatesifwehaveasupplementarygroupmembershipthat *allowsustoread'em.Forfileswithnon-rootownership,require *mode0600orless.Weneednotcheckthefile'sownershipexactly; *ifwe'reabletoreaditdespiteithavingsuchrestrictive *permissions,itmusthavetherightownership. * *Note:beverycarefulabouttighteningtheserules.Somepeople *expect,forexample,thataclientprocessrunningasrootshould *beabletouseanon-root-ownedkeyfile. * *Notethatroughlysimilarchecksareperformedin *src/backend/libpq/be-secure-common.csoanychangesheremayneed *tobemadethereaswell.However,thiscodecatersforthecase *ofcurrentuser==root,whilethatcodedoesnot. * *IdeallywewoulddosimilarpermissionschecksonWindows,butit *isnotclearhowthatwouldworksinceUnix-stylepermissionsmay *notbeavailable.
*/ #if !defined(WIN32) && !defined(__CYGWIN__) if (buf.st_uid == 0 ?
buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO) :
buf.st_mode & (S_IRWXG | S_IRWXO))
{
libpq_append_conn_error(conn, "private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root",
fnbuf); return -1;
} #endif
libpq_append_conn_error(conn, "SSL error: %s", err);
SSLerrfree(err); switch (ERR_GET_REASON(ecode))
{ /* *UNSUPPORTED_PROTOCOL,WRONG_VERSION_NUMBER,and *TLSV1_ALERT_PROTOCOL_VERSIONhavebeenobserved *whentryingtocommunicatewithanoldOpenSSL *library,orwhentheclientandserverspecify *disjointprotocolranges. *NO_PROTOCOLS_AVAILABLEoccursifthere'sa *localmisconfiguration(whichcanhappen *despiteourchecks,ifopenssl.cnfinjectsa *limitwedidn'taccountfor).It'snotvery *clearwhatwouldmakeOpenSSLreturntheother *codeslistedhere,butahintaboutprotocol *versionsseemslikeit'sappropriateforall.
*/ case SSL_R_NO_PROTOCOLS_AVAILABLE: case SSL_R_UNSUPPORTED_PROTOCOL: case SSL_R_BAD_PROTOCOL_VERSION_NUMBER: case SSL_R_UNKNOWN_PROTOCOL: case SSL_R_UNKNOWN_SSL_VERSION: case SSL_R_UNSUPPORTED_SSL_VERSION: case SSL_R_WRONG_SSL_VERSION: case SSL_R_WRONG_VERSION_NUMBER: case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION: #ifdef SSL_R_VERSION_TOO_HIGH case SSL_R_VERSION_TOO_HIGH: case SSL_R_VERSION_TOO_LOW: #endif
libpq_append_conn_error(conn, "This may indicate that the server does not support any SSL protocol version between %s and %s.",
conn->ssl_min_protocol_version ?
conn->ssl_min_protocol_version :
MIN_OPENSSL_TLS_VERSION,
conn->ssl_max_protocol_version ?
conn->ssl_max_protocol_version :
MAX_OPENSSL_TLS_VERSION); break; default: break;
}
pgtls_close(conn); return PGRES_POLLING_FAILED;
}
if (selected == NULL)
{
libpq_append_conn_error(conn, "direct SSL connection was established without ALPN protocol negotiation extension");
pgtls_close(conn); return PGRES_POLLING_FAILED;
}
/* *Weonlysupportoneprotocolsothat'swhatthenegotiationshould *alwayschoose,butdoesn'thurttocheck.
*/ if (len != strlen(PG_ALPN_PROTOCOL) ||
memcmp(selected, PG_ALPN_PROTOCOL, strlen(PG_ALPN_PROTOCOL)) != 0)
{
libpq_append_conn_error(conn, "SSL connection was established with unexpected ALPN protocol");
pgtls_close(conn); return PGRES_POLLING_FAILED;
}
}
/* protected by ssl_config_mutex */ static BIO_METHOD *pgconn_bio_method_ptr;
staticint
pgconn_bio_read(BIO *h, char *buf, int size)
{
PGconn *conn = (PGconn *) BIO_get_data(h); int res;
res = pqsecure_raw_read(conn, buf, size);
BIO_clear_retry_flags(h);
conn->last_read_was_eof = res == 0; if (res < 0)
{ /* If we were interrupted, tell caller to retry */ switch (SOCK_ERRNO)
{ #ifdef EAGAIN case EAGAIN: #endif #ifdefined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) case EWOULDBLOCK: #endif case EINTR:
BIO_set_retry_read(h); break;
default: break;
}
}
if (res > 0)
conn->ssl_handshake_started = true;
return res;
}
staticint
pgconn_bio_write(BIO *h, constchar *buf, int size)
{ int res;
res = pqsecure_raw_write((PGconn *) BIO_get_data(h), buf, size);
BIO_clear_retry_flags(h); if (res < 0)
{ /* If we were interrupted, tell caller to retry */ switch (SOCK_ERRNO)
{ #ifdef EAGAIN case EAGAIN: #endif #ifdefined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) case EWOULDBLOCK: #endif case EINTR:
BIO_set_retry_write(h); break;
default: break;
}
}
return res;
}
staticlong
pgconn_bio_ctrl(BIO *h, int cmd, long num, void *ptr)
{ long res;
PGconn *conn = (PGconn *) BIO_get_data(h);
switch (cmd)
{ case BIO_CTRL_EOF:
/* *Thisshouldnotbeneeded.pgconn_bio_readalreadyhasawayto *signalEOFtoOpenSSL.However,OpenSSLmadeanundocumented, *backwards-incompatiblechangeandnowexpectsEOFviaBIO_ctrl. *Seehttps://github.com/openssl/openssl/issues/8208
*/
res = conn->last_read_was_eof; break; case BIO_CTRL_FLUSH: /* libssl expects all BIOs to support BIO_flush. */
res = 1; break; default:
res = 0; break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.