diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/libpq/auth.c | 273 | ||||
| -rw-r--r-- | src/backend/libpq/hba.c | 4 | ||||
| -rw-r--r-- | src/backend/libpq/pg_hba.conf.sample | 2 | ||||
| -rw-r--r-- | src/backend/libpq/pqcomm.c | 12 | ||||
| -rw-r--r-- | src/backend/postmaster/postmaster.c | 11 | ||||
| -rw-r--r-- | src/backend/utils/misc/guc.c | 4 | ||||
| -rw-r--r-- | src/backend/utils/misc/postgresql.conf.sample | 6 |
7 files changed, 302 insertions, 10 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 37bfa81abd..dafc965445 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.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); /* diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index a3accd47c5..9f917b1666 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -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) diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index f52d1bfe81..ebdf76904a 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -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. # diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index ee590be1dd..a40e6a6cb2 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -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); diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index b0dfb6cdbe..feb3db6402 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -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); } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a542a22565..feb5354940 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -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, diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 9729eb2ce7..738eb9a5f7 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -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 - |
