Age Owner Branch data 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-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
2749 peter_e@gmx.net 40 :CBC 6 : run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
41 : : {
42 [ + - ]: 6 : int loglevel = is_server_start ? ERROR : LOG;
43 : : char *command;
44 : : FILE *fh;
45 : : int pclose_rc;
46 : 6 : size_t len = 0;
47 : :
48 [ - + ]: 6 : Assert(prompt);
49 [ - + ]: 6 : Assert(size > 0);
50 : 6 : buf[0] = '\0';
51 : :
969 peter@eisentraut.org 52 : 6 : command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt);
53 : :
54 : 6 : fh = OpenPipeStream(command, "r");
2749 peter_e@gmx.net 55 [ - + ]: 6 : if (fh == NULL)
56 : : {
2749 peter_e@gmx.net 57 [ # # ]:UBC 0 : ereport(loglevel,
58 : : (errcode_for_file_access(),
59 : : errmsg("could not execute command \"%s\": %m",
60 : : command)));
61 : 0 : goto error;
62 : : }
63 : :
2749 peter_e@gmx.net 64 [ - + ]:CBC 6 : if (!fgets(buf, size, fh))
65 : : {
2749 peter_e@gmx.net 66 [ # # ]:UBC 0 : if (ferror(fh))
67 : : {
2193 peter@eisentraut.org 68 : 0 : explicit_bzero(buf, size);
2749 peter_e@gmx.net 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 : : }
76 : :
2749 peter_e@gmx.net 77 :CBC 6 : pclose_rc = ClosePipeStream(fh);
78 [ - + ]: 6 : if (pclose_rc == -1)
79 : : {
2193 peter@eisentraut.org 80 :UBC 0 : explicit_bzero(buf, size);
2749 peter_e@gmx.net 81 [ # # ]: 0 : ereport(loglevel,
82 : : (errcode_for_file_access(),
83 : : errmsg("could not close pipe to external command: %m")));
84 : 0 : goto error;
85 : : }
2749 peter_e@gmx.net 86 [ - + ]:CBC 6 : else if (pclose_rc != 0)
87 : : {
88 : : char *reason;
89 : :
2193 peter@eisentraut.org 90 :UBC 0 : explicit_bzero(buf, size);
480 dgustafsson@postgres 91 : 0 : reason = wait_result_to_str(pclose_rc);
2749 peter_e@gmx.net 92 [ # # ]: 0 : ereport(loglevel,
93 : : (errcode_for_file_access(),
94 : : errmsg("command \"%s\" failed",
95 : : command),
96 : : errdetail_internal("%s", reason)));
480 dgustafsson@postgres 97 : 0 : pfree(reason);
2749 peter_e@gmx.net 98 : 0 : goto error;
99 : : }
100 : :
101 : : /* strip trailing newline and carriage return */
2220 michael@paquier.xyz 102 :CBC 6 : len = pg_strip_crlf(buf);
103 : :
2749 peter_e@gmx.net 104 : 6 : error:
969 peter@eisentraut.org 105 : 6 : pfree(command);
2749 peter_e@gmx.net 106 : 6 : return len;
107 : : }
108 : :
109 : :
110 : : /*
111 : : * Check permissions for SSL key files.
112 : : */
113 : : bool
2714 114 : 30 : check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
115 : : {
116 [ + + ]: 30 : int loglevel = isServerStart ? FATAL : LOG;
117 : : struct stat buf;
118 : :
119 [ - + ]: 30 : if (stat(ssl_key_file, &buf) != 0)
120 : : {
2714 peter_e@gmx.net 121 [ # # ]:UBC 0 : ereport(loglevel,
122 : : (errcode_for_file_access(),
123 : : errmsg("could not access private key file \"%s\": %m",
124 : : ssl_key_file)));
125 : 0 : return false;
126 : : }
127 : :
128 : : /* Key file must be a regular file */
2714 peter_e@gmx.net 129 [ - + ]:CBC 30 : if (!S_ISREG(buf.st_mode))
130 : : {
2714 peter_e@gmx.net 131 [ # # ]:UBC 0 : ereport(loglevel,
132 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
133 : : errmsg("private key file \"%s\" is not a regular file",
134 : : ssl_key_file)));
135 : 0 : return false;
136 : : }
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 : : #if !defined(WIN32) && !defined(__CYGWIN__)
2714 peter_e@gmx.net 155 [ - + - - ]:CBC 30 : if (buf.st_uid != geteuid() && buf.st_uid != 0)
156 : : {
2714 peter_e@gmx.net 157 [ # # ]:UBC 0 : ereport(loglevel,
158 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
159 : : errmsg("private key file \"%s\" must be owned by the database user or root",
160 : : ssl_key_file)));
161 : 0 : return false;
162 : : }
163 : :
2714 peter_e@gmx.net 164 [ + - + - ]:CBC 30 : if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
165 [ - + - - ]: 30 : (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
166 : : {
2714 peter_e@gmx.net 167 [ # # ]:UBC 0 : ereport(loglevel,
168 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
169 : : errmsg("private key file \"%s\" has group or world access",
170 : : ssl_key_file),
171 : : 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 : : }
174 : : #endif
175 : :
2714 peter_e@gmx.net 176 :CBC 30 : return true;
177 : : }
|