patch: Client certificate requirements

Поиск
Список
Период
Сортировка
От Magnus Hagander
Тема patch: Client certificate requirements
Дата
Msg-id 49009683.6020208@hagander.net
обсуждение исходный текст
Ответы Re: patch: Client certificate requirements  (Magnus Hagander <magnus@hagander.net>)
Список pgsql-hackers
This patch adds a configuration option to pg_hba.conf for "clientcert".
This makes it possible to have different client certificate requirements
on different connections. It also makes sure that if you specify that
you want client cert verification and the root store isn't there, we
give an error instead of silently allowing the user in (like we do now).

This still does not implement actual client certificate validation -
that's for a later step. It just cleans up the handling we have now.

//Magnus
*** a/src/backend/libpq/auth.c
--- b/src/backend/libpq/auth.c
***************
*** 273,278 **** ClientAuthentication(Port *port)
--- 273,304 ----
                   errmsg("missing or erroneous pg_hba.conf file"),
                   errhint("See server log for details.")));

+     /*
+      * This is the first point where we have access to the hba record for
+      * the current connection, so perform any verifications based on the
+      * hba options field that should be done *before* the authentication
+      * here.
+      */
+     if (port->hba->clientcert)
+     {
+         /*
+          * When we parse pg_hba.conf, we have already made sure that we have
+          * been able to load a certificate store. Thus, if a certificate is
+          * present on the client, it has been verified against our root
+          * certificate store, and the connection would have been aborted
+          * already if it didn't verify ok.
+          */
+         if (!port->peer)
+         {
+             ereport(FATAL,
+                     (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                      errmsg("connection requires a valid client certificate")));
+         }
+     }
+
+     /*
+      * Now proceed to do the actual authentication check
+      */
      switch (port->hba->auth_method)
      {
          case uaReject:
*** a/src/backend/libpq/be-secure.c
--- b/src/backend/libpq/be-secure.c
***************
*** 128,133 **** static const char *SSLerrmessage(void);
--- 128,134 ----
  #define RENEGOTIATION_LIMIT (512 * 1024 * 1024)

  static SSL_CTX *SSL_context = NULL;
+ static bool ssl_loaded_verify_locations = false;

  /* GUC variable controlling SSL cipher list */
  char       *SSLCipherSuites = NULL;
***************
*** 229,234 **** secure_destroy(void)
--- 230,248 ----
  }

  /*
+  * Indicate if we have loaded the root CA store to verify certificates
+  */
+ bool
+ secure_loaded_verify_locations(void)
+ {
+ #ifdef USE_SSL
+     return ssl_loaded_verify_locations;
+ #endif
+
+     return false;
+ }
+
+ /*
   *    Attempt to negotiate secure session.
   */
  int
***************
*** 780,794 **** initialize_SSL(void)
          elog(FATAL, "could not set the cipher list (no valid ciphers available)");

      /*
!      * Require and check client certificates only if we have a root.crt file.
       */
!     if (!SSL_CTX_load_verify_locations(SSL_context, ROOT_CERT_FILE, NULL))
      {
!         /* Not fatal - we do not require client certificates */
          ereport(LOG,
                  (errmsg("could not load root certificate file \"%s\": %s",
                          ROOT_CERT_FILE, SSLerrmessage()),
!                  errdetail("Will not verify client certificates.")));
      }
      else
      {
--- 794,821 ----
          elog(FATAL, "could not set the cipher list (no valid ciphers available)");

      /*
!      * Attempt to load CA store, so we can verify client certificates if needed.
       */
!     if (access(ROOT_CERT_FILE, R_OK))
      {
!         /*
!          * Root certificate file simply not found. Don't log an error here, because
!          * it's quite likely the user isn't planning on using client certificates.
!          */
!         ssl_loaded_verify_locations = false;
!     }
!     else if (!SSL_CTX_load_verify_locations(SSL_context, ROOT_CERT_FILE, NULL))
!     {
!         /*
!          * File was there, but we could not load it. This means the file is somehow
!          * broken, and we should log this. Don't log it as a fatal error, because
!          * there is still a chance that the user isn't going to use client certs.
!          */
!         ssl_loaded_verify_locations = false;
          ereport(LOG,
                  (errmsg("could not load root certificate file \"%s\": %s",
                          ROOT_CERT_FILE, SSLerrmessage()),
!                  errdetail("Will not be able to verify client certificates.")));
      }
      else
      {
***************
*** 821,833 **** initialize_SSL(void)
                                  ROOT_CRL_FILE, SSLerrmessage()),
                           errdetail("Certificates will not be checked against revocation list.")));
              }
-         }

!         SSL_CTX_set_verify(SSL_context,
!                            (SSL_VERIFY_PEER |
!                             SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
!                             SSL_VERIFY_CLIENT_ONCE),
!                            verify_cb);
      }
  }

--- 848,865 ----
                                  ROOT_CRL_FILE, SSLerrmessage()),
                           errdetail("Certificates will not be checked against revocation list.")));
              }

!             /*
!              * Always ask for SSL client cert, but don't fail if it's not presented. We'll fail later in this case,
!              * based on what we find in pg_hba.conf.
!              */
!             SSL_CTX_set_verify(SSL_context,
!                                (SSL_VERIFY_PEER |
!                                 SSL_VERIFY_CLIENT_ONCE),
!                                verify_cb);
!
!             ssl_loaded_verify_locations = true;
!         }
      }
  }

*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 873,878 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 873,910 ----
                      INVALID_AUTH_OPTION("map", "ident, krb5, gssapi and sspi");
                  parsedline->usermap = pstrdup(c);
              }
+             else if (strcmp(token, "clientcert") == 0)
+             {
+                 /*
+                  * Since we require ctHostSSL, this really can never happen on non-SSL-enabled
+                  * builds, so don't bother checking for USE_SSL.
+                  */
+                 if (parsedline->conntype != ctHostSSL)
+                 {
+                     ereport(LOG,
+                             (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                              errmsg("clientcert can only be configured for \"hostssl\" rows"),
+                              errcontext("line %d of configuration file \"%s\"",
+                                         line_num, HbaFileName)));
+                     goto hba_other_error;
+                 }
+                 if (strcmp(c, "1") == 0)
+                 {
+                     if (!secure_loaded_verify_locations())
+                     {
+                         ereport(LOG,
+                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                  errmsg("client certificates can only be checked if a root certificate store is
available"),
+                                  errdetail("make sure the root certificate store is present and readable"),
+                                  errcontext("line %d of configuration file \"%s\"",
+                                             line_num, HbaFileName)));
+                         goto hba_other_error;
+                     }
+                     parsedline->clientcert = true;
+                 }
+                 else
+                     parsedline->clientcert = false;
+             }
              else if (strcmp(token, "pamservice") == 0)
              {
                  REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam");
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 55,60 **** typedef struct
--- 55,61 ----
      int            ldapport;
      char       *ldapprefix;
      char       *ldapsuffix;
+     bool        clientcert;
  } HbaLine;

  typedef struct Port hbaPort;
*** a/src/include/libpq/libpq.h
--- b/src/include/libpq/libpq.h
***************
*** 67,72 **** extern void pq_endcopyout(bool errorAbort);
--- 67,73 ----
   * prototypes for functions in be-secure.c
   */
  extern int    secure_initialize(void);
+ extern bool secure_loaded_verify_locations(void);
  extern void secure_destroy(void);
  extern int    secure_open_server(Port *port);
  extern void secure_close(Port *port);

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Zdenek Kotala
Дата:
Сообщение: Re: Any reason to have heap_(de)formtuple?
Следующее
От: "Koichi Suzuki"
Дата:
Сообщение: Re: Making pg_standby compression-friendly