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-2018, 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 "libpq/libpq.h" 26 #include "storage/fd.h" 27 28 /* 29 * Run ssl_passphrase_command 30 * 31 * prompt will be substituted for %p. is_server_start determines the loglevel 32 * of error messages. 33 * 34 * The result will be put in buffer buf, which is of size size. The return 35 * value is the length of the actual result. 36 */ 37 int 38 run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size) 39 { 40 int loglevel = is_server_start ? ERROR : LOG; 41 StringInfoData command; 42 char *p; 43 FILE *fh; 44 int pclose_rc; 45 size_t len = 0; 46 47 Assert(prompt); 48 Assert(size > 0); 49 buf[0] = '\0'; 50 51 initStringInfo(&command); 52 53 for (p = ssl_passphrase_command; *p; p++) 54 { 55 if (p[0] == '%') 56 { 57 switch (p[1]) 58 { 59 case 'p': 60 appendStringInfoString(&command, prompt); 61 p++; 62 break; 63 case '%': 64 appendStringInfoChar(&command, '%'); 65 p++; 66 break; 67 default: 68 appendStringInfoChar(&command, p[0]); 69 } 70 } 71 else 72 appendStringInfoChar(&command, p[0]); 73 } 74 75 fh = OpenPipeStream(command.data, "r"); 76 if (fh == NULL) 77 { 78 ereport(loglevel, 79 (errcode_for_file_access(), 80 errmsg("could not execute command \"%s\": %m", 81 command.data))); 82 goto error; 83 } 84 85 if (!fgets(buf, size, fh)) 86 { 87 if (ferror(fh)) 88 { 89 ereport(loglevel, 90 (errcode_for_file_access(), 91 errmsg("could not read from command \"%s\": %m", 92 command.data))); 93 goto error; 94 } 95 } 96 97 pclose_rc = ClosePipeStream(fh); 98 if (pclose_rc == -1) 99 { 100 ereport(loglevel, 101 (errcode_for_file_access(), 102 errmsg("could not close pipe to external command: %m"))); 103 goto error; 104 } 105 else if (pclose_rc != 0) 106 { 107 ereport(loglevel, 108 (errcode_for_file_access(), 109 errmsg("command \"%s\" failed", 110 command.data), 111 errdetail_internal("%s", wait_result_to_str(pclose_rc)))); 112 goto error; 113 } 114 115 /* strip trailing newline, including \r in case we're on Windows */ 116 len = strlen(buf); 117 while (len > 0 && (buf[len - 1] == '\n' || 118 buf[len - 1] == '\r')) 119 buf[--len] = '\0'; 120 121 error: 122 pfree(command.data); 123 return len; 124 } 125 126 127 /* 128 * Check permissions for SSL key files. 129 */ 130 bool 131 check_ssl_key_file_permissions(const char *ssl_key_file, bool isServerStart) 132 { 133 int loglevel = isServerStart ? FATAL : LOG; 134 struct stat buf; 135 136 if (stat(ssl_key_file, &buf) != 0) 137 { 138 ereport(loglevel, 139 (errcode_for_file_access(), 140 errmsg("could not access private key file \"%s\": %m", 141 ssl_key_file))); 142 return false; 143 } 144 145 if (!S_ISREG(buf.st_mode)) 146 { 147 ereport(loglevel, 148 (errcode(ERRCODE_CONFIG_FILE_ERROR), 149 errmsg("private key file \"%s\" is not a regular file", 150 ssl_key_file))); 151 return false; 152 } 153 154 /* 155 * Refuse to load key files owned by users other than us or root. 156 * 157 * XXX surely we can check this on Windows somehow, too. 158 */ 159 #if !defined(WIN32) && !defined(__CYGWIN__) 160 if (buf.st_uid != geteuid() && buf.st_uid != 0) 161 { 162 ereport(loglevel, 163 (errcode(ERRCODE_CONFIG_FILE_ERROR), 164 errmsg("private key file \"%s\" must be owned by the database user or root", 165 ssl_key_file))); 166 return false; 167 } 168 #endif 169 170 /* 171 * Require no public access to key file. If the file is owned by us, 172 * require mode 0600 or less. If owned by root, require 0640 or less to 173 * allow read access through our gid, or a supplementary gid that allows 174 * to read system-wide certificates. 175 * 176 * XXX temporarily suppress check when on Windows, because there may not 177 * be proper support for Unix-y file permissions. Need to think of a 178 * reasonable check to apply on Windows. (See also the data directory 179 * permission check in postmaster.c) 180 */ 181 #if !defined(WIN32) && !defined(__CYGWIN__) 182 if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) || 183 (buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO))) 184 { 185 ereport(loglevel, 186 (errcode(ERRCODE_CONFIG_FILE_ERROR), 187 errmsg("private key file \"%s\" has group or world access", 188 ssl_key_file), 189 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."))); 190 return false; 191 } 192 #endif 193 194 return true; 195 } 196