From b16566d77168540730d7ca26f8fe1832f15d450e Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Sun, 18 Dec 2005 02:17:16 +0000 Subject: [PATCH] Add new psql command \password for changing role password with client-side password encryption. Also alter createuser command to the same effect. --- doc/src/sgml/ref/alter_role.sgml | 12 ++++- doc/src/sgml/ref/create_role.sgml | 14 ++++- doc/src/sgml/ref/psql-ref.sgml | 15 +++++- src/bin/psql/Makefile | 21 +++++--- src/bin/psql/command.c | 87 +++++++++++++++++++++++++------ src/bin/psql/command.h | 14 ++--- src/bin/psql/mainloop.c | 22 ++++---- src/bin/psql/startup.c | 4 +- src/bin/psql/tab-complete.c | 6 ++- src/bin/scripts/createuser.c | 18 ++++++- 10 files changed, 163 insertions(+), 50 deletions(-) diff --git a/doc/src/sgml/ref/alter_role.sgml b/doc/src/sgml/ref/alter_role.sgml index 7ebd8014f2..641c4ef377 100644 --- a/doc/src/sgml/ref/alter_role.sgml +++ b/doc/src/sgml/ref/alter_role.sgml @@ -1,5 +1,5 @@ @@ -182,6 +182,16 @@ ALTER ROLE name RESET to do that. + + Caution must be exercised when specifying an unencrypted password + with this command. The password will be transmitted to the server + in cleartext, and it might also be logged in the client's command + history or the server log. contains a command + \password that can be used to safely change a + role's password. + + It is also possible to tie a session default to a specific database rather than to a role; see diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml index 16cd16ef5a..9af33ce212 100644 --- a/doc/src/sgml/ref/create_role.sgml +++ b/doc/src/sgml/ref/create_role.sgml @@ -1,5 +1,5 @@ @@ -357,6 +357,18 @@ where option can be: connection slot remains for the role, it is possible that both will fail. Also, the limit is never enforced for superusers. + + + Caution must be exercised when specifying an unencrypted password + with this command. The password will be transmitted to the server + in cleartext, and it might also be logged in the client's command + history or the server log. The command , however, transmits + the password encrypted. Also, contains a command + \password that can be used to safely change the + password later. + diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 3d6d0a1d7f..c0c075f11e 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1,5 +1,5 @@ @@ -1379,6 +1379,19 @@ lo_import 152801 + + \password [ username ] + + + Changes the password of the specified user or by default the + current user. This command prompts for the new password, + encrypts it, and sends it to the server. This makes sure that + the new password does not appear in the command history, the + server log, or elsewhere in cleartext. + + + + \pset parameter [ value ] diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile index 0499d726a9..0c68922936 100644 --- a/src/bin/psql/Makefile +++ b/src/bin/psql/Makefile @@ -5,7 +5,7 @@ # Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $PostgreSQL: pgsql/src/bin/psql/Makefile,v 1.55 2005/12/09 21:19:35 petere Exp $ +# $PostgreSQL: pgsql/src/bin/psql/Makefile,v 1.56 2005/12/18 02:17:16 petere Exp $ # #------------------------------------------------------------------------- @@ -17,22 +17,27 @@ include $(top_builddir)/src/Makefile.global REFDOCDIR= $(top_srcdir)/doc/src/sgml/ref -override CPPFLAGS := -DFRONTEND -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS) +override CPPFLAGS := -DFRONTEND -I$(srcdir) -I$(libpq_srcdir) -I$(top_srcdir)/src/bin/pg_dump $(CPPFLAGS) OBJS= command.o common.o help.o input.o stringutils.o mainloop.o copy.o \ startup.o prompt.o variables.o large_obj.o print.o describe.o \ - psqlscan.o tab-complete.o mbprint.o $(WIN32RES) + psqlscan.o tab-complete.o mbprint.o dumputils.o $(WIN32RES) + +EXTRA_OBJS = $(top_builddir)/src/backend/parser/keywords.o FLEXFLAGS = -Cfe -all: submake-libpq submake-libpgport psql +all: submake-libpq submake-libpgport submake-backend psql psql: $(OBJS) $(libpq_builddir)/libpq.a - $(CC) $(CFLAGS) $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LIBS) -o $@$(X) + $(CC) $(CFLAGS) $(OBJS) $(EXTRA_OBJS) $(libpq_pgport) $(LDFLAGS) $(LIBS) -o $@$(X) help.o: $(srcdir)/sql_help.h +dumputils.c: % : $(top_srcdir)/src/bin/pg_dump/% + rm -f $@ && $(LN_S) $< . + ifdef PERL $(srcdir)/sql_help.h: create_help.pl $(wildcard $(REFDOCDIR)/*.sgml) $(PERL) $< $(REFDOCDIR) $@ @@ -48,6 +53,10 @@ else @$(missing) flex $< $@ endif +.PHONY: submake-backend +submake-backend: + $(MAKE) -C $(top_builddir)/src/backend/parser keywords.o + distprep: $(srcdir)/sql_help.h $(srcdir)/psqlscan.c install: all installdirs @@ -62,7 +71,7 @@ uninstall: # psqlscan.c is in the distribution tarball, so is not cleaned here clean distclean: - rm -f psql$(X) $(OBJS) + rm -f psql$(X) $(OBJS) dumputils.c maintainer-clean: distclean rm -f $(srcdir)/sql_help.h $(srcdir)/psqlscan.c diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index fbff209262..35da9cdf82 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.155 2005/12/08 21:18:22 petere Exp $ + * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.156 2005/12/18 02:17:16 petere Exp $ */ #include "postgres_fe.h" #include "command.h" @@ -35,6 +35,8 @@ #include "libpq-fe.h" #include "pqexpbuffer.h" +#include "libpq/crypt.h" +#include "dumputils.h" #include "common.h" #include "copy.h" @@ -81,7 +83,7 @@ backslashResult HandleSlashCmds(PsqlScanState scan_state, PQExpBuffer query_buf) { - backslashResult status = CMD_SKIP_LINE; + backslashResult status = PSQL_CMD_SKIP_LINE; char *cmd; char *arg; @@ -93,7 +95,7 @@ HandleSlashCmds(PsqlScanState scan_state, /* And try to execute it */ status = exec_command(cmd, scan_state, query_buf); - if (status == CMD_UNKNOWN && strlen(cmd) > 1) + if (status == PSQL_CMD_UNKNOWN && strlen(cmd) > 1) { /* * If the command was not recognized, try to parse it as a one-letter @@ -110,23 +112,23 @@ HandleSlashCmds(PsqlScanState scan_state, status = exec_command(new_cmd, scan_state, query_buf); - if (status != CMD_UNKNOWN) + if (status != PSQL_CMD_UNKNOWN) { /* adjust cmd for possible messages below */ cmd[1] = '\0'; } } - if (status == CMD_UNKNOWN) + if (status == PSQL_CMD_UNKNOWN) { if (pset.cur_cmd_interactive) fprintf(stderr, _("Invalid command \\%s. Try \\? for help.\n"), cmd); else psql_error("invalid command \\%s\n", cmd); - status = CMD_ERROR; + status = PSQL_CMD_ERROR; } - if (status != CMD_ERROR) + if (status != PSQL_CMD_ERROR) { /* eat any remaining arguments after a valid command */ /* note we suppress evaluation of backticks here */ @@ -164,7 +166,7 @@ exec_command(const char *cmd, bool success = true; /* indicate here if the command ran ok or * failed */ bool quiet = QUIET(); - backslashResult status = CMD_SKIP_LINE; + backslashResult status = PSQL_CMD_SKIP_LINE; /* * \a -- toggle field alignment This makes little sense but we keep it @@ -368,7 +370,7 @@ exec_command(const char *cmd, break; default: - status = CMD_UNKNOWN; + status = PSQL_CMD_UNKNOWN; } if (pattern) @@ -387,7 +389,7 @@ exec_command(const char *cmd, if (!query_buf) { psql_error("no query buffer\n"); - status = CMD_ERROR; + status = PSQL_CMD_ERROR; } else { @@ -396,7 +398,7 @@ exec_command(const char *cmd, expand_tilde(&fname); if (fname) canonicalize_path(fname); - status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR; + status = do_edit(fname, query_buf) ? PSQL_CMD_NEWEDIT : PSQL_CMD_ERROR; free(fname); } } @@ -486,7 +488,7 @@ exec_command(const char *cmd, pset.gfname = pg_strdup(fname); } free(fname); - status = CMD_SEND; + status = PSQL_CMD_SEND; } /* help */ @@ -590,7 +592,7 @@ exec_command(const char *cmd, } else - status = CMD_UNKNOWN; + status = PSQL_CMD_UNKNOWN; free(opt1); free(opt2); @@ -618,6 +620,57 @@ exec_command(const char *cmd, fflush(stdout); } + /* \password -- set user password */ + else if (strcmp(cmd, "password") == 0) + { + char *pw1; + char *pw2; + + pw1 = simple_prompt("Enter new password: ", 100, false); + pw2 = simple_prompt("Enter it again: ", 100, false); + + if (strcmp(pw1, pw2) != 0) + { + fprintf(stderr, _("Passwords didn't match.\n")); + success = false; + } + else + { + char *opt0 = psql_scan_slash_option(scan_state, OT_SQLID, NULL, true); + char *user; + char encrypted_password[MD5_PASSWD_LEN + 1]; + + if (opt0) + user = opt0; + else + user = PQuser(pset.db); + + if (!pg_md5_encrypt(pw1, user, strlen(user), encrypted_password)) + { + fprintf(stderr, _("Password encryption failed.\n")); + success = false; + } + else + { + PQExpBufferData buf; + PGresult *res; + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, "ALTER ROLE %s PASSWORD '%s';", + fmtId(user), encrypted_password); + res = PSQLexec(buf.data, false); + termPQExpBuffer(&buf); + if (!res) + success = false; + else + PQclear(res); + } + } + + free(pw1); + free(pw2); + } + /* \pset -- set printing parameters */ else if (strcmp(cmd, "pset") == 0) { @@ -640,7 +693,7 @@ exec_command(const char *cmd, /* \q or \quit */ else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0) - status = CMD_TERMINATE; + status = PSQL_CMD_TERMINATE; /* reset(clear) the buffer */ else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0) @@ -780,7 +833,7 @@ exec_command(const char *cmd, if (!query_buf) { psql_error("no query buffer\n"); - status = CMD_ERROR; + status = PSQL_CMD_ERROR; } else { @@ -884,10 +937,10 @@ exec_command(const char *cmd, #endif else - status = CMD_UNKNOWN; + status = PSQL_CMD_UNKNOWN; if (!success) - status = CMD_ERROR; + status = PSQL_CMD_ERROR; return status; } diff --git a/src/bin/psql/command.h b/src/bin/psql/command.h index ffb47f8520..0d969e015b 100644 --- a/src/bin/psql/command.h +++ b/src/bin/psql/command.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/command.h,v 1.22 2005/01/01 05:43:08 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/command.h,v 1.23 2005/12/18 02:17:16 petere Exp $ */ #ifndef COMMAND_H #define COMMAND_H @@ -15,12 +15,12 @@ typedef enum _backslashResult { - CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */ - CMD_SEND, /* query complete; send off */ - CMD_SKIP_LINE, /* keep building query */ - CMD_TERMINATE, /* quit program */ - CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */ - CMD_ERROR /* the execution of the backslash command + PSQL_CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */ + PSQL_CMD_SEND, /* query complete; send off */ + PSQL_CMD_SKIP_LINE, /* keep building query */ + PSQL_CMD_TERMINATE, /* quit program */ + PSQL_CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */ + PSQL_CMD_ERROR /* the execution of the backslash command * resulted in an error */ } backslashResult; diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index 775701a010..cebeda70c0 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.68 2005/10/15 02:49:40 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.69 2005/12/18 02:17:16 petere Exp $ */ #include "postgres_fe.h" #include "mainloop.h" @@ -41,7 +41,7 @@ MainLoop(FILE *source) int added_nl_pos; bool success; volatile int successResult = EXIT_SUCCESS; - volatile backslashResult slashCmdStatus = CMD_UNKNOWN; + volatile backslashResult slashCmdStatus = PSQL_CMD_UNKNOWN; volatile promptStatus_t prompt_status = PROMPT_READY; volatile int count_eof = 0; volatile bool die_on_error = false; @@ -104,7 +104,7 @@ MainLoop(FILE *source) psql_scan_finish(scan_state); psql_scan_reset(scan_state); count_eof = 0; - slashCmdStatus = CMD_UNKNOWN; + slashCmdStatus = PSQL_CMD_UNKNOWN; prompt_status = PROMPT_READY; if (pset.cur_cmd_interactive) @@ -126,7 +126,7 @@ MainLoop(FILE *source) fflush(stdout); - if (slashCmdStatus == CMD_NEWEDIT) + if (slashCmdStatus == PSQL_CMD_NEWEDIT) { /* * just returned from editing the line? then just copy to the @@ -136,7 +136,7 @@ MainLoop(FILE *source) /* reset parsing state since we are rescanning whole line */ resetPQExpBuffer(query_buf); psql_scan_reset(scan_state); - slashCmdStatus = CMD_UNKNOWN; + slashCmdStatus = PSQL_CMD_UNKNOWN; prompt_status = PROMPT_READY; } @@ -231,7 +231,7 @@ MainLoop(FILE *source) { /* execute query */ success = SendQuery(query_buf->data); - slashCmdStatus = success ? CMD_SEND : CMD_ERROR; + slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; resetPQExpBuffer(previous_buf); appendPQExpBufferStr(previous_buf, query_buf->data); @@ -257,16 +257,16 @@ MainLoop(FILE *source) query_buf->len > 0 ? query_buf : previous_buf); - success = slashCmdStatus != CMD_ERROR; + success = slashCmdStatus != PSQL_CMD_ERROR; - if ((slashCmdStatus == CMD_SEND || slashCmdStatus == CMD_NEWEDIT) && + if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) && query_buf->len == 0) { /* copy previous buffer to current for handling */ appendPQExpBufferStr(query_buf, previous_buf->data); } - if (slashCmdStatus == CMD_SEND) + if (slashCmdStatus == PSQL_CMD_SEND) { success = SendQuery(query_buf->data); @@ -278,7 +278,7 @@ MainLoop(FILE *source) psql_scan_reset(scan_state); } - if (slashCmdStatus == CMD_TERMINATE) + if (slashCmdStatus == PSQL_CMD_TERMINATE) break; } @@ -291,7 +291,7 @@ MainLoop(FILE *source) psql_scan_finish(scan_state); free(line); - if (slashCmdStatus == CMD_TERMINATE) + if (slashCmdStatus == PSQL_CMD_TERMINATE) { successResult = EXIT_SUCCESS; break; diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 8aa2b626b3..ee70c7b379 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.128 2005/11/22 18:17:29 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.129 2005/12/18 02:17:16 petere Exp $ */ #include "postgres_fe.h" @@ -286,7 +286,7 @@ main(int argc, char *argv[]) options.action_string, strlen(options.action_string)); - successResult = HandleSlashCmds(scan_state, NULL) != CMD_ERROR + successResult = HandleSlashCmds(scan_state, NULL) != PSQL_CMD_ERROR ? EXIT_SUCCESS : EXIT_FAILURE; psql_scan_destroy(scan_state); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index f8629257f7..a6b230e32b 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.142 2005/12/08 21:33:58 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.143 2005/12/18 02:17:16 petere Exp $ */ /*---------------------------------------------------------------------- @@ -536,7 +536,7 @@ psql_completion(char *text, int start, int end) "\\e", "\\echo", "\\encoding", "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", - "\\o", "\\p", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T", + "\\o", "\\p", "\\password", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T", "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL }; @@ -1808,6 +1808,8 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_QUERY(Query_for_list_of_encodings); else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0) COMPLETE_WITH_LIST(sql_commands); + else if (strcmp(prev_wd, "\\password") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); else if (strcmp(prev_wd, "\\pset") == 0) { static const char *const my_list[] = diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c index bb68775fe9..adf9c41b3a 100644 --- a/src/bin/scripts/createuser.c +++ b/src/bin/scripts/createuser.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/scripts/createuser.c,v 1.23 2005/12/12 15:48:04 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/scripts/createuser.c,v 1.24 2005/12/18 02:17:16 petere Exp $ * *------------------------------------------------------------------------- */ @@ -13,6 +13,7 @@ #include "postgres_fe.h" #include "common.h" #include "dumputils.h" +#include "libpq/crypt.h" static void help(const char *progname); @@ -246,7 +247,20 @@ main(int argc, char *argv[]) if (encrypted == TRI_NO) appendPQExpBuffer(&sql, " UNENCRYPTED"); appendPQExpBuffer(&sql, " PASSWORD "); - appendStringLiteral(&sql, newpassword, false); + + if (encrypted != TRI_NO) + { + char encrypted_password[MD5_PASSWD_LEN + 1]; + + if (!pg_md5_encrypt(newpassword, newuser, strlen(newuser), encrypted_password)) + { + fprintf(stderr, _("Password encryption failed.\n")); + exit(1); + } + appendStringLiteral(&sql, encrypted_password, false); + } + else + appendStringLiteral(&sql, newpassword, false); } if (superuser == TRI_YES) appendPQExpBuffer(&sql, " SUPERUSER"); -- 2.40.0