diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/libpq/auth-scram.c | 20 | ||||
-rw-r--r-- | src/backend/libpq/be-secure-openssl.c | 61 |
2 files changed, 76 insertions, 5 deletions
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 1b07eaebfac..48eb531d0f0 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -849,13 +849,14 @@ read_client_first_message(scram_state *state, char *input) } /* - * Read value provided by client; only tls-unique is supported - * for now. (It is not safe to print the name of an - * unsupported binding type in the error message. Pranksters - * could print arbitrary strings into the log that way.) + * Read value provided by client. (It is not safe to print + * the name of an unsupported binding type in the error + * message. Pranksters could print arbitrary strings into the + * log that way.) */ channel_binding_type = read_attr_value(&input, 'p'); - if (strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) != 0) + if (strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) != 0 && + strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_END_POINT) != 0) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), (errmsg("unsupported SCRAM channel-binding type")))); @@ -1116,6 +1117,15 @@ read_client_final_message(scram_state *state, char *input) cbind_data = be_tls_get_peer_finished(state->port, &cbind_data_len); #endif } + else if (strcmp(state->channel_binding_type, + SCRAM_CHANNEL_BINDING_TLS_END_POINT) == 0) + { + /* Fetch hash data of server's SSL certificate */ +#ifdef USE_SSL + cbind_data = be_tls_get_certificate_hash(state->port, + &cbind_data_len); +#endif + } else { /* should not happen */ diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 3a7aa018767..f75cc2ef18f 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -1240,6 +1240,67 @@ be_tls_get_peer_finished(Port *port, size_t *len) } /* + * Get the server certificate hash for SCRAM channel binding type + * tls-server-end-point. + * + * The result is a palloc'd hash of the server certificate with its + * size, and NULL if there is no certificate available. + */ +char * +be_tls_get_certificate_hash(Port *port, size_t *len) +{ + X509 *server_cert; + char *cert_hash; + const EVP_MD *algo_type = NULL; + unsigned char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */ + unsigned int hash_size; + int algo_nid; + + *len = 0; + server_cert = SSL_get_certificate(port->ssl); + if (server_cert == NULL) + return NULL; + + /* + * Get the signature algorithm of the certificate to determine the + * hash algorithm to use for the result. + */ + if (!OBJ_find_sigid_algs(X509_get_signature_nid(server_cert), + &algo_nid, NULL)) + elog(ERROR, "could not determine server certificate signature algorithm"); + + /* + * The TLS server's certificate bytes need to be hashed with SHA-256 if + * its signature algorithm is MD5 or SHA-1 as per RFC 5929 + * (https://tools.ietf.org/html/rfc5929#section-4.1). If something else + * is used, the same hash as the signature algorithm is used. + */ + switch (algo_nid) + { + case NID_md5: + case NID_sha1: + algo_type = EVP_sha256(); + break; + default: + algo_type = EVP_get_digestbynid(algo_nid); + if (algo_type == NULL) + elog(ERROR, "could not find digest for NID %s", + OBJ_nid2sn(algo_nid)); + break; + } + + /* generate and save the certificate hash */ + if (!X509_digest(server_cert, algo_type, hash, &hash_size)) + elog(ERROR, "could not generate server certificate hash"); + + cert_hash = palloc(hash_size); + memcpy(cert_hash, hash, hash_size); + *len = hash_size; + + return cert_hash; +} + +/* * Convert an X509 subject name to a cstring. * */ |