Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a7be2a6

Browse files
committed
Add new function, PQchangePassword(), to libpq
Essentially this moves the non-interactive part of psql's "\password" command into an exported client function. The password is not sent to the server in cleartext because it is "encrypted" (in the case of scram and md5 it is actually hashed, but we have called these encrypted passwords for a long time now) on the client side. This is good because it ensures the cleartext password is never known by the server, and therefore won't end up in logs, pg_stat displays, etc. In other words, it exists for the same reason as PQencryptPasswordConn(), but is more convenient as it both builds and runs the "ALTER USER" command for you. PQchangePassword() uses PQencryptPasswordConn() to do the password encryption. PQencryptPasswordConn() is passed a NULL for the algorithm argument, hence encryption is done according to the server's password_encryption setting. Also modify the psql client to use the new function. That provides a builtin test case. Ultimately drivers built on top of libpq should expose this function and its use should be generally encouraged over doing ALTER USER directly for password changes. Author: Joe Conway Reviewed-by: Tom Lane Discussion: https://postgr.es/m/flat/b75955f7-e8cc-4bbd-817f-ef536bacbe93%40joeconway.com
1 parent d596736 commit a7be2a6

File tree

5 files changed

+125
-17
lines changed

5 files changed

+125
-17
lines changed

doc/src/sgml/libpq.sgml

+39
Original file line numberDiff line numberDiff line change
@@ -7116,6 +7116,45 @@ char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
71167116
</listitem>
71177117
</varlistentry>
71187118

7119+
<varlistentry id="libpq-PQchangePassword">
7120+
<term><function>PQchangePassword</function><indexterm><primary>PQchangePassword</primary></indexterm></term>
7121+
7122+
<listitem>
7123+
<para>
7124+
Changes a <productname>PostgreSQL</productname> password.
7125+
<synopsis>
7126+
PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd);
7127+
</synopsis>
7128+
This function uses <function>PQencryptPasswordConn</function>
7129+
to build and execute the command <literal>ALTER USER ... PASSWORD
7130+
'...'</literal>, thereby changing the user's password. It exists for
7131+
the same reason as <function>PQencryptPasswordConn</function>, but
7132+
is more convenient as it both builds and runs the command for you.
7133+
<xref linkend="libpq-PQencryptPasswordConn"/> is passed a
7134+
<symbol>NULL</symbol> for the algorithm argument, hence encryption is
7135+
done according to the server's <xref linkend="guc-password-encryption"/>
7136+
setting.
7137+
</para>
7138+
7139+
<para>
7140+
The <parameter>user</parameter> and <parameter>passwd</parameter> arguments
7141+
are the SQL name of the target user, and the new cleartext password.
7142+
</para>
7143+
7144+
<para>
7145+
Returns a <structname>PGresult</structname> pointer representing
7146+
the result of the <literal>ALTER USER</literal> command, or
7147+
a null pointer if the routine failed before issuing any command.
7148+
The <xref linkend="libpq-PQresultStatus"/> function should be called
7149+
to check the return value for any errors (including the value of a null
7150+
pointer, in which case it will return
7151+
<symbol>PGRES_FATAL_ERROR</symbol>). Use
7152+
<xref linkend="libpq-PQerrorMessage"/> to get more information about
7153+
such errors.
7154+
</para>
7155+
</listitem>
7156+
</varlistentry>
7157+
71197158
<varlistentry id="libpq-PQencryptPassword">
71207159
<term><function>PQencryptPassword</function><indexterm><primary>PQencryptPassword</primary></indexterm></term>
71217160

src/bin/psql/command.c

+3-17
Original file line numberDiff line numberDiff line change
@@ -2158,29 +2158,15 @@ exec_command_password(PsqlScanState scan_state, bool active_branch)
21582158
}
21592159
else
21602160
{
2161-
char *encrypted_password;
2161+
PGresult *res = PQchangePassword(pset.db, user, pw1);
21622162

2163-
encrypted_password = PQencryptPasswordConn(pset.db, pw1, user, NULL);
2164-
2165-
if (!encrypted_password)
2163+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
21662164
{
21672165
pg_log_info("%s", PQerrorMessage(pset.db));
21682166
success = false;
21692167
}
2170-
else
2171-
{
2172-
PGresult *res;
21732168

2174-
printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ",
2175-
fmtId(user));
2176-
appendStringLiteralConn(&buf, encrypted_password, pset.db);
2177-
res = PSQLexec(buf.data);
2178-
if (!res)
2179-
success = false;
2180-
else
2181-
PQclear(res);
2182-
PQfreemem(encrypted_password);
2183-
}
2169+
PQclear(res);
21842170
}
21852171

21862172
free(user);

src/interfaces/libpq/exports.txt

+1
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,4 @@ PQclosePrepared 188
191191
PQclosePortal 189
192192
PQsendClosePrepared 190
193193
PQsendClosePortal 191
194+
PQchangePassword 192

src/interfaces/libpq/fe-auth.c

+81
Original file line numberDiff line numberDiff line change
@@ -1372,3 +1372,84 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
13721372

13731373
return crypt_pwd;
13741374
}
1375+
1376+
/*
1377+
* PQchangePassword -- exported routine to change a password
1378+
*
1379+
* This is intended to be used by client applications that wish to
1380+
* change the password for a user. The password is not sent in
1381+
* cleartext because it is encrypted on the client side. This is
1382+
* good because it ensures the cleartext password is never known by
1383+
* the server, and therefore won't end up in logs, pg_stat displays,
1384+
* etc. The password encryption is performed by PQencryptPasswordConn(),
1385+
* which is passed a NULL for the algorithm argument. Hence encryption
1386+
* is done according to the server's password_encryption
1387+
* setting. We export the function so that clients won't be dependent
1388+
* on the implementation specific details with respect to how the
1389+
* server changes passwords.
1390+
*
1391+
* Arguments are a connection object, the SQL name of the target user,
1392+
* and the cleartext password.
1393+
*
1394+
* Return value is the PGresult of the executed ALTER USER statement
1395+
* or NULL if we never get there. The caller is responsible to PQclear()
1396+
* the returned PGresult.
1397+
*
1398+
* PQresultStatus() should be called to check the return value for errors,
1399+
* and PQerrorMessage() used to get more information about such errors.
1400+
*/
1401+
PGresult *
1402+
PQchangePassword(PGconn *conn, const char *user, const char *passwd)
1403+
{
1404+
char *encrypted_password = PQencryptPasswordConn(conn, passwd,
1405+
user, NULL);
1406+
1407+
if (!encrypted_password)
1408+
{
1409+
/* PQencryptPasswordConn() already registered the error */
1410+
return NULL;
1411+
}
1412+
else
1413+
{
1414+
char *fmtpw = PQescapeLiteral(conn, encrypted_password,
1415+
strlen(encrypted_password));
1416+
1417+
/* no longer needed, so clean up now */
1418+
PQfreemem(encrypted_password);
1419+
1420+
if (!fmtpw)
1421+
{
1422+
/* PQescapeLiteral() already registered the error */
1423+
return NULL;
1424+
}
1425+
else
1426+
{
1427+
char *fmtuser = PQescapeIdentifier(conn, user, strlen(user));
1428+
1429+
if (!fmtuser)
1430+
{
1431+
/* PQescapeIdentifier() already registered the error */
1432+
PQfreemem(fmtpw);
1433+
return NULL;
1434+
}
1435+
else
1436+
{
1437+
PQExpBufferData buf;
1438+
PGresult *res;
1439+
1440+
initPQExpBuffer(&buf);
1441+
printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD %s",
1442+
fmtuser, fmtpw);
1443+
1444+
res = PQexec(conn, buf.data);
1445+
1446+
/* clean up */
1447+
termPQExpBuffer(&buf);
1448+
PQfreemem(fmtuser);
1449+
PQfreemem(fmtpw);
1450+
1451+
return res;
1452+
}
1453+
}
1454+
}
1455+
}

src/interfaces/libpq/libpq-fe.h

+1
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,7 @@ extern int PQenv2encoding(void);
659659

660660
extern char *PQencryptPassword(const char *passwd, const char *user);
661661
extern char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm);
662+
extern PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd);
662663

663664
/* === in encnames.c === */
664665

0 commit comments

Comments
 (0)