Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8f8b9be

Browse files
committed
Add PQencryptPasswordConn function to libpq, use it in psql and createuser.
The new function supports creating SCRAM verifiers, in addition to md5hashes. The algorithm is chosen based on password_encryption, by default.This fixes the issue reported by Jeff Janes, that there was previouslyno way to create a SCRAM verifier with "\password".Michael Paquier and meDiscussion:https://www.postgresql.org/message-id/CAMkU%3D1wfBgFPbfAMYZQE78p%3DVhZX7nN86aWkp0QcCp%3D%2BKxZ%3Dbg%40mail.gmail.com
1 parentaf2c5aa commit8f8b9be

File tree

13 files changed

+291
-76
lines changed

13 files changed

+291
-76
lines changed

‎doc/src/sgml/libpq.sgml

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5875,32 +5875,77 @@ void PQconninfoFree(PQconninfoOption *connOptions);
58755875
</listitem>
58765876
</varlistentry>
58775877

5878-
<varlistentry id="libpq-pqencryptpassword">
5878+
<varlistentry id="libpq-pqencryptpasswordconn">
58795879
<term>
5880-
<function>PQencryptPassword</function>
5880+
<function>PQencryptPasswordConn</function>
58815881
<indexterm>
5882-
<primary>PQencryptPassword</primary>
5882+
<primary>PQencryptPasswordConn</primary>
58835883
</indexterm>
58845884
</term>
58855885

58865886
<listitem>
58875887
<para>
58885888
Prepares the encrypted form of a <productname>PostgreSQL</> password.
58895889
<synopsis>
5890-
char * PQencryptPassword(const char *passwd, const char *user);
5890+
char *PQencryptPasswordConn(PGconn *conn,const char *passwd, const char *user, const char *algorithm);
58915891
</synopsis>
58925892
This function is intended to be used by client applications that
58935893
wish to send commands like <literal>ALTER USER joe PASSWORD
58945894
'pwd'</>. It is good practice not to send the original cleartext
58955895
password in such a command, because it might be exposed in command
58965896
logs, activity displays, and so on. Instead, use this function to
5897-
convert the password to encrypted form before it is sent. The
5898-
arguments are the cleartext password, and the SQL name of the user
5899-
it is for. The return value is a string allocated by
5900-
<function>malloc</function>, or <symbol>NULL</symbol> if out of
5901-
memory. The caller can assume the string doesn't contain any
5902-
special characters that would require escaping. Use
5903-
<function>PQfreemem</> to free the result when done with it.
5897+
convert the password to encrypted form before it is sent.
5898+
</para>
5899+
5900+
<para>
5901+
The <parameter>passwd</> and <parameter>user</> arguments
5902+
are the cleartext password, and the SQL name of the user it is for.
5903+
<parameter>algorithm</> specifies the encryption algorithm
5904+
to use to encrypt the password. Currently supported algorithms are
5905+
<literal>md5</>, <literal>scram-sha-256</> and <literal>plain</>.
5906+
<literal>scram-sha-256</> was introduced in <productname>PostgreSQL</>
5907+
version 10, and will not work correctly with older server versions. If
5908+
<parameter>algorithm</> is <symbol>NULL</>, this function will query
5909+
the server for the current value of the
5910+
<xref linkend="guc-password-encryption"> setting. That can block, and
5911+
will fail if the current transaction is aborted, or if the connection
5912+
is busy executing another query. If you wish to use the default
5913+
algorithm for the server but want to avoid blocking, query
5914+
<varname>password_encryption</> yourself before calling
5915+
<function>PQencryptPasswordConn</>, and pass that value as the
5916+
<parameter>algorithm</>.
5917+
</para>
5918+
5919+
<para>
5920+
The return value is a string allocated by <function>malloc</>.
5921+
The caller can assume the string doesn't contain any special characters
5922+
that would require escaping. Use <function>PQfreemem</> to free the
5923+
result when done with it. On error, returns <symbol>NULL</>, and
5924+
a suitable message is stored in the connection object.
5925+
</para>
5926+
5927+
</listitem>
5928+
</varlistentry>
5929+
5930+
<varlistentry id="libpq-pqencryptpassword">
5931+
<term>
5932+
<function>PQencryptPassword</function>
5933+
<indexterm>
5934+
<primary>PQencryptPassword</primary>
5935+
</indexterm>
5936+
</term>
5937+
5938+
<listitem>
5939+
<para>
5940+
Prepares the md5-encrypted form of a <productname>PostgreSQL</> password.
5941+
<synopsis>
5942+
char *PQencryptPassword(const char *passwd, const char *user);
5943+
</synopsis>
5944+
<function>PQencryptPassword</> is an older, deprecated version of
5945+
<function>PQencryptPasswodConn</>. The difference is that
5946+
<function>PQencryptPassword</> does not
5947+
require a connection object, and <literal>md5</> is always used as the
5948+
encryption algorithm.
59045949
</para>
59055950
</listitem>
59065951
</varlistentry>

‎src/backend/libpq/auth-scram.c

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ pg_be_scram_init(const char *username, const char *shadow_pass)
207207
*/
208208
char*verifier;
209209

210-
verifier=scram_build_verifier(username,shadow_pass,0);
210+
verifier=pg_be_scram_build_verifier(shadow_pass);
211211

212212
(void)parse_scram_verifier(verifier,&state->iterations,&state->salt,
213213
state->StoredKey,state->ServerKey);
@@ -387,22 +387,14 @@ pg_be_scram_exchange(void *opaq, char *input, int inputlen,
387387
/*
388388
* Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
389389
*
390-
* If iterations is 0, default number of iterations is used. The result is
391-
* palloc'd, so caller is responsible for freeing it.
390+
* The result is palloc'd, so caller is responsible for freeing it.
392391
*/
393392
char*
394-
scram_build_verifier(constchar*username,constchar*password,
395-
intiterations)
393+
pg_be_scram_build_verifier(constchar*password)
396394
{
397395
char*prep_password=NULL;
398396
pg_saslprep_rcrc;
399397
charsaltbuf[SCRAM_DEFAULT_SALT_LEN];
400-
uint8salted_password[SCRAM_KEY_LEN];
401-
uint8keybuf[SCRAM_KEY_LEN];
402-
char*encoded_salt;
403-
char*encoded_storedkey;
404-
char*encoded_serverkey;
405-
intencoded_len;
406398
char*result;
407399

408400
/*
@@ -414,10 +406,7 @@ scram_build_verifier(const char *username, const char *password,
414406
if (rc==SASLPREP_SUCCESS)
415407
password= (constchar*)prep_password;
416408

417-
if (iterations <=0)
418-
iterations=SCRAM_DEFAULT_ITERATIONS;
419-
420-
/* Generate salt, and encode it in base64 */
409+
/* Generate random salt */
421410
if (!pg_backend_random(saltbuf,SCRAM_DEFAULT_SALT_LEN))
422411
{
423412
ereport(LOG,
@@ -426,37 +415,11 @@ scram_build_verifier(const char *username, const char *password,
426415
returnNULL;
427416
}
428417

429-
encoded_salt=palloc(pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN)+1);
430-
encoded_len=pg_b64_encode(saltbuf,SCRAM_DEFAULT_SALT_LEN,encoded_salt);
431-
encoded_salt[encoded_len]='\0';
432-
433-
/* Calculate StoredKey, and encode it in base64 */
434-
scram_SaltedPassword(password,saltbuf,SCRAM_DEFAULT_SALT_LEN,
435-
iterations,salted_password);
436-
scram_ClientKey(salted_password,keybuf);
437-
scram_H(keybuf,SCRAM_KEY_LEN,keybuf);/* StoredKey */
438-
439-
encoded_storedkey=palloc(pg_b64_enc_len(SCRAM_KEY_LEN)+1);
440-
encoded_len=pg_b64_encode((constchar*)keybuf,SCRAM_KEY_LEN,
441-
encoded_storedkey);
442-
encoded_storedkey[encoded_len]='\0';
443-
444-
/* And same for ServerKey */
445-
scram_ServerKey(salted_password,keybuf);
446-
447-
encoded_serverkey=palloc(pg_b64_enc_len(SCRAM_KEY_LEN)+1);
448-
encoded_len=pg_b64_encode((constchar*)keybuf,SCRAM_KEY_LEN,
449-
encoded_serverkey);
450-
encoded_serverkey[encoded_len]='\0';
451-
452-
result=psprintf("SCRAM-SHA-256$%d:%s$%s:%s",iterations,encoded_salt,
453-
encoded_storedkey,encoded_serverkey);
418+
result=scram_build_verifier(saltbuf,SCRAM_DEFAULT_SALT_LEN,
419+
SCRAM_DEFAULT_ITERATIONS,password);
454420

455421
if (prep_password)
456422
pfree(prep_password);
457-
pfree(encoded_salt);
458-
pfree(encoded_storedkey);
459-
pfree(encoded_serverkey);
460423

461424
returnresult;
462425
}
@@ -1194,7 +1157,7 @@ scram_MockSalt(const char *username)
11941157
* Generate salt using a SHA256 hash of the username and the cluster's
11951158
* mock authentication nonce. (This works as long as the salt length is
11961159
* not larger the SHA256 digest length. If the salt is smaller, the caller
1197-
* will just ignore the extra data))
1160+
* will just ignore the extra data.)
11981161
*/
11991162
StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >=SCRAM_DEFAULT_SALT_LEN,
12001163
"salt length greater than SHA256 digest length");

‎src/backend/libpq/crypt.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ encrypt_password(PasswordType target_type, const char *role,
156156
switch (guessed_type)
157157
{
158158
casePASSWORD_TYPE_PLAINTEXT:
159-
returnscram_build_verifier(role,password,0);
159+
returnpg_be_scram_build_verifier(password);
160160

161161
casePASSWORD_TYPE_MD5:
162162

‎src/bin/psql/command.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,11 +1878,11 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
18781878
else
18791879
user=PQuser(pset.db);
18801880

1881-
encrypted_password=PQencryptPassword(pw1,user);
1881+
encrypted_password=PQencryptPasswordConn(pset.db,pw1,user,NULL);
18821882

18831883
if (!encrypted_password)
18841884
{
1885-
psql_error("Password encryption failed.\n");
1885+
psql_error("%s",PQerrorMessage(pset.db));
18861886
success= false;
18871887
}
18881888
else

‎src/bin/scripts/createuser.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,14 @@ main(int argc, char *argv[])
274274
{
275275
char*encrypted_password;
276276

277-
encrypted_password=PQencryptPassword(newpassword,
278-
newuser);
277+
encrypted_password=PQencryptPasswordConn(conn,
278+
newpassword,
279+
newuser,
280+
NULL);
279281
if (!encrypted_password)
280282
{
281-
fprintf(stderr,_("Password encryption failed.\n"));
283+
fprintf(stderr,_("%s: password encryption failed: %s"),
284+
progname,PQerrorMessage(conn));
282285
exit(1);
283286
}
284287
appendStringLiteralConn(&sql,encrypted_password,conn);

‎src/common/scram-common.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include<netinet/in.h>
2424
#include<arpa/inet.h>
2525

26+
#include"common/base64.h"
2627
#include"common/scram-common.h"
2728

2829
#defineHMAC_IPAD 0x36
@@ -180,3 +181,66 @@ scram_ServerKey(const uint8 *salted_password, uint8 *result)
180181
scram_HMAC_update(&ctx,"Server Key",strlen("Server Key"));
181182
scram_HMAC_final(result,&ctx);
182183
}
184+
185+
186+
/*
187+
* Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
188+
*
189+
* The password should already have been processed with SASLprep, if necessary!
190+
*
191+
* If iterations is 0, default number of iterations is used. The result is
192+
* palloc'd or malloc'd, so caller is responsible for freeing it.
193+
*/
194+
char*
195+
scram_build_verifier(constchar*salt,intsaltlen,intiterations,
196+
constchar*password)
197+
{
198+
uint8salted_password[SCRAM_KEY_LEN];
199+
uint8stored_key[SCRAM_KEY_LEN];
200+
uint8server_key[SCRAM_KEY_LEN];
201+
char*result;
202+
char*p;
203+
intmaxlen;
204+
205+
if (iterations <=0)
206+
iterations=SCRAM_DEFAULT_ITERATIONS;
207+
208+
/* Calculate StoredKey and ServerKey */
209+
scram_SaltedPassword(password,salt,saltlen,iterations,
210+
salted_password);
211+
scram_ClientKey(salted_password,stored_key);
212+
scram_H(stored_key,SCRAM_KEY_LEN,stored_key);
213+
214+
scram_ServerKey(salted_password,server_key);
215+
216+
/*
217+
* The format is:
218+
* SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
219+
*/
220+
maxlen=strlen("SCRAM-SHA-256")+1
221+
+10+1/* iteration count */
222+
+pg_b64_enc_len(saltlen)+1/* Base64-encoded salt */
223+
+pg_b64_enc_len(SCRAM_KEY_LEN)+1/* Base64-encoded StoredKey */
224+
+pg_b64_enc_len(SCRAM_KEY_LEN)+1;/* Base64-encoded ServerKey */
225+
226+
#ifdefFRONTEND
227+
result=malloc(maxlen);
228+
if (!result)
229+
returnNULL;
230+
#else
231+
result=palloc(maxlen);
232+
#endif
233+
234+
p=result+sprintf(result,"SCRAM-SHA-256$%d:",iterations);
235+
236+
p+=pg_b64_encode(salt,saltlen,p);
237+
*(p++)='$';
238+
p+=pg_b64_encode((char*)stored_key,SCRAM_KEY_LEN,p);
239+
*(p++)=':';
240+
p+=pg_b64_encode((char*)server_key,SCRAM_KEY_LEN,p);
241+
*(p++)='\0';
242+
243+
Assert(p-result <=maxlen);
244+
245+
returnresult;
246+
}

‎src/include/common/scram-common.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,7 @@ extern void scram_H(const uint8 *str, int len, uint8 *result);
5353
externvoidscram_ClientKey(constuint8*salted_password,uint8*result);
5454
externvoidscram_ServerKey(constuint8*salted_password,uint8*result);
5555

56+
externchar*scram_build_verifier(constchar*salt,intsaltlen,intiterations,
57+
constchar*password);
58+
5659
#endif/* SCRAM_COMMON_H */

‎src/include/libpq/scram.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen,
2727
char**output,int*outputlen,char**logdetail);
2828

2929
/* Routines to handle and check SCRAM-SHA-256 verifier */
30-
externchar*scram_build_verifier(constchar*username,
31-
constchar*password,
32-
intiterations);
30+
externchar*pg_be_scram_build_verifier(constchar*password);
3331
externboolis_scram_verifier(constchar*verifier);
3432
externboolscram_verify_plain_password(constchar*username,
3533
constchar*password,constchar*verifier);

‎src/interfaces/libpq/exports.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,4 @@ PQsslAttributeNames 168
171171
PQsslAttribute 169
172172
PQsetErrorContextVisibility 170
173173
PQresultVerboseErrorMessage 171
174+
PQencryptPasswordConn 172

‎src/interfaces/libpq/fe-auth-scram.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,41 @@ verify_server_signature(fe_scram_state *state)
614614
return true;
615615
}
616616

617+
/*
618+
* Build a new SCRAM verifier.
619+
*/
620+
char*
621+
pg_fe_scram_build_verifier(constchar*password)
622+
{
623+
char*prep_password=NULL;
624+
pg_saslprep_rcrc;
625+
charsaltbuf[SCRAM_DEFAULT_SALT_LEN];
626+
char*result;
627+
628+
/*
629+
* Normalize the password with SASLprep. If that doesn't work, because
630+
* the password isn't valid UTF-8 or contains prohibited characters, just
631+
* proceed with the original password. (See comments at top of file.)
632+
*/
633+
rc=pg_saslprep(password,&prep_password);
634+
if (rc==SASLPREP_OOM)
635+
returnNULL;
636+
if (rc==SASLPREP_SUCCESS)
637+
password= (constchar*)prep_password;
638+
639+
/* Generate a random salt */
640+
if (!pg_frontend_random(saltbuf,SCRAM_DEFAULT_SALT_LEN))
641+
returnNULL;
642+
643+
result=scram_build_verifier(saltbuf,SCRAM_DEFAULT_SALT_LEN,
644+
SCRAM_DEFAULT_ITERATIONS,password);
645+
646+
if (prep_password)
647+
free(prep_password);
648+
649+
returnresult;
650+
}
651+
617652
/*
618653
* Random number generator.
619654
*/

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp