mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
Use the explicit_bzero() function in places where it is important that security information such as passwords is cleared from memory. There might be other places where it could be useful; this is just an initial collection. For platforms that don't have explicit_bzero(), provide various fallback implementations. (explicit_bzero() itself isn't standard, but as Linux/glibc, FreeBSD, and OpenBSD have it, it's the most common spelling, so it makes sense to make that the invocation point.) Discussion: https://www.postgresql.org/message-id/flat/42d26bde-5d5b-c90d-87ae-6cab875f73be%402ndquadrant.com
197 lines
4.8 KiB
C
197 lines
4.8 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* be-secure-common.c
|
|
*
|
|
* common implementation-independent SSL support code
|
|
*
|
|
* While be-secure.c contains the interfaces that the rest of the
|
|
* communications code calls, this file contains support routines that are
|
|
* used by the library-specific implementations such as be-secure-openssl.c.
|
|
*
|
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/libpq/be-secure-common.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "common/string.h"
|
|
#include "libpq/libpq.h"
|
|
#include "storage/fd.h"
|
|
|
|
/*
|
|
* Run ssl_passphrase_command
|
|
*
|
|
* prompt will be substituted for %p. is_server_start determines the loglevel
|
|
* of error messages.
|
|
*
|
|
* The result will be put in buffer buf, which is of size size. The return
|
|
* value is the length of the actual result.
|
|
*/
|
|
int
|
|
run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
|
|
{
|
|
int loglevel = is_server_start ? ERROR : LOG;
|
|
StringInfoData command;
|
|
char *p;
|
|
FILE *fh;
|
|
int pclose_rc;
|
|
size_t len = 0;
|
|
|
|
Assert(prompt);
|
|
Assert(size > 0);
|
|
buf[0] = '\0';
|
|
|
|
initStringInfo(&command);
|
|
|
|
for (p = ssl_passphrase_command; *p; p++)
|
|
{
|
|
if (p[0] == '%')
|
|
{
|
|
switch (p[1])
|
|
{
|
|
case 'p':
|
|
appendStringInfoString(&command, prompt);
|
|
p++;
|
|
break;
|
|
case '%':
|
|
appendStringInfoChar(&command, '%');
|
|
p++;
|
|
break;
|
|
default:
|
|
appendStringInfoChar(&command, p[0]);
|
|
}
|
|
}
|
|
else
|
|
appendStringInfoChar(&command, p[0]);
|
|
}
|
|
|
|
fh = OpenPipeStream(command.data, "r");
|
|
if (fh == NULL)
|
|
{
|
|
ereport(loglevel,
|
|
(errcode_for_file_access(),
|
|
errmsg("could not execute command \"%s\": %m",
|
|
command.data)));
|
|
goto error;
|
|
}
|
|
|
|
if (!fgets(buf, size, fh))
|
|
{
|
|
if (ferror(fh))
|
|
{
|
|
explicit_bzero(buf, size);
|
|
ereport(loglevel,
|
|
(errcode_for_file_access(),
|
|
errmsg("could not read from command \"%s\": %m",
|
|
command.data)));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
pclose_rc = ClosePipeStream(fh);
|
|
if (pclose_rc == -1)
|
|
{
|
|
explicit_bzero(buf, size);
|
|
ereport(loglevel,
|
|
(errcode_for_file_access(),
|
|
errmsg("could not close pipe to external command: %m")));
|
|
goto error;
|
|
}
|
|
else if (pclose_rc != 0)
|
|
{
|
|
explicit_bzero(buf, size);
|
|
ereport(loglevel,
|
|
(errcode_for_file_access(),
|
|
errmsg("command \"%s\" failed",
|
|
command.data),
|
|
errdetail_internal("%s", wait_result_to_str(pclose_rc))));
|
|
goto error;
|
|
}
|
|
|
|
/* strip trailing newline and carriage return */
|
|
len = pg_strip_crlf(buf);
|
|
|
|
error:
|
|
pfree(command.data);
|
|
return len;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check permissions for SSL key files.
|
|
*/
|
|
bool
|
|
check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
|
|
{
|
|
int loglevel = isServerStart ? FATAL : LOG;
|
|
struct stat buf;
|
|
|
|
if (stat(ssl_key_file, &buf) != 0)
|
|
{
|
|
ereport(loglevel,
|
|
(errcode_for_file_access(),
|
|
errmsg("could not access private key file \"%s\": %m",
|
|
ssl_key_file)));
|
|
return false;
|
|
}
|
|
|
|
if (!S_ISREG(buf.st_mode))
|
|
{
|
|
ereport(loglevel,
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
errmsg("private key file \"%s\" is not a regular file",
|
|
ssl_key_file)));
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Refuse to load key files owned by users other than us or root.
|
|
*
|
|
* XXX surely we can check this on Windows somehow, too.
|
|
*/
|
|
#if !defined(WIN32) && !defined(__CYGWIN__)
|
|
if (buf.st_uid != geteuid() && buf.st_uid != 0)
|
|
{
|
|
ereport(loglevel,
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
errmsg("private key file \"%s\" must be owned by the database user or root",
|
|
ssl_key_file)));
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Require no public access to key file. If the file is owned by us,
|
|
* require mode 0600 or less. If owned by root, require 0640 or less to
|
|
* allow read access through our gid, or a supplementary gid that allows
|
|
* to read system-wide certificates.
|
|
*
|
|
* XXX temporarily suppress check when on Windows, because there may not
|
|
* be proper support for Unix-y file permissions. Need to think of a
|
|
* reasonable check to apply on Windows. (See also the data directory
|
|
* permission check in postmaster.c)
|
|
*/
|
|
#if !defined(WIN32) && !defined(__CYGWIN__)
|
|
if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
|
|
(buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
|
|
{
|
|
ereport(loglevel,
|
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
errmsg("private key file \"%s\" has group or world access",
|
|
ssl_key_file),
|
|
errdetail("File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.")));
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|