v1-4-server-GSSAPI-encryption-and-decryption.patch
text/x-diff
Filename: v1-4-server-GSSAPI-encryption-and-decryption.patch
Type: text/x-diff
Part: 3
Message:
[PATCH v1] GSSAPI encryption support
Patch
Format: format-patch
Series: patch v1
Subject: server: GSSAPI encryption and decryption
| File | + | − |
|---|---|---|
| src/backend/libpq/auth.c | 1 | 1 |
| src/backend/libpq/be-secure-gss.c | 101 | 0 |
| src/backend/libpq/Makefile | 4 | 0 |
| src/backend/libpq/pqcomm.c | 39 | 0 |
| src/backend/tcop/postgres.c | 17 | 1 |
| src/backend/utils/misc/guc.c | 19 | 0 |
| src/include/libpq/auth.h | 5 | 0 |
| src/include/libpq/libpq-be.h | 9 | 0 |
| src/include/libpq/libpq.h | 4 | 0 |
From 0991d42173394e68e989d84e6574476d6a98e571 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <rharwood@redhat.com>
Date: Tue, 9 Jun 2015 14:38:26 -0400
Subject: server: GSSAPI encryption and decryption
---
src/backend/libpq/Makefile | 4 ++
src/backend/libpq/auth.c | 2 +-
src/backend/libpq/be-secure-gss.c | 101 ++++++++++++++++++++++++++++++++++++++
src/backend/libpq/pqcomm.c | 39 +++++++++++++++
src/backend/tcop/postgres.c | 18 ++++++-
src/backend/utils/misc/guc.c | 19 +++++++
src/include/libpq/auth.h | 5 ++
src/include/libpq/libpq-be.h | 9 ++++
src/include/libpq/libpq.h | 4 ++
9 files changed, 199 insertions(+), 2 deletions(-)
create mode 100644 src/backend/libpq/be-secure-gss.c
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 09410c4..359e9d5 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -21,4 +21,8 @@ ifeq ($(with_openssl),yes)
OBJS += be-secure-openssl.o
endif
+ifeq ($(with_gssapi),yes)
+OBJS += be-secure-gss.o
+endif
+
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 4699efa..913d356 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -728,7 +728,7 @@ static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
#endif
-static void
+void
pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
{
gss_buffer_desc gmsg;
diff --git a/src/backend/libpq/be-secure-gss.c b/src/backend/libpq/be-secure-gss.c
new file mode 100644
index 0000000..64a4ed7
--- /dev/null
+++ b/src/backend/libpq/be-secure-gss.c
@@ -0,0 +1,101 @@
+#include <assert.h>
+
+#include "postgres.h"
+
+#include "libpq/libpq.h"
+#include "libpq/auth.h"
+#include "miscadmin.h"
+
+/* GUC value */
+bool gss_encrypt;
+
+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);
+ char *newbuf = palloc(len + 5);
+
+ len += 4;
+ len_n = htonl(len);
+
+ newbuf[0] = msgtype;
+ memcpy(newbuf + 1, &len_n, 4);
+ memcpy(newbuf + 5, ptr, len - 4);
+
+ input.length = len + 1; /* include type */
+ input.value = newbuf;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_wrap(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT, &input,
+ &conf, &output);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error(ERROR, gettext_noop("unwrapping GSS message failed"),
+ major, minor);
+ return -1;
+ }
+ assert(conf);
+
+ newbuf = repalloc(newbuf, output.length);
+ memcpy(newbuf, output.value, output.length);
+
+ len = output.length;
+ *msgptr = newbuf;
+ gss_release_buffer(&minor, &output);
+
+ return len;
+}
+
+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;
+ }
+
+ 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;
+ }
+
+ 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
+
PqCommBusy = true;
if (msgtype)
if (internal_putbytes(&msgtype, 1))
@@ -1500,10 +1513,20 @@ socket_putmessage(char msgtype, const char *s, size_t len)
if (internal_putbytes(s, len))
goto fail;
PqCommBusy = false;
+#ifdef ENABLE_GSS
+ /* if we're GSSAPI encrypting, s was allocated in be_gss_encrypt */
+ if (msgtype == 'g')
+ pfree((char *)s);
+#endif
return 0;
fail:
PqCommBusy = false;
+#ifdef ENABLE_GSS
+ /* if we're GSSAPI encrypting, s was allocated in be_gss_encrypt */
+ if (msgtype == 'g')
+ pfree((char *)s);
+#endif
return EOF;
}
@@ -1519,6 +1542,22 @@ socket_putmessage_noblock(char msgtype, const char *s, size_t len)
int res PG_USED_FOR_ASSERTS_ONLY;
int required;
+#ifdef ENABLE_GSS
+ /*
+ * Because socket_putmessage is also a front-facing function, we need the
+ * ability to GSSAPI encrypt from either. Since socket_putmessage_noblock
+ * calls into socket_putmessage, socket_putmessage will handle freeing the
+ * allocated string.
+ */
+ if (gss_encrypt && msgtype != 'R' && msgtype != 'g')
+ {
+ len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
+ if (len < 0)
+ return;
+ msgtype = 'g';
+ }
+#endif
+
/*
* Ensure we have enough space in the output buffer for the message header
* as well as the message itself.
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index ce4bdaf..8510908 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -336,6 +336,7 @@ static int
SocketBackend(StringInfo inBuf)
{
int qtype;
+ bool msg_got = false;
/*
* Get message type code from the frontend.
@@ -365,6 +366,21 @@ SocketBackend(StringInfo inBuf)
return qtype;
}
+#ifdef ENABLE_GSS
+ else if (qtype == 'g' && gss_encrypt &&
+ MyProcPort->hba->auth_method == uaGSS)
+ {
+ /* GSSAPI wrapping implies protocol >= 3 */
+ if (pq_getmessage(inBuf, 0))
+ return EOF;
+ msg_got = true;
+
+ qtype = be_gss_inplace_decrypt(inBuf);
+ if (qtype < 0)
+ return EOF;
+ }
+#endif
+
/*
* Validate message type code before trying to read body; if we have lost
* sync, better to say "command unknown" than to run out of memory because
@@ -490,7 +506,7 @@ SocketBackend(StringInfo inBuf)
* after the type code; we can read the message contents independently of
* the type.
*/
- if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3 && !msg_got)
{
if (pq_getmessage(inBuf, 0))
return EOF; /* suitable message already logged */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 0356ecb..a978af0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -186,6 +186,10 @@ static const char *show_log_file_mode(void);
static ConfigVariable *ProcessConfigFileInternal(GucContext context,
bool applySettings, int elevel);
+#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;
}
+static void
+assign_gss_encrypt(bool newval, void *extra)
+{
+ gss_encrypt = newval;
+}
+
#include "guc-file.c"
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 80f26a8..e98f560 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -26,4 +26,9 @@ extern void ClientAuthentication(Port *port);
typedef void (*ClientAuthentication_hook_type) (Port *, int);
extern PGDLLIMPORT ClientAuthentication_hook_type ClientAuthentication_hook;
+#ifdef ENABLE_GSS
+void pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat,
+ OM_uint32 min_stat);
+#endif
+
#endif /* AUTH_H */
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"
+
#if defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#else
@@ -219,6 +221,13 @@ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
#endif
+#ifdef ENABLE_GSS
+/* These functions are implemented in be-secure-gss.c */
+extern size_t
+be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len);
+extern int be_gss_inplace_decrypt(StringInfo inBuf);
+#endif
+
extern ProtocolVersion FrontendProtocol;
/* 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
+
#endif /* LIBPQ_H */
--
2.1.4