summaryrefslogtreecommitdiffstats
path: root/src/backend/libpq/be-secure-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/be-secure-common.c')
-rw-r--r--src/backend/libpq/be-secure-common.c195
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;
+}