Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * be-secure-common.c
4 : *
5 : * common implementation-independent SSL support code
6 : *
7 : * While be-secure.c contains the interfaces that the rest of the
8 : * communications code calls, this file contains support routines that are
9 : * used by the library-specific implementations such as be-secure-openssl.c.
10 : *
11 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
12 : * Portions Copyright (c) 1994, Regents of the University of California
13 : *
14 : * IDENTIFICATION
15 : * src/backend/libpq/be-secure-common.c
16 : *
17 : *-------------------------------------------------------------------------
18 : */
19 :
20 : #include "postgres.h"
21 :
22 : #include <sys/stat.h>
23 : #include <unistd.h>
24 :
25 : #include "common/percentrepl.h"
26 : #include "common/string.h"
27 : #include "libpq/libpq.h"
28 : #include "storage/fd.h"
29 :
30 : /*
31 : * Run ssl_passphrase_command
32 : *
33 : * prompt will be substituted for %p. is_server_start determines the loglevel
34 : * of error messages.
35 : *
36 : * The result will be put in buffer buf, which is of size size. The return
37 : * value is the length of the actual result.
38 : */
39 : int
1868 peter_e 40 GIC 4 : run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
1868 peter_e 41 ECB : {
1868 peter_e 42 GIC 4 : int loglevel = is_server_start ? ERROR : LOG;
43 : char *command;
44 : FILE *fh;
45 : int pclose_rc;
1868 peter_e 46 CBC 4 : size_t len = 0;
47 :
48 4 : Assert(prompt);
49 4 : Assert(size > 0);
50 4 : buf[0] = '\0';
51 :
88 peter 52 GNC 4 : command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt);
53 :
54 4 : fh = OpenPipeStream(command, "r");
1868 peter_e 55 CBC 4 : if (fh == NULL)
1868 peter_e 56 ECB : {
1868 peter_e 57 UIC 0 : ereport(loglevel,
1868 peter_e 58 EUB : (errcode_for_file_access(),
59 : errmsg("could not execute command \"%s\": %m",
60 : command)));
1868 peter_e 61 UIC 0 : goto error;
1868 peter_e 62 EUB : }
63 :
1868 peter_e 64 CBC 4 : if (!fgets(buf, size, fh))
65 : {
1868 peter_e 66 UBC 0 : if (ferror(fh))
1868 peter_e 67 EUB : {
1312 peter 68 UIC 0 : explicit_bzero(buf, size);
1868 peter_e 69 0 : ereport(loglevel,
70 : (errcode_for_file_access(),
71 : errmsg("could not read from command \"%s\": %m",
72 : command)));
73 0 : goto error;
74 : }
75 : }
1868 peter_e 76 ECB :
1868 peter_e 77 GIC 4 : pclose_rc = ClosePipeStream(fh);
1868 peter_e 78 CBC 4 : if (pclose_rc == -1)
1868 peter_e 79 ECB : {
1312 peter 80 LBC 0 : explicit_bzero(buf, size);
1868 peter_e 81 UIC 0 : ereport(loglevel,
82 : (errcode_for_file_access(),
83 : errmsg("could not close pipe to external command: %m")));
84 0 : goto error;
85 : }
1868 peter_e 86 GIC 4 : else if (pclose_rc != 0)
87 : {
1312 peter 88 LBC 0 : explicit_bzero(buf, size);
1868 peter_e 89 UIC 0 : ereport(loglevel,
1868 peter_e 90 ECB : (errcode_for_file_access(),
91 : errmsg("command \"%s\" failed",
92 : command),
93 : errdetail_internal("%s", wait_result_to_str(pclose_rc))));
1868 peter_e 94 UIC 0 : goto error;
1868 peter_e 95 EUB : }
96 :
97 : /* strip trailing newline and carriage return */
1339 michael 98 GIC 4 : len = pg_strip_crlf(buf);
1868 peter_e 99 EUB :
1868 peter_e 100 GIC 4 : error:
88 peter 101 GNC 4 : pfree(command);
1868 peter_e 102 GIC 4 : return len;
1868 peter_e 103 ECB : }
104 :
1833 peter_e 105 EUB :
106 : /*
107 : * Check permissions for SSL key files.
108 : */
109 : bool
1833 peter_e 110 GIC 26 : check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
111 : {
112 26 : int loglevel = isServerStart ? FATAL : LOG;
113 : struct stat buf;
114 :
115 26 : if (stat(ssl_key_file, &buf) != 0)
116 : {
1833 peter_e 117 UIC 0 : ereport(loglevel,
118 : (errcode_for_file_access(),
119 : errmsg("could not access private key file \"%s\": %m",
120 : ssl_key_file)));
121 0 : return false;
122 : }
123 :
124 : /* Key file must be a regular file */
1833 peter_e 125 GIC 26 : if (!S_ISREG(buf.st_mode))
126 : {
1833 peter_e 127 UIC 0 : ereport(loglevel,
128 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
1833 peter_e 129 ECB : errmsg("private key file \"%s\" is not a regular file",
130 : ssl_key_file)));
1833 peter_e 131 UBC 0 : return false;
132 : }
133 :
134 : /*
405 tgl 135 EUB : * Refuse to load key files owned by users other than us or root, and
136 : * require no public access to the key file. If the file is owned by us,
137 : * require mode 0600 or less. If owned by root, require 0640 or less to
405 tgl 138 ECB : * allow read access through either our gid or a supplementary gid that
139 : * allows us to read system-wide certificates.
140 : *
318 tgl 141 EUB : * Note that roughly similar checks are performed in
142 : * src/interfaces/libpq/fe-secure-openssl.c so any changes here may need
143 : * to be made there as well. The environment is different though; this
144 : * code can assume that we're not running as root.
145 : *
405 146 : * Ideally we would do similar permissions checks on Windows, but it is
147 : * not clear how that would work since Unix-style permissions may not be
148 : * available.
149 : */
1833 peter_e 150 ECB : #if !defined(WIN32) && !defined(__CYGWIN__)
1833 peter_e 151 GIC 26 : if (buf.st_uid != geteuid() && buf.st_uid != 0)
152 : {
1833 peter_e 153 UIC 0 : ereport(loglevel,
154 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
155 : errmsg("private key file \"%s\" must be owned by the database user or root",
156 : ssl_key_file)));
157 0 : return false;
158 : }
159 :
1833 peter_e 160 GIC 26 : if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
161 26 : (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
162 : {
1833 peter_e 163 UIC 0 : ereport(loglevel,
164 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
165 : errmsg("private key file \"%s\" has group or world access",
166 : ssl_key_file),
167 : 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.")));
168 0 : return false;
169 : }
170 : #endif
171 :
1833 peter_e 172 GIC 26 : return true;
173 : }
|