diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index ed88ac001a..21195e0e72 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -7116,6 +7116,45 @@ char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, + + PQchangePasswordPQchangePassword + + + + Changes a PostgreSQL password. + +PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd); + + This function uses PQencryptPasswordConn + to build and execute the command ALTER USER ... PASSWORD + '...', thereby changing the user's password. It exists for + the same reason as PQencryptPasswordConn, but + is more convenient as it both builds and runs the command for you. + is passed a + NULL for the algorithm argument, hence encryption is + done according to the server's + setting. + + + + The user and passwd arguments + are the SQL name of the target user, and the new cleartext password. + + + + Returns a PGresult pointer representing + the result of the ALTER USER command, or + a null pointer if the routine failed before issuing any command. + The function should be called + to check the return value for any errors (including the value of a null + pointer, in which case it will return + PGRES_FATAL_ERROR). Use + to get more information about + such errors. + + + + PQencryptPasswordPQencryptPassword diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 9103bc3465..eb216b7c09 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -2158,29 +2158,15 @@ exec_command_password(PsqlScanState scan_state, bool active_branch) } else { - char *encrypted_password; + PGresult *res = PQchangePassword(pset.db, user, pw1); - encrypted_password = PQencryptPasswordConn(pset.db, pw1, user, NULL); - - if (!encrypted_password) + if (PQresultStatus(res) != PGRES_COMMAND_OK) { pg_log_info("%s", PQerrorMessage(pset.db)); success = false; } - else - { - PGresult *res; - printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ", - fmtId(user)); - appendStringLiteralConn(&buf, encrypted_password, pset.db); - res = PSQLexec(buf.data); - if (!res) - success = false; - else - PQclear(res); - PQfreemem(encrypted_password); - } + PQclear(res); } free(user); diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index 850734ac96..28b861fd93 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -191,3 +191,4 @@ PQclosePrepared 188 PQclosePortal 189 PQsendClosePrepared 190 PQsendClosePortal 191 +PQchangePassword 192 diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index da5ccd324d..1a8e4f6fbf 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -1372,3 +1372,84 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, return crypt_pwd; } + +/* + * PQchangePassword -- exported routine to change a password + * + * This is intended to be used by client applications that wish to + * change the password for a user. The password is not sent in + * cleartext because it is encrypted 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. The password encryption is performed by PQencryptPasswordConn(), + * which is passed a NULL for the algorithm argument. Hence encryption + * is done according to the server's password_encryption + * setting. We export the function so that clients won't be dependent + * on the implementation specific details with respect to how the + * server changes passwords. + * + * Arguments are a connection object, the SQL name of the target user, + * and the cleartext password. + * + * Return value is the PGresult of the executed ALTER USER statement + * or NULL if we never get there. The caller is responsible to PQclear() + * the returned PGresult. + * + * PQresultStatus() should be called to check the return value for errors, + * and PQerrorMessage() used to get more information about such errors. + */ +PGresult * +PQchangePassword(PGconn *conn, const char *user, const char *passwd) +{ + char *encrypted_password = PQencryptPasswordConn(conn, passwd, + user, NULL); + + if (!encrypted_password) + { + /* PQencryptPasswordConn() already registered the error */ + return NULL; + } + else + { + char *fmtpw = PQescapeLiteral(conn, encrypted_password, + strlen(encrypted_password)); + + /* no longer needed, so clean up now */ + PQfreemem(encrypted_password); + + if (!fmtpw) + { + /* PQescapeLiteral() already registered the error */ + return NULL; + } + else + { + char *fmtuser = PQescapeIdentifier(conn, user, strlen(user)); + + if (!fmtuser) + { + /* PQescapeIdentifier() already registered the error */ + PQfreemem(fmtpw); + return NULL; + } + else + { + PQExpBufferData buf; + PGresult *res; + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD %s", + fmtuser, fmtpw); + + res = PQexec(conn, buf.data); + + /* clean up */ + termPQExpBuffer(&buf); + PQfreemem(fmtuser); + PQfreemem(fmtpw); + + return res; + } + } + } +} diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 12eb72c1fe..f0ec660cb6 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -659,6 +659,7 @@ extern int PQenv2encoding(void); extern char *PQencryptPassword(const char *passwd, const char *user); extern char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm); +extern PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd); /* === in encnames.c === */