Add PQencryptPasswordConn function to libpq, use it in psql and createuser.
The new function supports creating SCRAM verifiers, in addition to md5 hashes. The algorithm is chosen based on password_encryption, by default. This fixes the issue reported by Jeff Janes, that there was previously no way to create a SCRAM verifier with "\password". Michael Paquier and me Discussion: https://www.postgresql.org/message-id/CAMkU%3D1wfBgFPbfAMYZQE78p%3DVhZX7nN86aWkp0QcCp%3D%2BKxZ%3Dbg%40mail.gmail.com
This commit is contained in:
parent
af2c5aa88d
commit
8f8b9be51f
doc/src/sgml
src
backend/libpq
bin
common
include
interfaces/libpq
@ -5875,6 +5875,58 @@ void PQconninfoFree(PQconninfoOption *connOptions);
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry id="libpq-pqencryptpasswordconn">
|
||||||
|
<term>
|
||||||
|
<function>PQencryptPasswordConn</function>
|
||||||
|
<indexterm>
|
||||||
|
<primary>PQencryptPasswordConn</primary>
|
||||||
|
</indexterm>
|
||||||
|
</term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Prepares the encrypted form of a <productname>PostgreSQL</> password.
|
||||||
|
<synopsis>
|
||||||
|
char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm);
|
||||||
|
</synopsis>
|
||||||
|
This function is intended to be used by client applications that
|
||||||
|
wish to send commands like <literal>ALTER USER joe PASSWORD
|
||||||
|
'pwd'</>. It is good practice not to send the original cleartext
|
||||||
|
password in such a command, because it might be exposed in command
|
||||||
|
logs, activity displays, and so on. Instead, use this function to
|
||||||
|
convert the password to encrypted form before it is sent.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <parameter>passwd</> and <parameter>user</> arguments
|
||||||
|
are the cleartext password, and the SQL name of the user it is for.
|
||||||
|
<parameter>algorithm</> specifies the encryption algorithm
|
||||||
|
to use to encrypt the password. Currently supported algorithms are
|
||||||
|
<literal>md5</>, <literal>scram-sha-256</> and <literal>plain</>.
|
||||||
|
<literal>scram-sha-256</> was introduced in <productname>PostgreSQL</>
|
||||||
|
version 10, and will not work correctly with older server versions. If
|
||||||
|
<parameter>algorithm</> is <symbol>NULL</>, this function will query
|
||||||
|
the server for the current value of the
|
||||||
|
<xref linkend="guc-password-encryption"> setting. That can block, and
|
||||||
|
will fail if the current transaction is aborted, or if the connection
|
||||||
|
is busy executing another query. If you wish to use the default
|
||||||
|
algorithm for the server but want to avoid blocking, query
|
||||||
|
<varname>password_encryption</> yourself before calling
|
||||||
|
<function>PQencryptPasswordConn</>, and pass that value as the
|
||||||
|
<parameter>algorithm</>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The return value is a string allocated by <function>malloc</>.
|
||||||
|
The caller can assume the string doesn't contain any special characters
|
||||||
|
that would require escaping. Use <function>PQfreemem</> to free the
|
||||||
|
result when done with it. On error, returns <symbol>NULL</>, and
|
||||||
|
a suitable message is stored in the connection object.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry id="libpq-pqencryptpassword">
|
<varlistentry id="libpq-pqencryptpassword">
|
||||||
<term>
|
<term>
|
||||||
<function>PQencryptPassword</function>
|
<function>PQencryptPassword</function>
|
||||||
@ -5885,22 +5937,15 @@ void PQconninfoFree(PQconninfoOption *connOptions);
|
|||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Prepares the encrypted form of a <productname>PostgreSQL</> password.
|
Prepares the md5-encrypted form of a <productname>PostgreSQL</> password.
|
||||||
<synopsis>
|
<synopsis>
|
||||||
char * PQencryptPassword(const char *passwd, const char *user);
|
char *PQencryptPassword(const char *passwd, const char *user);
|
||||||
</synopsis>
|
</synopsis>
|
||||||
This function is intended to be used by client applications that
|
<function>PQencryptPassword</> is an older, deprecated version of
|
||||||
wish to send commands like <literal>ALTER USER joe PASSWORD
|
<function>PQencryptPasswodConn</>. The difference is that
|
||||||
'pwd'</>. It is good practice not to send the original cleartext
|
<function>PQencryptPassword</> does not
|
||||||
password in such a command, because it might be exposed in command
|
require a connection object, and <literal>md5</> is always used as the
|
||||||
logs, activity displays, and so on. Instead, use this function to
|
encryption algorithm.
|
||||||
convert the password to encrypted form before it is sent. The
|
|
||||||
arguments are the cleartext password, and the SQL name of the user
|
|
||||||
it is for. The return value is a string allocated by
|
|
||||||
<function>malloc</function>, or <symbol>NULL</symbol> if out of
|
|
||||||
memory. The caller can assume the string doesn't contain any
|
|
||||||
special characters that would require escaping. Use
|
|
||||||
<function>PQfreemem</> to free the result when done with it.
|
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -207,7 +207,7 @@ pg_be_scram_init(const char *username, const char *shadow_pass)
|
|||||||
*/
|
*/
|
||||||
char *verifier;
|
char *verifier;
|
||||||
|
|
||||||
verifier = scram_build_verifier(username, shadow_pass, 0);
|
verifier = pg_be_scram_build_verifier(shadow_pass);
|
||||||
|
|
||||||
(void) parse_scram_verifier(verifier, &state->iterations, &state->salt,
|
(void) parse_scram_verifier(verifier, &state->iterations, &state->salt,
|
||||||
state->StoredKey, state->ServerKey);
|
state->StoredKey, state->ServerKey);
|
||||||
@ -387,22 +387,14 @@ pg_be_scram_exchange(void *opaq, char *input, int inputlen,
|
|||||||
/*
|
/*
|
||||||
* Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
|
* Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
|
||||||
*
|
*
|
||||||
* If iterations is 0, default number of iterations is used. The result is
|
* The result is palloc'd, so caller is responsible for freeing it.
|
||||||
* palloc'd, so caller is responsible for freeing it.
|
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
scram_build_verifier(const char *username, const char *password,
|
pg_be_scram_build_verifier(const char *password)
|
||||||
int iterations)
|
|
||||||
{
|
{
|
||||||
char *prep_password = NULL;
|
char *prep_password = NULL;
|
||||||
pg_saslprep_rc rc;
|
pg_saslprep_rc rc;
|
||||||
char saltbuf[SCRAM_DEFAULT_SALT_LEN];
|
char saltbuf[SCRAM_DEFAULT_SALT_LEN];
|
||||||
uint8 salted_password[SCRAM_KEY_LEN];
|
|
||||||
uint8 keybuf[SCRAM_KEY_LEN];
|
|
||||||
char *encoded_salt;
|
|
||||||
char *encoded_storedkey;
|
|
||||||
char *encoded_serverkey;
|
|
||||||
int encoded_len;
|
|
||||||
char *result;
|
char *result;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -414,10 +406,7 @@ scram_build_verifier(const char *username, const char *password,
|
|||||||
if (rc == SASLPREP_SUCCESS)
|
if (rc == SASLPREP_SUCCESS)
|
||||||
password = (const char *) prep_password;
|
password = (const char *) prep_password;
|
||||||
|
|
||||||
if (iterations <= 0)
|
/* Generate random salt */
|
||||||
iterations = SCRAM_DEFAULT_ITERATIONS;
|
|
||||||
|
|
||||||
/* Generate salt, and encode it in base64 */
|
|
||||||
if (!pg_backend_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
|
if (!pg_backend_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
|
||||||
{
|
{
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
@ -426,37 +415,11 @@ scram_build_verifier(const char *username, const char *password,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
encoded_salt = palloc(pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN) + 1);
|
result = scram_build_verifier(saltbuf, SCRAM_DEFAULT_SALT_LEN,
|
||||||
encoded_len = pg_b64_encode(saltbuf, SCRAM_DEFAULT_SALT_LEN, encoded_salt);
|
SCRAM_DEFAULT_ITERATIONS, password);
|
||||||
encoded_salt[encoded_len] = '\0';
|
|
||||||
|
|
||||||
/* Calculate StoredKey, and encode it in base64 */
|
|
||||||
scram_SaltedPassword(password, saltbuf, SCRAM_DEFAULT_SALT_LEN,
|
|
||||||
iterations, salted_password);
|
|
||||||
scram_ClientKey(salted_password, keybuf);
|
|
||||||
scram_H(keybuf, SCRAM_KEY_LEN, keybuf); /* StoredKey */
|
|
||||||
|
|
||||||
encoded_storedkey = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1);
|
|
||||||
encoded_len = pg_b64_encode((const char *) keybuf, SCRAM_KEY_LEN,
|
|
||||||
encoded_storedkey);
|
|
||||||
encoded_storedkey[encoded_len] = '\0';
|
|
||||||
|
|
||||||
/* And same for ServerKey */
|
|
||||||
scram_ServerKey(salted_password, keybuf);
|
|
||||||
|
|
||||||
encoded_serverkey = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1);
|
|
||||||
encoded_len = pg_b64_encode((const char *) keybuf, SCRAM_KEY_LEN,
|
|
||||||
encoded_serverkey);
|
|
||||||
encoded_serverkey[encoded_len] = '\0';
|
|
||||||
|
|
||||||
result = psprintf("SCRAM-SHA-256$%d:%s$%s:%s", iterations, encoded_salt,
|
|
||||||
encoded_storedkey, encoded_serverkey);
|
|
||||||
|
|
||||||
if (prep_password)
|
if (prep_password)
|
||||||
pfree(prep_password);
|
pfree(prep_password);
|
||||||
pfree(encoded_salt);
|
|
||||||
pfree(encoded_storedkey);
|
|
||||||
pfree(encoded_serverkey);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1194,7 +1157,7 @@ scram_MockSalt(const char *username)
|
|||||||
* Generate salt using a SHA256 hash of the username and the cluster's
|
* Generate salt using a SHA256 hash of the username and the cluster's
|
||||||
* mock authentication nonce. (This works as long as the salt length is
|
* mock authentication nonce. (This works as long as the salt length is
|
||||||
* not larger the SHA256 digest length. If the salt is smaller, the caller
|
* not larger the SHA256 digest length. If the salt is smaller, the caller
|
||||||
* will just ignore the extra data))
|
* will just ignore the extra data.)
|
||||||
*/
|
*/
|
||||||
StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN,
|
StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN,
|
||||||
"salt length greater than SHA256 digest length");
|
"salt length greater than SHA256 digest length");
|
||||||
|
@ -156,7 +156,7 @@ encrypt_password(PasswordType target_type, const char *role,
|
|||||||
switch (guessed_type)
|
switch (guessed_type)
|
||||||
{
|
{
|
||||||
case PASSWORD_TYPE_PLAINTEXT:
|
case PASSWORD_TYPE_PLAINTEXT:
|
||||||
return scram_build_verifier(role, password, 0);
|
return pg_be_scram_build_verifier(password);
|
||||||
|
|
||||||
case PASSWORD_TYPE_MD5:
|
case PASSWORD_TYPE_MD5:
|
||||||
|
|
||||||
|
@ -1878,11 +1878,11 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
|
|||||||
else
|
else
|
||||||
user = PQuser(pset.db);
|
user = PQuser(pset.db);
|
||||||
|
|
||||||
encrypted_password = PQencryptPassword(pw1, user);
|
encrypted_password = PQencryptPasswordConn(pset.db, pw1, user, NULL);
|
||||||
|
|
||||||
if (!encrypted_password)
|
if (!encrypted_password)
|
||||||
{
|
{
|
||||||
psql_error("Password encryption failed.\n");
|
psql_error("%s", PQerrorMessage(pset.db));
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -274,11 +274,14 @@ main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
char *encrypted_password;
|
char *encrypted_password;
|
||||||
|
|
||||||
encrypted_password = PQencryptPassword(newpassword,
|
encrypted_password = PQencryptPasswordConn(conn,
|
||||||
newuser);
|
newpassword,
|
||||||
|
newuser,
|
||||||
|
NULL);
|
||||||
if (!encrypted_password)
|
if (!encrypted_password)
|
||||||
{
|
{
|
||||||
fprintf(stderr, _("Password encryption failed.\n"));
|
fprintf(stderr, _("%s: password encryption failed: %s"),
|
||||||
|
progname, PQerrorMessage(conn));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
appendStringLiteralConn(&sql, encrypted_password, conn);
|
appendStringLiteralConn(&sql, encrypted_password, conn);
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "common/base64.h"
|
||||||
#include "common/scram-common.h"
|
#include "common/scram-common.h"
|
||||||
|
|
||||||
#define HMAC_IPAD 0x36
|
#define HMAC_IPAD 0x36
|
||||||
@ -180,3 +181,66 @@ scram_ServerKey(const uint8 *salted_password, uint8 *result)
|
|||||||
scram_HMAC_update(&ctx, "Server Key", strlen("Server Key"));
|
scram_HMAC_update(&ctx, "Server Key", strlen("Server Key"));
|
||||||
scram_HMAC_final(result, &ctx);
|
scram_HMAC_final(result, &ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
|
||||||
|
*
|
||||||
|
* The password should already have been processed with SASLprep, if necessary!
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
scram_build_verifier(const char *salt, int saltlen, int iterations,
|
||||||
|
const char *password)
|
||||||
|
{
|
||||||
|
uint8 salted_password[SCRAM_KEY_LEN];
|
||||||
|
uint8 stored_key[SCRAM_KEY_LEN];
|
||||||
|
uint8 server_key[SCRAM_KEY_LEN];
|
||||||
|
char *result;
|
||||||
|
char *p;
|
||||||
|
int maxlen;
|
||||||
|
|
||||||
|
if (iterations <= 0)
|
||||||
|
iterations = SCRAM_DEFAULT_ITERATIONS;
|
||||||
|
|
||||||
|
/* Calculate StoredKey and ServerKey */
|
||||||
|
scram_SaltedPassword(password, salt, saltlen, iterations,
|
||||||
|
salted_password);
|
||||||
|
scram_ClientKey(salted_password, stored_key);
|
||||||
|
scram_H(stored_key, SCRAM_KEY_LEN, stored_key);
|
||||||
|
|
||||||
|
scram_ServerKey(salted_password, server_key);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The format is:
|
||||||
|
* SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
|
||||||
|
*/
|
||||||
|
maxlen = strlen("SCRAM-SHA-256") + 1
|
||||||
|
+ 10 + 1 /* iteration count */
|
||||||
|
+ pg_b64_enc_len(saltlen) + 1 /* Base64-encoded salt */
|
||||||
|
+ pg_b64_enc_len(SCRAM_KEY_LEN) + 1 /* Base64-encoded StoredKey */
|
||||||
|
+ pg_b64_enc_len(SCRAM_KEY_LEN) + 1; /* Base64-encoded ServerKey */
|
||||||
|
|
||||||
|
#ifdef FRONTEND
|
||||||
|
result = malloc(maxlen);
|
||||||
|
if (!result)
|
||||||
|
return NULL;
|
||||||
|
#else
|
||||||
|
result = palloc(maxlen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
|
||||||
|
|
||||||
|
p += pg_b64_encode(salt, saltlen, p);
|
||||||
|
*(p++) = '$';
|
||||||
|
p += pg_b64_encode((char *) stored_key, SCRAM_KEY_LEN, p);
|
||||||
|
*(p++) = ':';
|
||||||
|
p += pg_b64_encode((char *) server_key, SCRAM_KEY_LEN, p);
|
||||||
|
*(p++) = '\0';
|
||||||
|
|
||||||
|
Assert(p - result <= maxlen);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -53,4 +53,7 @@ extern void scram_H(const uint8 *str, int len, uint8 *result);
|
|||||||
extern void scram_ClientKey(const uint8 *salted_password, uint8 *result);
|
extern void scram_ClientKey(const uint8 *salted_password, uint8 *result);
|
||||||
extern void scram_ServerKey(const uint8 *salted_password, uint8 *result);
|
extern void scram_ServerKey(const uint8 *salted_password, uint8 *result);
|
||||||
|
|
||||||
|
extern char *scram_build_verifier(const char *salt, int saltlen, int iterations,
|
||||||
|
const char *password);
|
||||||
|
|
||||||
#endif /* SCRAM_COMMON_H */
|
#endif /* SCRAM_COMMON_H */
|
||||||
|
@ -27,9 +27,7 @@ extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen,
|
|||||||
char **output, int *outputlen, char **logdetail);
|
char **output, int *outputlen, char **logdetail);
|
||||||
|
|
||||||
/* Routines to handle and check SCRAM-SHA-256 verifier */
|
/* Routines to handle and check SCRAM-SHA-256 verifier */
|
||||||
extern char *scram_build_verifier(const char *username,
|
extern char *pg_be_scram_build_verifier(const char *password);
|
||||||
const char *password,
|
|
||||||
int iterations);
|
|
||||||
extern bool is_scram_verifier(const char *verifier);
|
extern bool is_scram_verifier(const char *verifier);
|
||||||
extern bool scram_verify_plain_password(const char *username,
|
extern bool scram_verify_plain_password(const char *username,
|
||||||
const char *password, const char *verifier);
|
const char *password, const char *verifier);
|
||||||
|
@ -171,3 +171,4 @@ PQsslAttributeNames 168
|
|||||||
PQsslAttribute 169
|
PQsslAttribute 169
|
||||||
PQsetErrorContextVisibility 170
|
PQsetErrorContextVisibility 170
|
||||||
PQresultVerboseErrorMessage 171
|
PQresultVerboseErrorMessage 171
|
||||||
|
PQencryptPasswordConn 172
|
||||||
|
@ -614,6 +614,41 @@ verify_server_signature(fe_scram_state *state)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a new SCRAM verifier.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
pg_fe_scram_build_verifier(const char *password)
|
||||||
|
{
|
||||||
|
char *prep_password = NULL;
|
||||||
|
pg_saslprep_rc rc;
|
||||||
|
char saltbuf[SCRAM_DEFAULT_SALT_LEN];
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Normalize the password with SASLprep. If that doesn't work, because
|
||||||
|
* the password isn't valid UTF-8 or contains prohibited characters, just
|
||||||
|
* proceed with the original password. (See comments at top of file.)
|
||||||
|
*/
|
||||||
|
rc = pg_saslprep(password, &prep_password);
|
||||||
|
if (rc == SASLPREP_OOM)
|
||||||
|
return NULL;
|
||||||
|
if (rc == SASLPREP_SUCCESS)
|
||||||
|
password = (const char *) prep_password;
|
||||||
|
|
||||||
|
/* Generate a random salt */
|
||||||
|
if (!pg_frontend_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
result = scram_build_verifier(saltbuf, SCRAM_DEFAULT_SALT_LEN,
|
||||||
|
SCRAM_DEFAULT_ITERATIONS, password);
|
||||||
|
|
||||||
|
if (prep_password)
|
||||||
|
free(prep_password);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Random number generator.
|
* Random number generator.
|
||||||
*/
|
*/
|
||||||
|
@ -1077,22 +1077,12 @@ pg_fe_getauthname(PQExpBuffer errorMessage)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PQencryptPassword -- exported routine to encrypt a password
|
* PQencryptPassword -- exported routine to encrypt a password with MD5
|
||||||
*
|
*
|
||||||
* This is intended to be used by client applications that wish to send
|
* This function is equivalent to calling PQencryptPasswordConn with
|
||||||
* commands like ALTER USER joe PASSWORD 'pwd'. The password need not
|
* "md5" as the encryption method, except that this doesn't require
|
||||||
* be sent in cleartext if it is encrypted on the client side. This is
|
* a connection object. This function is deprecated, use
|
||||||
* good because it ensures the cleartext password won't end up in logs,
|
* PQencryptPasswordConn instead.
|
||||||
* pg_stat displays, etc. We export the function so that clients won't
|
|
||||||
* be dependent on low-level details like whether the encryption is MD5
|
|
||||||
* or something else.
|
|
||||||
*
|
|
||||||
* Arguments are the cleartext password, and the SQL name of the user it
|
|
||||||
* is for.
|
|
||||||
*
|
|
||||||
* Return value is a malloc'd string, or NULL if out-of-memory. The client
|
|
||||||
* may assume the string doesn't contain any special characters that would
|
|
||||||
* require escaping.
|
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
PQencryptPassword(const char *passwd, const char *user)
|
PQencryptPassword(const char *passwd, const char *user)
|
||||||
@ -1111,3 +1101,114 @@ PQencryptPassword(const char *passwd, const char *user)
|
|||||||
|
|
||||||
return crypt_pwd;
|
return crypt_pwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PQencryptPasswordConn -- exported routine to encrypt a password
|
||||||
|
*
|
||||||
|
* This is intended to be used by client applications that wish to send
|
||||||
|
* commands like ALTER USER joe PASSWORD 'pwd'. The password need not
|
||||||
|
* be sent in cleartext if it is encrypted on the client side. This is
|
||||||
|
* good because it ensures the cleartext password won't end up in logs,
|
||||||
|
* pg_stat displays, etc. We export the function so that clients won't
|
||||||
|
* be dependent on low-level details like whether the encryption is MD5
|
||||||
|
* or something else.
|
||||||
|
*
|
||||||
|
* Arguments are a connection object, the cleartext password, the SQL
|
||||||
|
* name of the user it is for, and a string indicating the algorithm to
|
||||||
|
* use for encrypting the password. If algorithm is NULL, this queries
|
||||||
|
* the server for the current 'password_encryption' value. If you wish
|
||||||
|
* to avoid that, e.g. to avoid blocking, you can execute
|
||||||
|
* 'show password_encryption' yourself before calling this function, and
|
||||||
|
* pass it as the algorithm.
|
||||||
|
*
|
||||||
|
* Return value is a malloc'd string. The client may assume the string
|
||||||
|
* doesn't contain any special characters that would require escaping.
|
||||||
|
* On error, an error message is stored in the connection object, and
|
||||||
|
* returns NULL.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
|
||||||
|
const char *algorithm)
|
||||||
|
{
|
||||||
|
#define MAX_ALGORITHM_NAME_LEN 50
|
||||||
|
char algobuf[MAX_ALGORITHM_NAME_LEN + 1];
|
||||||
|
char *crypt_pwd = NULL;
|
||||||
|
|
||||||
|
if (!conn)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* If no algorithm was given, ask the server. */
|
||||||
|
if (algorithm == NULL)
|
||||||
|
{
|
||||||
|
PGresult *res;
|
||||||
|
char *val;
|
||||||
|
|
||||||
|
res = PQexec(conn, "show password_encryption");
|
||||||
|
if (res == NULL)
|
||||||
|
{
|
||||||
|
/* PQexec() should've set conn->errorMessage already */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
|
{
|
||||||
|
/* PQexec() should've set conn->errorMessage already */
|
||||||
|
PQclear(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (PQntuples(res) != 1 || PQnfields(res) != 1)
|
||||||
|
{
|
||||||
|
PQclear(res);
|
||||||
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
|
libpq_gettext("unexpected shape of result set returned for SHOW\n"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
val = PQgetvalue(res, 0, 0);
|
||||||
|
|
||||||
|
if (strlen(val) > MAX_ALGORITHM_NAME_LEN)
|
||||||
|
{
|
||||||
|
PQclear(res);
|
||||||
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
|
libpq_gettext("password_encryption value too long\n"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strcpy(algobuf, val);
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
algorithm = algobuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ok, now we know what algorithm to use */
|
||||||
|
|
||||||
|
if (strcmp(algorithm, "scram-sha-256") == 0)
|
||||||
|
{
|
||||||
|
crypt_pwd = pg_fe_scram_build_verifier(passwd);
|
||||||
|
}
|
||||||
|
else if (strcmp(algorithm, "md5") == 0)
|
||||||
|
{
|
||||||
|
crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
|
||||||
|
if (crypt_pwd)
|
||||||
|
{
|
||||||
|
if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))
|
||||||
|
{
|
||||||
|
free(crypt_pwd);
|
||||||
|
crypt_pwd = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(algorithm, "plain") == 0)
|
||||||
|
{
|
||||||
|
crypt_pwd = strdup(passwd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
|
libpq_gettext("unknown password encryption algorithm\n"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!crypt_pwd)
|
||||||
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
|
libpq_gettext("out of memory\n"));
|
||||||
|
|
||||||
|
return crypt_pwd;
|
||||||
|
}
|
||||||
|
@ -28,5 +28,6 @@ extern void pg_fe_scram_free(void *opaq);
|
|||||||
extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
|
extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
|
||||||
char **output, int *outputlen,
|
char **output, int *outputlen,
|
||||||
bool *done, bool *success, PQExpBuffer errorMessage);
|
bool *done, bool *success, PQExpBuffer errorMessage);
|
||||||
|
extern char *pg_fe_scram_build_verifier(const char *password);
|
||||||
|
|
||||||
#endif /* FE_AUTH_H */
|
#endif /* FE_AUTH_H */
|
||||||
|
@ -597,6 +597,7 @@ extern int PQenv2encoding(void);
|
|||||||
/* === in fe-auth.c === */
|
/* === in fe-auth.c === */
|
||||||
|
|
||||||
extern char *PQencryptPassword(const char *passwd, const char *user);
|
extern char *PQencryptPassword(const char *passwd, const char *user);
|
||||||
|
extern char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm);
|
||||||
|
|
||||||
/* === in encnames.c === */
|
/* === in encnames.c === */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user