diff options
author | Magnus Hagander <magnus@hagander.net> | 2007-07-10 13:14:22 +0000 |
---|---|---|
committer | Magnus Hagander <magnus@hagander.net> | 2007-07-10 13:14:22 +0000 |
commit | 6160106c7495509310b709ee320b55cea8a60928 (patch) | |
tree | 3a1be553f436ebf563d6ec4aaae3eff42d615db9 /src/interfaces/libpq | |
parent | ff481ca0d463d33246a331c92bdf3dfc018c24eb (diff) | |
download | postgresql-6160106c7495509310b709ee320b55cea8a60928.tar.gz postgresql-6160106c7495509310b709ee320b55cea8a60928.zip |
Add support for GSSAPI authentication.
Documentation still being written, will be committed later.
Henry B. Hotz and Magnus Hagander
Diffstat (limited to 'src/interfaces/libpq')
-rw-r--r-- | src/interfaces/libpq/Makefile | 6 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-auth.c | 209 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 65 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-int.h | 21 |
4 files changed, 288 insertions, 13 deletions
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile index 2b33e766b96..7d5ede01cc7 100644 --- a/src/interfaces/libpq/Makefile +++ b/src/interfaces/libpq/Makefile @@ -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)) diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 82d7394457d..8e6dca6fb05 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -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: diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 5263099ba49..25768f1964f 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -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) { diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 98080f2350a..cbefdd8e0fb 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -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 === */ |