/src/postgres/src/backend/libpq/be-secure-common.c
Line | Count | Source (jump to first uncovered line) |
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-2025, 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 |
40 | | run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size) |
41 | 0 | { |
42 | 0 | int loglevel = is_server_start ? ERROR : LOG; |
43 | 0 | char *command; |
44 | 0 | FILE *fh; |
45 | 0 | int pclose_rc; |
46 | 0 | size_t len = 0; |
47 | |
|
48 | 0 | Assert(prompt); |
49 | 0 | Assert(size > 0); |
50 | 0 | buf[0] = '\0'; |
51 | |
|
52 | 0 | command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt); |
53 | |
|
54 | 0 | fh = OpenPipeStream(command, "r"); |
55 | 0 | if (fh == NULL) |
56 | 0 | { |
57 | 0 | ereport(loglevel, |
58 | 0 | (errcode_for_file_access(), |
59 | 0 | errmsg("could not execute command \"%s\": %m", |
60 | 0 | command))); |
61 | 0 | goto error; |
62 | 0 | } |
63 | | |
64 | 0 | if (!fgets(buf, size, fh)) |
65 | 0 | { |
66 | 0 | if (ferror(fh)) |
67 | 0 | { |
68 | 0 | explicit_bzero(buf, size); |
69 | 0 | ereport(loglevel, |
70 | 0 | (errcode_for_file_access(), |
71 | 0 | errmsg("could not read from command \"%s\": %m", |
72 | 0 | command))); |
73 | 0 | goto error; |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | 0 | pclose_rc = ClosePipeStream(fh); |
78 | 0 | if (pclose_rc == -1) |
79 | 0 | { |
80 | 0 | explicit_bzero(buf, size); |
81 | 0 | ereport(loglevel, |
82 | 0 | (errcode_for_file_access(), |
83 | 0 | errmsg("could not close pipe to external command: %m"))); |
84 | 0 | goto error; |
85 | 0 | } |
86 | 0 | else if (pclose_rc != 0) |
87 | 0 | { |
88 | 0 | char *reason; |
89 | |
|
90 | 0 | explicit_bzero(buf, size); |
91 | 0 | reason = wait_result_to_str(pclose_rc); |
92 | 0 | ereport(loglevel, |
93 | 0 | (errcode_for_file_access(), |
94 | 0 | errmsg("command \"%s\" failed", |
95 | 0 | command), |
96 | 0 | errdetail_internal("%s", reason))); |
97 | 0 | pfree(reason); |
98 | 0 | goto error; |
99 | 0 | } |
100 | | |
101 | | /* strip trailing newline and carriage return */ |
102 | 0 | len = pg_strip_crlf(buf); |
103 | |
|
104 | 0 | error: |
105 | 0 | pfree(command); |
106 | 0 | return len; |
107 | 0 | } |
108 | | |
109 | | |
110 | | /* |
111 | | * Check permissions for SSL key files. |
112 | | */ |
113 | | bool |
114 | | check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart) |
115 | 0 | { |
116 | 0 | int loglevel = isServerStart ? FATAL : LOG; |
117 | 0 | struct stat buf; |
118 | |
|
119 | 0 | if (stat(ssl_key_file, &buf) != 0) |
120 | 0 | { |
121 | 0 | ereport(loglevel, |
122 | 0 | (errcode_for_file_access(), |
123 | 0 | errmsg("could not access private key file \"%s\": %m", |
124 | 0 | ssl_key_file))); |
125 | 0 | return false; |
126 | 0 | } |
127 | | |
128 | | /* Key file must be a regular file */ |
129 | 0 | if (!S_ISREG(buf.st_mode)) |
130 | 0 | { |
131 | 0 | ereport(loglevel, |
132 | 0 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
133 | 0 | errmsg("private key file \"%s\" is not a regular file", |
134 | 0 | ssl_key_file))); |
135 | 0 | return false; |
136 | 0 | } |
137 | | |
138 | | /* |
139 | | * Refuse to load key files owned by users other than us or root, and |
140 | | * require no public access to the key file. If the file is owned by us, |
141 | | * require mode 0600 or less. If owned by root, require 0640 or less to |
142 | | * allow read access through either our gid or a supplementary gid that |
143 | | * allows us to read system-wide certificates. |
144 | | * |
145 | | * Note that roughly similar checks are performed in |
146 | | * src/interfaces/libpq/fe-secure-openssl.c so any changes here may need |
147 | | * to be made there as well. The environment is different though; this |
148 | | * code can assume that we're not running as root. |
149 | | * |
150 | | * Ideally we would do similar permissions checks on Windows, but it is |
151 | | * not clear how that would work since Unix-style permissions may not be |
152 | | * available. |
153 | | */ |
154 | 0 | #if !defined(WIN32) && !defined(__CYGWIN__) |
155 | 0 | if (buf.st_uid != geteuid() && buf.st_uid != 0) |
156 | 0 | { |
157 | 0 | ereport(loglevel, |
158 | 0 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
159 | 0 | errmsg("private key file \"%s\" must be owned by the database user or root", |
160 | 0 | ssl_key_file))); |
161 | 0 | return false; |
162 | 0 | } |
163 | | |
164 | 0 | if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) || |
165 | 0 | (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO))) |
166 | 0 | { |
167 | 0 | ereport(loglevel, |
168 | 0 | (errcode(ERRCODE_CONFIG_FILE_ERROR), |
169 | 0 | errmsg("private key file \"%s\" has group or world access", |
170 | 0 | ssl_key_file), |
171 | 0 | 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."))); |
172 | 0 | return false; |
173 | 0 | } |
174 | 0 | #endif |
175 | | |
176 | 0 | return true; |
177 | 0 | } |