--- src/bin/scripts/pqpasswd.c.orig Tue May 9 22:30:53 2006 +++ src/bin/scripts/pqpasswd.c Tue May 9 22:36:06 2006 @@ -0,0 +1,575 @@ +/*------------------------------------------------------------------------- + * + * pqpasswd + * + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL: pgsql/src/bin/scripts/pqpasswd.c,v 1.00 2006/01/16 02:11:21 h-saito Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" +#include +#include +#include +#include + +#include "common.h" + +#ifndef WIN32 +#define PGPASSFILE ".pgpass" +#else +#define PGPASSFILE "pgpass.conf" +#endif + +#ifndef MD5_PASSWD_LEN +#define MD5_PASSWD_LEN 35 +#endif +#define PGPASS_MD5 ".md5" +bool md5 = FALSE; + +char homedir[MAXPGPATH]; +char pgpassfile[MAXPGPATH]; +char *passfile_env; + +/* setting options */ +#define DefaultHost "localhost" +char *hostname = NULL; +char *port = NULL; +char *dbname = NULL; +char *username = NULL; +char *password = NULL; + +/* pqGetHomeDirectory is libpgport and shared. */ +extern bool +pqGetHomeDirectory(char *buf, int bufsize); +extern bool +pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, char *buf); + +static void help(const char *progname); + +/* + * this actually returns the user's home directory. + */ +static bool +get_homedirectry(char *buf, int bufsize) +{ +#ifdef WIN32 + struct stat stat_buf; +#endif + if (!pqGetHomeDirectory(buf, bufsize)) + return FALSE; +#ifdef WIN32 + /* If home directory */ + if (stat(buf, &stat_buf) == -1) + CreateDirectory(buf, 0); +#endif + return TRUE; +} + +/* + * String sequence interpretation. + */ +static char * +get_pwdstringseq(char *buf) +{ + char *tbuf; + bool bslash = FALSE; + + if (buf == NULL) + return NULL; + tbuf = buf; + if (*tbuf == '*') + return tbuf + 2; + while (*tbuf != 0) + { + if (*tbuf == '\\' && !bslash) + { + tbuf++; + bslash = TRUE; + } + if (*tbuf == ':' && !bslash) + return tbuf + 1; + bslash = FALSE; + tbuf++; + } + return NULL; +} + +/* + * Get a password from the password file. Return is the congruous positions. + * format is "hostname:port:database:username:password" + */ +static int +get_passwordfile(bool listpgpass, char *hostname, char *port, char *dbname, char *username) +{ + FILE *fp; + struct stat stat_buf; + int result_position = 0; + int line_position = 0; + char buf[BUFSIZ]; + + if (stat(pgpassfile, &stat_buf) == -1) + return result_position; + +#ifndef WIN32 + + if (!S_ISREG(stat_buf.st_mode)) + { + fprintf(stderr, _("WARNING: password file \"%s\" is not a plain file\n"), pgpassfile); + free(pgpassfile); + return result_position; + } + + /* If password file is insecure, alert the user and ignore it. */ + if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) + { + fprintf(stderr, _("WARNING: password file \"%s\" has world or group read access; permission should be u=rw (0600)\n"), pgpassfile); + return result_position; + } +#endif + + fp = fopen(pgpassfile, "r"); + if (fp == NULL) + return result_position; + + while (!feof(fp)) + { + char *t, + *ret; + char result[BUFSIZ]; + int len, + pos; + bool cond; + buf[0] = '\0'; + fgets(buf, BUFSIZ - 1, fp); + + t = ret = buf; + + len = strlen(buf); + if (len == 0) + continue; + + line_position++; + cond = TRUE; + + /* Remove trailing newline */ + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + pos = 0; + while((t = get_pwdstringseq(t)) != NULL) + { + strncpy(result,ret,t - ret -1); + result[t - ret -1] = '\0'; + ret = t; + + switch(pos){ + case 0: if (listpgpass) + printf("hostname=%s ",result); + if ((strcmp(hostname, result)) && + (strcmp(result, "*"))) + cond = FALSE; + break; + case 1: if (listpgpass) + printf("port=%s ",result); + if ((strcmp(port, result)) && + (strcmp(result, "*"))) + cond = FALSE; + break; + case 2: if (listpgpass) + printf("dbname=%s ",result); + if ((strcmp(dbname, result)) && + (strcmp(result, "*"))) + cond = FALSE; + break; + case 3: if (listpgpass) + { + printf("username=%s ",result); + printf("password=**********\n"); + } + if ((strcmp(username, result)) && + (strcmp(result, "*"))) + cond = FALSE; + // printf("password=%s\n",t); + break; + default: + break; + } + pos++; + continue; + } + + if ( cond ) + result_position = line_position; + + } + + fclose(fp); + + return result_position; + +} + +/* + * Get the temporary file name. + */ +static char +*get_temporaryfile() +{ + const char *tmpdir; +#ifdef WIN32 + char tmppath[MAXPGPATH]; +#endif + struct timeval curtime; + static char filename[MAXPGPATH]; + +#ifdef WIN32 + if ((tmpdir = getenv("TEMP")) == NULL) + { + GetTempPath(sizeof(tmppath), tmppath); + tmpdir = tmppath; + } +#else + if ((tmpdir = getenv("TMPDIR")) == NULL) + { + if (getuid() == 0) + tmpdir = "/tmp"; + else + tmpdir = "/var/tmp"; + } +#endif + + gettimeofday(&curtime, NULL); + + sprintf(filename, "%s/%08lx%05lx", tmpdir, + (unsigned long)curtime.tv_sec, (unsigned long)curtime.tv_usec); + + return filename; + +} + +/* + * Orignal file move. + */ +static bool +filemove(char *fromfile, char *tofile) +{ + char line[BUFSIZ]; + FILE *fip, + *fop; + + fip = fopen(fromfile, "r"); + if (fip == NULL) + return FALSE; + fop = fopen(tofile, "w"); + if (fop == NULL) + { + fclose(fip); + return FALSE; + } + + while (fgets(line, sizeof(line), fip) != NULL) + { + fputs(line, fop); + } + + fclose(fip); + fclose(fop); + + unlink(fromfile); + + return TRUE; +} + +int +main(int argc, char *argv[]) +{ + static struct option long_options[] = { + {"list", no_argument, NULL, 'l'}, + {"remove", no_argument, NULL, 'r'}, + {"host", required_argument, NULL, 'h'}, + {"port", required_argument, NULL, 'p'}, + {"dbname", required_argument, NULL, 'd'}, + {"username", required_argument, NULL, 'U'}, + {"md5", required_argument, NULL, 'm'}, + {NULL, 0, NULL, 0} + }; + + const char *progname; + int optindex; + int c; + + char *crypt_pwd; + + bool listpgpass = FALSE; + bool removepgpass = FALSE; + int update_position = 0; + char *repassword = NULL; + FILE *fip, + *fop; + char *pwtempfile = NULL; + + progname = get_progname(argv[0]); + set_pglocale_pgservice(argv[0], "pgscripts"); + + handle_help_version_opts(argc, argv, "pqpasswd", help); + + while ((c = getopt_long(argc, argv, "lrh:p:U:d:m", long_options, &optindex)) != -1) + { + switch (c) + { + case 'l': + listpgpass = TRUE; + break; + case 'r': + removepgpass = TRUE; + break; + case 'h': + hostname = optarg; + break; + case 'p': + port = optarg; + break; + case 'U': + username = optarg; + break; + case 'd': + dbname = optarg; + break; + case 'm': + md5 = TRUE; + break; + default: + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); + exit(1); + } + } + + if (argc - optind > 0) + { + if (listpgpass) + dbname = argv[optind++]; + else + { + dbname = argv[optind++]; + if (argc - optind > 0) + dbname = argv[optind++]; + } + } + + if (argc - optind > 0) + { + fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), + progname, argv[optind]); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); + exit(1); + } + + /* get the environment */ + + if (hostname == NULL) + { + if (getenv("PGHOST")) + hostname = getenv("PGHOST"); + else + hostname = DefaultHost; + } + + if (port == NULL) + { + if (getenv("PGPORT")) + port = getenv("PGPORT"); + else + port = DEF_PGPORT_STR; + } + + if (dbname == NULL) + { + if (getenv("PGDATABASE")) + dbname = getenv("PGDATABASE"); + else if (getenv("PGUSER")) + dbname = getenv("PGUSER"); + else + dbname = (char *)get_user_name(progname); + } + + if (username == NULL) + { + if (getenv("PGUSER")) + username = getenv("PGUSER"); + else + username = (char *)get_user_name(progname); + } + + if (password == NULL) + { + if (getenv("PGPASSWORD")) + password = getenv("PGPASSWORD"); + } + + if ((passfile_env = getenv("PGPASSFILE")) != NULL) + /* use the literal path from the environment, if set */ + StrNCpy(pgpassfile, passfile_env, MAXPGPATH); + else + { + + if (!get_homedirectry(homedir, sizeof(homedir))) + { + fprintf(stderr,_("Oops?,home directry access denied.\n")); + exit(1); + } + snprintf(pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE); + if ( md5 ) + strcat(pgpassfile, PGPASS_MD5); + } + + /* + * List option + */ + if (listpgpass) + { + printf("%s\n", pgpassfile); + /* view pgpass */ + printf(_("This %dth line is used.\n"), + get_passwordfile(listpgpass, hostname, port, dbname, username)); + exit(0); + } + else + { + update_position = + get_passwordfile(listpgpass, hostname, port, dbname, username); + } + + /* + * Remove option + */ + if (removepgpass) + { + char *reply; + + reply = simple_prompt("Do you want to remove pgpass? (y/n) ", 1, true); + if (check_yesno_response(reply) == 1) + { + unlink(pgpassfile); + printf(_("Succeeded.\n")); + exit(0); + } + else + { + printf(_("Canceled.\n")); + exit(1); + } + } + + password = simple_prompt("New Password: ", 100, false); + repassword = simple_prompt("Retype New Password: ", 100, false); + + if (strcmp(password, repassword)) + { + fprintf(stderr, _("Passwords do not match!\n")); + exit(1); + } + + /* new md5 */ + if ( md5 ) + { + crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); + if (!crypt_pwd) + { + fprintf(stderr, _("out of memory\n")); + exit(1); + } + if (!pg_md5_encrypt(password, username, strlen(username), crypt_pwd)) + { + free(crypt_pwd); + fprintf(stderr, _("passwords encription error\n")); + exit(1); + } + password = crypt_pwd; + } + /* write the temp directory password */ + pwtempfile = get_temporaryfile(); + fop = fopen(pwtempfile, "w"); + + if (fop == NULL) + { + fprintf(stderr,_("tempfile(%s) create error!\n"), pwtempfile); + exit(1); + } + + fip = fopen(pgpassfile, "r"); + + if (fip != NULL) + { + int rec_count = 0; + while (!feof(fip)) + { + char buf[BUFSIZ]; + buf[0] = '\0'; + + fgets(buf, BUFSIZ - 1, fip); + if (strlen(buf)== 0) + break; + rec_count++; + + if (update_position == rec_count) + fprintf(fop, "%s:%s:%s:%s:%s\n", + hostname, port, dbname, username, password); + else + fputs(buf, fop); + } + + fclose(fip); + } + + if (update_position == 0) + fprintf(fop, "%s:%s:%s:%s:%s\n", + hostname, port, dbname, username, password); + + fclose(fop); + + unlink(pgpassfile); + +#ifdef DEBUG + fprintf(stderr,"%s\n",pwtempfile); +#endif + if (!filemove(pwtempfile, pgpassfile)) + { + fprintf(stderr,_("pgpass work error keep the tempfile(%s)!\n"), pwtempfile); + exit(1); + } + +#ifndef WIN32 + chmod(pgpassfile, 0600); +#endif + if (getenv("PGPASSWORD")) + { + fprintf(stderr,_("Notice:You specify the password by the environment variable.\n")); + fprintf(stderr,_(" It may become the connection demand which is not meant.\n")); + fprintf(stderr,_(" Please check environment PGPASSWORD.\n")); + } + + printf(_("Succeeded in creation.\n")); + + exit(0); +} + +static void +help(const char *progname) +{ + printf(_("%s installs a pgpass(libpq) connect a PostgreSQL database.\n\n"), progname); + printf(_("Usage:\n")); + printf(_(" %s [OPTION]... [DBNAME]\n"), progname); + printf(_("\nOptions:\n")); + printf(_(" -l, --list show a list of installed pgpass\n")); + printf(_(" -r, --remove remove the my pgpass\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); + printf(_(" -p, --port=PORT database server port\n")); + printf(_(" -d, --dbname=DBNAME database to connect as\n")); + printf(_(" -U, --username=USERNAME user name to connect as\n")); + printf(_(" -m, --md5 password is enciphered by md5\n")); + printf(_(" --help show this help, then exit\n")); + printf(_(" --version output version information, then exit\n")); + printf(_("\nReport bugs to .\n")); +} --- src/bin/scripts/Makefile.orig Tue May 9 22:36:34 2006 +++ src/bin/scripts/Makefile Tue May 9 22:39:49 2006 @@ -14,7 +14,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -PROGRAMS = createdb createlang createuser dropdb droplang dropuser clusterdb vacuumdb reindexdb +PROGRAMS = createdb createlang createuser dropdb droplang dropuser clusterdb vacuumdb reindexdb pqpasswd override CPPFLAGS := -DFRONTEND -I$(top_srcdir)/src/bin/pg_dump -I$(top_srcdir)/src/bin/psql -I$(libpq_srcdir) $(CPPFLAGS) @@ -32,6 +32,7 @@ clusterdb: clusterdb.o common.o dumputils.o $(top_builddir)/src/backend/parser/keywords.o vacuumdb: vacuumdb.o common.o reindexdb: reindexdb.o common.o dumputils.o $(top_builddir)/src/backend/parser/keywords.o +pqpasswd: pqpasswd.o common.o dumputils.c: % : $(top_srcdir)/src/bin/pg_dump/% rm -f $@ && $(LN_S) $< . @@ -54,6 +55,7 @@ $(INSTALL_PROGRAM) clusterdb$(X) '$(DESTDIR)$(bindir)'/clusterdb$(X) $(INSTALL_PROGRAM) vacuumdb$(X) '$(DESTDIR)$(bindir)'/vacuumdb$(X) $(INSTALL_PROGRAM) reindexdb$(X) '$(DESTDIR)$(bindir)'/reindexdb$(X) + $(INSTALL_PROGRAM) pqpasswd$(X) '$(DESTDIR)$(bindir)'/pqpasswd$(X) installdirs: $(mkinstalldirs) '$(DESTDIR)$(bindir)' --- src/interfaces/libpq/fe-auth.c.orig Tue May 9 22:41:30 2006 +++ src/interfaces/libpq/fe-auth.c Tue May 9 22:53:49 2006 @@ -399,11 +399,19 @@ } crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1; - if (!pg_md5_encrypt(password, conn->pguser, - strlen(conn->pguser), crypt_pwd2)) + /* new encryption correspondence of .pgpass.md5 */ + if (!strncmp(conn->pgpass, "md5",3) && (strlen(conn->pgpass) == MD5_PASSWD_LEN ) ) + { + StrNCpy(crypt_pwd2, conn->pgpass, strlen(conn->pgpass) + 1); + } + else { - free(crypt_pwd); - return STATUS_ERROR; + if (!pg_md5_encrypt(password, conn->pguser, + strlen(conn->pguser), crypt_pwd2)) + { + free(crypt_pwd); + return STATUS_ERROR; + } } if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt, sizeof(conn->md5Salt), crypt_pwd)) --- src/interfaces/libpq/fe-connect.c.orig Tue May 9 22:46:54 2006 +++ src/interfaces/libpq/fe-connect.c Tue May 9 22:49:31 2006 @@ -76,6 +76,8 @@ #define PGPASSFILE "pgpass.conf" #endif +#define PGPASS_MD5 ".md5" + /* fall back options if they are not specified by arguments or defined by environment variables */ #define DefaultHost "localhost" @@ -3126,7 +3128,11 @@ /* If password file cannot be opened, ignore it. */ if (stat(pgpassfile, &stat_buf) == -1) - return NULL; + { + strcat(pgpassfile, PGPASS_MD5); + if (stat(pgpassfile, &stat_buf) == -1) + return NULL; + } #ifndef WIN32