diff options
Diffstat (limited to 'src/backend/libpq/be-secure-common.c')
-rw-r--r-- | src/backend/libpq/be-secure-common.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c new file mode 100644 index 0000000..7d082d7 --- /dev/null +++ b/src/backend/libpq/be-secure-common.c @@ -0,0 +1,195 @@ +/*------------------------------------------------------------------------- + * + * 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-2021, 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; + } + + /* Key file must be a regular file */ + 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, and + * require no public access to the 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 either our gid or a supplementary gid that + * allows us to read system-wide certificates. + * + * Note that roughly similar checks are performed in + * src/interfaces/libpq/fe-secure-openssl.c so any changes here may need + * to be made there as well. The environment is different though; this + * code can assume that we're not running as root. + * + * Ideally we would do similar permissions checks on Windows, but it is + * not clear how that would work since Unix-style permissions may not be + * available. + */ +#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; + } + + 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; +} |