Re: [PATCH v1] GSSAPI encryption support - Mailing list pgsql-hackers

From Andres Freund
Subject Re: [PATCH v1] GSSAPI encryption support
Date
Msg-id 20151003161810.GD30738@alap3.anarazel.de
Whole thread Raw
In response to [PATCH v1] GSSAPI encryption support  (Robbie Harwood <rharwood@redhat.com>)
Responses Re: [PATCH v1] GSSAPI encryption support  (Michael Paquier <michael.paquier@gmail.com>)
Re: [PATCH v1] GSSAPI encryption support  (Robbie Harwood <rharwood@redhat.com>)
List pgsql-hackers
Hi,

I quickly read through the patch, trying to understand what exactly is
happening here. To me the way the patch is split doesn't make much sense
- I don't mind incremental patches, but right now the steps don't
individually make sense.

On 2015-07-02 14:22:13 -0400, Robbie Harwood wrote:
> +#include <assert.h>

postgres.h should be the first header included.

> +size_t
> +be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len)
> +{
> +    OM_uint32 major, minor;
> +    gss_buffer_desc input, output;
> +    uint32 len_n;
> +    int conf;
> +    char *ptr = *((char **)msgptr);

trivial nitpick, missing spaces...

> +int
> +be_gss_inplace_decrypt(StringInfo inBuf)
> +{
> +    OM_uint32 major, minor;
> +    gss_buffer_desc input, output;
> +    int qtype, conf;
> +    size_t msglen = 0;
> +
> +    input.length = inBuf->len;
> +    input.value = inBuf->data;
> +    output.length = 0;
> +    output.value = NULL;
> +
> +    major = gss_unwrap(&minor, MyProcPort->gss->ctx, &input, &output,
> +                       &conf, NULL);
> +    if (GSS_ERROR(major))
> +    {
> +        pg_GSS_error(ERROR, gettext_noop("wrapping GSS message failed"),
> +                     major, minor);
> +        return -1;
> +    }
> +    else if (conf == 0)
> +    {
> +        ereport(COMMERROR,
> +                (errcode(ERRCODE_PROTOCOL_VIOLATION),
> +                 errmsg("Expected GSSAPI confidentiality but it was not received")));
> +        return -1;
> +    }

Hm. Aren't we leaking the gss buffer here?

> +    qtype = ((char *)output.value)[0]; /* first byte is message type */
> +    inBuf->len = output.length - 5; /* message starts */
> +
> +    memcpy((char *)&msglen, ((char *)output.value) + 1, 4);
> +    msglen = ntohl(msglen);
> +    if (msglen - 4 != inBuf->len)
> +    {
> +        ereport(COMMERROR,
> +                (errcode(ERRCODE_PROTOCOL_VIOLATION),
> +                 errmsg("Length value inside GSSAPI-encrypted packet was malformed")));
> +        return -1;
> +    }

and here?

Arguably it doesn't matter that much, since we'll usually disconnect
around here, but ...

> +    memcpy(inBuf->data, ((char *)output.value) + 5, inBuf->len);
> +    inBuf->data[inBuf->len] = '\0'; /* invariant */
> +    gss_release_buffer(&minor, &output);
> +
> +    return qtype;
> +}

> diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
> index a4b37ed..5a929a8 100644
> --- a/src/backend/libpq/pqcomm.c
> +++ b/src/backend/libpq/pqcomm.c
> @@ -1485,6 +1485,19 @@ socket_putmessage(char msgtype, const char *s, size_t len)
>  {
>      if (DoingCopyOut || PqCommBusy)
>          return 0;
> +
> +#ifdef ENABLE_GSS
> +    /* Do not wrap auth requests. */
> +    if (MyProcPort->hba->auth_method == uaGSS && gss_encrypt &&
> +        msgtype != 'R' && msgtype != 'g')
> +    {
> +        len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
> +        if (len < 0)
> +            goto fail;
> +        msgtype = 'g';
> +    }
> +#endif

Putting encryption specific code here feels rather wrong to me.


> diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
> index 6171ef3..58712fc 100644
> --- a/src/include/libpq/libpq-be.h
> +++ b/src/include/libpq/libpq-be.h
> @@ -30,6 +30,8 @@
>  #endif
>  
>  #ifdef ENABLE_GSS
> +#include "lib/stringinfo.h"
> +

Conditionally including headers providing generic infrastructure strikes
me as a recipe for build failures in different configurations.

>  /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
> diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
> index c408e5b..e788cc8 100644
> --- a/src/include/libpq/libpq.h
> +++ b/src/include/libpq/libpq.h
> @@ -99,4 +99,8 @@ extern char *SSLCipherSuites;
>  extern char *SSLECDHCurve;
>  extern bool SSLPreferServerCiphers;
>  
> +#ifdef ENABLE_GSS
> +extern bool gss_encrypt;
> +#endif

> --- a/src/backend/utils/misc/guc.c
> +++ b/src/backend/utils/misc/guc.c
>  
> +#ifdef ENABLE_GSS
> +static void assign_gss_encrypt(bool newval, void *extra);
> +#endif
> +
>  
>  /*
>   * Options for enum values defined in this module.
> @@ -1618,6 +1622,15 @@ static struct config_bool ConfigureNamesBool[] =
>          NULL, NULL, NULL
>      },
>  
> +    {
> +        {"gss_encrypt", PGC_USERSET, CONN_AUTH_SECURITY,
> +         gettext_noop("Whether client wants encryption for this connection."),
> +         NULL,
> +         GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
> +        },
> +        &gss_encrypt, false, NULL, assign_gss_encrypt, NULL
> +    },
> +
>      /* End-of-list marker */
>      {
>          {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
> @@ -10114,4 +10127,10 @@ show_log_file_mode(void)
>      return buf;
>  }

The guc should always be present, not just when gss is built in. It
should error out when not supported. As is you're going to get linker
errors around gss_encrypt, assign_gss_encrypt.

> From e55795e0638ca37447ef200f21f74db80b07ebf4 Mon Sep 17 00:00:00 2001
> From: "Robbie Harwood (frozencemetery)" <rharwood@redhat.com>
> Date: Fri, 12 Jun 2015 13:27:50 -0400
> Subject: Error when receiving plaintext on GSS-encrypted connections

I don't see why this makes sense as a separate patch.

> Subject: server: hba option for requiring GSSAPI encryption
> 
> Also includes logic for failing clients that do not request encryption
> in the startup packet when encryption is required.
> ---
>  src/backend/libpq/hba.c           |  9 +++++++++
>  src/backend/utils/init/postinit.c |  7 ++++++-
>  src/backend/utils/misc/guc.c      | 12 +++++++++++-
>  src/include/libpq/hba.h           |  1 +
>  4 files changed, 27 insertions(+), 2 deletions(-)
> 
> diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
> index 23c8b5d..90fe57f 100644
> --- a/src/backend/libpq/hba.c
> +++ b/src/backend/libpq/hba.c
> @@ -1570,6 +1570,15 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
>          else
>              hbaline->include_realm = false;
>      }
> +    else if (strcmp(name, "require_encrypt") == 0)
> +    {
> +        if (hbaline->auth_method != uaGSS)
> +            INVALID_AUTH_OPTION("require_encrypt", "gssapi");
> +        if (strcmp(val, "1") == 0)
> +            hbaline->require_encrypt = true;
> +        else
> +            hbaline->require_encrypt = false;
> +    }

So this is a new, undocumented, option that makes a connection require
encryption? But despite the generic name, it's gss specific?

> @@ -1628,7 +1629,7 @@ static struct config_bool ConfigureNamesBool[] =
>           NULL,
>           GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
>          },
> -        &gss_encrypt, false, NULL, assign_gss_encrypt, NULL
> +        &gss_encrypt, false, check_gss_encrypt, assign_gss_encrypt, NULL
>      },
>  
>      /* End-of-list marker */
> @@ -10133,4 +10134,13 @@ assign_gss_encrypt(bool newval, void *extra)
>      gss_encrypt = newval;
>  }
>  
> +static bool
> +check_gss_encrypt(bool *newval, void **extra, GucSource source)
> +{
> +    if (MyProcPort && MyProcPort->hba && MyProcPort->hba->require_encrypt &&
> +        !*newval)
> +        return false;
> +    return true;
> +}

Doing such checks in a guc assign hook seems like a horrible idea. Yes,
there's some precedent, but still.

Greetings,

Andres Freund



pgsql-hackers by date:

Previous
From: Petr Jelinek
Date:
Subject: Re: creating extension including dependencies
Next
From: Andres Freund
Date:
Subject: Re: creating extension including dependencies