mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
Remove arbitrary restrictions on password length.
This patch started out with the goal of harmonizing various arbitrary limits on password length, but after awhile a better idea emerged: let's just get rid of those fixed limits. recv_password_packet() has an arbitrary limit on the packet size, which we don't really need, so just drop it. (Note that this doesn't really affect anything for MD5 or SCRAM password verification, since those will hash the user's password to something shorter anyway. It does matter for auth methods that require a cleartext password.) Likewise remove the arbitrary error condition in pg_saslprep(). The remaining limits are mostly in client-side code that prompts for passwords. To improve those, refactor simple_prompt() so that it allocates its own result buffer that can be made as big as necessary. Actually, it proves best to make a separate routine pg_get_line() that has essentially the semantics of fgets(), except that it allocates a suitable result buffer and hence will never return a truncated line. (pg_get_line has a lot of potential applications to replace randomly-sized fgets buffers elsewhere, but I'll leave that for another patch.) I built pg_get_line() atop stringinfo.c, which requires moving that code to src/common/; but that seems fine since it was a poor fit for src/port/ anyway. This patch is mostly mine, but it owes a good deal to Nathan Bossart who pressed for a solution to the password length problem and created a predecessor patch. Also thanks to Peter Eisentraut and Stephen Frost for ideas and discussion. Discussion: https://postgr.es/m/09512C4F-8CB9-4021-B455-EF4C4F0D55A0@amazon.com
This commit is contained in:
@@ -63,6 +63,7 @@ OBJS_COMMON = \
|
||||
kwlookup.o \
|
||||
link-canary.o \
|
||||
md5.o \
|
||||
pg_get_line.o \
|
||||
pg_lzcompress.o \
|
||||
pgfnames.o \
|
||||
psprintf.o \
|
||||
@@ -92,7 +93,8 @@ OBJS_FRONTEND = \
|
||||
fe_memutils.o \
|
||||
file_utils.o \
|
||||
logging.o \
|
||||
restricted_token.o
|
||||
restricted_token.o \
|
||||
sprompt.o
|
||||
|
||||
# foo.o, foo_shlib.o, and foo_srv.o are all built from foo.c
|
||||
OBJS_SHLIB = $(OBJS_FRONTEND:%.o=%_shlib.o)
|
||||
|
85
src/common/pg_get_line.c
Normal file
85
src/common/pg_get_line.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pg_get_line.c
|
||||
* fgets() with an expansible result buffer
|
||||
*
|
||||
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/common/pg_get_line.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef FRONTEND
|
||||
#include "postgres.h"
|
||||
#else
|
||||
#include "postgres_fe.h"
|
||||
#endif
|
||||
|
||||
#include "common/string.h"
|
||||
#include "lib/stringinfo.h"
|
||||
|
||||
|
||||
/*
|
||||
* pg_get_line()
|
||||
*
|
||||
* This is meant to be equivalent to fgets(), except that instead of
|
||||
* reading into a caller-supplied, fixed-size buffer, it reads into
|
||||
* a palloc'd (in frontend, really malloc'd) string, which is resized
|
||||
* as needed to handle indefinitely long input lines. The caller is
|
||||
* responsible for pfree'ing the result string when appropriate.
|
||||
*
|
||||
* As with fgets(), returns NULL if there is a read error or if no
|
||||
* characters are available before EOF. The caller can distinguish
|
||||
* these cases by checking ferror(stream).
|
||||
*
|
||||
* Since this is meant to be equivalent to fgets(), the trailing newline
|
||||
* (if any) is not stripped. Callers may wish to apply pg_strip_crlf().
|
||||
*
|
||||
* Note that while I/O errors are reflected back to the caller to be
|
||||
* dealt with, an OOM condition for the palloc'd buffer will not be;
|
||||
* there'll be an ereport(ERROR) or exit(1) inside stringinfo.c.
|
||||
*/
|
||||
char *
|
||||
pg_get_line(FILE *stream)
|
||||
{
|
||||
StringInfoData buf;
|
||||
|
||||
initStringInfo(&buf);
|
||||
|
||||
/* Read some data, appending it to whatever we already have */
|
||||
while (fgets(buf.data + buf.len, buf.maxlen - buf.len, stream) != NULL)
|
||||
{
|
||||
buf.len += strlen(buf.data + buf.len);
|
||||
|
||||
/* Done if we have collected a newline */
|
||||
if (buf.len > 0 && buf.data[buf.len - 1] == '\n')
|
||||
return buf.data;
|
||||
|
||||
/* Make some more room in the buffer, and loop to read more data */
|
||||
enlargeStringInfo(&buf, 128);
|
||||
}
|
||||
|
||||
/* Did fgets() fail because of an I/O error? */
|
||||
if (ferror(stream))
|
||||
{
|
||||
/* ensure that free() doesn't mess up errno */
|
||||
int save_errno = errno;
|
||||
|
||||
pfree(buf.data);
|
||||
errno = save_errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If we read no data before reaching EOF, we should return NULL */
|
||||
if (buf.len == 0)
|
||||
{
|
||||
pfree(buf.data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* No newline at EOF ... so return what we have */
|
||||
return buf.data;
|
||||
}
|
@@ -29,12 +29,6 @@
|
||||
#include "common/unicode_norm.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
|
||||
/*
|
||||
* Limit on how large password's we will try to process. A password
|
||||
* larger than this will be treated the same as out-of-memory.
|
||||
*/
|
||||
#define MAX_PASSWORD_LENGTH 1024
|
||||
|
||||
/*
|
||||
* In backend, we will use palloc/pfree. In frontend, use malloc, and
|
||||
* return SASLPREP_OOM on out-of-memory.
|
||||
@@ -1078,18 +1072,6 @@ pg_saslprep(const char *input, char **output)
|
||||
/* Ensure we return *output as NULL on failure */
|
||||
*output = NULL;
|
||||
|
||||
/* Check that the password isn't stupendously long */
|
||||
if (strlen(input) > MAX_PASSWORD_LENGTH)
|
||||
{
|
||||
#ifndef FRONTEND
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||
errmsg("password too long")));
|
||||
#else
|
||||
return SASLPREP_OOM;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick check if the input is pure ASCII. An ASCII string requires no
|
||||
* further processing.
|
||||
|
159
src/common/sprompt.c
Normal file
159
src/common/sprompt.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* sprompt.c
|
||||
* simple_prompt() routine
|
||||
*
|
||||
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/common/sprompt.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "c.h"
|
||||
|
||||
#include "common/fe_memutils.h"
|
||||
#include "common/string.h"
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* simple_prompt
|
||||
*
|
||||
* Generalized function especially intended for reading in usernames and
|
||||
* passwords interactively. Reads from /dev/tty or stdin/stderr.
|
||||
*
|
||||
* prompt: The prompt to print, or NULL if none (automatically localized)
|
||||
* echo: Set to false if you want to hide what is entered (for passwords)
|
||||
*
|
||||
* The input (without trailing newline) is returned as a malloc'd string.
|
||||
* Caller is responsible for freeing it when done.
|
||||
*/
|
||||
char *
|
||||
simple_prompt(const char *prompt, bool echo)
|
||||
{
|
||||
char *result;
|
||||
FILE *termin,
|
||||
*termout;
|
||||
#if defined(HAVE_TERMIOS_H)
|
||||
struct termios t_orig,
|
||||
t;
|
||||
#elif defined(WIN32)
|
||||
HANDLE t = NULL;
|
||||
DWORD t_orig = 0;
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
/*
|
||||
* A Windows console has an "input code page" and an "output code page";
|
||||
* these usually match each other, but they rarely match the "Windows ANSI
|
||||
* code page" defined at system boot and expected of "char *" arguments to
|
||||
* Windows API functions. The Microsoft CRT write() implementation
|
||||
* automatically converts text between these code pages when writing to a
|
||||
* console. To identify such file descriptors, it calls GetConsoleMode()
|
||||
* on the underlying HANDLE, which in turn requires GENERIC_READ access on
|
||||
* the HANDLE. Opening termout in mode "w+" allows that detection to
|
||||
* succeed. Otherwise, write() would not recognize the descriptor as a
|
||||
* console, and non-ASCII characters would display incorrectly.
|
||||
*
|
||||
* XXX fgets() still receives text in the console's input code page. This
|
||||
* makes non-ASCII credentials unportable.
|
||||
*
|
||||
* Unintuitively, we also open termin in mode "w+", even though we only
|
||||
* read it; that's needed for SetConsoleMode() to succeed.
|
||||
*/
|
||||
termin = fopen("CONIN$", "w+");
|
||||
termout = fopen("CONOUT$", "w+");
|
||||
#else
|
||||
|
||||
/*
|
||||
* Do not try to collapse these into one "w+" mode file. Doesn't work on
|
||||
* some platforms (eg, HPUX 10.20).
|
||||
*/
|
||||
termin = fopen("/dev/tty", "r");
|
||||
termout = fopen("/dev/tty", "w");
|
||||
#endif
|
||||
if (!termin || !termout
|
||||
#ifdef WIN32
|
||||
|
||||
/*
|
||||
* Direct console I/O does not work from the MSYS 1.0.10 console. Writes
|
||||
* reach nowhere user-visible; reads block indefinitely. XXX This affects
|
||||
* most Windows terminal environments, including rxvt, mintty, Cygwin
|
||||
* xterm, Cygwin sshd, and PowerShell ISE. Switch to a more-generic test.
|
||||
*/
|
||||
|| (getenv("OSTYPE") && strcmp(getenv("OSTYPE"), "msys") == 0)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (termin)
|
||||
fclose(termin);
|
||||
if (termout)
|
||||
fclose(termout);
|
||||
termin = stdin;
|
||||
termout = stderr;
|
||||
}
|
||||
|
||||
if (!echo)
|
||||
{
|
||||
#if defined(HAVE_TERMIOS_H)
|
||||
/* disable echo via tcgetattr/tcsetattr */
|
||||
tcgetattr(fileno(termin), &t);
|
||||
t_orig = t;
|
||||
t.c_lflag &= ~ECHO;
|
||||
tcsetattr(fileno(termin), TCSAFLUSH, &t);
|
||||
#elif defined(WIN32)
|
||||
/* need the file's HANDLE to turn echo off */
|
||||
t = (HANDLE) _get_osfhandle(_fileno(termin));
|
||||
|
||||
/* save the old configuration first */
|
||||
GetConsoleMode(t, &t_orig);
|
||||
|
||||
/* set to the new mode */
|
||||
SetConsoleMode(t, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (prompt)
|
||||
{
|
||||
fputs(_(prompt), termout);
|
||||
fflush(termout);
|
||||
}
|
||||
|
||||
result = pg_get_line(termin);
|
||||
|
||||
/* If we failed to read anything, just return an empty string */
|
||||
if (result == NULL)
|
||||
result = pg_strdup("");
|
||||
|
||||
/* strip trailing newline, including \r in case we're on Windows */
|
||||
(void) pg_strip_crlf(result);
|
||||
|
||||
if (!echo)
|
||||
{
|
||||
/* restore previous echo behavior, then echo \n */
|
||||
#if defined(HAVE_TERMIOS_H)
|
||||
tcsetattr(fileno(termin), TCSAFLUSH, &t_orig);
|
||||
fputs("\n", termout);
|
||||
fflush(termout);
|
||||
#elif defined(WIN32)
|
||||
SetConsoleMode(t, t_orig);
|
||||
fputs("\n", termout);
|
||||
fflush(termout);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (termin != stdin)
|
||||
{
|
||||
fclose(termin);
|
||||
fclose(termout);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
Reference in New Issue
Block a user