LCOV - code coverage report
Current view: top level - src/backend/libpq - be-secure-common.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 23 45 51.1 %
Date: 2024-11-21 08:14:44 Functions: 2 2 100.0 %
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-2024, 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          12 : run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
      41             : {
      42          12 :     int         loglevel = is_server_start ? ERROR : LOG;
      43             :     char       *command;
      44             :     FILE       *fh;
      45             :     int         pclose_rc;
      46          12 :     size_t      len = 0;
      47             : 
      48             :     Assert(prompt);
      49             :     Assert(size > 0);
      50          12 :     buf[0] = '\0';
      51             : 
      52          12 :     command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt);
      53             : 
      54          12 :     fh = OpenPipeStream(command, "r");
      55          12 :     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          12 :     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          12 :     pclose_rc = ClosePipeStream(fh);
      78          12 :     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          12 :     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          12 :     len = pg_strip_crlf(buf);
     103             : 
     104          12 : error:
     105          12 :     pfree(command);
     106          12 :     return len;
     107             : }
     108             : 
     109             : 
     110             : /*
     111             :  * Check permissions for SSL key files.
     112             :  */
     113             : bool
     114          64 : check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
     115             : {
     116          64 :     int         loglevel = isServerStart ? FATAL : LOG;
     117             :     struct stat buf;
     118             : 
     119          64 :     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          64 :     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          64 :     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          64 :     if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
     165          64 :         (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          64 :     return true;
     177             : }

Generated by: LCOV version 1.14