LCOV - code coverage report
Current view: top level - src/backend/libpq - be-secure-common.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 51.1 % 45 23
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 2 2
Legend: Lines:     hit not hit

            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-2026, 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            8 : run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
      41              : {
      42            8 :     int         loglevel = is_server_start ? ERROR : LOG;
      43              :     char       *command;
      44              :     FILE       *fh;
      45              :     int         pclose_rc;
      46            8 :     size_t      len = 0;
      47              : 
      48              :     Assert(prompt);
      49              :     Assert(size > 0);
      50            8 :     buf[0] = '\0';
      51              : 
      52            8 :     command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt);
      53              : 
      54            8 :     fh = OpenPipeStream(command, "r");
      55            8 :     if (fh == NULL)
      56              :     {
      57            0 :         ereport(loglevel,
      58              :                 (errcode_for_file_access(),
      59              :                  errmsg("could not execute command \"%s\": %m",
      60              :                         command)));
      61            0 :         goto error;
      62              :     }
      63              : 
      64            8 :     if (!fgets(buf, size, fh))
      65              :     {
      66            0 :         if (ferror(fh))
      67              :         {
      68            0 :             explicit_bzero(buf, size);
      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              : 
      77            8 :     pclose_rc = ClosePipeStream(fh);
      78            8 :     if (pclose_rc == -1)
      79              :     {
      80            0 :         explicit_bzero(buf, size);
      81            0 :         ereport(loglevel,
      82              :                 (errcode_for_file_access(),
      83              :                  errmsg("could not close pipe to external command: %m")));
      84            0 :         goto error;
      85              :     }
      86            8 :     else if (pclose_rc != 0)
      87              :     {
      88              :         char       *reason;
      89              : 
      90            0 :         explicit_bzero(buf, size);
      91            0 :         reason = wait_result_to_str(pclose_rc);
      92            0 :         ereport(loglevel,
      93              :                 (errcode_for_file_access(),
      94              :                  errmsg("command \"%s\" failed",
      95              :                         command),
      96              :                  errdetail_internal("%s", reason)));
      97            0 :         pfree(reason);
      98            0 :         goto error;
      99              :     }
     100              : 
     101              :     /* strip trailing newline and carriage return */
     102            8 :     len = pg_strip_crlf(buf);
     103              : 
     104            8 : error:
     105            8 :     pfree(command);
     106            8 :     return len;
     107              : }
     108              : 
     109              : 
     110              : /*
     111              :  * Check permissions for SSL key files.
     112              :  */
     113              : bool
     114           35 : check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
     115              : {
     116           35 :     int         loglevel = isServerStart ? FATAL : LOG;
     117              :     struct stat buf;
     118              : 
     119           35 :     if (stat(ssl_key_file, &buf) != 0)
     120              :     {
     121            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 */
     129           35 :     if (!S_ISREG(buf.st_mode))
     130              :     {
     131            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__)
     155           35 :     if (buf.st_uid != geteuid() && buf.st_uid != 0)
     156              :     {
     157            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              : 
     164           35 :     if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
     165           35 :         (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
     166              :     {
     167            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              : 
     176           35 :     return true;
     177              : }
        

Generated by: LCOV version 2.0-1