aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/auth.c')
-rw-r--r--src/backend/libpq/auth.c262
1 files changed, 255 insertions, 7 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 224115fde8e..c475c6429d6 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.153 2007/07/12 20:36:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.154 2007/07/23 10:16:53 mha Exp $
*
*-------------------------------------------------------------------------
*/
@@ -464,10 +464,14 @@ pg_GSS_recvauth(Port *port)
/*
* Negotiation generated data to be sent to the client.
*/
+ OM_uint32 lmin_s;
+
elog(DEBUG4, "sending GSS response token of length %u",
(unsigned int) port->gss->outbuf.length);
sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+ gss_release_buffer(&lmin_s, &port->gss->outbuf);
}
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
@@ -536,7 +540,7 @@ pg_GSS_recvauth(Port *port)
return STATUS_OK;
}
-#else /* no ENABLE_GSS */
+#else /* no ENABLE_GSS */
static int
pg_GSS_recvauth(Port *port)
{
@@ -547,6 +551,245 @@ pg_GSS_recvauth(Port *port)
}
#endif /* ENABLE_GSS */
+#ifdef ENABLE_SSPI
+static void
+pg_SSPI_error(int severity, char *errmsg, SECURITY_STATUS r)
+{
+ char sysmsg[256];
+
+ if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0, sysmsg, sizeof(sysmsg), NULL) == 0)
+ ereport(severity,
+ (errmsg_internal("%s", errmsg),
+ errdetail("sspi error %x", r)));
+ else
+ ereport(severity,
+ (errmsg_internal("%s", errmsg),
+ errdetail("%s (%x)", sysmsg, r)));
+}
+
+
+static int
+pg_SSPI_recvauth(Port *port)
+{
+ int mtype;
+ StringInfoData buf;
+ SECURITY_STATUS r;
+ CredHandle sspicred;
+ CtxtHandle *sspictx = NULL,
+ newctx;
+ TimeStamp expiry;
+ ULONG contextattr;
+ SecBufferDesc inbuf;
+ SecBufferDesc outbuf;
+ SecBuffer OutBuffers[1];
+ SecBuffer InBuffers[1];
+ HANDLE token;
+ TOKEN_USER *tokenuser;
+ DWORD retlen;
+ char accountname[MAXPGPATH];
+ char domainname[MAXPGPATH];
+ DWORD accountnamesize = sizeof(accountname);
+ DWORD domainnamesize = sizeof(domainname);
+ SID_NAME_USE accountnameuse;
+
+
+ /*
+ * Acquire a handle to the server credentials.
+ */
+ r = AcquireCredentialsHandle(NULL,
+ "negotiate",
+ SECPKG_CRED_INBOUND,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &sspicred,
+ &expiry);
+ if (r != SEC_E_OK)
+ pg_SSPI_error(ERROR,
+ gettext_noop("could not acquire SSPI credentials handle"), r);
+
+ /*
+ * Loop through SSPI 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 SSPI response, got message type %d",
+ mtype)));
+ return STATUS_ERROR;
+ }
+
+ /* Get the actual SSPI token */
+ initStringInfo(&buf);
+ if (pq_getmessage(&buf, 2000))
+ {
+ /* EOF - pq_getmessage already logged error */
+ pfree(buf.data);
+ return STATUS_ERROR;
+ }
+
+ /* Map to SSPI style buffer */
+ inbuf.ulVersion = SECBUFFER_VERSION;
+ inbuf.cBuffers = 1;
+ inbuf.pBuffers = InBuffers;
+ InBuffers[0].pvBuffer = buf.data;
+ InBuffers[0].cbBuffer = buf.len;
+ InBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ /* Prepare output buffer */
+ OutBuffers[0].pvBuffer = NULL;
+ OutBuffers[0].BufferType = SECBUFFER_TOKEN;
+ OutBuffers[0].cbBuffer = 0;
+ outbuf.cBuffers = 1;
+ outbuf.pBuffers = OutBuffers;
+ outbuf.ulVersion = SECBUFFER_VERSION;
+
+
+ elog(DEBUG4, "Processing received SSPI token of length %u",
+ (unsigned int) buf.len);
+
+ r = AcceptSecurityContext(&sspicred,
+ sspictx,
+ &inbuf,
+ ASC_REQ_ALLOCATE_MEMORY,
+ SECURITY_NETWORK_DREP,
+ &newctx,
+ &outbuf,
+ &contextattr,
+ NULL);
+
+ /* input buffer no longer used */
+ pfree(buf.data);
+
+ if (outbuf.cBuffers > 0 && outbuf.pBuffers[0].cbBuffer > 0)
+ {
+ /*
+ * Negotiation generated data to be sent to the client.
+ */
+ elog(DEBUG4, "sending SSPI response token of length %u",
+ (unsigned int) outbuf.pBuffers[0].cbBuffer);
+
+ port->gss->outbuf.length = outbuf.pBuffers[0].cbBuffer;
+ port->gss->outbuf.value = outbuf.pBuffers[0].pvBuffer;
+
+ sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+ FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
+ }
+
+ if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
+ {
+ if (sspictx != NULL)
+ {
+ DeleteSecurityContext(sspictx);
+ free(sspictx);
+ }
+ FreeCredentialsHandle(&sspicred);
+ pg_SSPI_error(ERROR,
+ gettext_noop("could not accept SSPI security context"), r);
+ }
+
+ if (sspictx == NULL)
+ {
+ sspictx = malloc(sizeof(CtxtHandle));
+ if (sspictx == NULL)
+ ereport(ERROR,
+ (errmsg("out of memory")));
+
+ memcpy(sspictx, &newctx, sizeof(CtxtHandle));
+ }
+
+ if (r == SEC_I_CONTINUE_NEEDED)
+ elog(DEBUG4, "SSPI continue needed");
+
+ } while (r == SEC_I_CONTINUE_NEEDED);
+
+
+ /*
+ * Release service principal credentials
+ */
+ FreeCredentialsHandle(&sspicred);
+
+
+ /*
+ * SEC_E_OK 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.
+ */
+
+ r = QuerySecurityContextToken(sspictx, &token);
+ if (r != SEC_E_OK)
+ pg_SSPI_error(ERROR,
+ gettext_noop("could not get security token from context"), r);
+
+ /*
+ * No longer need the security context, everything from here on uses the
+ * token instead.
+ */
+ DeleteSecurityContext(sspictx);
+ free(sspictx);
+
+ if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
+ ereport(ERROR,
+ (errmsg_internal("could not get token user size: error code %d",
+ (int) GetLastError())));
+
+ tokenuser = malloc(retlen);
+ if (tokenuser == NULL)
+ ereport(ERROR,
+ (errmsg("out of memory")));
+
+ if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
+ ereport(ERROR,
+ (errmsg_internal("could not get user token: error code %d",
+ (int) GetLastError())));
+
+ if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
+ domainname, &domainnamesize, &accountnameuse))
+ ereport(ERROR,
+ (errmsg_internal("could not lookup acconut sid: error code %d",
+ (int) GetLastError())));
+
+ free(tokenuser);
+
+ /*
+ * We have the username (without domain/realm) in accountname, compare
+ * to the supplied value. In SSPI, always compare case insensitive.
+ */
+ if (pg_strcasecmp(port->user_name, accountname))
+ {
+ /* GSS name and PGUSER are not equivalent */
+ elog(DEBUG2,
+ "provided username (%s) and SSPI username (%s) don't match",
+ port->user_name, accountname);
+
+ return STATUS_ERROR;
+ }
+
+ return STATUS_OK;
+}
+#else /* no ENABLE_SSPI */
+static int
+pg_SSPI_recvauth(Port *port)
+{
+ ereport(LOG,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SSPI not implemented on this server.")));
+ return STATUS_ERROR;
+}
+#endif /* ENABLE_SSPI */
+
/*
* Tell the user the authentication failed, but not (much about) why.
@@ -589,6 +832,9 @@ auth_failed(Port *port, int status)
case uaGSS:
errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
break;
+ case uaSSPI:
+ errstr = gettext_noop("SSPI authentication failed for user \"%s\"");
+ break;
case uaTrust:
errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
break;
@@ -689,6 +935,11 @@ ClientAuthentication(Port *port)
status = pg_GSS_recvauth(port);
break;
+ case uaSSPI:
+ sendAuthRequest(port, AUTH_REQ_SSPI);
+ status = pg_SSPI_recvauth(port);
+ break;
+
case uaIdent:
/*
@@ -778,20 +1029,17 @@ sendAuthRequest(Port *port, AuthRequest areq)
else if (areq == AUTH_REQ_CRYPT)
pq_sendbytes(&buf, port->cryptSalt, 2);
-#ifdef ENABLE_GSS
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
/* Add the authentication data for the next step of
- * the GSSAPI negotiation. */
+ * the GSSAPI or SSPI negotiation. */
else if (areq == AUTH_REQ_GSS_CONT)
{
if (port->gss->outbuf.length > 0)
{
- OM_uint32 lmin_s;
-
elog(DEBUG4, "sending GSS token of length %u",
(unsigned int) port->gss->outbuf.length);
pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length);
- gss_release_buffer(&lmin_s, &port->gss->outbuf);
}
}
#endif