diff options
Diffstat (limited to 'src/interfaces')
-rw-r--r-- | src/interfaces/libpq/fe-auth.c | 277 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 51 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-int.h | 40 |
3 files changed, 324 insertions, 44 deletions
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 4c3207f63a6..e08dae8125d 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.127 2007/07/12 14:43:21 mha Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.128 2007/07/23 10:16:54 mha Exp $ * *------------------------------------------------------------------------- */ @@ -329,11 +329,6 @@ pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *s /* * GSSAPI authentication system. */ -#if defined(HAVE_GSSAPI_H) -#include <gssapi.h> -#else -#include <gssapi/gssapi.h> -#endif #if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) /* @@ -378,7 +373,7 @@ pg_GSS_error_int(char *mprefix, char *msg, int msglen, * GSSAPI errors contains two parts. Put as much as possible of * both parts into the string. */ -void +static void pg_GSS_error(char *mprefix, char *msg, int msglen, OM_uint32 maj_stat, OM_uint32 min_stat) { @@ -407,7 +402,7 @@ pg_GSS_continue(char *PQerrormsg, PGconn *conn) &conn->gctx, conn->gtarg_nam, GSS_C_NO_OID, - conn->gflags, + GSS_C_MUTUAL_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, (conn->gctx==GSS_C_NO_CONTEXT)?GSS_C_NO_BUFFER:&conn->ginbuf, @@ -504,7 +499,192 @@ pg_GSS_startup(char *PQerrormsg, PGconn *conn) return pg_GSS_continue(PQerrormsg, conn); } -#endif +#endif /* ENABLE_GSS */ + + +#ifdef ENABLE_SSPI +/* + * SSPI authentication system (Windows only) + */ + +static void +pg_SSPI_error(char *mprefix, char *msg, int msglen, SECURITY_STATUS r) +{ + char sysmsg[256]; + + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0, sysmsg, sizeof(sysmsg), NULL) == 0) + snprintf(msg, msglen, "%s: sspi error %x", mprefix, r); + else + snprintf(msg, msglen, "%s: %s (%x)", mprefix, sysmsg, r); +} + +/* + * Continue SSPI authentication with next token as needed. + */ +static int +pg_SSPI_continue(char *PQerrormsg, PGconn *conn) +{ + SECURITY_STATUS r; + CtxtHandle newContext; + ULONG contextAttr; + SecBufferDesc inbuf; + SecBufferDesc outbuf; + SecBuffer OutBuffers[1]; + SecBuffer InBuffers[1]; + + if (conn->sspictx != NULL) + { + /* + * On runs other than the first we have some data to send. Put this + * data in a SecBuffer type structure. + */ + inbuf.ulVersion = SECBUFFER_VERSION; + inbuf.cBuffers = 1; + inbuf.pBuffers = InBuffers; + InBuffers[0].pvBuffer = conn->ginbuf.value; + InBuffers[0].cbBuffer = conn->ginbuf.length; + InBuffers[0].BufferType = SECBUFFER_TOKEN; + } + + OutBuffers[0].pvBuffer = NULL; + OutBuffers[0].BufferType = SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = 0; + outbuf.cBuffers = 1; + outbuf.pBuffers = OutBuffers; + outbuf.ulVersion = SECBUFFER_VERSION; + + r = InitializeSecurityContext(conn->sspicred, + conn->sspictx, + conn->sspitarget, + ISC_REQ_ALLOCATE_MEMORY, + 0, + SECURITY_NETWORK_DREP, + (conn->sspictx == NULL)?NULL:&inbuf, + 0, + &newContext, + &outbuf, + &contextAttr, + NULL); + + if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED) + { + pg_SSPI_error(libpq_gettext("SSPI continuation error"), + PQerrormsg, PQERRORMSG_LENGTH, r); + + return STATUS_ERROR; + } + + if (conn->sspictx == NULL) + { + /* On first run, transfer retreived context handle */ + conn->sspictx = malloc(sizeof(CtxtHandle)); + if (conn->sspictx == NULL) + { + strncpy(PQerrormsg, libpq_gettext("out of memory\n"), PQERRORMSG_LENGTH); + return STATUS_ERROR; + } + memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle)); + } + else + { + /* + * On subsequent runs when we had data to send, free buffers that contained + * this data. + */ + free(conn->ginbuf.value); + conn->ginbuf.value = NULL; + conn->ginbuf.length = 0; + } + + /* + * If SSPI returned any data to be sent to the server (as it normally would), + * send this data as a password packet. + */ + if (outbuf.cBuffers > 0) + { + if (outbuf.cBuffers != 1) + { + /* + * This should never happen, at least not for Kerberos authentication. Keep check + * in case it shows up with other authentication methods later. + */ + strncpy(PQerrormsg, "SSPI returned invalid number of output buffers\n", PQERRORMSG_LENGTH); + return STATUS_ERROR; + } + + if (pqPacketSend(conn, 'p', + outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer)) + { + FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); + return STATUS_ERROR; + } + FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); + } + + /* Cleanup is handled by the code in freePGconn() */ + return STATUS_OK; +} + +/* + * Send initial SSPI authentication token. + * If use_negotiate is 0, use kerberos authentication package which is + * compatible with Unix. If use_negotiate is 1, use the negotiate package + * which supports both kerberos and NTLM, but is not compatible with Unix. + */ +static int +pg_SSPI_startup(char *PQerrormsg, PGconn *conn, int use_negotiate) +{ + SECURITY_STATUS r; + TimeStamp expire; + + conn->sspictx = NULL; + + /* + * Retreive credentials handle + */ + conn->sspicred = malloc(sizeof(CredHandle)); + if (conn->sspicred == NULL) + { + strncpy(PQerrormsg, libpq_gettext("out of memory\n"), PQERRORMSG_LENGTH); + return STATUS_ERROR; + } + + r = AcquireCredentialsHandle(NULL, use_negotiate?"negotiate":"kerberos", SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, conn->sspicred, &expire); + if (r != SEC_E_OK) + { + pg_SSPI_error("acquire credentials failed", PQerrormsg, PQERRORMSG_LENGTH, r); + free(conn->sspicred); + conn->sspicred = NULL; + return STATUS_ERROR; + } + + /* + * Compute target principal name. SSPI has a different format from GSSAPI, but + * not more complex. We can skip the @REALM part, because Windows will fill that + * in for us automatically. + */ + if (conn->pghost == NULL) + { + strncpy(PQerrormsg, libpq_gettext("hostname must be specified\n"), PQERRORMSG_LENGTH); + return STATUS_ERROR; + } + conn->sspitarget = malloc(strlen(conn->krbsrvname)+strlen(conn->pghost)+2); + if (!conn->sspitarget) + { + strncpy(PQerrormsg, libpq_gettext("out of memory\n"), PQERRORMSG_LENGTH); + return STATUS_ERROR; + } + sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost); + + /* + * Indicate that we're in SSPI authentication mode to make sure that + * pg_SSPI_continue is called next time in the negotiation. + */ + conn->usesspi = 1; + + return pg_SSPI_continue(PQerrormsg, conn); +} +#endif /* ENABLE_SSPI */ /* * Respond to AUTH_REQ_SCM_CREDS challenge. @@ -671,27 +851,60 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname, return STATUS_ERROR; #endif -#ifdef ENABLE_GSS +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) case AUTH_REQ_GSS: - pglock_thread(); - if (pg_GSS_startup(PQerrormsg, conn) != STATUS_OK) { - /* PQerrormsg already filled in. */ + int r; + pglock_thread(); + /* + * If we have both GSS and SSPI support compiled in, use SSPI + * support by default. This is overridable by a connection string parameter. + * Note that when using SSPI we still leave the negotiate parameter off, + * since we want SSPI to use the GSSAPI kerberos protocol. For actual + * SSPI negotiate protocol, we use AUTH_REQ_SSPI. + */ +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0)) + r = pg_GSS_startup(PQerrormsg, conn); + else + r = pg_SSPI_startup(PQerrormsg, conn, 0); +#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) + r = pg_GSS_startup(PQerrormsg, conn); +#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) + r = pg_SSPI_startup(PQerrormsg, conn, 0); +#endif + if (r != STATUS_OK) + { + /* PQerrormsg already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } 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. */ + int r; + pglock_thread(); +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + if (conn->usesspi) + r = pg_SSPI_continue(PQerrormsg, conn); + else + r = pg_GSS_continue(PQerrormsg, conn); +#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) + r = pg_GSS_continue(PQerrormsg, conn); +#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) + r = pg_SSPI_continue(PQerrormsg, conn); +#endif + if (r != STATUS_OK) + { + /* PQerrormsg already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } pgunlock_thread(); - return STATUS_ERROR; } - pgunlock_thread(); break; #else @@ -702,6 +915,30 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname, return STATUS_ERROR; #endif +#ifdef ENABLE_SSPI + case AUTH_REQ_SSPI: + /* + * SSPI has it's own startup message so libpq can decide which + * method to use. Indicate to pg_SSPI_startup that we want + * SSPI negotiation instead of Kerberos. + */ + pglock_thread(); + if (pg_SSPI_startup(PQerrormsg, conn, 1) != STATUS_OK) + { + /* PQerrormsg already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } + pgunlock_thread(); + break; +#else + case AUTH_REQ_SSPI: + snpritnf(PQerrormsg, PQERRORMSG_LENGTH, + libpq_gettext("SSPI 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 aebce1caf6e..f3eafb1eeb4 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.349 2007/07/11 08:27:33 mha Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.350 2007/07/23 10:16:54 mha Exp $ * *------------------------------------------------------------------------- */ @@ -181,12 +181,18 @@ static const PQconninfoOption PQconninfoOptions[] = { {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL, "SSL-Mode", "", 8}, /* sizeof("disable") == 8 */ -#if defined(KRB5) || defined(ENABLE_GSS) +#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) /* Kerberos and GSSAPI authentication support specifying the service name */ {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL, "Kerberos-service-name", "", 20}, #endif +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + /* GSSAPI and SSPI both enabled, give a way to override which is used by default */ + {"gsslib", "PGGSSLIB", NULL, NULL, + "GSS-library", "", 7}, /* sizeof("gssapi") = 7 */ +#endif + /* Terminating entry --- MUST BE LAST */ {NULL, NULL, NULL, NULL, NULL, NULL, 0} @@ -412,10 +418,14 @@ connectOptions1(PGconn *conn, const char *conninfo) conn->sslmode = strdup("require"); } #endif -#if defined(KRB5) || defined(ENABLE_GSS) +#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) tmp = conninfo_getval(connOptions, "krbsrvname"); conn->krbsrvname = tmp ? strdup(tmp) : NULL; #endif +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + tmp = conninfo_getval(connOptions, "gsslib"); + conn->gsslib = tmp ? strdup(tmp) : NULL; +#endif /* * Free the option info - all is in conn now @@ -1661,22 +1671,13 @@ keep_going: /* We will come back to here until there is return PGRES_POLLING_READING; } } -#ifdef ENABLE_GSS +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) /* - * 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 + * Continue GSSAPI/SSPI authentication */ 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 - @@ -2002,7 +2003,7 @@ freePGconn(PGconn *conn) free(conn->pgpass); if (conn->sslmode) free(conn->sslmode); -#if defined(KRB5) || defined(ENABLE_GSS) +#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) if (conn->krbsrvname) free(conn->krbsrvname); #endif @@ -2031,6 +2032,26 @@ freePGconn(PGconn *conn) gss_release_buffer(&min_s, &conn->goutbuf); } #endif +#ifdef ENABLE_SSPI + { + if (conn->ginbuf.length) + free(conn->ginbuf.value); + + if (conn->sspitarget) + free(conn->sspitarget); + + if (conn->sspicred) + { + FreeCredentialsHandle(conn->sspicred); + free(conn->sspicred); + } + if (conn->sspictx) + { + DeleteSecurityContext(conn->sspictx); + free(conn->sspictx); + } + } +#endif pstatus = conn->pstatus; while (pstatus != NULL) { diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index b497e1d68d1..cc3fa0106d2 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.123 2007/07/12 14:36:52 mha Exp $ + * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.124 2007/07/23 10:16:54 mha Exp $ * *------------------------------------------------------------------------- */ @@ -52,6 +52,22 @@ #endif #endif +#ifdef ENABLE_SSPI +#define SECURITY_WIN32 +#include <security.h> +#undef SECURITY_WIN32 + +#ifndef ENABLE_GSS +/* + * Define a fake structure compatible with GSSAPI on Unix. + */ +typedef struct { + void *value; + int length; +} gss_buffer_desc; +#endif +#endif /* ENABLE_SSPI */ + #ifdef USE_SSL #include <openssl/ssl.h> #include <openssl/err.h> @@ -276,7 +292,7 @@ struct pg_conn char *pguser; /* Postgres username and password, if any */ char *pgpass; char *sslmode; /* SSL mode (require,prefer,allow,disable) */ -#if defined(KRB5) || defined(ENABLE_GSS) +#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) char *krbsrvname; /* Kerberos service name */ #endif @@ -361,11 +377,23 @@ struct pg_conn #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 +#ifdef ENABLE_SSPI +#ifndef ENABLE_GSS + gss_buffer_desc ginbuf; /* GSS input token */ +#else + char *gsslib; /* What GSS librart to use ("gssapi" or "sspi") */ +#endif + CredHandle *sspicred; /* SSPI credentials handle */ + CtxtHandle *sspictx; /* SSPI context */ + char *sspitarget;/* SSPI target name */ + int usesspi; /* Indicate if SSPI is in use on the connection */ +#endif + + /* Buffer for current error message */ PQExpBufferData errorMessage; /* expansible string */ @@ -415,12 +443,6 @@ 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 === */ extern void pqSetResultError(PGresult *res, const char *msg); |