Commit 6160106c authored by Magnus Hagander's avatar Magnus Hagander

Add support for GSSAPI authentication.

Documentation still being written, will be committed later.

Henry B. Hotz and Magnus Hagander
parent ff481ca0
This diff is collapsed.
dnl Process this file with autoconf to produce a configure script.
dnl $PostgreSQL: pgsql/configure.in,v 1.516 2007/06/29 16:18:43 tgl Exp $
dnl $PostgreSQL: pgsql/configure.in,v 1.517 2007/07/10 13:14:20 mha Exp $
dnl
dnl Developers, please strive to achieve this order:
dnl
......@@ -462,6 +462,19 @@ PGAC_ARG_BOOL(with, python, no, [ --with-python build Python modules
AC_MSG_RESULT([$with_python])
AC_SUBST(with_python)
#
# GSSAPI
#
AC_MSG_CHECKING([wether to build with GSSAPI support])
PGAC_ARG_BOOL(with, gssapi, no, [ --with-gssapi build with GSSAPI support],
[
AC_DEFINE(ENABLE_GSS, 1, [Define to build with GSSAPI support. (--with-gssapi)])
krb_srvtab="FILE:\$(sysconfdir)/krb5.keytab"
])
AC_MSG_RESULT([$with_gssapi])
AC_SUBST(with_gssapi)
#
# Kerberos 5
#
......@@ -753,6 +766,15 @@ else
*** Not using spinlocks will cause poor performance.])
fi
if test "$with_gssapi" = yes ; then
if test "$PORTNAME" != "win32"; then
AC_SEARCH_LIBS(gss_init_sec_context, [gssapi_krb5], [],
[AC_MSG_ERROR([could not find function 'gss_init_sec_context' required for GSSAPI])])
else
LIBS="$LIBS -lgssapi32"
fi
fi
if test "$with_krb5" = yes ; then
if test "$PORTNAME" != "win32"; then
AC_SEARCH_LIBS(com_err, [krb5 'krb5 -lcrypto -ldes -lasn1 -lroken' com_err], [],
......@@ -848,6 +870,10 @@ failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
fi
if test "$with_gssapi" = yes ; then
AC_CHECK_HEADER(gssapi/gssapi.h, [], [AC_MSG_ERROR([header file <gssapi/gssapi.h> is required for GSSAPI])])
fi
if test "$with_krb5" = yes ; then
AC_CHECK_HEADER(krb5.h, [], [AC_MSG_ERROR([header file <krb5.h> is required for Kerberos 5])])
fi
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.148 2007/02/08 04:52:18 momjian Exp $
* $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.149 2007/07/10 13:14:20 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -23,6 +23,7 @@
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "libpq/auth.h"
#include "libpq/crypt.h"
......@@ -295,6 +296,250 @@ pg_krb5_recvauth(Port *port)
}
#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_USER_NAME_desc =
{10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
#endif
static void
pg_GSS_error(int severity, char *text, OM_uint32 maj_stat, OM_uint32 min_stat)
{
gss_buffer_desc gmsg;
OM_uint32 lmaj_s, lmin_s, msg_ctx;
char localmsg1[128],
localmsg2[128];
/* Fetch major status message */
msg_ctx = 0;
lmaj_s = gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
GSS_C_NO_OID, &msg_ctx, &gmsg);
strlcpy(localmsg1, gmsg.value, sizeof(localmsg1));
gss_release_buffer(&lmin_s, &gmsg);
if (msg_ctx)
/* More than one message available.
* XXX: Should we loop and read all messages?
* (same below)
*/
ereport(WARNING,
(errmsg_internal("incomplete GSS error report")));
/* Fetch mechanism minor status message */
msg_ctx = 0;
lmaj_s = gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
GSS_C_NO_OID, &msg_ctx, &gmsg);
strlcpy(localmsg2, gmsg.value, sizeof(localmsg2));
gss_release_buffer(&lmin_s, &gmsg);
if (msg_ctx)
ereport(WARNING,
(errmsg_internal("incomplete GSS minor error report")));
/* errmsg_internal, since translation of the first part must be
* done before calling this function anyway. */
ereport(severity,
(errmsg_internal("%s:%s\n%s", text, localmsg1, localmsg2)));
}
static int
pg_GSS_recvauth(Port *port)
{
OM_uint32 maj_stat, min_stat, lmin_s, gflags;
char *kt_path;
int mtype;
int ret;
StringInfoData buf;
gss_buffer_desc gbuf;
if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
{
/*
* Set default Kerberos keytab file for the Krb5 mechanism.
*
* setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0);
* except setenv() not always available.
*/
if (!getenv("KRB5_KTNAME"))
{
kt_path = palloc(PATH_MAX + 13);
snprintf(kt_path, PATH_MAX + 13,
"KRB5_KTNAME=%s", pg_krb_server_keyfile);
putenv(kt_path);
}
}
/*
* We accept any service principal that's present in our
* keytab. This increases interoperability between kerberos
* implementations that see for example case sensitivity
* differently, while not really opening up any vector
* of attack.
*/
port->gss->cred = GSS_C_NO_CREDENTIAL;
/*
* Initialize sequence with an empty context
*/
port->gss->ctx = GSS_C_NO_CONTEXT;
/*
* Loop through GSSAPI message exchange. This exchange can consist
* of multiple messags sent in both directions. First message is always
* from the client. All messages from client to server are password
* packets (type 'p').
*/
do
{
mtype = pq_getbyte();
if (mtype != 'p')
{
/* Only log error if client didn't disconnect. */
if (mtype != EOF)
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("expected GSS response, got message type %d",
mtype)));
return STATUS_ERROR;
}
/* Get the actual GSS token */
initStringInfo(&buf);
if (pq_getmessage(&buf, 2000))
{
/* EOF - pq_getmessage already logged error */
pfree(buf.data);
return STATUS_ERROR;
}
/* Map to GSSAPI style buffer */
gbuf.length = buf.len;
gbuf.value = buf.data;
ereport(DEBUG4,
(errmsg_internal("Processing received GSS token of length: %u",
gbuf.length)));
maj_stat = gss_accept_sec_context(
&min_stat,
&port->gss->ctx,
port->gss->cred,
&gbuf,
GSS_C_NO_CHANNEL_BINDINGS,
&port->gss->name,
NULL,
&port->gss->outbuf,
&gflags,
NULL,
NULL);
/* gbuf no longer used */
pfree(buf.data);
ereport(DEBUG5,
(errmsg_internal("gss_accept_sec_context major: %i, "
"minor: %i, outlen: %u, outflags: %x",
maj_stat, min_stat,
port->gss->outbuf.length, gflags)));
if (port->gss->outbuf.length != 0)
{
/*
* Negotiation generated data to be sent to the client.
*/
ereport(DEBUG4,
(errmsg_internal("sending GSS response token of length %u",
port->gss->outbuf.length)));
sendAuthRequest(port, AUTH_REQ_GSS_CONT);
}
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
{
OM_uint32 lmin_s;
gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
pg_GSS_error(ERROR,
gettext_noop("accepting GSS security context failed"),
maj_stat, min_stat);
}
if (maj_stat == GSS_S_CONTINUE_NEEDED)
ereport(DEBUG4,
(errmsg_internal("GSS continue needed")));
} while (maj_stat == GSS_S_CONTINUE_NEEDED);
if (port->gss->cred != GSS_C_NO_CREDENTIAL)
{
/*
* Release service principal credentials
*/
gss_release_cred(&min_stat, port->gss->cred);
}
/*
* GSS_S_COMPLETE indicates that authentication is now complete.
*
* Get the name of the user that authenticated, and compare it to the
* pg username that was specified for the connection.
*/
maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
ereport(DEBUG1,
(errmsg("GSSAPI authenticated name: %s", (char *)gbuf.value)));
/*
* Compare the part of the username that comes before the @
* sign only (ignore realm). The GSSAPI libraries won't have
* authenticated the user if he's from an invalid realm.
*/
if (strchr(gbuf.value, '@'))
{
char *cp = strchr(gbuf.value, '@');
*cp = '\0';
}
if (pg_krb_caseins_users)
ret = pg_strcasecmp(port->user_name, gbuf.value);
else
ret = strcmp(port->user_name, gbuf.value);
if (ret)
/* GSS name and PGUSER are not equivalent */
ereport(ERROR,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("provided username and GSSAPI username don't match"),
errdetail("provided: %s, GSSAPI: %s",
port->user_name, (char *)gbuf.value)));
gss_release_buffer(&lmin_s, &gbuf);
return STATUS_OK;
}
#else /* no ENABLE_GSS */
static int
pg_GSS_recvauth(Port *port)
{
ereport(LOG,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("GSSAPI not implemented on this server.")));
return STATUS_ERROR;
}
#endif /* ENABLE_GSS */
/*
* Tell the user the authentication failed, but not (much about) why.
......@@ -334,6 +579,9 @@ auth_failed(Port *port, int status)
case uaKrb5:
errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
break;
case uaGSS:
errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
break;
case uaTrust:
errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
break;
......@@ -429,6 +677,11 @@ ClientAuthentication(Port *port)
status = pg_krb5_recvauth(port);
break;
case uaGSS:
sendAuthRequest(port, AUTH_REQ_GSS);
status = pg_GSS_recvauth(port);
break;
case uaIdent:
/*
......@@ -518,6 +771,24 @@ sendAuthRequest(Port *port, AuthRequest areq)
else if (areq == AUTH_REQ_CRYPT)
pq_sendbytes(&buf, port->cryptSalt, 2);
#ifdef ENABLE_GSS
/* Add the authentication data for the next step of
* the GSSAPI negotiation. */
else if (areq == AUTH_REQ_GSS_CONT)
{
if (port->gss->outbuf.length > 0)
{
OM_uint32 lmin_s;
ereport(DEBUG4,
(errmsg_internal("sending GSS token of length %u",
port->gss->outbuf.length)));
pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length);
gss_release_buffer(&lmin_s, &port->gss->outbuf);
}
}
#endif
pq_endmessage(&buf);
/*
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.160 2007/02/10 14:58:54 petere Exp $
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.161 2007/07/10 13:14:20 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -602,6 +602,8 @@ parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
*userauth_p = uaPassword;
else if (strcmp(token, "krb5") == 0)
*userauth_p = uaKrb5;
else if (strcmp(token, "gss") == 0)
*userauth_p = uaGSS;
else if (strcmp(token, "reject") == 0)
*userauth_p = uaReject;
else if (strcmp(token, "md5") == 0)
......
......@@ -34,7 +34,7 @@
# the number of significant bits in the mask. Alternatively, you can write
# an IP address and netmask in separate columns to specify the set of hosts.
#
# METHOD can be "trust", "reject", "md5", "crypt", "password",
# METHOD can be "trust", "reject", "md5", "crypt", "password", "gss",
# "krb5", "ident", "pam" or "ldap". Note that "password" sends passwords
# in clear text; "md5" is preferred since it sends encrypted passwords.
#
......
......@@ -30,7 +30,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.192 2007/06/04 11:59:20 mha Exp $
* $PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.193 2007/07/10 13:14:20 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -173,6 +173,16 @@ pq_close(int code, Datum arg)
{
if (MyProcPort != NULL)
{
#ifdef ENABLE_GSS
OM_uint32 min_s;
/* Shutdown GSSAPI layer */
if (MyProcPort->gss->ctx)
gss_delete_sec_context(&min_s, MyProcPort->gss->ctx, NULL);
if (MyProcPort->gss->cred)
gss_release_cred(&min_s, MyProcPort->gss->cred);
#endif
/* Cleanly shut down SSL layer */
secure_close(MyProcPort);
......
......@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.530 2007/07/01 18:28:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.531 2007/07/10 13:14:21 mha Exp $
*
* NOTES
*
......@@ -1727,6 +1727,13 @@ ConnCreate(int serverFd)
RandomSalt(port->cryptSalt, port->md5Salt);
}
/*
* Allocate GSSAPI specific state struct
*/
#ifdef ENABLE_GSS
port->gss = (pg_gssinfo *)calloc(1, sizeof(pg_gssinfo));
#endif
return port;
}
......@@ -1740,6 +1747,8 @@ ConnFree(Port *conn)
#ifdef USE_SSL
secure_close(conn);
#endif
if (conn->gss)
free(conn->gss);
free(conn);
}
......
......@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.404 2007/06/30 19:12:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.405 2007/07/10 13:14:21 mha Exp $
*
*--------------------------------------------------------------------
*/
......@@ -1040,7 +1040,7 @@ static struct config_bool ConfigureNamesBool[] =
{
{"krb_caseins_users", PGC_POSTMASTER, CONN_AUTH_SECURITY,
gettext_noop("Sets whether Kerberos user names should be treated as case-insensitive."),
gettext_noop("Sets whether Kerberos and GSSAPI user names should be treated as case-insensitive."),
NULL
},
&pg_krb_caseins_users,
......
......@@ -79,11 +79,11 @@
#password_encryption = on
#db_user_namespace = off
# Kerberos
# Kerberos and GSSAPI
#krb_server_keyfile = '' # (change requires restart)
#krb_srvname = 'postgres' # (change requires restart)
#krb_srvname = 'postgres' # (change requires restart, kerberos only)
#krb_server_hostname = '' # empty string matches any keytab entry
# (change requires restart)
# (change requires restart, kerberos only)
#krb_caseins_users = off # (change requires restart)
# - TCP Keepalives -
......
......@@ -4,7 +4,7 @@
* Interface to hba.c
*
*
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.45 2006/11/05 22:42:10 tgl Exp $
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.46 2007/07/10 13:14:21 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -22,7 +22,8 @@ typedef enum UserAuth
uaIdent,
uaPassword,
uaCrypt,
uaMD5
uaMD5,
uaGSS
#ifdef USE_PAM
,uaPAM
#endif /* USE_PAM */
......
......@@ -11,7 +11,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.58 2007/01/05 22:19:55 momjian Exp $
* $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.59 2007/07/10 13:14:21 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -29,6 +29,10 @@
#include <netinet/tcp.h>
#endif
#ifdef ENABLE_GSS
#include <gssapi/gssapi.h>
#endif
#include "libpq/hba.h"
#include "libpq/pqcomm.h"
#include "utils/timestamp.h"
......@@ -39,6 +43,20 @@ typedef enum CAC_state
CAC_OK, CAC_STARTUP, CAC_SHUTDOWN, CAC_RECOVERY, CAC_TOOMANY
} CAC_state;
/*
* GSSAPI specific state information
*/
#ifdef ENABLE_GSS
typedef struct
{
gss_cred_id_t cred; /* GSSAPI connection cred's */
gss_ctx_id_t ctx; /* GSSAPI connection context */
gss_name_t name; /* GSSAPI client name */
gss_buffer_desc outbuf; /* GSSAPI output token buffer */
} pg_gssinfo;
#endif
/*
* This is used by the postmaster in its communication with frontends. It
* contains all state information needed during this communication before the
......@@ -98,6 +116,17 @@ typedef struct Port
int keepalives_interval;
int keepalives_count;
#ifdef ENABLE_GSS
/*
* If GSSAPI is supported, store GSSAPI information.
* Oterwise, store a NULL pointer to make sure offsets
* in the struct remain the same.
*/
pg_gssinfo *gss;
#else
void *gss;
#endif
/*
* SSL structures (keep these last so that USE_SSL doesn't affect
* locations of other fields)
......
......@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/libpq/pqcomm.h,v 1.104 2007/07/08 18:28:55 tgl Exp $
* $PostgreSQL: pgsql/src/include/libpq/pqcomm.h,v 1.105 2007/07/10 13:14:21 mha Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -156,6 +156,8 @@ extern bool Db_user_namespace;
#define AUTH_REQ_CRYPT 4 /* crypt password */
#define AUTH_REQ_MD5 5 /* md5 password */
#define AUTH_REQ_SCM_CREDS 6 /* transfer SCM credentials */
#define AUTH_REQ_GSS 7 /* GSSAPI without wrap() */
#define AUTH_REQ_GSS_CONT 8 /* Continue GSS exchanges */
typedef uint32 AuthRequest;
......
......@@ -568,6 +568,9 @@
/* Define to the appropriate snprintf format for 64-bit ints, if any. */
#undef INT64_FORMAT
/* Define to build with GSSAPI support. (--with-gssapi) */
#undef ENABLE_GSS
/* Define to build with Kerberos 5 support. (--with-krb5) */
#undef KRB5
......
......@@ -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))
......
......@@ -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:
......
......@@ -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)
{
......
......@@ -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 === */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment