PostgreSQL Source Code git master
be-secure-common.c
Go to the documentation of this file.
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 */
39int
40run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
41{
42 int loglevel = is_server_start ? ERROR : LOG;
43 char *command;
44 FILE *fh;
45 int pclose_rc;
46 size_t len = 0;
47
48 Assert(prompt);
49 Assert(size > 0);
50 buf[0] = '\0';
51
52 command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt);
53
54 fh = OpenPipeStream(command, "r");
55 if (fh == NULL)
56 {
57 ereport(loglevel,
59 errmsg("could not execute command \"%s\": %m",
60 command)));
61 goto error;
62 }
63
64 if (!fgets(buf, size, fh))
65 {
66 if (ferror(fh))
67 {
69 ereport(loglevel,
71 errmsg("could not read from command \"%s\": %m",
72 command)));
73 goto error;
74 }
75 }
76
77 pclose_rc = ClosePipeStream(fh);
78 if (pclose_rc == -1)
79 {
81 ereport(loglevel,
83 errmsg("could not close pipe to external command: %m")));
84 goto error;
85 }
86 else if (pclose_rc != 0)
87 {
88 char *reason;
89
91 reason = wait_result_to_str(pclose_rc);
92 ereport(loglevel,
94 errmsg("command \"%s\" failed",
95 command),
96 errdetail_internal("%s", reason)));
97 pfree(reason);
98 goto error;
99 }
100
101 /* strip trailing newline and carriage return */
103
104error:
105 pfree(command);
106 return len;
107}
108
109
110/*
111 * Check permissions for SSL key files.
112 */
113bool
114check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
115{
116 int loglevel = isServerStart ? FATAL : LOG;
117 struct stat buf;
118
119 if (stat(ssl_key_file, &buf) != 0)
120 {
121 ereport(loglevel,
123 errmsg("could not access private key file \"%s\": %m",
124 ssl_key_file)));
125 return false;
126 }
127
128 /* Key file must be a regular file */
129 if (!S_ISREG(buf.st_mode))
130 {
131 ereport(loglevel,
132 (errcode(ERRCODE_CONFIG_FILE_ERROR),
133 errmsg("private key file \"%s\" is not a regular file",
134 ssl_key_file)));
135 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__)
155 if (buf.st_uid != geteuid() && buf.st_uid != 0)
156 {
157 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 return false;
162 }
163
164 if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
165 (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
166 {
167 ereport(loglevel,
168 (errcode(ERRCODE_CONFIG_FILE_ERROR),
169 errmsg("private key file \"%s\" has group or world access",
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 return false;
173 }
174#endif
175
176 return true;
177}
bool check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
int run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
char * ssl_passphrase_command
Definition: be-secure.c:43
char * ssl_key_file
Definition: be-secure.c:38
#define Assert(condition)
Definition: c.h:815
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1230
int errcode_for_file_access(void)
Definition: elog.c:876
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define LOG
Definition: elog.h:31
#define FATAL
Definition: elog.h:41
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
FILE * OpenPipeStream(const char *command, const char *mode)
Definition: fd.c:2708
int ClosePipeStream(FILE *file)
Definition: fd.c:3013
void pfree(void *pointer)
Definition: mcxt.c:1521
char * replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...)
Definition: percentrepl.c:59
const void size_t len
static char * buf
Definition: pg_test_fsync.c:72
void explicit_bzero(void *buf, size_t len)
static pg_noinline void Size size
Definition: slab.c:607
static void error(void)
Definition: sql-dyntest.c:147
int pg_strip_crlf(char *str)
Definition: string.c:154
char * wait_result_to_str(int exitstatus)
Definition: wait_error.c:33
#define S_IXGRP
Definition: win32_port.h:297
#define stat
Definition: win32_port.h:274
#define S_IRWXG
Definition: win32_port.h:300
#define S_IRWXO
Definition: win32_port.h:312
#define S_ISREG(m)
Definition: win32_port.h:318
#define S_IWGRP
Definition: win32_port.h:294