1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-24 14:22:24 +03:00

Add support for GSSAPI authentication.

Documentation still being written, will be committed later.

Henry B. Hotz and Magnus Hagander
This commit is contained in:
Magnus Hagander
2007-07-10 13:14:22 +00:00
parent ff481ca0d4
commit 6160106c74
17 changed files with 989 additions and 29 deletions

View File

@ -5,7 +5,7 @@
# Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.154 2007/01/07 08:49:31 petere Exp $
# $PostgreSQL: pgsql/src/interfaces/libpq/Makefile,v 1.155 2007/07/10 13:14:21 mha Exp $
#
#-------------------------------------------------------------------------
@ -57,9 +57,9 @@ endif
# shared library link. (The order in which you list them here doesn't
# matter.)
ifneq ($(PORTNAME), win32)
SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lssl -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
else
SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
endif
ifeq ($(PORTNAME), win32)
SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS))

View File

@ -10,7 +10,7 @@
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.123 2007/02/10 14:58:55 petere Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.124 2007/07/10 13:14:21 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -313,6 +313,182 @@ pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *s
}
#endif /* KRB5 */
#ifdef ENABLE_GSS
/*
* GSSAPI authentication system.
*/
#include <gssapi/gssapi.h>
#ifdef WIN32
/*
* MIT Kerberos GSSAPI DLL doesn't properly export the symbols
* that contain the OIDs required. Redefine here, values copied
* from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
*/
static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
{10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
#endif
/*
* Fetch all errors of a specific type that fit into a buffer
* and append them.
*/
static void
pg_GSS_error_int(char *mprefix, char *msg, int msglen,
OM_uint32 stat, int type)
{
int curlen = 0;
OM_uint32 lmaj_s, lmin_s;
gss_buffer_desc lmsg;
OM_uint32 msg_ctx = 0;
do
{
lmaj_s = gss_display_status(&lmin_s, stat, type,
GSS_C_NO_OID, &msg_ctx, &lmsg);
if (curlen < msglen)
{
snprintf(msg + curlen, msglen - curlen, "%s: %s\n",
mprefix, (char *)lmsg.value);
curlen += lmsg.length;
}
gss_release_buffer(&lmin_s, &lmsg);
} while (msg_ctx);
}
/*
* GSSAPI errors contains two parts. Put as much as possible of
* both parts into the string.
*/
void
pg_GSS_error(char *mprefix, char *msg, int msglen,
OM_uint32 maj_stat, OM_uint32 min_stat)
{
int mlen;
/* Fetch major error codes */
pg_GSS_error_int(mprefix, msg, msglen, maj_stat, GSS_C_GSS_CODE);
mlen = strlen(msg);
/* If there is room left, try to add the minor codes as well */
if (mlen < msglen-1)
pg_GSS_error_int(mprefix, msg + mlen, msglen - mlen,
min_stat, GSS_C_MECH_CODE);
}
/*
* Continue GSS authentication with next token as needed.
*/
static int
pg_GSS_continue(char *PQerrormsg, PGconn *conn)
{
OM_uint32 maj_stat, min_stat, lmin_s;
maj_stat = gss_init_sec_context(&min_stat,
GSS_C_NO_CREDENTIAL,
&conn->gctx,
conn->gtarg_nam,
GSS_C_NO_OID,
conn->gflags,
0,
GSS_C_NO_CHANNEL_BINDINGS,
(conn->gctx==GSS_C_NO_CONTEXT)?GSS_C_NO_BUFFER:&conn->ginbuf,
NULL,
&conn->goutbuf,
NULL,
NULL);
if (conn->gctx != GSS_C_NO_CONTEXT)
{
free(conn->ginbuf.value);
conn->ginbuf.value = NULL;
conn->ginbuf.length = 0;
}
if (conn->goutbuf.length != 0)
{
/*
* GSS generated data to send to the server. We don't care if it's
* the first or subsequent packet, just send the same kind of
* password packet.
*/
if (pqPacketSend(conn, 'p',
conn->goutbuf.value, conn->goutbuf.length)
!= STATUS_OK)
{
gss_release_buffer(&lmin_s, &conn->goutbuf);
return STATUS_ERROR;
}
}
gss_release_buffer(&lmin_s, &conn->goutbuf);
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
{
pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
PQerrormsg, PQERRORMSG_LENGTH,
maj_stat, min_stat);
gss_release_name(&lmin_s, &conn->gtarg_nam);
if (conn->gctx)
gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
return STATUS_ERROR;
}
if (maj_stat == GSS_S_COMPLETE)
gss_release_name(&lmin_s, &conn->gtarg_nam);
return STATUS_OK;
}
/*
* Send initial GSS authentication token
*/
static int
pg_GSS_startup(char *PQerrormsg, PGconn *conn)
{
OM_uint32 maj_stat, min_stat;
int maxlen;
gss_buffer_desc temp_gbuf;
if (conn->gctx)
{
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
libpq_gettext("duplicate GSS auth request\n"));
return STATUS_ERROR;
}
/*
* Import service principal name so the proper ticket can be
* acquired by the GSSAPI system.
*/
maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
temp_gbuf.value = (char*)malloc(maxlen);
snprintf(temp_gbuf.value, maxlen, "%s@%s",
conn->krbsrvname, conn->pghost);
temp_gbuf.length = strlen(temp_gbuf.value);
maj_stat = gss_import_name(&min_stat, &temp_gbuf,
GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
free(temp_gbuf.value);
if (maj_stat != GSS_S_COMPLETE)
{
pg_GSS_error(libpq_gettext("GSSAPI name import error"),
PQerrormsg, PQERRORMSG_LENGTH,
maj_stat, min_stat);
return STATUS_ERROR;
}
/*
* Initial packet is the same as a continuation packet with
* no initial context.
*/
conn->gctx = GSS_C_NO_CONTEXT;
return pg_GSS_continue(PQerrormsg, conn);
}
#endif
/*
* Respond to AUTH_REQ_SCM_CREDS challenge.
@ -479,6 +655,37 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
return STATUS_ERROR;
#endif
#ifdef ENABLE_GSS
case AUTH_REQ_GSS:
pglock_thread();
if (pg_GSS_startup(PQerrormsg, conn) != STATUS_OK)
{
/* PQerrormsg already filled in. */
pgunlock_thread();
return STATUS_ERROR;
}
pgunlock_thread();
break;
case AUTH_REQ_GSS_CONT:
pglock_thread();
if (pg_GSS_continue(PQerrormsg, conn) != STATUS_OK)
{
/* PQerrormsg already filled in. */
pgunlock_thread();
return STATUS_ERROR;
}
pgunlock_thread();
break;
#else
case AUTH_REQ_GSS:
case AUTH_REQ_GSS_CONT:
snprintf(PQerrormsg, PQERRORMSG_LENGTH,
libpq_gettext("GSSAPI authentication not supported\n"));
return STATUS_ERROR;
#endif
case AUTH_REQ_MD5:
case AUTH_REQ_CRYPT:
case AUTH_REQ_PASSWORD:

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.347 2007/07/08 18:28:55 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.348 2007/07/10 13:14:21 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -181,8 +181,8 @@ static const PQconninfoOption PQconninfoOptions[] = {
{"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
"SSL-Mode", "", 8}, /* sizeof("disable") == 8 */
#ifdef KRB5
/* Kerberos authentication supports specifying the service name */
#if defined(KRB5) || defined(ENABLE_GSS)
/* Kerberos and GSSAPI authentication support specifying the service name */
{"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
"Kerberos-service-name", "", 20},
#endif
@ -412,7 +412,7 @@ connectOptions1(PGconn *conn, const char *conninfo)
conn->sslmode = strdup("require");
}
#endif
#ifdef KRB5
#if defined(KRB5) || defined(ENABLE_GSS)
tmp = conninfo_getval(connOptions, "krbsrvname");
conn->krbsrvname = tmp ? strdup(tmp) : NULL;
#endif
@ -1496,12 +1496,13 @@ keep_going: /* We will come back to here until there is
/*
* Try to validate message length before using it.
* Authentication requests can't be very large. Errors can be
* Authentication requests can't be very large, although GSS
* auth requests may not be that small. Errors can be
* a little larger, but not huge. If we see a large apparent
* length in an error, it means we're really talking to a
* pre-3.0-protocol server; cope.
*/
if (beresp == 'R' && (msgLength < 8 || msgLength > 100))
if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext(
@ -1660,6 +1661,43 @@ keep_going: /* We will come back to here until there is
return PGRES_POLLING_READING;
}
}
#ifdef ENABLE_GSS
/*
* AUTH_REQ_GSS provides no input data
* Just set the request flags
*/
if (areq == AUTH_REQ_GSS)
conn->gflags = GSS_C_MUTUAL_FLAG;
/*
* Read GSSAPI data packets
*/
if (areq == AUTH_REQ_GSS_CONT)
{
/* Continue GSSAPI authentication */
int llen = msgLength - 4;
/*
* We can be called repeatedly for the same buffer.
* Avoid re-allocating the buffer in this case -
* just re-use the old buffer.
*/
if (llen != conn->ginbuf.length)
{
if (conn->ginbuf.value)
free(conn->ginbuf.value);
conn->ginbuf.length = llen;
conn->ginbuf.value = malloc(llen);
}
if (pqGetnchar(conn->ginbuf.value, llen, conn))
{
/* We'll come back when there is more data. */
return PGRES_POLLING_READING;
}
}
#endif
/*
* OK, we successfully read the message; mark data consumed
@ -1957,7 +1995,7 @@ freePGconn(PGconn *conn)
free(conn->pgpass);
if (conn->sslmode)
free(conn->sslmode);
#ifdef KRB5
#if defined(KRB5) || defined(ENABLE_GSS)
if (conn->krbsrvname)
free(conn->krbsrvname);
#endif
@ -1973,6 +2011,19 @@ freePGconn(PGconn *conn)
notify = notify->next;
free(prev);
}
#ifdef ENABLE_GSS
{
OM_uint32 min_s;
if (conn->gctx)
gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER);
if (conn->gtarg_nam)
gss_release_name(&min_s, &conn->gtarg_nam);
if (conn->ginbuf.length)
gss_release_buffer(&min_s, &conn->ginbuf);
if (conn->goutbuf.length)
gss_release_buffer(&min_s, &conn->goutbuf);
}
#endif
pstatus = conn->pstatus;
while (pstatus != NULL)
{

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.121 2007/07/08 18:28:56 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.122 2007/07/10 13:14:22 mha Exp $
*
*-------------------------------------------------------------------------
*/
@ -44,6 +44,10 @@
/* include stuff found in fe only */
#include "pqexpbuffer.h"
#ifdef ENABLE_GSS
#include <gssapi/gssapi.h>
#endif
#ifdef USE_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
@ -268,7 +272,7 @@ struct pg_conn
char *pguser; /* Postgres username and password, if any */
char *pgpass;
char *sslmode; /* SSL mode (require,prefer,allow,disable) */
#ifdef KRB5
#if defined(KRB5) || defined(ENABLE_GSS)
char *krbsrvname; /* Kerberos service name */
#endif
@ -350,6 +354,14 @@ struct pg_conn
char peer_cn[SM_USER + 1]; /* peer common name */
#endif
#ifdef ENABLE_GSS
gss_ctx_id_t gctx; /* GSS context */
gss_name_t gtarg_nam; /* GSS target name */
OM_uint32 gflags; /* GSS service request flags */
gss_buffer_desc ginbuf; /* GSS input token */
gss_buffer_desc goutbuf; /* GSS output token */
#endif
/* Buffer for current error message */
PQExpBufferData errorMessage; /* expansible string */
@ -399,6 +411,11 @@ extern pgthreadlock_t pg_g_threadlock;
#define pgunlock_thread() ((void) 0)
#endif
/* === in fe-auth.c === */
#ifdef ENABLE_GSS
extern void pg_GSS_error(char *mprefix, char *msg, int msglen,
OM_uint32 maj_stat, OM_uint32 min_stat);
#endif
/* === in fe-exec.c === */