diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index d23df0261c..42263431ed 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1342,10 +1342,11 @@ SASL is a framework for authentication in connection-oriented -protocols. At the moment, PostgreSQL implements only one SASL -authentication mechanism, SCRAM-SHA-256, but more might be added in the -future. The below steps illustrate how SASL authentication is performed in -general, while the next subsection gives more details on SCRAM-SHA-256. +protocols. At the moment, PostgreSQL implements only two SASL +authentication mechanisms, SCRAM-SHA-256 and SCRAM-SHA-256-PLUS, but more +might be added in the future. The below steps illustrate how SASL +authentication is performed in general, while the next subsection gives +more details on SCRAM-SHA-256 and SCRAM-SHA-256-PLUS. @@ -1430,7 +1431,9 @@ the password is in. -Channel binding has not been implemented yet. +Channel binding is supported in builds with SSL support, and +uses as mechanism name SCRAM-SHA-256-PLUS for this purpose as +defined per IANA. @@ -1439,14 +1442,18 @@ the password is in. The server sends an AuthenticationSASL message. It includes a list of SASL authentication mechanisms that the server can accept. + SCRAM-SHA-256 and SCRAM-SHA-256-PLUS are the + two mechanism names that the server lists in this message. Support for + channel binding is not included if the server is built without SSL + support and if the connection attempt is done without SSL. The client responds by sending a SASLInitialResponse message, which - indicates the chosen mechanism, SCRAM-SHA-256. In the Initial - Client response field, the message contains the SCRAM - client-first-message. + indicates the chosen mechanism, SCRAM-SHA-256 or + SCRAM-SHA-256-PLUS. In the Initial Client response field, + the message contains the SCRAM client-first-message. diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 99feb0ce94..a372b08bba 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -17,8 +17,6 @@ * by the SASLprep profile, we skip the SASLprep pre-processing and use * the raw bytes in calculating the hash. * - * - Channel binding is not supported yet. - * * * The password stored in pg_authid consists of the iteration count, salt, * StoredKey and ServerKey. @@ -112,6 +110,10 @@ typedef struct const char *username; /* username from startup packet */ + bool ssl_in_use; + char *tls_finish_message; + int tls_finish_len; + int iterations; char *salt; /* base64-encoded */ uint8 StoredKey[SCRAM_KEY_LEN]; @@ -168,7 +170,11 @@ static char *scram_mock_salt(const char *username); * it will fail, as if an incorrect password was given. */ void * -pg_be_scram_init(const char *username, const char *shadow_pass) +pg_be_scram_init(const char *username, + const char *shadow_pass, + bool ssl_in_use, + char *tls_finish_message, + int tls_finish_len) { scram_state *state; bool got_verifier; @@ -176,6 +182,9 @@ pg_be_scram_init(const char *username, const char *shadow_pass) state = (scram_state *) palloc0(sizeof(scram_state)); state->state = SCRAM_AUTH_INIT; state->username = username; + state->ssl_in_use = ssl_in_use; + state->tls_finish_message = tls_finish_message; + state->tls_finish_len = tls_finish_len; /* * Parse the stored password verifier. @@ -767,43 +776,97 @@ read_client_first_message(scram_state *state, char *input) *------ */ - /* read gs2-cbind-flag */ + /* + * Read gs2-cbind-flag. Server handles its value as described in RFC 5802, + * section 6 dealing with channel binding. + */ switch (*input) { case 'n': - /* Client does not support channel binding */ + /* + * Client does not support channel binding, this is authorized + * only in builds not supporting SSL. If SSL is supported, the + * server cannot support this option either. + */ +#ifdef USE_SSL + if (state->ssl_in_use) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("client does not support SCRAM channel binding, but server needs it for SSL connections"))); + else + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("client does not support SCRAM channel binding, but server does"))); +#endif + input++; + if (*input != ',') + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("malformed SCRAM message (comma expected, got %s)", + sanitize_char(*input)))); input++; break; case 'y': - /* Client supports channel binding, but we're not doing it today */ + /* + * Client supports channel binding, but we're not doing it today + * in the context of a non-SSL connection. Complain though if + * the client is trying to trick the server in not doing channel + * binding with a downgrade attack if a SSL connection is + * attempted. + */ + if (state->ssl_in_use) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("client support SCRAM channel binding, but server needs it for SSL connections"))); + input++; + if (*input != ',') + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("malformed SCRAM message (comma expected, got %s)", + sanitize_char(*input)))); input++; break; case 'p': + { +#ifdef USE_SSL + char *channel_name; - /* - * Client requires channel binding. We don't support it. - * - * RFC 5802 specifies a particular error code, - * e=server-does-support-channel-binding, for this. But it can - * only be sent in the server-final message, and we don't want to - * go through the motions of the authentication, knowing it will - * fail, just to send that error message. - */ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("client requires SCRAM channel binding, but it is not supported"))); + if (!state->ssl_in_use) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("client supports SCRAM channel binding, but server does not need it for non-SSL connections"))); + + /* + * Read value provided by client, only tls-unique is supported + * for now. + */ + channel_name = read_attr_value(&input, 'p'); + if (strcmp(channel_name, "tls-unique") != 0) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + (errmsg("unexpected SCRAM channel-binding type")))); +#else + /* + * Client requires channel binding. We don't support it. + * + * RFC 5802 specifies a particular error code, + * e=server-does-support-channel-binding, for this. But it + * can only be sent in the server-final message, and we don't + * want to go through the motions of the authentication, + * knowing it will fail, just to send that error message. + */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("client requires SCRAM channel binding, but it is not supported"))); +#endif + } + break; default: ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), (errmsg("malformed SCRAM message (unexpected channel-binding flag %s)", sanitize_char(*input))))); } - if (*input != ',') - ereport(ERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("malformed SCRAM message (comma expected, got %s)", - sanitize_char(*input)))); - input++; /* * Forbid optional authzid (authorization identity). We don't support it. @@ -1023,14 +1086,47 @@ read_client_final_message(scram_state *state, char *input) */ /* - * Read channel-binding. We don't support channel binding, so it's - * expected to always be "biws", which is "n,,", base64-encoded. + * Read channel-binding. We don't support channel binding for builds + * without SSL support, so it's expected to always be "biws" in this case, + * which is "n,,", base64-encoded. In builds supporting SSL, "biws" is + * used for Non-SSL connection attempts, and for SSL connections the + * client has to provide channel binding value. */ channel_binding = read_attr_value(&p, 'c'); +#ifdef USE_SSL + if (state->ssl_in_use) + { + char *enc_tls_message; + int enc_tls_len; + + enc_tls_message = palloc(pg_b64_enc_len(state->tls_finish_len) + 1); + enc_tls_len = pg_b64_encode(state->tls_finish_message, + state->tls_finish_len, + enc_tls_message); + enc_tls_message[enc_tls_len] = '\0'; + + /* + * Compare the value sent by the client with the TLS finish message + * expected by the server. + */ + if (strcmp(channel_binding, enc_tls_message) != 0) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + (errmsg("no match for SCRAM channel-binding attribute in client-final-message")))); + } + else + { + if (strcmp(channel_binding, "biws") != 0) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + (errmsg("unexpected SCRAM channel-binding attribute in client-final-message")))); + } +#else if (strcmp(channel_binding, "biws") != 0) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), (errmsg("unexpected SCRAM channel-binding attribute in client-final-message")))); +#endif state->client_final_nonce = read_attr_value(&p, 'r'); /* ignore optional extensions */ diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 5b68e3b7a1..89dfa744b1 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -841,8 +841,10 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) void *scram_opaq; char *output = NULL; int outputlen = 0; - char *input; + char *input, *p; + char *sasl_mechs; int inputlen; + int listlen = 0; int result; bool initial; @@ -861,12 +863,38 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) /* * Send the SASL authentication request to user. It includes the list of - * authentication mechanisms (which is trivial, because we only support - * SCRAM-SHA-256 at the moment). The extra "\0" is for an empty string to - * terminate the list. + * authentication mechanisms that are supported: + * - SCRAM-SHA-256, which is the mechanism with the same name. + * - SCRAM-SHA-256-PLUS, which is SCRAM-SHA-256 with channel binding. This + * is advertised to the client only if connection is attempted with SSL. + * The order of mechanisms is advertised in decreasing order of importance. + * The extra "\0" is for an empty string to terminate the list, and each + * mechanism listed needs to be separated with "\0". */ - sendAuthRequest(port, AUTH_REQ_SASL, SCRAM_SHA256_NAME "\0", - strlen(SCRAM_SHA256_NAME) + 2); + listlen = 0; + sasl_mechs = (char *) palloc(strlen(SCRAM_SHA256_PLUS_NAME) + + strlen(SCRAM_SHA256_NAME) + 3); + p = sasl_mechs; + + /* add SCRAM-SHA-256-PLUS, which depends on if SSL is in use */ + if (port->ssl_in_use) + { + strcpy(p, SCRAM_SHA256_PLUS_NAME); + listlen += strlen(SCRAM_SHA256_PLUS_NAME) + 1; + p += strlen(SCRAM_SHA256_PLUS_NAME) + 1; + } + + /* add generic SCRAM-SHA-256 */ + strcpy(p, SCRAM_SHA256_NAME); + listlen += strlen(SCRAM_SHA256_NAME) + 1; + p += strlen(SCRAM_SHA256_NAME) + 1; + + /* put "\0" to mark that list is finished */ + p[0] = '\0'; + listlen++; + + sendAuthRequest(port, AUTH_REQ_SASL, sasl_mechs, listlen); + pfree(sasl_mechs); /* * Initialize the status tracker for message exchanges. @@ -879,7 +907,11 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) * This is because we don't want to reveal to an attacker what usernames * are valid, nor which users have a valid password. */ - scram_opaq = pg_be_scram_init(port->user_name, shadow_pass); + scram_opaq = pg_be_scram_init(port->user_name, + shadow_pass, + port->ssl_in_use, + port->tls_finish, + port->tls_finish_len); /* * Loop through SASL message exchange. This exchange can consist of @@ -933,7 +965,8 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) * is an error. */ selected_mech = pq_getmsgrawstring(&buf); - if (strcmp(selected_mech, SCRAM_SHA256_NAME) != 0) + if (strcmp(selected_mech, SCRAM_SHA256_NAME) != 0 && + strcmp(selected_mech, SCRAM_SHA256_PLUS_NAME) != 0) { ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 44c84a7869..eff8b300de 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -458,6 +458,7 @@ be_tls_open_server(Port *port) int err; int waitfor; unsigned long ecode; + char tls_finish_buf[20]; Assert(!port->ssl); Assert(!port->peer); @@ -616,6 +617,25 @@ aloop: /* set up debugging/info callback */ SSL_CTX_set_info_callback(SSL_context, info_cb); + /* + * Save the TLS finish message expected to be found, useful for + * authentication checks related to channel binding. + * SSL_get_peer_finished() does not offer a way to know the exact length + * of a TLS finish message beforehand, so attempt first with a fixed-length + * buffer, and try again if the message does not fit. + */ + port->tls_finish_len = SSL_get_peer_finished(port->ssl, + tls_finish_buf, + sizeof(tls_finish_buf)); + port->tls_finish = MemoryContextAlloc(TopMemoryContext, + port->tls_finish_len); + if (port->tls_finish_len > sizeof(tls_finish_buf)) + memcpy(port->tls_finish, tls_finish_buf, port->tls_finish_len); + else + (void) SSL_get_peer_finished(port->ssl, + port->tls_finish, + port->tls_finish_len); + return 0; } diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 0669b924cf..6eb39e0678 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -181,6 +181,8 @@ typedef struct Port bool ssl_in_use; char *peer_cn; bool peer_cert_valid; + char *tls_finish; /* TLS message expected from client */ + int tls_finish_len; /* length expected of TLS message */ /* * OpenSSL structures. (Keep these last so that the locations of other diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h index 14b48af12f..fc6fe2431f 100644 --- a/src/include/libpq/scram.h +++ b/src/include/libpq/scram.h @@ -13,8 +13,9 @@ #ifndef PG_SCRAM_H #define PG_SCRAM_H -/* Name of SCRAM-SHA-256 per IANA */ +/* Name of SCRAM mechanisms per IANA */ #define SCRAM_SHA256_NAME "SCRAM-SHA-256" +#define SCRAM_SHA256_PLUS_NAME "SCRAM-SHA-256-PLUS" /* with channel binding */ /* Status codes for message exchange */ #define SASL_EXCHANGE_CONTINUE 0 @@ -22,7 +23,9 @@ #define SASL_EXCHANGE_FAILURE 2 /* Routines dedicated to authentication */ -extern void *pg_be_scram_init(const char *username, const char *shadow_pass); +extern void *pg_be_scram_init(const char *username, const char *shadow_pass, + bool ssl_in_use, char *tls_finish_message, + int tls_finish_len); extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen, char **output, int *outputlen, char **logdetail); diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index d2e355a8b8..5e0837a2c9 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -44,6 +44,9 @@ typedef struct /* These are supplied by the user */ const char *username; char *password; + bool ssl_in_use; + char *tls_finish_message; + int tls_finish_len; /* We construct these */ uint8 SaltedPassword[SCRAM_KEY_LEN]; @@ -81,7 +84,11 @@ static bool pg_frontend_random(char *dst, int len); * Initialize SCRAM exchange status. */ void * -pg_fe_scram_init(const char *username, const char *password) +pg_fe_scram_init(const char *username, + const char *password, + bool ssl_in_use, + char *tls_finish_message, + int tls_finish_len) { fe_scram_state *state; char *prep_password; @@ -93,6 +100,9 @@ pg_fe_scram_init(const char *username, const char *password) memset(state, 0, sizeof(fe_scram_state)); state->state = FE_SCRAM_INIT; state->username = username; + state->ssl_in_use = ssl_in_use; + state->tls_finish_message = tls_finish_message; + state->tls_finish_len = tls_finish_len; /* Normalize the password with SASLprep, if possible */ rc = pg_saslprep(password, &prep_password); @@ -297,9 +307,10 @@ static char * build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage) { char raw_nonce[SCRAM_RAW_NONCE_LEN + 1]; - char *buf; - char buflen; + char *result; + int channel_info_len; int encoded_len; + PQExpBufferData buf; /* * Generate a "raw" nonce. This is converted to ASCII-printable form by @@ -328,26 +339,55 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage) * prepared with SASLprep, the message parsing would fail if it includes * '=' or ',' characters. */ - buflen = 8 + strlen(state->client_nonce) + 1; - buf = malloc(buflen); - if (buf == NULL) - { - printfPQExpBuffer(errormessage, - libpq_gettext("out of memory\n")); - return NULL; - } - snprintf(buf, buflen, "n,,n=,r=%s", state->client_nonce); + initPQExpBuffer(&buf); - state->client_first_message_bare = strdup(buf + 3); + /* + * First build the query field for channel binding. If the client is not + * built with SSL support, it cannot support channel binding so it needs + * to use "n" to let the server know. If built with SSL support, client + * needs to use "y" to let the server know that client has such support + * but that it is not using it as a non-SSL connection is requested. + * Finally if a SSL connection is done, use p=cb-name, for which only + * "tls-unique" is supported now. + */ +#ifdef USE_SSL + if (state->ssl_in_use) + appendPQExpBuffer(&buf, "p=tls-unique"); + else + appendPQExpBuffer(&buf, "y"); +#else + appendPQExpBuffer(&buf, "n"); +#endif + + if (PQExpBufferDataBroken(buf)) + goto oom_error; + + channel_info_len = buf.len; + + appendPQExpBuffer(&buf, ",,n=,r=%s", state->client_nonce); + if (PQExpBufferDataBroken(buf)) + goto oom_error; + + /* + * The first message content needs to be saved without channel binding + * information. + */ + state->client_first_message_bare = strdup(buf.data + channel_info_len + 2); if (!state->client_first_message_bare) - { - free(buf); - printfPQExpBuffer(errormessage, - libpq_gettext("out of memory\n")); - return NULL; - } + goto oom_error; - return buf; + result = strdup(buf.data); + if (result == NULL) + goto oom_error; + + termPQExpBuffer(&buf); + return result; + +oom_error: + termPQExpBuffer(&buf); + printfPQExpBuffer(errormessage, + libpq_gettext("out of memory\n")); + return NULL; } /* @@ -365,8 +405,30 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage) /* * Construct client-final-message-without-proof. We need to remember it * for verifying the server proof in the final step of authentication. + * Client needs to provide a b64 encoded string of the TLS finish message + * only if a SSL connection is attempted. */ - appendPQExpBuffer(&buf, "c=biws,r=%s", state->nonce); +#ifdef USE_SSL + if (state->ssl_in_use) + { + appendPQExpBuffer(&buf, "c="); + if (!enlargePQExpBuffer(&buf, pg_b64_enc_len(state->tls_finish_len))) + goto oom_error; + buf.len += pg_b64_encode(state->tls_finish_message, + state->tls_finish_len, + buf.data + buf.len); + buf.data[buf.len] = '\0'; + } + else + appendPQExpBuffer(&buf, "c=biws"); +#else + appendPQExpBuffer(&buf, "c=biws"); +#endif + + if (PQExpBufferDataBroken(buf)) + goto oom_error; + + appendPQExpBuffer(&buf, ",r=%s", state->nonce); if (PQExpBufferDataBroken(buf)) goto oom_error; diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index f4397afc64..ea4bbbae95 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -494,7 +494,8 @@ pg_SASL_init(PGconn *conn, int payloadlen) /* * Parse the list of SASL authentication mechanisms in the * AuthenticationSASL message, and select the best mechanism that we - * support. (Only SCRAM-SHA-256 is supported at the moment.) + * support. SCRAM-SHA-256 and SCRAM-SHA-256-PLUS are the only ones + * supported at the moment. */ selected_mechanism = NULL; for (;;) @@ -522,7 +523,8 @@ pg_SASL_init(PGconn *conn, int payloadlen) /* * Do we support this mechanism? */ - if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0) + if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0 || + strcmp(mechanism_buf.data, SCRAM_SHA256_PLUS_NAME) == 0) { char *password; @@ -537,10 +539,18 @@ pg_SASL_init(PGconn *conn, int payloadlen) goto error; } - conn->sasl_state = pg_fe_scram_init(conn->pguser, password); + conn->sasl_state = pg_fe_scram_init(conn->pguser, + password, + conn->ssl_in_use, + conn->tls_finish, + conn->tls_finish_len); if (!conn->sasl_state) goto oom_error; - selected_mechanism = SCRAM_SHA256_NAME; + + if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0) + selected_mechanism = SCRAM_SHA256_NAME; + else + selected_mechanism = SCRAM_SHA256_PLUS_NAME; } } diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h index 9f4c2a50d8..2ee9c6c48c 100644 --- a/src/interfaces/libpq/fe-auth.h +++ b/src/interfaces/libpq/fe-auth.h @@ -23,7 +23,9 @@ extern int pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn); extern char *pg_fe_getauthname(PQExpBuffer errorMessage); /* Prototypes for functions in fe-auth-scram.c */ -extern void *pg_fe_scram_init(const char *username, const char *password); +extern void *pg_fe_scram_init(const char *username, const char *password, + bool ssl_in_use, char *tls_finish_message, + int tls_finish_len); extern void pg_fe_scram_free(void *opaq); extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen, char **output, int *outputlen, diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index a7c3d7af64..b701b18c1c 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -1297,6 +1297,7 @@ static PostgresPollingStatusType open_client_SSL(PGconn *conn) { int r; + char tls_finish_buf[20]; ERR_clear_error(); r = SSL_connect(conn->ssl); @@ -1376,6 +1377,24 @@ open_client_SSL(PGconn *conn) return PGRES_POLLING_FAILED; } + /* + * Save the TLS finish message sent to the server, useful for + * authentication checks related to channel binding. SSL_get_finished() + * does not offer a way to know the exact length of a TLS finish message + * beforehand, so attempt first with a fixed-length buffer, and try again + * if the message does not fit. + */ + conn->tls_finish_len = SSL_get_finished(conn->ssl, + tls_finish_buf, + sizeof(tls_finish_buf)); + conn->tls_finish = malloc(conn->tls_finish_len); + if (conn->tls_finish_len > sizeof(tls_finish_buf)) + memcpy(conn->tls_finish, tls_finish_buf, conn->tls_finish_len); + else + (void) SSL_get_finished(conn->ssl, + conn->tls_finish, + conn->tls_finish_len); + /* SSL handshake is complete */ return PGRES_POLLING_OK; } diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 335568b790..ab2b9befbf 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -454,11 +454,15 @@ struct pg_conn /* Assorted state for SASL, SSL, GSS, etc */ void *sasl_state; + /* SSL structures */ + bool ssl_in_use; + char *tls_finish; /* TLS finish message sent */ + int tls_finish_len; /* length of TLS message sent */ + #ifdef USE_SSL bool allow_ssl_try; /* Allowed to try SSL negotiation */ bool wait_ssl_try; /* Delay SSL negotiation until after * attempting normal connection */ - bool ssl_in_use; #ifdef USE_OPENSSL SSL *ssl; /* SSL status, if have SSL connection */ X509 *peer; /* X509 cert of server */