LCOV - code coverage report
Current view: top level - src/backend/libpq - be-secure-common.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 23 43 53.5 %
Date: 2024-04-27 02:11:35 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           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           0 :         explicit_bzero(buf, size);
      89           0 :         ereport(loglevel,
      90             :                 (errcode_for_file_access(),
      91             :                  errmsg("command \"%s\" failed",
      92             :                         command),
      93             :                  errdetail_internal("%s", wait_result_to_str(pclose_rc))));
      94           0 :         goto error;
      95             :     }
      96             : 
      97             :     /* strip trailing newline and carriage return */
      98           8 :     len = pg_strip_crlf(buf);
      99             : 
     100           8 : error:
     101           8 :     pfree(command);
     102           8 :     return len;
     103             : }
     104             : 
     105             : 
     106             : /*
     107             :  * Check permissions for SSL key files.
     108             :  */
     109             : bool
     110          52 : check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart)
     111             : {
     112          52 :     int         loglevel = isServerStart ? FATAL : LOG;
     113             :     struct stat buf;
     114             : 
     115          52 :     if (stat(ssl_key_file, &buf) != 0)
     116             :     {
     117           0 :         ereport(loglevel,
     118             :                 (errcode_for_file_access(),
     119             :                  errmsg("could not access private key file \"%s\": %m",
     120             :                         ssl_key_file)));
     121           0 :         return false;
     122             :     }
     123             : 
     124             :     /* Key file must be a regular file */
     125          52 :     if (!S_ISREG(buf.st_mode))
     126             :     {
     127           0 :         ereport(loglevel,
     128             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
     129             :                  errmsg("private key file \"%s\" is not a regular file",
     130             :                         ssl_key_file)));
     131           0 :         return false;
     132             :     }
     133             : 
     134             :     /*
     135             :      * Refuse to load key files owned by users other than us or root, and
     136             :      * require no public access to the key file.  If the file is owned by us,
     137             :      * require mode 0600 or less.  If owned by root, require 0640 or less to
     138             :      * allow read access through either our gid or a supplementary gid that
     139             :      * allows us to read system-wide certificates.
     140             :      *
     141             :      * Note that roughly similar checks are performed in
     142             :      * src/interfaces/libpq/fe-secure-openssl.c so any changes here may need
     143             :      * to be made there as well.  The environment is different though; this
     144             :      * code can assume that we're not running as root.
     145             :      *
     146             :      * Ideally we would do similar permissions checks on Windows, but it is
     147             :      * not clear how that would work since Unix-style permissions may not be
     148             :      * available.
     149             :      */
     150             : #if !defined(WIN32) && !defined(__CYGWIN__)
     151          52 :     if (buf.st_uid != geteuid() && buf.st_uid != 0)
     152             :     {
     153           0 :         ereport(loglevel,
     154             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
     155             :                  errmsg("private key file \"%s\" must be owned by the database user or root",
     156             :                         ssl_key_file)));
     157           0 :         return false;
     158             :     }
     159             : 
     160          52 :     if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
     161          52 :         (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
     162             :     {
     163           0 :         ereport(loglevel,
     164             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
     165             :                  errmsg("private key file \"%s\" has group or world access",
     166             :                         ssl_key_file),
     167             :                  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.")));
     168           0 :         return false;
     169             :     }
     170             : #endif
     171             : 
     172          52 :     return true;
     173             : }

Generated by: LCOV version 1.14