diff --git a/contrib/passwordcheck/passwordcheck.c b/contrib/passwordcheck/passwordcheck.c index 3d644be8dd5..abe06383c56 100644 --- a/contrib/passwordcheck/passwordcheck.c +++ b/contrib/passwordcheck/passwordcheck.c @@ -74,7 +74,7 @@ check_password(const char *username, * * We only check for username = password. */ - char *logdetail; + const char *logdetail = NULL; if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK) ereport(ERROR, diff --git a/contrib/pgcrypto/internal-sha2.c b/contrib/pgcrypto/internal-sha2.c index ecf3004e95b..a35f113a14a 100644 --- a/contrib/pgcrypto/internal-sha2.c +++ b/contrib/pgcrypto/internal-sha2.c @@ -101,7 +101,8 @@ int_sha2_update(PX_MD *h, const uint8 *data, unsigned dlen) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_update(ctx, data, dlen) < 0) - elog(ERROR, "could not update %s context", "SHA2"); + elog(ERROR, "could not update %s context: %s", "SHA2", + pg_cryptohash_error(ctx)); } static void @@ -110,7 +111,8 @@ int_sha2_reset(PX_MD *h) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_init(ctx) < 0) - elog(ERROR, "could not initialize %s context", "SHA2"); + elog(ERROR, "could not initialize %s context: %s", "SHA2", + pg_cryptohash_error(ctx)); } static void @@ -119,7 +121,8 @@ int_sha2_finish(PX_MD *h, uint8 *dst) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_final(ctx, dst, h->result_size(h)) < 0) - elog(ERROR, "could not finalize %s context", "SHA2"); + elog(ERROR, "could not finalize %s context: %s", "SHA2", + pg_cryptohash_error(ctx)); } static void diff --git a/contrib/pgcrypto/internal.c b/contrib/pgcrypto/internal.c index dd45fee7ed6..5dd48351512 100644 --- a/contrib/pgcrypto/internal.c +++ b/contrib/pgcrypto/internal.c @@ -89,7 +89,8 @@ int_md5_update(PX_MD *h, const uint8 *data, unsigned dlen) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_update(ctx, data, dlen) < 0) - elog(ERROR, "could not update %s context", "MD5"); + elog(ERROR, "could not update %s context: %s", "MD5", + pg_cryptohash_error(ctx)); } static void @@ -98,7 +99,8 @@ int_md5_reset(PX_MD *h) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_init(ctx) < 0) - elog(ERROR, "could not initialize %s context", "MD5"); + elog(ERROR, "could not initialize %s context: %s", "MD5", + pg_cryptohash_error(ctx)); } static void @@ -107,7 +109,8 @@ int_md5_finish(PX_MD *h, uint8 *dst) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_final(ctx, dst, h->result_size(h)) < 0) - elog(ERROR, "could not finalize %s context", "MD5"); + elog(ERROR, "could not finalize %s context: %s", "MD5", + pg_cryptohash_error(ctx)); } static void @@ -139,7 +142,8 @@ int_sha1_update(PX_MD *h, const uint8 *data, unsigned dlen) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_update(ctx, data, dlen) < 0) - elog(ERROR, "could not update %s context", "SHA1"); + elog(ERROR, "could not update %s context: %s", "SHA1", + pg_cryptohash_error(ctx)); } static void @@ -148,7 +152,8 @@ int_sha1_reset(PX_MD *h) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_init(ctx) < 0) - elog(ERROR, "could not initialize %s context", "SHA1"); + elog(ERROR, "could not initialize %s context: %s", "SHA1", + pg_cryptohash_error(ctx)); } static void @@ -157,7 +162,8 @@ int_sha1_finish(PX_MD *h, uint8 *dst) pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr; if (pg_cryptohash_final(ctx, dst, h->result_size(h)) < 0) - elog(ERROR, "could not finalize %s context", "SHA1"); + elog(ERROR, "could not finalize %s context: %s", "SHA1", + pg_cryptohash_error(ctx)); } static void diff --git a/contrib/uuid-ossp/uuid-ossp.c b/contrib/uuid-ossp/uuid-ossp.c index f9335f28631..f52f007a089 100644 --- a/contrib/uuid-ossp/uuid-ossp.c +++ b/contrib/uuid-ossp/uuid-ossp.c @@ -319,14 +319,17 @@ uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len) pg_cryptohash_ctx *ctx = pg_cryptohash_create(PG_MD5); if (pg_cryptohash_init(ctx) < 0) - elog(ERROR, "could not initialize %s context", "MD5"); + elog(ERROR, "could not initialize %s context: %s", "MD5", + pg_cryptohash_error(ctx)); if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 || pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0) - elog(ERROR, "could not update %s context", "MD5"); + elog(ERROR, "could not update %s context: %s", "MD5", + pg_cryptohash_error(ctx)); /* we assume sizeof MD5 result is 16, same as UUID size */ if (pg_cryptohash_final(ctx, (unsigned char *) &uu, sizeof(uu)) < 0) - elog(ERROR, "could not finalize %s context", "MD5"); + elog(ERROR, "could not finalize %s context: %s", "MD5", + pg_cryptohash_error(ctx)); pg_cryptohash_free(ctx); } else @@ -335,12 +338,15 @@ uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len) unsigned char sha1result[SHA1_DIGEST_LENGTH]; if (pg_cryptohash_init(ctx) < 0) - elog(ERROR, "could not initialize %s context", "SHA1"); + elog(ERROR, "could not initialize %s context: %s", "SHA1", + pg_cryptohash_error(ctx)); if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 || pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0) - elog(ERROR, "could not update %s context", "SHA1"); + elog(ERROR, "could not update %s context: %s", "SHA1", + pg_cryptohash_error(ctx)); if (pg_cryptohash_final(ctx, sha1result, sizeof(sha1result)) < 0) - elog(ERROR, "could not finalize %s context", "SHA1"); + elog(ERROR, "could not finalize %s context: %s", "SHA1", + pg_cryptohash_error(ctx)); pg_cryptohash_free(ctx); memcpy(&uu, sha1result, sizeof(uu)); diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 65bb7339589..a415100c3b1 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -393,7 +393,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt) if (password) { char *shadow_pass; - char *logdetail; + const char *logdetail = NULL; /* * Don't allow an empty password. Libpq treats an empty password the @@ -835,7 +835,7 @@ AlterRole(AlterRoleStmt *stmt) if (password) { char *shadow_pass; - char *logdetail; + const char *logdetail = NULL; /* Like in CREATE USER, don't allow an empty password. */ if (password[0] == '\0' || diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index f9e1026a12c..043134419c3 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -327,7 +327,7 @@ pg_be_scram_init(Port *port, */ int pg_be_scram_exchange(void *opaq, const char *input, int inputlen, - char **output, int *outputlen, char **logdetail) + char **output, int *outputlen, const char **logdetail) { scram_state *state = (scram_state *) opaq; int result; diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index be14f2f16dd..0dd83cde066 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -47,7 +47,7 @@ */ static void sendAuthRequest(Port *port, AuthRequest areq, const char *extradata, int extralen); -static void auth_failed(Port *port, int status, char *logdetail); +static void auth_failed(Port *port, int status, const char *logdetail); static char *recv_password_packet(Port *port); static void set_authn_id(Port *port, const char *id); @@ -56,11 +56,11 @@ static void set_authn_id(Port *port, const char *id); * Password-based authentication methods (password, md5, and scram-sha-256) *---------------------------------------------------------------- */ -static int CheckPasswordAuth(Port *port, char **logdetail); -static int CheckPWChallengeAuth(Port *port, char **logdetail); +static int CheckPasswordAuth(Port *port, const char **logdetail); +static int CheckPWChallengeAuth(Port *port, const char **logdetail); -static int CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail); -static int CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail); +static int CheckMD5Auth(Port *port, char *shadow_pass, const char **logdetail); +static int CheckSCRAMAuth(Port *port, char *shadow_pass, const char **logdetail); /*---------------------------------------------------------------- @@ -258,7 +258,7 @@ ClientAuthentication_hook_type ClientAuthentication_hook = NULL; * particular, if logdetail isn't NULL, we send that string to the log. */ static void -auth_failed(Port *port, int status, char *logdetail) +auth_failed(Port *port, int status, const char *logdetail) { const char *errstr; char *cdetail; @@ -394,7 +394,7 @@ void ClientAuthentication(Port *port) { int status = STATUS_ERROR; - char *logdetail = NULL; + const char *logdetail = NULL; /* * Get the authentication method to use for this frontend/database @@ -780,7 +780,7 @@ recv_password_packet(Port *port) * Plaintext password authentication. */ static int -CheckPasswordAuth(Port *port, char **logdetail) +CheckPasswordAuth(Port *port, const char **logdetail) { char *passwd; int result; @@ -815,7 +815,7 @@ CheckPasswordAuth(Port *port, char **logdetail) * MD5 and SCRAM authentication. */ static int -CheckPWChallengeAuth(Port *port, char **logdetail) +CheckPWChallengeAuth(Port *port, const char **logdetail) { int auth_result; char *shadow_pass; @@ -875,7 +875,7 @@ CheckPWChallengeAuth(Port *port, char **logdetail) } static int -CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail) +CheckMD5Auth(Port *port, char *shadow_pass, const char **logdetail) { char md5Salt[4]; /* Password salt */ char *passwd; @@ -912,7 +912,7 @@ CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail) } static int -CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) +CheckSCRAMAuth(Port *port, char *shadow_pass, const char **logdetail) { StringInfoData sasl_mechs; int mtype; @@ -3240,6 +3240,8 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por md5trailer = packet->vector; for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH) { + const char *errstr = NULL; + memcpy(cryptvector + strlen(secret), md5trailer, RADIUS_VECTOR_LENGTH); /* @@ -3248,10 +3250,12 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por */ md5trailer = encryptedpassword + i; - if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i)) + if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH, + encryptedpassword + i, &errstr)) { ereport(LOG, - (errmsg("could not perform MD5 encryption of password"))); + (errmsg("could not perform MD5 encryption of password: %s", + errstr))); pfree(cryptvector); pg_freeaddrinfo_all(hint.ai_family, serveraddrs); return STATUS_ERROR; @@ -3336,6 +3340,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por struct timeval timeout; struct timeval now; int64 timeoutval; + const char *errstr = NULL; gettimeofday(&now, NULL); timeoutval = (endtime.tv_sec * 1000000 + endtime.tv_usec) - (now.tv_sec * 1000000 + now.tv_usec); @@ -3454,10 +3459,11 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por if (!pg_md5_binary(cryptvector, packetlength + strlen(secret), - encryptedpassword)) + encryptedpassword, &errstr)) { ereport(LOG, - (errmsg("could not perform MD5 encryption of received packet"))); + (errmsg("could not perform MD5 encryption of received packet: %s", + errstr))); pfree(cryptvector); continue; } diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 3fcad991a7e..321603c1d8c 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -34,7 +34,7 @@ * sent to the client, to avoid giving away user information! */ char * -get_role_password(const char *role, char **logdetail) +get_role_password(const char *role, const char **logdetail) { TimestampTz vuntil = 0; HeapTuple roleTup; @@ -116,6 +116,7 @@ encrypt_password(PasswordType target_type, const char *role, { PasswordType guessed_type = get_password_type(password); char *encrypted_password; + const char *errstr = NULL; if (guessed_type != PASSWORD_TYPE_PLAINTEXT) { @@ -132,8 +133,8 @@ encrypt_password(PasswordType target_type, const char *role, encrypted_password = palloc(MD5_PASSWD_LEN + 1); if (!pg_md5_encrypt(password, role, strlen(role), - encrypted_password)) - elog(ERROR, "password encryption failed"); + encrypted_password, &errstr)) + elog(ERROR, "password encryption failed: %s", errstr); return encrypted_password; case PASSWORD_TYPE_SCRAM_SHA_256: @@ -159,17 +160,18 @@ encrypt_password(PasswordType target_type, const char *role, * 'client_pass' is the response given by the remote user to the MD5 challenge. * 'md5_salt' is the salt used in the MD5 authentication challenge. * - * In the error case, optionally store a palloc'd string at *logdetail - * that will be sent to the postmaster log (but not the client). + * In the error case, save a string at *logdetail that will be sent to the + * postmaster log (but not the client). */ int md5_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, const char *md5_salt, int md5_salt_len, - char **logdetail) + const char **logdetail) { int retval; char crypt_pwd[MD5_PASSWD_LEN + 1]; + const char *errstr = NULL; Assert(md5_salt_len > 0); @@ -183,16 +185,13 @@ md5_crypt_verify(const char *role, const char *shadow_pass, /* * Compute the correct answer for the MD5 challenge. - * - * We do not bother setting logdetail for any pg_md5_encrypt failure - * below: the only possible error is out-of-memory, which is unlikely, and - * if it did happen adding a psprintf call would only make things worse. */ /* stored password already encrypted, only do salt */ if (!pg_md5_encrypt(shadow_pass + strlen("md5"), md5_salt, md5_salt_len, - crypt_pwd)) + crypt_pwd, &errstr)) { + *logdetail = errstr; return STATUS_ERROR; } @@ -215,15 +214,16 @@ md5_crypt_verify(const char *role, const char *shadow_pass, * pg_authid.rolpassword. * 'client_pass' is the password given by the remote user. * - * In the error case, optionally store a palloc'd string at *logdetail - * that will be sent to the postmaster log (but not the client). + * In the error case, store a string at *logdetail that will be sent to the + * postmaster log (but not the client). */ int plain_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, - char **logdetail) + const char **logdetail) { char crypt_client_pass[MD5_PASSWD_LEN + 1]; + const char *errstr = NULL; /* * Client sent password in plaintext. If we have an MD5 hash stored, hash @@ -251,14 +251,10 @@ plain_crypt_verify(const char *role, const char *shadow_pass, if (!pg_md5_encrypt(client_pass, role, strlen(role), - crypt_client_pass)) + crypt_client_pass, + &errstr)) { - /* - * We do not bother setting logdetail for pg_md5_encrypt - * failure: the only possible error is out-of-memory, which is - * unlikely, and if it did happen adding a psprintf call would - * only make things worse. - */ + *logdetail = errstr; return STATUS_ERROR; } if (strcmp(crypt_client_pass, shadow_pass) == 0) diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c index 04ca455ace8..e6b91f5e2bd 100644 --- a/src/backend/replication/backup_manifest.c +++ b/src/backend/replication/backup_manifest.c @@ -67,7 +67,8 @@ InitializeBackupManifest(backup_manifest_info *manifest, manifest->buffile = BufFileCreateTemp(false); manifest->manifest_ctx = pg_cryptohash_create(PG_SHA256); if (pg_cryptohash_init(manifest->manifest_ctx) < 0) - elog(ERROR, "failed to initialize checksum of backup manifest"); + elog(ERROR, "failed to initialize checksum of backup manifest: %s", + pg_cryptohash_error(manifest->manifest_ctx)); } manifest->manifest_size = UINT64CONST(0); @@ -334,7 +335,8 @@ SendBackupManifest(backup_manifest_info *manifest) manifest->still_checksumming = false; if (pg_cryptohash_final(manifest->manifest_ctx, checksumbuf, sizeof(checksumbuf)) < 0) - elog(ERROR, "failed to finalize checksum of backup manifest"); + elog(ERROR, "failed to finalize checksum of backup manifest: %s", + pg_cryptohash_error(manifest->manifest_ctx)); AppendStringToManifest(manifest, "\"Manifest-Checksum\": \""); hex_encode((char *) checksumbuf, sizeof checksumbuf, checksumstringbuf); @@ -401,7 +403,8 @@ AppendStringToManifest(backup_manifest_info *manifest, char *s) if (manifest->still_checksumming) { if (pg_cryptohash_update(manifest->manifest_ctx, (uint8 *) s, len) < 0) - elog(ERROR, "failed to update checksum of backup manifest"); + elog(ERROR, "failed to update checksum of backup manifest: %s", + pg_cryptohash_error(manifest->manifest_ctx)); } BufFileWrite(manifest->buffile, s, len); manifest->manifest_size += len; diff --git a/src/backend/utils/adt/cryptohashfuncs.c b/src/backend/utils/adt/cryptohashfuncs.c index 6a0f0258e60..c977db2f0ea 100644 --- a/src/backend/utils/adt/cryptohashfuncs.c +++ b/src/backend/utils/adt/cryptohashfuncs.c @@ -35,15 +35,17 @@ md5_text(PG_FUNCTION_ARGS) text *in_text = PG_GETARG_TEXT_PP(0); size_t len; char hexsum[MD5_HASH_LEN + 1]; + const char *errstr = NULL; /* Calculate the length of the buffer using varlena metadata */ len = VARSIZE_ANY_EXHDR(in_text); /* get the hash result */ - if (pg_md5_hash(VARDATA_ANY(in_text), len, hexsum) == false) + if (pg_md5_hash(VARDATA_ANY(in_text), len, hexsum, &errstr) == false) ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not compute %s hash: %s", "MD5", + errstr))); /* convert to text and return it */ PG_RETURN_TEXT_P(cstring_to_text(hexsum)); @@ -58,12 +60,14 @@ md5_bytea(PG_FUNCTION_ARGS) bytea *in = PG_GETARG_BYTEA_PP(0); size_t len; char hexsum[MD5_HASH_LEN + 1]; + const char *errstr = NULL; len = VARSIZE_ANY_EXHDR(in); - if (pg_md5_hash(VARDATA_ANY(in), len, hexsum) == false) + if (pg_md5_hash(VARDATA_ANY(in), len, hexsum, &errstr) == false) ereport(ERROR, - (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of memory"))); + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not compute %s hash: %s", "MD5", + errstr))); PG_RETURN_TEXT_P(cstring_to_text(hexsum)); } @@ -111,12 +115,15 @@ cryptohash_internal(pg_cryptohash_type type, bytea *input) ctx = pg_cryptohash_create(type); if (pg_cryptohash_init(ctx) < 0) - elog(ERROR, "could not initialize %s context", typestr); + elog(ERROR, "could not initialize %s context: %s", typestr, + pg_cryptohash_error(ctx)); if (pg_cryptohash_update(ctx, data, len) < 0) - elog(ERROR, "could not update %s context", typestr); + elog(ERROR, "could not update %s context: %s", typestr, + pg_cryptohash_error(ctx)); if (pg_cryptohash_final(ctx, (unsigned char *) VARDATA(result), digest_len) < 0) - elog(ERROR, "could not finalize %s context", typestr); + elog(ERROR, "could not finalize %s context: %s", typestr, + pg_cryptohash_error(ctx)); pg_cryptohash_free(ctx); SET_VARSIZE(result, digest_len + VARHDRSZ); diff --git a/src/common/cryptohash.c b/src/common/cryptohash.c index 0dab74a094b..f4e88bff950 100644 --- a/src/common/cryptohash.c +++ b/src/common/cryptohash.c @@ -40,10 +40,18 @@ #define FREE(ptr) free(ptr) #endif +/* Set of error states */ +typedef enum pg_cryptohash_errno +{ + PG_CRYPTOHASH_ERROR_NONE = 0, + PG_CRYPTOHASH_ERROR_DEST_LEN +} pg_cryptohash_errno; + /* Internal pg_cryptohash_ctx structure */ struct pg_cryptohash_ctx { pg_cryptohash_type type; + pg_cryptohash_errno error; union { @@ -76,9 +84,10 @@ pg_cryptohash_create(pg_cryptohash_type type) ctx = ALLOC(sizeof(pg_cryptohash_ctx)); if (ctx == NULL) return NULL; + memset(ctx, 0, sizeof(pg_cryptohash_ctx)); ctx->type = type; - + ctx->error = PG_CRYPTOHASH_ERROR_NONE; return ctx; } @@ -174,32 +183,50 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len) { case PG_MD5: if (len < MD5_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } pg_md5_final(&ctx->data.md5, dest); break; case PG_SHA1: if (len < SHA1_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } pg_sha1_final(&ctx->data.sha1, dest); break; case PG_SHA224: if (len < PG_SHA224_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } pg_sha224_final(&ctx->data.sha224, dest); break; case PG_SHA256: if (len < PG_SHA256_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } pg_sha256_final(&ctx->data.sha256, dest); break; case PG_SHA384: if (len < PG_SHA384_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } pg_sha384_final(&ctx->data.sha384, dest); break; case PG_SHA512: if (len < PG_SHA512_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } pg_sha512_final(&ctx->data.sha512, dest); break; } @@ -221,3 +248,31 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx) explicit_bzero(ctx, sizeof(pg_cryptohash_ctx)); FREE(ctx); } + +/* + * pg_cryptohash_error + * + * Returns a static string providing errors about an error that + * happened during a computation. + */ +const char * +pg_cryptohash_error(pg_cryptohash_ctx *ctx) +{ + /* + * This implementation would never fail because of an out-of-memory error, + * except when creating the context. + */ + if (ctx == NULL) + return _("out of memory"); + + switch (ctx->error) + { + case PG_CRYPTOHASH_ERROR_NONE: + return _("success"); + case PG_CRYPTOHASH_ERROR_DEST_LEN: + return _("destination buffer too small"); + } + + Assert(false); + return _("success"); +} diff --git a/src/common/cryptohash_openssl.c b/src/common/cryptohash_openssl.c index 643cc7aea2c..5fb22eff464 100644 --- a/src/common/cryptohash_openssl.c +++ b/src/common/cryptohash_openssl.c @@ -21,6 +21,7 @@ #include "postgres_fe.h" #endif +#include #include #include "common/cryptohash.h" @@ -46,6 +47,14 @@ #define FREE(ptr) free(ptr) #endif +/* Set of error states */ +typedef enum pg_cryptohash_errno +{ + PG_CRYPTOHASH_ERROR_NONE = 0, + PG_CRYPTOHASH_ERROR_DEST_LEN, + PG_CRYPTOHASH_ERROR_OPENSSL +} pg_cryptohash_errno; + /* * Internal pg_cryptohash_ctx structure. * @@ -55,6 +64,8 @@ struct pg_cryptohash_ctx { pg_cryptohash_type type; + pg_cryptohash_errno error; + const char *errreason; EVP_MD_CTX *evpctx; @@ -63,6 +74,19 @@ struct pg_cryptohash_ctx #endif }; +static const char * +SSLerrmessage(unsigned long ecode) +{ + if (ecode == 0) + return NULL; + + /* + * This may return NULL, but we would fall back to a default error path if + * that were the case. + */ + return ERR_reason_error_string(ecode); +} + /* * pg_cryptohash_create * @@ -88,6 +112,8 @@ pg_cryptohash_create(pg_cryptohash_type type) return NULL; memset(ctx, 0, sizeof(pg_cryptohash_ctx)); ctx->type = type; + ctx->error = PG_CRYPTOHASH_ERROR_NONE; + ctx->errreason = NULL; /* * Initialization takes care of assigning the correct type for OpenSSL. @@ -153,7 +179,11 @@ pg_cryptohash_init(pg_cryptohash_ctx *ctx) /* OpenSSL internals return 1 on success, 0 on failure */ if (status <= 0) + { + ctx->errreason = SSLerrmessage(ERR_get_error()); + ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL; return -1; + } return 0; } @@ -174,7 +204,11 @@ pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len) /* OpenSSL internals return 1 on success, 0 on failure */ if (status <= 0) + { + ctx->errreason = SSLerrmessage(ERR_get_error()); + ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL; return -1; + } return 0; } @@ -195,27 +229,45 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len) { case PG_MD5: if (len < MD5_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } break; case PG_SHA1: if (len < SHA1_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } break; case PG_SHA224: if (len < PG_SHA224_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } break; case PG_SHA256: if (len < PG_SHA256_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } break; case PG_SHA384: if (len < PG_SHA384_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } break; case PG_SHA512: if (len < PG_SHA512_DIGEST_LENGTH) + { + ctx->error = PG_CRYPTOHASH_ERROR_DEST_LEN; return -1; + } break; } @@ -223,7 +275,11 @@ pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len) /* OpenSSL internals return 1 on success, 0 on failure */ if (status <= 0) + { + ctx->errreason = SSLerrmessage(ERR_get_error()); + ctx->error = PG_CRYPTOHASH_ERROR_OPENSSL; return -1; + } return 0; } @@ -248,3 +304,40 @@ pg_cryptohash_free(pg_cryptohash_ctx *ctx) explicit_bzero(ctx, sizeof(pg_cryptohash_ctx)); FREE(ctx); } + +/* + * pg_cryptohash_error + * + * Returns a static string providing errors about an error that + * happened during a computation. + */ +const char * +pg_cryptohash_error(pg_cryptohash_ctx *ctx) +{ + /* + * This implementation would never fail because of an out-of-memory error, + * except when creating the context. + */ + if (ctx == NULL) + return _("out of memory"); + + /* + * If a reason is provided, rely on it, else fallback to any error code + * set. + */ + if (ctx->errreason) + return ctx->errreason; + + switch (ctx->error) + { + case PG_CRYPTOHASH_ERROR_NONE: + return _("success"); + case PG_CRYPTOHASH_ERROR_DEST_LEN: + return _("destination buffer too small"); + case PG_CRYPTOHASH_ERROR_OPENSSL: + return _("OpenSSL failure"); + } + + Assert(false); /* cannot be reached */ + return _("success"); +} diff --git a/src/common/md5_common.c b/src/common/md5_common.c index 2114890effe..382c2d0b8c7 100644 --- a/src/common/md5_common.c +++ b/src/common/md5_common.c @@ -67,7 +67,7 @@ bytesToHex(uint8 b[16], char *s) */ bool -pg_md5_hash(const void *buff, size_t len, char *hexsum) +pg_md5_hash(const void *buff, size_t len, char *hexsum, const char **errstr) { uint8 sum[MD5_DIGEST_LENGTH]; pg_cryptohash_ctx *ctx; @@ -80,6 +80,7 @@ pg_md5_hash(const void *buff, size_t len, char *hexsum) pg_cryptohash_update(ctx, buff, len) < 0 || pg_cryptohash_final(ctx, sum, sizeof(sum)) < 0) { + *errstr = pg_cryptohash_error(ctx); pg_cryptohash_free(ctx); return false; } @@ -90,18 +91,23 @@ pg_md5_hash(const void *buff, size_t len, char *hexsum) } bool -pg_md5_binary(const void *buff, size_t len, void *outbuf) +pg_md5_binary(const void *buff, size_t len, void *outbuf, const char **errstr) { pg_cryptohash_ctx *ctx; + *errstr = NULL; ctx = pg_cryptohash_create(PG_MD5); if (ctx == NULL) + { + *errstr = pg_cryptohash_error(NULL); /* returns OOM */ return false; + } if (pg_cryptohash_init(ctx) < 0 || pg_cryptohash_update(ctx, buff, len) < 0 || pg_cryptohash_final(ctx, outbuf, MD5_DIGEST_LENGTH) < 0) { + *errstr = pg_cryptohash_error(ctx); pg_cryptohash_free(ctx); return false; } @@ -118,11 +124,12 @@ pg_md5_binary(const void *buff, size_t len, void *outbuf) * Output format is "md5" followed by a 32-hex-digit MD5 checksum. * Hence, the output buffer "buf" must be at least 36 bytes long. * - * Returns true if okay, false on error (out of memory). + * Returns true if okay, false on error with *errstr providing some + * error context. */ bool pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, - char *buf) + char *buf, const char **errstr) { size_t passwd_len = strlen(passwd); @@ -131,7 +138,10 @@ pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, bool ret; if (!crypt_buf) + { + *errstr = _("out of memory"); return false; + } /* * Place salt at the end because it may be known by users trying to crack @@ -141,7 +151,7 @@ pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, memcpy(crypt_buf + passwd_len, salt, salt_len); strcpy(buf, "md5"); - ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3); + ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3, errstr); free(crypt_buf); diff --git a/src/include/common/cryptohash.h b/src/include/common/cryptohash.h index 541dc844c8b..c62c350d573 100644 --- a/src/include/common/cryptohash.h +++ b/src/include/common/cryptohash.h @@ -34,5 +34,6 @@ extern int pg_cryptohash_init(pg_cryptohash_ctx *ctx); extern int pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len); extern int pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len); extern void pg_cryptohash_free(pg_cryptohash_ctx *ctx); +extern const char *pg_cryptohash_error(pg_cryptohash_ctx *ctx); #endif /* PG_CRYPTOHASH_H */ diff --git a/src/include/common/md5.h b/src/include/common/md5.h index 62a31e6ed4e..cc43fc66064 100644 --- a/src/include/common/md5.h +++ b/src/include/common/md5.h @@ -26,9 +26,12 @@ #define MD5_PASSWD_LEN 35 /* Utilities common to all the MD5 implementations, as of md5_common.c */ -extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum); -extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf); +extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum, + const char **errstr); +extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf, + const char **errstr); extern bool pg_md5_encrypt(const char *passwd, const char *salt, - size_t salt_len, char *buf); + size_t salt_len, char *buf, + const char **errstr); #endif /* PG_MD5_H */ diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index a4fea6e477a..46799427e81 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -35,12 +35,13 @@ extern PasswordType get_password_type(const char *shadow_pass); extern char *encrypt_password(PasswordType target_type, const char *role, const char *password); -extern char *get_role_password(const char *role, char **logdetail); +extern char *get_role_password(const char *role, const char **logdetail); extern int md5_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, const char *md5_salt, - int md5_salt_len, char **logdetail); + int md5_salt_len, const char **logdetail); extern int plain_crypt_verify(const char *role, const char *shadow_pass, - const char *client_pass, char **logdetail); + const char *client_pass, + const char **logdetail); #endif diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h index 2c879150dac..a83910e18c4 100644 --- a/src/include/libpq/scram.h +++ b/src/include/libpq/scram.h @@ -25,7 +25,8 @@ extern void pg_be_scram_get_mechanisms(Port *port, StringInfo buf); extern void *pg_be_scram_init(Port *port, const char *selected_mech, const char *shadow_pass); extern int pg_be_scram_exchange(void *opaq, const char *input, int inputlen, - char **output, int *outputlen, char **logdetail); + char **output, int *outputlen, + const char **logdetail); /* Routines to handle and check SCRAM-SHA-256 secret */ extern char *pg_be_scram_build_secret(const char *password); diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index e8062647e60..24927fd040f 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -765,6 +765,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) case AUTH_REQ_MD5: { char *crypt_pwd2; + const char *errstr = NULL; /* Allocate enough space for two MD5 hashes */ crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); @@ -777,14 +778,21 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1; if (!pg_md5_encrypt(password, conn->pguser, - strlen(conn->pguser), crypt_pwd2)) + strlen(conn->pguser), crypt_pwd2, + &errstr)) { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not encrypt password: %s\n"), + errstr); free(crypt_pwd); return STATUS_ERROR; } if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), md5Salt, - 4, crypt_pwd)) + 4, crypt_pwd, &errstr)) { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not encrypt password: %s\n"), + errstr); free(crypt_pwd); return STATUS_ERROR; } @@ -1150,12 +1158,13 @@ char * PQencryptPassword(const char *passwd, const char *user) { char *crypt_pwd; + const char *errstr = NULL; crypt_pwd = malloc(MD5_PASSWD_LEN + 1); if (!crypt_pwd) return NULL; - if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd)) + if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd, &errstr)) { free(crypt_pwd); return NULL; @@ -1262,8 +1271,13 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, crypt_pwd = malloc(MD5_PASSWD_LEN + 1); if (crypt_pwd) { - if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd)) + const char *errstr = NULL; + + if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd, &errstr)) { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not encrypt password: %s\n"), + errstr); free(crypt_pwd); crypt_pwd = NULL; } diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index d71d1adbecd..f6bcfb84f70 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3330,6 +3330,7 @@ pg_conv_map pg_crc32 pg_crc32c pg_cryptohash_ctx +pg_cryptohash_errno pg_cryptohash_type pg_ctype_cache pg_enc