PostgreSQL Source Code  git master
fe-secure-common.c File Reference
#include "postgres_fe.h"
#include <arpa/inet.h>
#include "fe-secure-common.h"
#include "libpq-int.h"
#include "pqexpbuffer.h"
Include dependency graph for fe-secure-common.c:

Go to the source code of this file.

Functions

static bool wildcard_certificate_match (const char *pattern, const char *string)
 
int pq_verify_peer_name_matches_certificate_name (PGconn *conn, const char *namedata, size_t namelen, char **store_name)
 
int pq_verify_peer_name_matches_certificate_ip (PGconn *conn, const unsigned char *ipdata, size_t iplen, char **store_name)
 
bool pq_verify_peer_name_matches_certificate (PGconn *conn)
 

Function Documentation

◆ pq_verify_peer_name_matches_certificate()

bool pq_verify_peer_name_matches_certificate ( PGconn conn)

Definition at line 258 of file fe-secure-common.c.

259 {
260  char *host = conn->connhost[conn->whichhost].host;
261  int rc;
262  int names_examined = 0;
263  char *first_name = NULL;
264 
265  /*
266  * If told not to verify the peer name, don't do it. Return true
267  * indicating that the verification was successful.
268  */
269  if (strcmp(conn->sslmode, "verify-full") != 0)
270  return true;
271 
272  /* Check that we have a hostname to compare with. */
273  if (!(host && host[0] != '\0'))
274  {
276  libpq_gettext("host name must be specified for a verified SSL connection\n"));
277  return false;
278  }
279 
280  rc = pgtls_verify_peer_name_matches_certificate_guts(conn, &names_examined, &first_name);
281 
282  if (rc == 0)
283  {
284  /*
285  * No match. Include the name from the server certificate in the error
286  * message, to aid debugging broken configurations. If there are
287  * multiple names, only print the first one to avoid an overly long
288  * error message.
289  */
290  if (names_examined > 1)
291  {
293  libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host name \"%s\"\n",
294  "server certificate for \"%s\" (and %d other names) does not match host name \"%s\"\n",
295  names_examined - 1),
296  first_name, names_examined - 1, host);
297  }
298  else if (names_examined == 1)
299  {
301  libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"),
302  first_name, host);
303  }
304  else
305  {
307  libpq_gettext("could not get server's host name from server certificate\n"));
308  }
309  }
310 
311  /* clean up */
312  if (first_name)
313  free(first_name);
314 
315  return (rc == 1);
316 }
int pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn, int *names_examined, char **first_name)
#define free(a)
Definition: header.h:65
#define libpq_gettext(x)
Definition: libpq-int.h:878
#define libpq_ngettext(s, p, n)
Definition: libpq-int.h:879
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
PGconn * conn
Definition: streamutil.c:54
char * host
Definition: libpq-int.h:335
char * sslmode
Definition: libpq-int.h:379
PQExpBufferData errorMessage
Definition: libpq-int.h:585
int whichhost
Definition: libpq-int.h:426
pg_conn_host * connhost
Definition: libpq-int.h:427

References appendPQExpBuffer(), appendPQExpBufferStr(), conn, pg_conn::connhost, pg_conn::errorMessage, free, pg_conn_host::host, libpq_gettext, libpq_ngettext, pgtls_verify_peer_name_matches_certificate_guts(), pg_conn::sslmode, and pg_conn::whichhost.

Referenced by open_client_SSL().

◆ pq_verify_peer_name_matches_certificate_ip()

int pq_verify_peer_name_matches_certificate_ip ( PGconn conn,
const unsigned char *  ipdata,
size_t  iplen,
char **  store_name 
)

Definition at line 160 of file fe-secure-common.c.

164 {
165  char *addrstr;
166  int match = 0;
167  char *host = conn->connhost[conn->whichhost].host;
168  int family;
169  char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
170  char sebuf[PG_STRERROR_R_BUFLEN];
171 
172  *store_name = NULL;
173 
174  if (!(host && host[0] != '\0'))
175  {
177  libpq_gettext("host name must be specified\n"));
178  return -1;
179  }
180 
181  /*
182  * The data from the certificate is in network byte order. Convert our
183  * host string to network-ordered bytes as well, for comparison. (The host
184  * string isn't guaranteed to actually be an IP address, so if this
185  * conversion fails we need to consider it a mismatch rather than an
186  * error.)
187  */
188  if (iplen == 4)
189  {
190  /* IPv4 */
191  struct in_addr addr;
192 
193  family = AF_INET;
194 
195  /*
196  * The use of inet_aton() is deliberate; we accept alternative IPv4
197  * address notations that are accepted by inet_aton() but not
198  * inet_pton() as server addresses.
199  */
200  if (inet_aton(host, &addr))
201  {
202  if (memcmp(ipdata, &addr.s_addr, iplen) == 0)
203  match = 1;
204  }
205  }
206 
207  /*
208  * If they don't have inet_pton(), skip this. Then, an IPv6 address in a
209  * certificate will cause an error.
210  */
211 #ifdef HAVE_INET_PTON
212  else if (iplen == 16)
213  {
214  /* IPv6 */
215  struct in6_addr addr;
216 
217  family = AF_INET6;
218 
219  if (inet_pton(AF_INET6, host, &addr) == 1)
220  {
221  if (memcmp(ipdata, &addr.s6_addr, iplen) == 0)
222  match = 1;
223  }
224  }
225 #endif
226  else
227  {
228  /*
229  * Not IPv4 or IPv6. We could ignore the field, but leniency seems
230  * wrong given the subject matter.
231  */
233  libpq_gettext("certificate contains IP address with invalid length %lu\n"),
234  (unsigned long) iplen);
235  return -1;
236  }
237 
238  /* Generate a human-readable representation of the certificate's IP. */
239  addrstr = pg_inet_net_ntop(family, ipdata, 8 * iplen, tmp, sizeof(tmp));
240  if (!addrstr)
241  {
243  libpq_gettext("could not convert certificate's IP address to string: %s\n"),
244  strerror_r(errno, sebuf, sizeof(sebuf)));
245  return -1;
246  }
247 
248  *store_name = strdup(addrstr);
249  return match;
250 }
#define PG_STRERROR_R_BUFLEN
Definition: port.h:243
char * pg_inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size)
Definition: inet_net_ntop.c:77
int inet_aton(const char *cp, struct in_addr *addr)
Definition: inet_aton.c:56
#define strerror_r
Definition: port.h:242

References appendPQExpBuffer(), appendPQExpBufferStr(), conn, pg_conn::connhost, pg_conn::errorMessage, pg_conn_host::host, inet_aton(), libpq_gettext, pg_inet_net_ntop(), PG_STRERROR_R_BUFLEN, strerror_r, and pg_conn::whichhost.

Referenced by openssl_verify_peer_name_matches_certificate_ip().

◆ pq_verify_peer_name_matches_certificate_name()

int pq_verify_peer_name_matches_certificate_name ( PGconn conn,
const char *  namedata,
size_t  namelen,
char **  store_name 
)

Definition at line 87 of file fe-secure-common.c.

90 {
91  char *name;
92  int result;
93  char *host = conn->connhost[conn->whichhost].host;
94 
95  *store_name = NULL;
96 
97  if (!(host && host[0] != '\0'))
98  {
100  libpq_gettext("host name must be specified\n"));
101  return -1;
102  }
103 
104  /*
105  * There is no guarantee the string returned from the certificate is
106  * NULL-terminated, so make a copy that is.
107  */
108  name = malloc(namelen + 1);
109  if (name == NULL)
110  {
112  libpq_gettext("out of memory\n"));
113  return -1;
114  }
115  memcpy(name, namedata, namelen);
116  name[namelen] = '\0';
117 
118  /*
119  * Reject embedded NULLs in certificate common or alternative name to
120  * prevent attacks like CVE-2009-4034.
121  */
122  if (namelen != strlen(name))
123  {
124  free(name);
126  libpq_gettext("SSL certificate's name contains embedded null\n"));
127  return -1;
128  }
129 
130  if (pg_strcasecmp(name, host) == 0)
131  {
132  /* Exact name match */
133  result = 1;
134  }
135  else if (wildcard_certificate_match(name, host))
136  {
137  /* Matched wildcard name */
138  result = 1;
139  }
140  else
141  {
142  result = 0;
143  }
144 
145  *store_name = name;
146  return result;
147 }
const char * name
Definition: encode.c:561
static bool wildcard_certificate_match(const char *pattern, const char *string)
#define malloc(a)
Definition: header.h:50
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36

References appendPQExpBufferStr(), conn, pg_conn::connhost, pg_conn::errorMessage, free, pg_conn_host::host, libpq_gettext, malloc, name, pg_strcasecmp(), pg_conn::whichhost, and wildcard_certificate_match().

Referenced by openssl_verify_peer_name_matches_certificate_name().

◆ wildcard_certificate_match()

static bool wildcard_certificate_match ( const char *  pattern,
const char *  string 
)
static

Definition at line 45 of file fe-secure-common.c.

46 {
47  int lenpat = strlen(pattern);
48  int lenstr = strlen(string);
49 
50  /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
51  if (lenpat < 3 ||
52  pattern[0] != '*' ||
53  pattern[1] != '.')
54  return false;
55 
56  /* If pattern is longer than the string, we can never match */
57  if (lenpat > lenstr)
58  return false;
59 
60  /*
61  * If string does not end in pattern (minus the wildcard), we don't match
62  */
63  if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
64  return false;
65 
66  /*
67  * If there is a dot left of where the pattern started to match, we don't
68  * match (rule 3)
69  */
70  if (strchr(string, '.') < string + lenstr - lenpat)
71  return false;
72 
73  /* String ended with pattern, and didn't have a dot before, so we match */
74  return true;
75 }

References pg_strcasecmp().

Referenced by pq_verify_peer_name_matches_certificate_name().