mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-06 15:24:56 +08:00
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
@ -5875,6 +5875,58 @@ void PQconninfoFree(PQconninfoOption *connOptions);
|
||||
</listitem>
|
||||
</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">
|
||||
<term>
|
||||
<function>PQencryptPassword</function>
|
||||
@ -5885,22 +5937,15 @@ void PQconninfoFree(PQconninfoOption *connOptions);
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Prepares the encrypted form of a <productname>PostgreSQL</> password.
|
||||
<synopsis>
|
||||
char * PQencryptPassword(const char *passwd, const char *user);
|
||||
</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. 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.
|
||||
Prepares the md5-encrypted form of a <productname>PostgreSQL</> password.
|
||||
<synopsis>
|
||||
char *PQencryptPassword(const char *passwd, const char *user);
|
||||
</synopsis>
|
||||
<function>PQencryptPassword</> is an older, deprecated version of
|
||||
<function>PQencryptPasswodConn</>. The difference is that
|
||||
<function>PQencryptPassword</> does not
|
||||
require a connection object, and <literal>md5</> is always used as the
|
||||
encryption algorithm.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -207,7 +207,7 @@ pg_be_scram_init(const char *username, const char *shadow_pass)
|
||||
*/
|
||||
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,
|
||||
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.
|
||||
*
|
||||
* If iterations is 0, default number of iterations is used. The result is
|
||||
* palloc'd, so caller is responsible for freeing it.
|
||||
* The result is palloc'd, so caller is responsible for freeing it.
|
||||
*/
|
||||
char *
|
||||
scram_build_verifier(const char *username, const char *password,
|
||||
int iterations)
|
||||
pg_be_scram_build_verifier(const char *password)
|
||||
{
|
||||
char *prep_password = NULL;
|
||||
pg_saslprep_rc rc;
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -414,10 +406,7 @@ scram_build_verifier(const char *username, const char *password,
|
||||
if (rc == SASLPREP_SUCCESS)
|
||||
password = (const char *) prep_password;
|
||||
|
||||
if (iterations <= 0)
|
||||
iterations = SCRAM_DEFAULT_ITERATIONS;
|
||||
|
||||
/* Generate salt, and encode it in base64 */
|
||||
/* Generate random salt */
|
||||
if (!pg_backend_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
|
||||
{
|
||||
ereport(LOG,
|
||||
@ -426,37 +415,11 @@ scram_build_verifier(const char *username, const char *password,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
encoded_salt = palloc(pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN) + 1);
|
||||
encoded_len = pg_b64_encode(saltbuf, SCRAM_DEFAULT_SALT_LEN, encoded_salt);
|
||||
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);
|
||||
result = scram_build_verifier(saltbuf, SCRAM_DEFAULT_SALT_LEN,
|
||||
SCRAM_DEFAULT_ITERATIONS, password);
|
||||
|
||||
if (prep_password)
|
||||
pfree(prep_password);
|
||||
pfree(encoded_salt);
|
||||
pfree(encoded_storedkey);
|
||||
pfree(encoded_serverkey);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -1194,7 +1157,7 @@ scram_MockSalt(const char *username)
|
||||
* 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
|
||||
* 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,
|
||||
"salt length greater than SHA256 digest length");
|
||||
|
@ -156,7 +156,7 @@ encrypt_password(PasswordType target_type, const char *role,
|
||||
switch (guessed_type)
|
||||
{
|
||||
case PASSWORD_TYPE_PLAINTEXT:
|
||||
return scram_build_verifier(role, password, 0);
|
||||
return pg_be_scram_build_verifier(password);
|
||||
|
||||
case PASSWORD_TYPE_MD5:
|
||||
|
||||
|
@ -1878,11 +1878,11 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
|
||||
else
|
||||
user = PQuser(pset.db);
|
||||
|
||||
encrypted_password = PQencryptPassword(pw1, user);
|
||||
encrypted_password = PQencryptPasswordConn(pset.db, pw1, user, NULL);
|
||||
|
||||
if (!encrypted_password)
|
||||
{
|
||||
psql_error("Password encryption failed.\n");
|
||||
psql_error("%s", PQerrorMessage(pset.db));
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
|
@ -274,11 +274,14 @@ main(int argc, char *argv[])
|
||||
{
|
||||
char *encrypted_password;
|
||||
|
||||
encrypted_password = PQencryptPassword(newpassword,
|
||||
newuser);
|
||||
encrypted_password = PQencryptPasswordConn(conn,
|
||||
newpassword,
|
||||
newuser,
|
||||
NULL);
|
||||
if (!encrypted_password)
|
||||
{
|
||||
fprintf(stderr, _("Password encryption failed.\n"));
|
||||
fprintf(stderr, _("%s: password encryption failed: %s"),
|
||||
progname, PQerrorMessage(conn));
|
||||
exit(1);
|
||||
}
|
||||
appendStringLiteralConn(&sql, encrypted_password, conn);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "common/base64.h"
|
||||
#include "common/scram-common.h"
|
||||
|
||||
#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_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_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 */
|
||||
|
@ -27,9 +27,7 @@ extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen,
|
||||
char **output, int *outputlen, char **logdetail);
|
||||
|
||||
/* Routines to handle and check SCRAM-SHA-256 verifier */
|
||||
extern char *scram_build_verifier(const char *username,
|
||||
const char *password,
|
||||
int iterations);
|
||||
extern char *pg_be_scram_build_verifier(const char *password);
|
||||
extern bool is_scram_verifier(const char *verifier);
|
||||
extern bool scram_verify_plain_password(const char *username,
|
||||
const char *password, const char *verifier);
|
||||
|
@ -171,3 +171,4 @@ PQsslAttributeNames 168
|
||||
PQsslAttribute 169
|
||||
PQsetErrorContextVisibility 170
|
||||
PQresultVerboseErrorMessage 171
|
||||
PQencryptPasswordConn 172
|
||||
|
@ -614,6 +614,41 @@ verify_server_signature(fe_scram_state *state)
|
||||
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.
|
||||
*/
|
||||
|
@ -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
|
||||
* 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 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.
|
||||
* This function is equivalent to calling PQencryptPasswordConn with
|
||||
* "md5" as the encryption method, except that this doesn't require
|
||||
* a connection object. This function is deprecated, use
|
||||
* PQencryptPasswordConn instead.
|
||||
*/
|
||||
char *
|
||||
PQencryptPassword(const char *passwd, const char *user)
|
||||
@ -1111,3 +1101,114 @@ PQencryptPassword(const char *passwd, const char *user)
|
||||
|
||||
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,
|
||||
char **output, int *outputlen,
|
||||
bool *done, bool *success, PQExpBuffer errorMessage);
|
||||
extern char *pg_fe_scram_build_verifier(const char *password);
|
||||
|
||||
#endif /* FE_AUTH_H */
|
||||
|
@ -597,6 +597,7 @@ extern int PQenv2encoding(void);
|
||||
/* === in fe-auth.c === */
|
||||
|
||||
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 === */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user