Re: [PATCH v1] GSSAPI encryption support

From: Andres Freund <andres(at)anarazel(dot)de>
To: Robbie Harwood <rharwood(at)redhat(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: [PATCH v1] GSSAPI encryption support
Date: 2015-10-03 16:18:10
Message-ID: 20151003161810.GD30738@alap3.anarazel.de
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: 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(at)redhat(dot)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

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Andres Freund 2015-10-03 16:48:58 Re: creating extension including dependencies
Previous Message Petr Jelinek 2015-10-03 15:56:07 Re: creating extension including dependencies