diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/hmac.c | 64 | ||||
| -rw-r--r-- | src/common/hmac_openssl.c | 90 | ||||
| -rw-r--r-- | src/common/scram-common.c | 68 |
3 files changed, 205 insertions, 17 deletions
diff --git a/src/common/hmac.c b/src/common/hmac.c index d40026d3e99..a27778e86b3 100644 --- a/src/common/hmac.c +++ b/src/common/hmac.c @@ -38,11 +38,21 @@ #define FREE(ptr) free(ptr) #endif +/* Set of error states */ +typedef enum pg_hmac_errno +{ + PG_HMAC_ERROR_NONE = 0, + PG_HMAC_ERROR_OOM, + PG_HMAC_ERROR_INTERNAL +} pg_hmac_errno; + /* Internal pg_hmac_ctx structure */ struct pg_hmac_ctx { pg_cryptohash_ctx *hash; pg_cryptohash_type type; + pg_hmac_errno error; + const char *errreason; int block_size; int digest_size; @@ -73,6 +83,8 @@ pg_hmac_create(pg_cryptohash_type type) return NULL; memset(ctx, 0, sizeof(pg_hmac_ctx)); ctx->type = type; + ctx->error = PG_HMAC_ERROR_NONE; + ctx->errreason = NULL; /* * Initialize the context data. This requires to know the digest and @@ -150,12 +162,16 @@ pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len) /* temporary buffer for one-time shrink */ shrinkbuf = ALLOC(digest_size); if (shrinkbuf == NULL) + { + ctx->error = PG_HMAC_ERROR_OOM; return -1; + } memset(shrinkbuf, 0, digest_size); hash_ctx = pg_cryptohash_create(ctx->type); if (hash_ctx == NULL) { + ctx->error = PG_HMAC_ERROR_OOM; FREE(shrinkbuf); return -1; } @@ -164,6 +180,8 @@ pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len) pg_cryptohash_update(hash_ctx, key, len) < 0 || pg_cryptohash_final(hash_ctx, shrinkbuf, digest_size) < 0) { + ctx->error = PG_HMAC_ERROR_INTERNAL; + ctx->errreason = pg_cryptohash_error(hash_ctx); pg_cryptohash_free(hash_ctx); FREE(shrinkbuf); return -1; @@ -184,6 +202,8 @@ pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len) if (pg_cryptohash_init(ctx->hash) < 0 || pg_cryptohash_update(ctx->hash, ctx->k_ipad, ctx->block_size) < 0) { + ctx->error = PG_HMAC_ERROR_INTERNAL; + ctx->errreason = pg_cryptohash_error(ctx->hash); if (shrinkbuf) FREE(shrinkbuf); return -1; @@ -206,7 +226,11 @@ pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len) return -1; if (pg_cryptohash_update(ctx->hash, data, len) < 0) + { + ctx->error = PG_HMAC_ERROR_INTERNAL; + ctx->errreason = pg_cryptohash_error(ctx->hash); return -1; + } return 0; } @@ -226,11 +250,16 @@ pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len) h = ALLOC(ctx->digest_size); if (h == NULL) + { + ctx->error = PG_HMAC_ERROR_OOM; return -1; + } memset(h, 0, ctx->digest_size); if (pg_cryptohash_final(ctx->hash, h, ctx->digest_size) < 0) { + ctx->error = PG_HMAC_ERROR_INTERNAL; + ctx->errreason = pg_cryptohash_error(ctx->hash); FREE(h); return -1; } @@ -241,6 +270,8 @@ pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len) pg_cryptohash_update(ctx->hash, h, ctx->digest_size) < 0 || pg_cryptohash_final(ctx->hash, dest, len) < 0) { + ctx->error = PG_HMAC_ERROR_INTERNAL; + ctx->errreason = pg_cryptohash_error(ctx->hash); FREE(h); return -1; } @@ -264,3 +295,36 @@ pg_hmac_free(pg_hmac_ctx *ctx) explicit_bzero(ctx, sizeof(pg_hmac_ctx)); FREE(ctx); } + +/* + * pg_hmac_error + * + * Returns a static string providing details about an error that happened + * during a HMAC computation. + */ +const char * +pg_hmac_error(pg_hmac_ctx *ctx) +{ + 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_HMAC_ERROR_NONE: + return _("success"); + case PG_HMAC_ERROR_INTERNAL: + return _("internal error"); + case PG_HMAC_ERROR_OOM: + return _("out of memory"); + } + + Assert(false); /* cannot be reached */ + return _("success"); +} diff --git a/src/common/hmac_openssl.c b/src/common/hmac_openssl.c index 7efa90c99e6..44f36d51dcb 100644 --- a/src/common/hmac_openssl.c +++ b/src/common/hmac_openssl.c @@ -20,6 +20,8 @@ #include "postgres_fe.h" #endif + +#include <openssl/err.h> #include <openssl/hmac.h> #include "common/hmac.h" @@ -50,17 +52,40 @@ #define FREE(ptr) free(ptr) #endif /* FRONTEND */ +/* Set of error states */ +typedef enum pg_hmac_errno +{ + PG_HMAC_ERROR_NONE = 0, + PG_HMAC_ERROR_DEST_LEN, + PG_HMAC_ERROR_OPENSSL +} pg_hmac_errno; + /* Internal pg_hmac_ctx structure */ struct pg_hmac_ctx { HMAC_CTX *hmacctx; pg_cryptohash_type type; + pg_hmac_errno error; + const char *errreason; #ifndef FRONTEND ResourceOwner resowner; #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_hmac_create * @@ -78,6 +103,8 @@ pg_hmac_create(pg_cryptohash_type type) memset(ctx, 0, sizeof(pg_hmac_ctx)); ctx->type = type; + ctx->error = PG_HMAC_ERROR_NONE; + ctx->errreason = NULL; /* * Initialization takes care of assigning the correct type for OpenSSL. @@ -152,7 +179,11 @@ pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len) /* OpenSSL internals return 1 on success, 0 on failure */ if (status <= 0) + { + ctx->errreason = SSLerrmessage(ERR_get_error()); + ctx->error = PG_HMAC_ERROR_OPENSSL; return -1; + } return 0; } @@ -174,7 +205,11 @@ pg_hmac_update(pg_hmac_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_HMAC_ERROR_OPENSSL; return -1; + } return 0; } @@ -196,27 +231,45 @@ pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len) { case PG_MD5: if (len < MD5_DIGEST_LENGTH) + { + ctx->error = PG_HMAC_ERROR_DEST_LEN; return -1; + } break; case PG_SHA1: if (len < SHA1_DIGEST_LENGTH) + { + ctx->error = PG_HMAC_ERROR_DEST_LEN; return -1; + } break; case PG_SHA224: if (len < PG_SHA224_DIGEST_LENGTH) + { + ctx->error = PG_HMAC_ERROR_DEST_LEN; return -1; + } break; case PG_SHA256: if (len < PG_SHA256_DIGEST_LENGTH) + { + ctx->error = PG_HMAC_ERROR_DEST_LEN; return -1; + } break; case PG_SHA384: if (len < PG_SHA384_DIGEST_LENGTH) + { + ctx->error = PG_HMAC_ERROR_DEST_LEN; return -1; + } break; case PG_SHA512: if (len < PG_SHA512_DIGEST_LENGTH) + { + ctx->error = PG_HMAC_ERROR_DEST_LEN; return -1; + } break; } @@ -224,7 +277,11 @@ pg_hmac_final(pg_hmac_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_HMAC_ERROR_OPENSSL; return -1; + } return 0; } @@ -252,3 +309,36 @@ pg_hmac_free(pg_hmac_ctx *ctx) explicit_bzero(ctx, sizeof(pg_hmac_ctx)); FREE(ctx); } + +/* + * pg_hmac_error + * + * Returns a static string providing details about an error that happened + * during a HMAC computation. + */ +const char * +pg_hmac_error(pg_hmac_ctx *ctx) +{ + 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_HMAC_ERROR_NONE: + return _("success"); + case PG_HMAC_ERROR_DEST_LEN: + return _("destination buffer too small"); + case PG_HMAC_ERROR_OPENSSL: + return _("OpenSSL failure"); + } + + Assert(false); /* cannot be reached */ + return _("success"); +} diff --git a/src/common/scram-common.c b/src/common/scram-common.c index 23b68b14da7..12686259299 100644 --- a/src/common/scram-common.c +++ b/src/common/scram-common.c @@ -28,12 +28,13 @@ * Calculate SaltedPassword. * * The password should already be normalized by SASLprep. Returns 0 on - * success, -1 on failure. + * success, -1 on failure with *errstr pointing to a message about the + * error details. */ int scram_SaltedPassword(const char *password, const char *salt, int saltlen, int iterations, - uint8 *result) + uint8 *result, const char **errstr) { int password_len = strlen(password); uint32 one = pg_hton32(1); @@ -44,7 +45,10 @@ scram_SaltedPassword(const char *password, pg_hmac_ctx *hmac_ctx = pg_hmac_create(PG_SHA256); if (hmac_ctx == NULL) + { + *errstr = pg_hmac_error(NULL); /* returns OOM */ return -1; + } /* * Iterate hash calculation of HMAC entry using given salt. This is @@ -58,6 +62,7 @@ scram_SaltedPassword(const char *password, pg_hmac_update(hmac_ctx, (uint8 *) &one, sizeof(uint32)) < 0 || pg_hmac_final(hmac_ctx, Ui_prev, sizeof(Ui_prev)) < 0) { + *errstr = pg_hmac_error(hmac_ctx); pg_hmac_free(hmac_ctx); return -1; } @@ -71,6 +76,7 @@ scram_SaltedPassword(const char *password, pg_hmac_update(hmac_ctx, (uint8 *) Ui_prev, SCRAM_KEY_LEN) < 0 || pg_hmac_final(hmac_ctx, Ui, sizeof(Ui)) < 0) { + *errstr = pg_hmac_error(hmac_ctx); pg_hmac_free(hmac_ctx); return -1; } @@ -87,21 +93,26 @@ scram_SaltedPassword(const char *password, /* * Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is - * not included in the hash). Returns 0 on success, -1 on failure. + * not included in the hash). Returns 0 on success, -1 on failure with *errstr + * pointing to a message about the error details. */ int -scram_H(const uint8 *input, int len, uint8 *result) +scram_H(const uint8 *input, int len, uint8 *result, const char **errstr) { pg_cryptohash_ctx *ctx; ctx = pg_cryptohash_create(PG_SHA256); if (ctx == NULL) + { + *errstr = pg_cryptohash_error(NULL); /* returns OOM */ return -1; + } if (pg_cryptohash_init(ctx) < 0 || pg_cryptohash_update(ctx, input, len) < 0 || pg_cryptohash_final(ctx, result, SCRAM_KEY_LEN) < 0) { + *errstr = pg_cryptohash_error(ctx); pg_cryptohash_free(ctx); return -1; } @@ -111,20 +122,26 @@ scram_H(const uint8 *input, int len, uint8 *result) } /* - * Calculate ClientKey. Returns 0 on success, -1 on failure. + * Calculate ClientKey. Returns 0 on success, -1 on failure with *errstr + * pointing to a message about the error details. */ int -scram_ClientKey(const uint8 *salted_password, uint8 *result) +scram_ClientKey(const uint8 *salted_password, uint8 *result, + const char **errstr) { pg_hmac_ctx *ctx = pg_hmac_create(PG_SHA256); if (ctx == NULL) + { + *errstr = pg_hmac_error(NULL); /* returns OOM */ return -1; + } if (pg_hmac_init(ctx, salted_password, SCRAM_KEY_LEN) < 0 || pg_hmac_update(ctx, (uint8 *) "Client Key", strlen("Client Key")) < 0 || pg_hmac_final(ctx, result, SCRAM_KEY_LEN) < 0) { + *errstr = pg_hmac_error(ctx); pg_hmac_free(ctx); return -1; } @@ -134,20 +151,26 @@ scram_ClientKey(const uint8 *salted_password, uint8 *result) } /* - * Calculate ServerKey. Returns 0 on success, -1 on failure. + * Calculate ServerKey. Returns 0 on success, -1 on failure with *errstr + * pointing to a message about the error details. */ int -scram_ServerKey(const uint8 *salted_password, uint8 *result) +scram_ServerKey(const uint8 *salted_password, uint8 *result, + const char **errstr) { pg_hmac_ctx *ctx = pg_hmac_create(PG_SHA256); if (ctx == NULL) + { + *errstr = pg_hmac_error(NULL); /* returns OOM */ return -1; + } if (pg_hmac_init(ctx, salted_password, SCRAM_KEY_LEN) < 0 || pg_hmac_update(ctx, (uint8 *) "Server Key", strlen("Server Key")) < 0 || pg_hmac_final(ctx, result, SCRAM_KEY_LEN) < 0) { + *errstr = pg_hmac_error(ctx); pg_hmac_free(ctx); return -1; } @@ -164,10 +187,13 @@ scram_ServerKey(const uint8 *salted_password, uint8 *result) * * If iterations is 0, default number of iterations is used. The result is * palloc'd or malloc'd, so caller is responsible for freeing it. + * + * On error, returns NULL and sets *errstr to point to a message about the + * error details. */ char * scram_build_secret(const char *salt, int saltlen, int iterations, - const char *password) + const char *password, const char **errstr) { uint8 salted_password[SCRAM_KEY_LEN]; uint8 stored_key[SCRAM_KEY_LEN]; @@ -185,15 +211,17 @@ scram_build_secret(const char *salt, int saltlen, int iterations, /* Calculate StoredKey and ServerKey */ if (scram_SaltedPassword(password, salt, saltlen, iterations, - salted_password) < 0 || - scram_ClientKey(salted_password, stored_key) < 0 || - scram_H(stored_key, SCRAM_KEY_LEN, stored_key) < 0 || - scram_ServerKey(salted_password, server_key) < 0) + salted_password, errstr) < 0 || + scram_ClientKey(salted_password, stored_key, errstr) < 0 || + scram_H(stored_key, SCRAM_KEY_LEN, stored_key, errstr) < 0 || + scram_ServerKey(salted_password, server_key, errstr) < 0) { + /* errstr is filled already here */ #ifdef FRONTEND return NULL; #else - elog(ERROR, "could not calculate stored key and server key"); + elog(ERROR, "could not calculate stored key and server key: %s", + *errstr); #endif } @@ -215,7 +243,10 @@ scram_build_secret(const char *salt, int saltlen, int iterations, #ifdef FRONTEND result = malloc(maxlen); if (!result) + { + *errstr = _("out of memory"); return NULL; + } #else result = palloc(maxlen); #endif @@ -226,11 +257,12 @@ scram_build_secret(const char *salt, int saltlen, int iterations, encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len); if (encoded_result < 0) { + *errstr = _("could not encode salt"); #ifdef FRONTEND free(result); return NULL; #else - elog(ERROR, "could not encode salt"); + elog(ERROR, "%s", *errstr); #endif } p += encoded_result; @@ -241,11 +273,12 @@ scram_build_secret(const char *salt, int saltlen, int iterations, encoded_stored_len); if (encoded_result < 0) { + *errstr = _("could not encode stored key"); #ifdef FRONTEND free(result); return NULL; #else - elog(ERROR, "could not encode stored key"); + elog(ERROR, "%s", *errstr); #endif } @@ -257,11 +290,12 @@ scram_build_secret(const char *salt, int saltlen, int iterations, encoded_server_len); if (encoded_result < 0) { + *errstr = _("could not encode server key"); #ifdef FRONTEND free(result); return NULL; #else - elog(ERROR, "could not encode server key"); + elog(ERROR, "%s", *errstr); #endif } |
