diff options
Diffstat (limited to 'src/backend/libpq/be-secure-openssl.c')
-rw-r--r-- | src/backend/libpq/be-secure-openssl.c | 112 |
1 files changed, 109 insertions, 3 deletions
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 3d0168a3696..9cec6866a39 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -81,6 +81,9 @@ static bool ssl_is_server_start; static int ssl_protocol_version_to_openssl(int v); static const char *ssl_protocol_version_to_string(int v); +/* for passing data back from verify_cb() */ +static const char *cert_errdetail; + /* ------------------------------------------------------------ */ /* Public interface */ /* ------------------------------------------------------------ */ @@ -541,6 +544,7 @@ aloop: (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("could not accept SSL connection: %s", SSLerrmessage(ecode)), + cert_errdetail ? errdetail_internal("%s", cert_errdetail) : 0, give_proto_hint ? errhint("This may indicate that the client does not support any SSL protocol version between %s and %s.", ssl_min_protocol_version ? @@ -549,6 +553,7 @@ aloop: ssl_max_protocol_version ? ssl_protocol_version_to_string(ssl_max_protocol_version) : MAX_OPENSSL_TLS_VERSION) : 0)); + cert_errdetail = NULL; break; case SSL_ERROR_ZERO_RETURN: ereport(COMMERROR, @@ -1077,11 +1082,45 @@ dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata) } /* + * Examines the provided certificate name, and if it's too long to log, modifies + * and truncates it. The return value is NULL if no truncation was needed; it + * otherwise points into the middle of the input string, and should not be + * freed. + */ +static char * +truncate_cert_name(char *name) +{ + size_t namelen = strlen(name); + char *truncated; + + /* + * Common Names are 64 chars max, so for a common case where the CN is the + * last field, we can still print the longest possible CN with a + * 7-character prefix (".../CN=[64 chars]"), for a reasonable limit of 71 + * characters. + */ +#define MAXLEN 71 + + if (namelen <= MAXLEN) + return NULL; + + /* + * Keep the end of the name, not the beginning, since the most specific + * field is likely to give users the most information. + */ + truncated = name + namelen - MAXLEN; + truncated[0] = truncated[1] = truncated[2] = '.'; + +#undef MAXLEN + + return truncated; +} + +/* * Certificate verification callback * - * This callback allows us to log intermediate problems during - * verification, but for now we'll see if the final error message - * contains enough information. + * This callback allows us to examine intermediate problems during + * verification, for later logging. * * This callback also allows us to override the default acceptance * criteria (e.g., accepting self-signed or expired certs), but @@ -1090,6 +1129,73 @@ dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata) static int verify_cb(int ok, X509_STORE_CTX *ctx) { + int depth; + int errcode; + const char *errstring; + StringInfoData str; + X509 *cert; + + if (ok) + { + /* Nothing to do for the successful case. */ + return ok; + } + + /* Pull all the information we have on the verification failure. */ + depth = X509_STORE_CTX_get_error_depth(ctx); + errcode = X509_STORE_CTX_get_error(ctx); + errstring = X509_verify_cert_error_string(errcode); + + initStringInfo(&str); + appendStringInfo(&str, + _("Client certificate verification failed at depth %d: %s."), + depth, errstring); + + cert = X509_STORE_CTX_get_current_cert(ctx); + if (cert) + { + char *subject, + *issuer; + char *sub_truncated, + *iss_truncated; + char *serialno; + ASN1_INTEGER *sn; + BIGNUM *b; + + /* + * Get the Subject and Issuer for logging, but don't let maliciously + * huge certs flood the logs. + */ + subject = X509_NAME_to_cstring(X509_get_subject_name(cert)); + sub_truncated = truncate_cert_name(subject); + + issuer = X509_NAME_to_cstring(X509_get_issuer_name(cert)); + iss_truncated = truncate_cert_name(issuer); + + /* + * Pull the serial number, too, in case a Subject is still ambiguous. + * This mirrors be_tls_get_peer_serial(). + */ + sn = X509_get_serialNumber(cert); + b = ASN1_INTEGER_to_BN(sn, NULL); + serialno = BN_bn2dec(b); + + appendStringInfoChar(&str, '\n'); + appendStringInfo(&str, + _("Failed certificate data (unverified): subject \"%s\", serial number %s, issuer \"%s\"."), + sub_truncated ? sub_truncated : subject, + serialno ? serialno : _("unknown"), + iss_truncated ? iss_truncated : issuer); + + BN_free(b); + OPENSSL_free(serialno); + pfree(issuer); + pfree(subject); + } + + /* Store our detail message to be logged later. */ + cert_errdetail = str.data; + return ok; } |