PostgreSQL Source Code git master
Loading...
Searching...
No Matches
be-secure-common.c File Reference
#include "postgres.h"
#include <sys/stat.h>
#include <unistd.h>
#include "common/percentrepl.h"
#include "common/string.h"
#include "libpq/libpq.h"
#include "storage/fd.h"
#include "utils/builtins.h"
#include "utils/guc.h"
Include dependency graph for be-secure-common.c:

Go to the source code of this file.

Functions

static HostsLineparse_hosts_line (TokenizedAuthLine *tok_line, int elevel)
 
int run_ssl_passphrase_command (const char *cmd, const char *prompt, bool is_server_start, char *buf, int size)
 
bool check_ssl_key_file_permissions (const char *ssl_key_file, bool isServerStart)
 
int load_hosts (List **hosts, char **err_msg)
 

Function Documentation

◆ check_ssl_key_file_permissions()

bool check_ssl_key_file_permissions ( const char ssl_key_file,
bool  isServerStart 
)

Definition at line 121 of file be-secure-common.c.

122{
124 struct stat buf;
125
126 if (stat(ssl_key_file, &buf) != 0)
127 {
130 errmsg("could not access private key file \"%s\": %m",
131 ssl_key_file)));
132 return false;
133 }
134
135 /* Key file must be a regular file */
136 if (!S_ISREG(buf.st_mode))
137 {
140 errmsg("private key file \"%s\" is not a regular file",
141 ssl_key_file)));
142 return false;
143 }
144
145 /*
146 * Refuse to load key files owned by users other than us or root, and
147 * require no public access to the key file. If the file is owned by us,
148 * require mode 0600 or less. If owned by root, require 0640 or less to
149 * allow read access through either our gid or a supplementary gid that
150 * allows us to read system-wide certificates.
151 *
152 * Note that roughly similar checks are performed in
153 * src/interfaces/libpq/fe-secure-openssl.c so any changes here may need
154 * to be made there as well. The environment is different though; this
155 * code can assume that we're not running as root.
156 *
157 * Ideally we would do similar permissions checks on Windows, but it is
158 * not clear how that would work since Unix-style permissions may not be
159 * available.
160 */
161#if !defined(WIN32) && !defined(__CYGWIN__)
162 if (buf.st_uid != geteuid() && buf.st_uid != 0)
163 {
166 errmsg("private key file \"%s\" must be owned by the database user or root",
167 ssl_key_file)));
168 return false;
169 }
170
171 if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
172 (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
173 {
176 errmsg("private key file \"%s\" has group or world access",
178 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.")));
179 return false;
180 }
181#endif
182
183 return true;
184}
char * ssl_key_file
Definition be-secure.c:39
int errcode_for_file_access(void)
Definition elog.c:897
int errcode(int sqlerrcode)
Definition elog.c:874
#define LOG
Definition elog.h:31
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define FATAL
Definition elog.h:41
#define ereport(elevel,...)
Definition elog.h:150
static char * errmsg
static char buf[DEFAULT_XLOG_SEG_SIZE]
static int fb(int x)
#define S_IXGRP
Definition win32_port.h:297
#define stat
Definition win32_port.h:74
#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

References buf, ereport, errcode(), errcode_for_file_access(), errdetail(), errmsg, FATAL, fb(), LOG, S_IRWXG, S_IRWXO, S_ISREG, S_IWGRP, S_IXGRP, ssl_key_file, and stat.

Referenced by init_host_context().

◆ load_hosts()

int load_hosts ( List **  hosts,
char **  err_msg 
)

Definition at line 365 of file be-secure-common.c.

366{
367 FILE *file;
368 ListCell *line;
372 bool ok = true;
373
374 /*
375 * If we cannot return results then error out immediately. This implies
376 * API misuse or a similar kind of programmer error.
377 */
378 if (!hosts)
379 {
380 if (err_msg)
381 *err_msg = psprintf("cannot load config from \"%s\", return variable missing",
384 }
385 *hosts = NIL;
386
387 /*
388 * This is not an auth file per se, but it is using the same file format
389 * as the pg_hba and pg_ident files and thus the same code infrastructure.
390 * A future TODO might be to rename the supporting code with a more
391 * generic name?
392 */
393 file = open_auth_file(HostsFileName, LOG, 0, err_msg);
394 if (file == NULL)
395 {
396 if (errno == ENOENT)
397 return HOSTSFILE_MISSING;
398
400 }
401
403
404 foreach(line, hosts_lines)
405 {
407
408 /*
409 * Mark processing as not-ok in case lines are found with errors in
410 * tokenization (.err_msg is set) or during parsing.
411 */
412 if ((tok_line->err_msg != NULL) ||
414 {
415 ok = false;
416 continue;
417 }
418
420 }
421
422 /* Free memory from tokenizer */
423 free_auth_file(file, 0);
425
426 if (!ok)
427 {
428 if (err_msg)
429 *err_msg = psprintf("loading config from \"%s\" failed due to parsing error",
432 }
433
434 if (parsed_lines == NIL)
435 return HOSTSFILE_EMPTY;
436
437 return HOSTSFILE_LOAD_OK;
438}
static HostsLine * parse_hosts_line(TokenizedAuthLine *tok_line, int elevel)
char * HostsFileName
Definition guc_tables.c:568
void free_auth_file(FILE *file, int depth)
Definition hba.c:569
void tokenize_auth_file(const char *filename, FILE *file, List **tok_lines, int elevel, int depth)
Definition hba.c:688
FILE * open_auth_file(const char *filename, int elevel, int depth, char **err_msg)
Definition hba.c:594
@ HOSTSFILE_MISSING
Definition hba.h:180
@ HOSTSFILE_LOAD_OK
Definition hba.h:177
@ HOSTSFILE_EMPTY
Definition hba.h:179
@ HOSTSFILE_LOAD_FAILED
Definition hba.h:178
#define newline
List * lappend(List *list, void *datum)
Definition list.c:339
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
Definition pg_list.h:54

References fb(), free_auth_file(), HOSTSFILE_EMPTY, HOSTSFILE_LOAD_FAILED, HOSTSFILE_LOAD_OK, HOSTSFILE_MISSING, HostsFileName, lappend(), lfirst, LOG, newline, NIL, open_auth_file(), parse_hosts_line(), psprintf(), and tokenize_auth_file().

Referenced by be_tls_init().

◆ parse_hosts_line()

static HostsLine * parse_hosts_line ( TokenizedAuthLine tok_line,
int  elevel 
)
static

Definition at line 195 of file be-secure-common.c.

196{
198 List *tokens;
199 ListCell *field;
201
202 parsedline = palloc0(sizeof(HostsLine));
203 parsedline->sourcefile = pstrdup(tok_line->file_name);
204 parsedline->linenumber = tok_line->line_num;
205 parsedline->rawline = pstrdup(tok_line->raw_line);
206 parsedline->hostnames = NIL;
207
208 /* Initialize optional fields */
209 parsedline->ssl_passphrase_cmd = NULL;
210 parsedline->ssl_passphrase_reload = false;
211
212 /* Hostname */
213 field = list_head(tok_line->fields);
214 tokens = lfirst(field);
216 {
217 if ((tokens->length > 1) &&
218 (strcmp(hostname->string, "*") == 0 || strcmp(hostname->string, "/no_sni/") == 0))
219 {
220 ereport(elevel,
222 errmsg("default and non-SNI entries cannot be mixed with other entries"),
223 errcontext("line %d of configuration file \"%s\"",
224 tok_line->line_num, tok_line->file_name));
225 return NULL;
226 }
227
228 parsedline->hostnames = lappend(parsedline->hostnames, pstrdup(hostname->string));
229 }
230
231 /* SSL Certificate (Required) */
232 field = lnext(tok_line->fields, field);
233 if (!field)
234 {
235 ereport(elevel,
237 errmsg("missing entry at end of line"),
238 errcontext("line %d of configuration file \"%s\"",
239 tok_line->line_num, tok_line->file_name));
240 return NULL;
241 }
242 tokens = lfirst(field);
243 if (tokens->length > 1)
244 {
245 ereport(elevel,
247 errmsg("multiple values specified for SSL certificate"),
248 errcontext("line %d of configuration file \"%s\"",
249 tok_line->line_num, tok_line->file_name));
250 return NULL;
251 }
253 parsedline->ssl_cert = pstrdup(token->string);
254
255 /* SSL key (Required) */
256 field = lnext(tok_line->fields, field);
257 if (!field)
258 {
259 ereport(elevel,
261 errmsg("missing entry at end of line"),
262 errcontext("line %d of configuration file \"%s\"",
263 tok_line->line_num, tok_line->file_name));
264 return NULL;
265 }
266 tokens = lfirst(field);
267 if (tokens->length > 1)
268 {
269 ereport(elevel,
271 errmsg("multiple values specified for SSL key"),
272 errcontext("line %d of configuration file \"%s\"",
273 tok_line->line_num, tok_line->file_name));
274 return NULL;
275 }
277 parsedline->ssl_key = pstrdup(token->string);
278
279 /* SSL CA (optional) */
280 field = lnext(tok_line->fields, field);
281 if (!field)
282 return parsedline;
283 tokens = lfirst(field);
284 if (tokens->length > 1)
285 {
286 ereport(elevel,
288 errmsg("multiple values specified for SSL CA"),
289 errcontext("line %d of configuration file \"%s\"",
290 tok_line->line_num, tok_line->file_name));
291 return NULL;
292 }
294 parsedline->ssl_ca = pstrdup(token->string);
295
296 /* SSL Passphrase Command (optional) */
297 field = lnext(tok_line->fields, field);
298 if (field)
299 {
300 tokens = lfirst(field);
301 if (tokens->length > 1)
302 {
303 ereport(elevel,
305 errmsg("multiple values specified for SSL passphrase command"),
306 errcontext("line %d of configuration file \"%s\"",
307 tok_line->line_num, tok_line->file_name));
308 return NULL;
309 }
311 parsedline->ssl_passphrase_cmd = pstrdup(token->string);
312
313 /*
314 * SSL Passphrase Command support reload (optional). This field is
315 * only supported if there was a passphrase command parsed first, so
316 * nest it under the previous token.
317 */
318 field = lnext(tok_line->fields, field);
319 if (field)
320 {
321 tokens = lfirst(field);
323
324 /*
325 * There should be no more tokens after this, if there are break
326 * parsing and report error to avoid silently accepting incorrect
327 * config.
328 */
329 if (lnext(tok_line->fields, field))
330 {
331 ereport(elevel,
333 errmsg("extra fields at end of line"),
334 errcontext("line %d of configuration file \"%s\"",
335 tok_line->line_num, tok_line->file_name));
336 return NULL;
337 }
338
339 if (tokens->length > 1 || !parse_bool(token->string, &parsedline->ssl_passphrase_reload))
340 {
341 ereport(elevel,
343 errmsg("incorrect syntax for boolean value SSL_passphrase_cmd_reload"),
344 errcontext("line %d of configuration file \"%s\"",
345 tok_line->line_num, tok_line->file_name));
346 return NULL;
347 }
348 }
349 }
350
351 return parsedline;
352}
bool parse_bool(const char *value, bool *result)
Definition bool.c:31
#define errcontext
Definition elog.h:198
#define token
char * pstrdup(const char *in)
Definition mcxt.c:1781
void * palloc0(Size size)
Definition mcxt.c:1417
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
#define linitial(l)
Definition pg_list.h:178
static ListCell * list_head(const List *l)
Definition pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:375
static char * hostname
Definition pg_regress.c:114

References ereport, errcode(), errcontext, errmsg, fb(), foreach_ptr, hostname, lappend(), lfirst, linitial, list_head(), lnext(), NIL, palloc0(), parse_bool(), pstrdup(), and token.

Referenced by load_hosts().

◆ run_ssl_passphrase_command()

int run_ssl_passphrase_command ( const char cmd,
const char prompt,
bool  is_server_start,
char buf,
int  size 
)

Definition at line 46 of file be-secure-common.c.

48{
50 char *command;
51 FILE *fh;
52 int pclose_rc;
53 size_t len = 0;
54
56 Assert(size > 0);
57 buf[0] = '\0';
58
59 command = replace_percent_placeholders(cmd, "ssl_passphrase_command", "p", prompt);
60
61 fh = OpenPipeStream(command, "r");
62 if (fh == NULL)
63 {
66 errmsg("could not execute command \"%s\": %m",
67 command)));
68 goto error;
69 }
70
71 if (!fgets(buf, size, fh))
72 {
73 if (ferror(fh))
74 {
75 explicit_bzero(buf, size);
78 errmsg("could not read from command \"%s\": %m",
79 command)));
80 goto error;
81 }
82 }
83
85 if (pclose_rc == -1)
86 {
87 explicit_bzero(buf, size);
90 errmsg("could not close pipe to external command: %m")));
91 goto error;
92 }
93 else if (pclose_rc != 0)
94 {
95 char *reason;
96
97 explicit_bzero(buf, size);
101 errmsg("command \"%s\" failed",
102 command),
103 errdetail_internal("%s", reason)));
104 pfree(reason);
105 goto error;
106 }
107
108 /* strip trailing newline and carriage return */
110
111error:
112 pfree(command);
113 return len;
114}
#define Assert(condition)
Definition c.h:945
int int errdetail_internal(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:39
FILE * OpenPipeStream(const char *command, const char *mode)
Definition fd.c:2731
int ClosePipeStream(FILE *file)
Definition fd.c:3039
void pfree(void *pointer)
Definition mcxt.c:1616
char * replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...)
Definition percentrepl.c:59
const void size_t len
void explicit_bzero(void *buf, size_t len)
static void error(void)
int pg_strip_crlf(char *str)
Definition string.c:154
char * wait_result_to_str(int exitstatus)
Definition wait_error.c:33

References Assert, buf, ClosePipeStream(), ereport, errcode_for_file_access(), errdetail_internal(), errmsg, ERROR, error(), explicit_bzero(), fb(), len, LOG, OpenPipeStream(), pfree(), pg_strip_crlf(), replace_percent_placeholders(), and wait_result_to_str().

Referenced by ssl_external_passwd_cb().