diff --git a/doc/README.kerberos b/doc/README.kerberos index 4e617b7e06..189d035b91 100644 --- a/doc/README.kerberos +++ b/doc/README.kerberos @@ -1,45 +1,10 @@ -From pgsql-patches-owner@hub.org Mon May 8 13:28:54 2000 -Received: from walter.doc.ic.ac.uk (IDENT:VjgMrPQKQlAhOUagIW0/IaLLtzgPtWaj@walter.doc.ic.ac.uk [146.169.2.50]) - by hub.org (8.9.3/8.9.3) with ESMTP id NAA78580 - for ; Mon, 8 May 2000 13:27:41 -0400 (EDT) - (envelope-from mw@doc.ic.ac.uk) -Received: from [146.169.51.42] (helo=kungfu.doc.ic.ac.uk ident=mw) - by walter.doc.ic.ac.uk with esmtp (Exim 1.890 #1) - for pgsql-patches@postgresql.org - id 12orKe-0000J8-00; Mon, 8 May 2000 18:28:36 +0100 -Date: Mon, 8 May 2000 18:27:40 +0100 (BST) -From: Mike Wyer -To: pgsql-patches@postgresql.org -Subject: kerberos 5 patch against 7.0RC5 -Message-ID: -MIME-Version: 1.0 -Content-Type: TEXT/PLAIN; charset=US-ASCII -X-Archive-Number: 200005/24 -Status: ORr - -You can find it after my sig. Hideous abuse of netiquette, but needs -must ... - -Most (nearly all) of the work was done by David Wragg - -He patched 6.5.3. I've updated it for 7.0RC5. - -It works for MIT kerberos 1.1.1 (and previously for 1.0.6 as well). - -I've got the patch against 6.5.3, plus kerberized RPMS. - -Install: - -Assuming postgresql-7.0RC5 is in /usr/local/src - -cd /usr/local/src -patch -p0 < krb5-patch - Edit postgresql-7.0RC5/src/Makefile.global.in Change PG_KRB_SRVTAB to somewhere useful for you, and PG_KRB_SRVNAM to whatever you want your postgres kerberos service called. -make and install PostgreSQL. +Uncommment out KRBVERS=5 in Makefile.global.in. + +Run configure, make, and install PostgreSQL. Generate the keytab (PG_KRB_SRVTAB): kadmin% ank -randkey postgres/server.my.domain.org @@ -64,659 +29,3 @@ Mike Wyer || "Woof?" http://www.doc.ic.ac.uk/~mw || Gaspode the Wonder Dog Work: 020 7594 8440 || from "Moving Pictures" Mobile: 07879 697119 || by Terry Pratchett - - -===========================8<---------------------------------------------- - -diff -u -r postgresql-7.0RC5/src/Makefile.global.in postgresql-7.0RC5.krb5/src/Makefile.global.in ---- postgresql-7.0RC5/src/Makefile.global.in Mon May 8 17:22:40 2000 -+++ postgresql-7.0RC5.krb5/src/Makefile.global.in Sat May 6 17:23:49 2000 -@@ -120,7 +120,7 @@ - # Set KRBVERS to "4" for Kerberos v4, "5" for Kerberos v5. - # XXX Edit the default Kerberos variables below! - # --#KRBVERS= 5 -+KRBVERS=5 - - # Globally pass Kerberos file locations. - # these are used in the postmaster and all libpq applications. -@@ -132,9 +132,9 @@ - # PG_KRB_SRVTAB is the location of the server's keytab file. - # - ifdef KRBVERS --KRBINCS= -I/usr/athena/include --KRBLIBS= -L/usr/athena/lib --KRBFLAGS+= $(KRBINCS) -DPG_KRB_SRVNAM='"postgres_dbms"' -+KRBINCS= -I/usr/krb5/include -+KRBLIBS= -L/usr/krb5/lib -+KRBFLAGS+= $(KRBINCS) -DPG_KRB_SRVNAM='"postgres"' - ifeq ($(KRBVERS), 4) - KRBFLAGS+= -DKRB4 - KRBFLAGS+= -DPG_KRB_SRVTAB='"/etc/srvtab"' -@@ -142,8 +142,8 @@ - else - ifeq ($(KRBVERS), 5) - KRBFLAGS+= -DKRB5 --KRBFLAGS+= -DPG_KRB_SRVTAB='"FILE:/krb5/srvtab.postgres"' --KRBLIBS+= -lkrb5 -lcrypto -lcom_err -lisode -+KRBFLAGS+= -DPG_KRB_SRVTAB='"FILE:/usr/local/postgres/krb5.keytab"' -+KRBLIBS+= -lkrb5 -lcrypto -lcom_err - endif - endif - endif -diff -u -r postgresql-7.0RC5/src/backend/libpq/auth.c postgresql-7.0RC5.krb5/src/backend/libpq/auth.c ---- postgresql-7.0RC5/src/backend/libpq/auth.c Mon May 8 17:22:40 2000 -+++ postgresql-7.0RC5.krb5/src/backend/libpq/auth.c Sat May 6 17:17:13 2000 -@@ -149,7 +149,8 @@ - *---------------------------------------------------------------- - */ - --#include "krb5/krb5.h" -+#include -+#include - - /* - * pg_an_to_ln -- return the local name corresponding to an authentication -@@ -174,130 +175,134 @@ - return aname; - } - -+ - /* -- * pg_krb5_recvauth -- server routine to receive authentication information -- * from the client -- * -- * We still need to compare the username obtained from the client's setup -- * packet to the authenticated name, as described in pg_krb4_recvauth. This -- * is a bit more problematic in v5, as described above in pg_an_to_ln. -- * -- * In addition, as described above in pg_krb5_sendauth, we still need to -- * canonicalize the server name v4-style before constructing a principal -- * from it. Again, this is kind of iffy. -- * -- * Finally, we need to tangle with the fact that v5 doesn't let you explicitly -- * set server keytab file names -- you have to feed lower-level routines a -- * function to retrieve the contents of a keytab, along with a single argument -- * that allows them to open the keytab. We assume that a server keytab is -- * always a real file so we can allow people to specify their own filenames. -- * (This is important because the POSTGRES keytab needs to be readable by -- * non-root users/groups; the v4 tools used to force you do dump a whole -- * host's worth of keys into a file, effectively forcing you to use one file, -- * but kdb5_edit allows you to select which principals to dump. Yay!) -+ * Various krb5 state which is not connection specfic, and a flag to -+ * indicate whether we have initialised it yet. - */ -+static int pg_krb5_initialised; -+static krb5_context pg_krb5_context; -+static krb5_keytab pg_krb5_keytab; -+static krb5_principal pg_krb5_server; -+ -+ - static int --pg_krb5_recvauth(Port *port) -+pg_krb5_init(void) - { -- char servbuf[MAXHOSTNAMELEN + 1 + -- sizeof(PG_KRB_SRVNAM)]; -- char *hostp, -- *kusername = (char *) NULL; -- krb5_error_code code; -- krb5_principal client, -- server; -- krb5_address sender_addr; -- krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL; -- krb5_pointer keyprocarg = (krb5_pointer) NULL; -+ krb5_error_code retval; - -- /* -- * Set up server side -- since we have no ticket file to make this -- * easy, we construct our own name and parse it. See note on -- * canonicalization above. -- */ -- strcpy(servbuf, PG_KRB_SRVNAM); -- *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/'; -- if (gethostname(++hostp, MAXHOSTNAMELEN) < 0) -- strcpy(hostp, "localhost"); -- if (hostp = strchr(hostp, '.')) -- *hostp = '\0'; -- if (code = krb5_parse_name(servbuf, &server)) -- { -+ if (pg_krb5_initialised) -+ return STATUS_OK; -+ -+ retval = krb5_init_context(&pg_krb5_context); -+ if (retval) { - snprintf(PQerrormsg, PQERRORMSG_LENGTH, -- "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n", code); -- com_err("pg_krb5_recvauth", code, "in krb5_parse_name"); -+ "pg_krb5_init: krb5_init_context returned" -+ " Kerberos error %d\n", retval); -+ com_err("postgres", retval, "while initializing krb5"); - return STATUS_ERROR; - } - -- /* -- * krb5_sendauth needs this to verify the address in the client -- * authenticator. -- */ -- sender_addr.addrtype = port->raddr.in.sin_family; -- sender_addr.length = sizeof(port->raddr.in.sin_addr); -- sender_addr.contents = (krb5_octet *) & (port->raddr.in.sin_addr); -- -- if (strcmp(PG_KRB_SRVTAB, "")) -- { -- keyproc = krb5_kt_read_service_key; -- keyprocarg = PG_KRB_SRVTAB; -+ retval = krb5_kt_resolve(pg_krb5_context, PG_KRB_SRVTAB, &pg_krb5_keytab); -+ if (retval) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_init: krb5_kt_resolve returned" -+ " Kerberos error %d\n", retval); -+ com_err("postgres", retval, "while resolving keytab file %s", -+ PG_KRB_SRVTAB); -+ krb5_free_context(pg_krb5_context); -+ return STATUS_ERROR; - } - -- if (code = krb5_recvauth((krb5_pointer) & port->sock, -- PG_KRB5_VERSION, -- server, -- &sender_addr, -- (krb5_pointer) NULL, -- keyproc, -- keyprocarg, -- (char *) NULL, -- (krb5_int32 *) NULL, -- &client, -- (krb5_ticket **) NULL, -- (krb5_authenticator **) NULL)) -- { -+ retval = krb5_sname_to_principal(pg_krb5_context, NULL, PG_KRB_SRVNAM, -+ KRB5_NT_SRV_HST, &pg_krb5_server); -+ if (retval) { - snprintf(PQerrormsg, PQERRORMSG_LENGTH, -- "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n", code); -- com_err("pg_krb5_recvauth", code, "in krb5_recvauth"); -- krb5_free_principal(server); -+ "pg_krb5_init: krb5_sname_to_principal returned" -+ " Kerberos error %d\n", retval); -+ com_err("postgres", retval, -+ "while getting server principal for service %s", -+ PG_KRB_SRVTAB); -+ krb5_kt_close(pg_krb5_context, pg_krb5_keytab); -+ krb5_free_context(pg_krb5_context); - return STATUS_ERROR; - } -- krb5_free_principal(server); -+ -+ pg_krb5_initialised = 1; -+ return STATUS_OK; -+} -+ -+ -+/* -+ * pg_krb5_recvauth -- server routine to receive authentication information -+ * from the client -+ * -+ * We still need to compare the username obtained from the client's setup -+ * packet to the authenticated name, as described in pg_krb4_recvauth. This -+ * is a bit more problematic in v5, as described above in pg_an_to_ln. -+ * -+ * We have our own keytab file because postgres is unlikely to run as root, -+ * and so cannot read the default keytab. -+ */ -+static int -+pg_krb5_recvauth(Port *port) -+{ -+ krb5_error_code retval; -+ int ret; -+ krb5_auth_context auth_context = NULL; -+ krb5_ticket *ticket; -+ char *kusername; -+ -+ ret = pg_krb5_init(); -+ if (ret != STATUS_OK) -+ return ret; -+ -+ retval = krb5_recvauth(pg_krb5_context, &auth_context, -+ (krb5_pointer)&port->sock, PG_KRB_SRVNAM, -+ pg_krb5_server, 0, pg_krb5_keytab, &ticket); -+ if (retval) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_recvauth: krb5_recvauth returned" -+ " Kerberos error %d\n", retval); -+ com_err("postgres", retval, "from krb5_recvauth"); -+ return STATUS_ERROR; -+ } - - /* - * The "client" structure comes out of the ticket and is therefore - * authenticated. Use it to check the username obtained from the - * postmaster startup packet. -+ * -+ * I have no idea why this is considered necessary. - */ -- if ((code = krb5_unparse_name(client, &kusername))) -- { -+ retval = krb5_unparse_name(pg_krb5_context, -+ ticket->enc_part2->client, &kusername); -+ if (retval) { - snprintf(PQerrormsg, PQERRORMSG_LENGTH, -- "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n", code); -- com_err("pg_krb5_recvauth", code, "in krb5_unparse_name"); -- krb5_free_principal(client); -- return STATUS_ERROR; -- } -- krb5_free_principal(client); -- if (!kusername) -- { -- snprintf(PQerrormsg, PQERRORMSG_LENGTH, -- "pg_krb5_recvauth: could not decode username\n"); -- fputs(PQerrormsg, stderr); -- pqdebug("%s", PQerrormsg); -+ "pg_krb5_recvauth: krb5_unparse_name returned" -+ " Kerberos error %d\n", retval); -+ com_err("postgres", retval, "while unparsing client name"); -+ krb5_free_ticket(pg_krb5_context, ticket); -+ krb5_auth_con_free(pg_krb5_context, auth_context); - return STATUS_ERROR; - } -+ - kusername = pg_an_to_ln(kusername); -- if (strncmp(username, kusername, SM_USER)) -+ if (strncmp(port->user, kusername, SM_USER)) - { - snprintf(PQerrormsg, PQERRORMSG_LENGTH, -- "pg_krb5_recvauth: name \"%s\" != \"%s\"\n", port->user, kusername); -- fputs(PQerrormsg, stderr); -- pqdebug("%s", PQerrormsg); -- pfree(kusername); -- return STATUS_ERROR; -- } -- pfree(kusername); -- return STATUS_OK; -+ "pg_krb5_recvauth: user name \"%s\" != krb5 name \"%s\"\n", -+ port->user, kusername); -+ ret = STATUS_ERROR; -+ } -+ else -+ ret = STATUS_OK; -+ -+ krb5_free_ticket(pg_krb5_context, ticket); -+ krb5_auth_con_free(pg_krb5_context, auth_context); -+ free(kusername); -+ -+ return ret; - } - - #else -diff -u -r postgresql-7.0RC5/src/interfaces/libpq/Makefile.in postgresql-7.0RC5.krb5/src/interfaces/libpq/Makefile.in ---- postgresql-7.0RC5/src/interfaces/libpq/Makefile.in Mon May 8 17:22:40 2000 -+++ postgresql-7.0RC5.krb5/src/interfaces/libpq/Makefile.in Sat May 6 17:17:13 2000 -@@ -21,6 +21,7 @@ - - ifdef KRBVERS - CFLAGS+= $(KRBFLAGS) -+SHLIB_LINK += $(KRBLIBS) - endif - - OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ -diff -u -r postgresql-7.0RC5/src/interfaces/libpq/fe-auth.c postgresql-7.0RC5.krb5/src/interfaces/libpq/fe-auth.c ---- postgresql-7.0RC5/src/interfaces/libpq/fe-auth.c Mon May 8 17:22:40 2000 -+++ postgresql-7.0RC5.krb5/src/interfaces/libpq/fe-auth.c Sat May 6 17:17:13 2000 -@@ -39,6 +39,7 @@ - #include "win32.h" - #else - #include -+#include - #include /* for MAXHOSTNAMELEN on most */ - #ifndef MAXHOSTNAMELEN - #include /* for MAXHOSTNAMELEN on some */ -@@ -234,7 +235,8 @@ - *---------------------------------------------------------------- - */ - --#include "krb5/krb5.h" -+#include -+#include - - /* - * pg_an_to_ln -- return the local name corresponding to an authentication -@@ -250,7 +252,7 @@ - * and we can't afford to punt. - */ - static char * --pg_an_to_ln(const char *aname) -+pg_an_to_ln(char *aname) - { - char *p; - -@@ -261,197 +263,160 @@ - - - /* -- * pg_krb5_init -- initialization performed before any Kerberos calls are made -- * -- * With v5, we can no longer set the ticket (credential cache) file name; -- * we now have to provide a file handle for the open (well, "resolved") -- * ticket file everywhere. -- * -+ * Various krb5 state which is not connection specfic, and a flag to -+ * indicate whether we have initialised it yet. - */ -+static int pg_krb5_initialised; -+static krb5_context pg_krb5_context; -+static krb5_ccache pg_krb5_ccache; -+static krb5_principal pg_krb5_client; -+static char *pg_krb5_name; -+ -+ - static int -- krb5_ccache --pg_krb5_init(void) -+pg_krb5_init(char *PQerrormsg) - { -- krb5_error_code code; -- char *realm, -- *defname; -- char tktbuf[MAXPGPATH]; -- static krb5_ccache ccache = (krb5_ccache) NULL; -+ krb5_error_code retval; - -- if (ccache) -- return ccache; -+ if (pg_krb5_initialised) -+ return STATUS_OK; - -- /* -- * If the user set PGREALM, then we use a ticket file with a special -- * name: @ -- */ -- if (!(defname = krb5_cc_default_name())) -- { -- (void) sprintf(PQerrormsg, -- "pg_krb5_init: krb5_cc_default_name failed\n"); -- return (krb5_ccache) NULL; -- } -- strcpy(tktbuf, defname); -- if (realm = getenv("PGREALM")) -- { -- strcat(tktbuf, "@"); -- strcat(tktbuf, realm); -+ retval = krb5_init_context(&pg_krb5_context); -+ if (retval) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_init: krb5_init_context: %s", -+ error_message(retval)); -+ return STATUS_ERROR; - } - -- if (code = krb5_cc_resolve(tktbuf, &ccache)) -- { -- (void) sprintf(PQerrormsg, -- "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n", code); -- com_err("pg_krb5_init", code, "in krb5_cc_resolve"); -- return (krb5_ccache) NULL; -- } -- return ccache; -+ retval = krb5_cc_default(pg_krb5_context, &pg_krb5_ccache); -+ if (retval) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_init: krb5_cc_default: %s", -+ error_message(retval)); -+ krb5_free_context(pg_krb5_context); -+ return STATUS_ERROR; -+ } -+ -+ retval = krb5_cc_get_principal(pg_krb5_context, pg_krb5_ccache, -+ &pg_krb5_client); -+ if (retval) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_init: krb5_cc_get_principal: %s", -+ error_message(retval)); -+ krb5_cc_close(pg_krb5_context, pg_krb5_ccache); -+ krb5_free_context(pg_krb5_context); -+ return STATUS_ERROR; -+ } -+ -+ retval = krb5_unparse_name(pg_krb5_context, pg_krb5_client, &pg_krb5_name); -+ if (retval) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_init: krb5_unparse_name: %s", -+ error_message(retval)); -+ krb5_free_principal(pg_krb5_context, pg_krb5_client); -+ krb5_cc_close(pg_krb5_context, pg_krb5_ccache); -+ krb5_free_context(pg_krb5_context); -+ return STATUS_ERROR; -+ } -+ -+ pg_krb5_name = pg_an_to_ln(pg_krb5_name); -+ -+ pg_krb5_initialised = 1; -+ return STATUS_OK; - } - -+ - /* - * pg_krb5_authname -- returns a pointer to static space containing whatever - * name the user has authenticated to the system -- * -- * We obtain this information by digging around in the ticket file. -- */ -+ */ - static const char * --pg_krb5_authname(const char *PQerrormsg) -+pg_krb5_authname(char *PQerrormsg) - { -- krb5_ccache ccache; -- krb5_principal principal; -- krb5_error_code code; -- static char *authname = (char *) NULL; -- -- if (authname) -- return authname; -+ if (pg_krb5_init(PQerrormsg) != STATUS_OK) -+ return NULL; - -- ccache = pg_krb5_init(); /* don't free this */ -- -- if (code = krb5_cc_get_principal(ccache, &principal)) -- { -- (void) sprintf(PQerrormsg, -- "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code); -- com_err("pg_krb5_authname", code, "in krb5_cc_get_principal"); -- return (char *) NULL; -- } -- if (code = krb5_unparse_name(principal, &authname)) -- { -- (void) sprintf(PQerrormsg, -- "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code); -- com_err("pg_krb5_authname", code, "in krb5_unparse_name"); -- krb5_free_principal(principal); -- return (char *) NULL; -- } -- krb5_free_principal(principal); -- return pg_an_to_ln(authname); -+ return pg_krb5_name; - } - -+ - /* - * pg_krb5_sendauth -- client routine to send authentication information to - * the server -- * -- * This routine does not do mutual authentication, nor does it return enough -- * information to do encrypted connections. But then, if we want to do -- * encrypted connections, we'll have to redesign the whole RPC mechanism -- * anyway. -- * -- * Server hostnames are canonicalized v4-style, i.e., all domain suffixes -- * are simply chopped off. Hence, we are assuming that you've entered your -- * server instances as -- * / -- * in the PGREALM (or local) database. This is probably a bad assumption. - */ - static int --pg_krb5_sendauth(const char *PQerrormsg, int sock, -+pg_krb5_sendauth(char *PQerrormsg, int sock, - struct sockaddr_in * laddr, - struct sockaddr_in * raddr, - const char *hostname) - { -- char servbuf[MAXHOSTNAMELEN + 1 + -- sizeof(PG_KRB_SRVNAM)]; -- const char *hostp; -- const char *realm; -- krb5_error_code code; -- krb5_principal client, -- server; -- krb5_ccache ccache; -- krb5_error *error = (krb5_error *) NULL; -- -- ccache = pg_krb5_init(); /* don't free this */ -- -- /* -- * set up client -- this is easy, we can get it out of the ticket -- * file. -- */ -- if (code = krb5_cc_get_principal(ccache, &client)) -- { -- (void) sprintf(PQerrormsg, -- "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code); -- com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal"); -+ krb5_error_code retval; -+ int ret; -+ krb5_principal server; -+ krb5_auth_context auth_context = NULL; -+ krb5_error *err_ret = NULL; -+ int flags; -+ -+ ret = pg_krb5_init(PQerrormsg); -+ if (ret != STATUS_OK) -+ return ret; -+ -+ retval = krb5_sname_to_principal(pg_krb5_context, hostname, PG_KRB_SRVNAM, -+ KRB5_NT_SRV_HST, &server); -+ if (retval) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_sendauth: krb5_sname_to_principal: %s", -+ error_message(retval)); - return STATUS_ERROR; - } - -- /* -- * set up server -- canonicalize as described above -+ /* -+ * libpq uses a non-blocking socket. But kerberos needs a blocking -+ * socket, and we have to block somehow to do mutual authentication -+ * anyway. So we temporarily make it blocking. - */ -- strcpy(servbuf, PG_KRB_SRVNAM); -- *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/'; -- if (hostname || *hostname) -- strncpy(++hostp, hostname, MAXHOSTNAMELEN); -- else -- { -- if (gethostname(++hostp, MAXHOSTNAMELEN) < 0) -- strcpy(hostp, "localhost"); -- } -- if (hostp = strchr(hostp, '.')) -- *hostp = '\0'; -- if (realm = getenv("PGREALM")) -- { -- strcat(servbuf, "@"); -- strcat(servbuf, realm); -- } -- if (code = krb5_parse_name(servbuf, &server)) -- { -- (void) sprintf(PQerrormsg, -- "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code); -- com_err("pg_krb5_sendauth", code, "in krb5_parse_name"); -- krb5_free_principal(client); -+ flags = fcntl(sock, F_GETFL); -+ if (flags < 0 || fcntl(sock, F_SETFL, (long)(flags & ~O_NONBLOCK))) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_sendauth: fcntl: %s", strerror(errno)); -+ krb5_free_principal(pg_krb5_context, server); - return STATUS_ERROR; - } - -- /* -- * The only thing we want back from krb5_sendauth is an error status -- * and any error messages. -- */ -- if (code = krb5_sendauth((krb5_pointer) & sock, -- PG_KRB5_VERSION, -- client, -- server, -- (krb5_flags) 0, -- (krb5_checksum *) NULL, -- (krb5_creds *) NULL, -- ccache, -- (krb5_int32 *) NULL, -- (krb5_keyblock **) NULL, -- &error, -- (krb5_ap_rep_enc_part **) NULL)) -- { -- if ((code == KRB5_SENDAUTH_REJECTED) && error) -- { -- (void) sprintf(PQerrormsg, -- "pg_krb5_sendauth: authentication rejected: \"%*s\"\n", -- error->text.length, error->text.data); -+ retval = krb5_sendauth(pg_krb5_context, &auth_context, -+ (krb5_pointer) &sock, PG_KRB_SRVNAM, -+ pg_krb5_client, server, -+ AP_OPTS_MUTUAL_REQUIRED, -+ NULL, 0, /* no creds, use ccache instead */ -+ pg_krb5_ccache, &err_ret, NULL, NULL); -+ if (retval) { -+ if (retval == KRB5_SENDAUTH_REJECTED && err_ret) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_sendauth: authentication rejected: \"%*s\"", -+ err_ret->text.length, err_ret->text.data); - } -- else -- { -- (void) sprintf(PQerrormsg, -- "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code); -- com_err("pg_krb5_sendauth", code, "in krb5_sendauth"); -+ else { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_sendauth: krb5_sendauth: %s", -+ error_message(retval)); - } -+ -+ if (err_ret) -+ krb5_free_error(pg_krb5_context, err_ret); -+ -+ ret = STATUS_ERROR; -+ } -+ -+ krb5_free_principal(pg_krb5_context, server); -+ -+ if (fcntl(sock, F_SETFL, (long)flags)) { -+ snprintf(PQerrormsg, PQERRORMSG_LENGTH, -+ "pg_krb5_sendauth: fcntl: %s", strerror(errno)); -+ ret = STATUS_ERROR; - } -- krb5_free_principal(client); -- krb5_free_principal(server); -- return code ? STATUS_ERROR : STATUS_OK; -+ -+ return ret; - } - - #endif /* KRB5 */ -diff -u -r postgresql-7.0RC5/src/interfaces/libpq/libpq-int.h postgresql-7.0RC5.krb5/src/interfaces/libpq/libpq-int.h ---- postgresql-7.0RC5/src/interfaces/libpq/libpq-int.h Mon May 8 17:22:40 2000 -+++ postgresql-7.0RC5.krb5/src/interfaces/libpq/libpq-int.h Sat May 6 17:49:38 2000 -@@ -50,6 +50,7 @@ - * POSTGRES backend dependent Constants. - */ - -+#define PQERRORMSG_LENGTH 1024 - #define CMDSTATUS_LEN 40 - - /* - - diff --git a/src/Makefile.global.in b/src/Makefile.global.in index fd07ea0b53..afeadbc6f4 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -7,7 +7,7 @@ # # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/Makefile.global.in,v 1.73 2000/05/27 03:58:18 momjian Exp $ +# $Header: /cvsroot/pgsql/src/Makefile.global.in,v 1.74 2000/05/27 04:13:04 momjian Exp $ # # NOTES # Essentially all Postgres make files include this file and use the @@ -120,7 +120,7 @@ ENFORCE_ALIGNMENT= true # Set KRBVERS to "4" for Kerberos v4, "5" for Kerberos v5. # XXX Edit the default Kerberos variables below! # -#KRBVERS= 5 +#KRBVERS=5 # Globally pass Kerberos file locations. # these are used in the postmaster and all libpq applications. @@ -132,9 +132,9 @@ ENFORCE_ALIGNMENT= true # PG_KRB_SRVTAB is the location of the server's keytab file. # ifdef KRBVERS -KRBINCS= -I/usr/athena/include -KRBLIBS= -L/usr/athena/lib -KRBFLAGS+= $(KRBINCS) -DPG_KRB_SRVNAM='"postgres_dbms"' +KRBINCS= -I/usr/krb5/include +KRBLIBS= -L/usr/krb5/lib +KRBFLAGS+= $(KRBINCS) -DPG_KRB_SRVNAM='"postgres"' ifeq ($(KRBVERS), 4) KRBFLAGS+= -DKRB4 KRBFLAGS+= -DPG_KRB_SRVTAB='"/etc/srvtab"' @@ -142,8 +142,8 @@ KRBLIBS+= -lkrb -ldes else ifeq ($(KRBVERS), 5) KRBFLAGS+= -DKRB5 -KRBFLAGS+= -DPG_KRB_SRVTAB='"FILE:/krb5/srvtab.postgres"' -KRBLIBS+= -lkrb5 -lcrypto -lcom_err -lisode +KRBFLAGS+= -DPG_KRB_SRVTAB='"FILE:/usr/local/postgres/krb5.keytab"' +KRBLIBS+= -lkrb5 -lcrypto -lcom_err endif endif endif diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index bd53c4cabe..5cd049062c 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.46 2000/05/27 03:58:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.47 2000/05/27 04:13:05 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -149,7 +149,8 @@ pg_krb4_recvauth(Port *port) *---------------------------------------------------------------- */ -#include "krb5/krb5.h" +#include +#include /* * pg_an_to_ln -- return the local name corresponding to an authentication @@ -174,6 +175,64 @@ pg_an_to_ln(char *aname) return aname; } + +/* + * Various krb5 state which is not connection specfic, and a flag to + * indicate whether we have initialised it yet. + */ +static int pg_krb5_initialised; +static krb5_context pg_krb5_context; +static krb5_keytab pg_krb5_keytab; +static krb5_principal pg_krb5_server; + + +static int +pg_krb5_init(void) +{ + krb5_error_code retval; + + if (pg_krb5_initialised) + return STATUS_OK; + + retval = krb5_init_context(&pg_krb5_context); + if (retval) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_init: krb5_init_context returned" + " Kerberos error %d\n", retval); + com_err("postgres", retval, "while initializing krb5"); + return STATUS_ERROR; + } + + retval = krb5_kt_resolve(pg_krb5_context, PG_KRB_SRVTAB, &pg_krb5_keytab); + if (retval) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_init: krb5_kt_resolve returned" + " Kerberos error %d\n", retval); + com_err("postgres", retval, "while resolving keytab file %s", + PG_KRB_SRVTAB); + krb5_free_context(pg_krb5_context); + return STATUS_ERROR; + } + + retval = krb5_sname_to_principal(pg_krb5_context, NULL, PG_KRB_SRVNAM, + KRB5_NT_SRV_HST, &pg_krb5_server); + if (retval) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_init: krb5_sname_to_principal returned" + " Kerberos error %d\n", retval); + com_err("postgres", retval, + "while getting server principal for service %s", + PG_KRB_SRVTAB); + krb5_kt_close(pg_krb5_context, pg_krb5_keytab); + krb5_free_context(pg_krb5_context); + return STATUS_ERROR; + } + + pg_krb5_initialised = 1; + return STATUS_OK; +} + + /* * pg_krb5_recvauth -- server routine to receive authentication information * from the client @@ -182,122 +241,68 @@ pg_an_to_ln(char *aname) * packet to the authenticated name, as described in pg_krb4_recvauth. This * is a bit more problematic in v5, as described above in pg_an_to_ln. * - * In addition, as described above in pg_krb5_sendauth, we still need to - * canonicalize the server name v4-style before constructing a principal - * from it. Again, this is kind of iffy. - * - * Finally, we need to tangle with the fact that v5 doesn't let you explicitly - * set server keytab file names -- you have to feed lower-level routines a - * function to retrieve the contents of a keytab, along with a single argument - * that allows them to open the keytab. We assume that a server keytab is - * always a real file so we can allow people to specify their own filenames. - * (This is important because the POSTGRES keytab needs to be readable by - * non-root users/groups; the v4 tools used to force you do dump a whole - * host's worth of keys into a file, effectively forcing you to use one file, - * but kdb5_edit allows you to select which principals to dump. Yay!) + * We have our own keytab file because postgres is unlikely to run as root, + * and so cannot read the default keytab. */ static int pg_krb5_recvauth(Port *port) { - char servbuf[MAXHOSTNAMELEN + 1 + - sizeof(PG_KRB_SRVNAM)]; - char *hostp, - *kusername = (char *) NULL; - krb5_error_code code; - krb5_principal client, - server; - krb5_address sender_addr; - krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL; - krb5_pointer keyprocarg = (krb5_pointer) NULL; + krb5_error_code retval; + int ret; + krb5_auth_context auth_context = NULL; + krb5_ticket *ticket; + char *kusername; - /* - * Set up server side -- since we have no ticket file to make this - * easy, we construct our own name and parse it. See note on - * canonicalization above. - */ - strcpy(servbuf, PG_KRB_SRVNAM); - *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/'; - if (gethostname(++hostp, MAXHOSTNAMELEN) < 0) - strcpy(hostp, "localhost"); - if (hostp = strchr(hostp, '.')) - *hostp = '\0'; - if (code = krb5_parse_name(servbuf, &server)) - { + ret = pg_krb5_init(); + if (ret != STATUS_OK) + return ret; + + retval = krb5_recvauth(pg_krb5_context, &auth_context, + (krb5_pointer)&port->sock, PG_KRB_SRVNAM, + pg_krb5_server, 0, pg_krb5_keytab, &ticket); + if (retval) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n", code); - com_err("pg_krb5_recvauth", code, "in krb5_parse_name"); + "pg_krb5_recvauth: krb5_recvauth returned" + " Kerberos error %d\n", retval); + com_err("postgres", retval, "from krb5_recvauth"); return STATUS_ERROR; - } - - /* - * krb5_sendauth needs this to verify the address in the client - * authenticator. - */ - sender_addr.addrtype = port->raddr.in.sin_family; - sender_addr.length = sizeof(port->raddr.in.sin_addr); - sender_addr.contents = (krb5_octet *) & (port->raddr.in.sin_addr); - - if (strcmp(PG_KRB_SRVTAB, "")) - { - keyproc = krb5_kt_read_service_key; - keyprocarg = PG_KRB_SRVTAB; - } - - if (code = krb5_recvauth((krb5_pointer) & port->sock, - PG_KRB5_VERSION, - server, - &sender_addr, - (krb5_pointer) NULL, - keyproc, - keyprocarg, - (char *) NULL, - (krb5_int32 *) NULL, - &client, - (krb5_ticket **) NULL, - (krb5_authenticator **) NULL)) - { - snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n", code); - com_err("pg_krb5_recvauth", code, "in krb5_recvauth"); - krb5_free_principal(server); - return STATUS_ERROR; - } - krb5_free_principal(server); + } /* * The "client" structure comes out of the ticket and is therefore * authenticated. Use it to check the username obtained from the * postmaster startup packet. + * + * I have no idea why this is considered necessary. */ - if ((code = krb5_unparse_name(client, &kusername))) - { + retval = krb5_unparse_name(pg_krb5_context, + ticket->enc_part2->client, &kusername); + if (retval) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n", code); - com_err("pg_krb5_recvauth", code, "in krb5_unparse_name"); - krb5_free_principal(client); - return STATUS_ERROR; - } - krb5_free_principal(client); - if (!kusername) - { - snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "pg_krb5_recvauth: could not decode username\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); + "pg_krb5_recvauth: krb5_unparse_name returned" + " Kerberos error %d\n", retval); + com_err("postgres", retval, "while unparsing client name"); + krb5_free_ticket(pg_krb5_context, ticket); + krb5_auth_con_free(pg_krb5_context, auth_context); return STATUS_ERROR; } + kusername = pg_an_to_ln(kusername); - if (strncmp(username, kusername, SM_USER)) + if (strncmp(port->user, kusername, SM_USER)) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "pg_krb5_recvauth: name \"%s\" != \"%s\"\n", port->user, kusername); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - pfree(kusername); - return STATUS_ERROR; + "pg_krb5_recvauth: user name \"%s\" != krb5 name \"%s\"\n", + port->user, kusername); + ret = STATUS_ERROR; } - pfree(kusername); - return STATUS_OK; + else + ret = STATUS_OK; + + krb5_free_ticket(pg_krb5_context, ticket); + krb5_auth_con_free(pg_krb5_context, auth_context); + free(kusername); + + return ret; } #else diff --git a/src/interfaces/libpq/Makefile.in b/src/interfaces/libpq/Makefile.in index 8e0f65d612..7e38434592 100644 --- a/src/interfaces/libpq/Makefile.in +++ b/src/interfaces/libpq/Makefile.in @@ -6,7 +6,7 @@ # Copyright (c) 1994, Regents of the University of California # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/interfaces/libpq/Attic/Makefile.in,v 1.56 2000/05/27 03:58:20 momjian Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/libpq/Attic/Makefile.in,v 1.57 2000/05/27 04:13:05 momjian Exp $ # #------------------------------------------------------------------------- @@ -21,6 +21,7 @@ CFLAGS+= -DFRONTEND ifdef KRBVERS CFLAGS+= $(KRBFLAGS) +SHLIB_LINK += $(KRBLIBS) endif OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index d05bf63ce2..8cb9a889ad 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 - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.41 2000/05/27 03:58:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.42 2000/05/27 04:13:05 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -39,6 +39,7 @@ #include "win32.h" #else #include +#include #include /* for MAXHOSTNAMELEN on most */ #ifndef MAXHOSTNAMELEN #include /* for MAXHOSTNAMELEN on some */ @@ -234,7 +235,8 @@ pg_krb4_sendauth(const char *PQerrormsg, int sock, *---------------------------------------------------------------- */ -#include "krb5/krb5.h" +#include +#include /* * pg_an_to_ln -- return the local name corresponding to an authentication @@ -250,7 +252,7 @@ pg_krb4_sendauth(const char *PQerrormsg, int sock, * and we can't afford to punt. */ static char * -pg_an_to_ln(const char *aname) +pg_an_to_ln(char *aname) { char *p; @@ -261,197 +263,160 @@ pg_an_to_ln(const char *aname) /* - * pg_krb5_init -- initialization performed before any Kerberos calls are made - * - * With v5, we can no longer set the ticket (credential cache) file name; - * we now have to provide a file handle for the open (well, "resolved") - * ticket file everywhere. - * + * Various krb5 state which is not connection specfic, and a flag to + * indicate whether we have initialised it yet. */ +static int pg_krb5_initialised; +static krb5_context pg_krb5_context; +static krb5_ccache pg_krb5_ccache; +static krb5_principal pg_krb5_client; +static char *pg_krb5_name; + + static int - krb5_ccache -pg_krb5_init(void) +pg_krb5_init(char *PQerrormsg) { - krb5_error_code code; - char *realm, - *defname; - char tktbuf[MAXPGPATH]; - static krb5_ccache ccache = (krb5_ccache) NULL; + krb5_error_code retval; - if (ccache) - return ccache; + if (pg_krb5_initialised) + return STATUS_OK; - /* - * If the user set PGREALM, then we use a ticket file with a special - * name: @ - */ - if (!(defname = krb5_cc_default_name())) - { - (void) sprintf(PQerrormsg, - "pg_krb5_init: krb5_cc_default_name failed\n"); - return (krb5_ccache) NULL; - } - strcpy(tktbuf, defname); - if (realm = getenv("PGREALM")) - { - strcat(tktbuf, "@"); - strcat(tktbuf, realm); + retval = krb5_init_context(&pg_krb5_context); + if (retval) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_init: krb5_init_context: %s", + error_message(retval)); + return STATUS_ERROR; } - if (code = krb5_cc_resolve(tktbuf, &ccache)) - { - (void) sprintf(PQerrormsg, - "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n", code); - com_err("pg_krb5_init", code, "in krb5_cc_resolve"); - return (krb5_ccache) NULL; - } - return ccache; + retval = krb5_cc_default(pg_krb5_context, &pg_krb5_ccache); + if (retval) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_init: krb5_cc_default: %s", + error_message(retval)); + krb5_free_context(pg_krb5_context); + return STATUS_ERROR; + } + + retval = krb5_cc_get_principal(pg_krb5_context, pg_krb5_ccache, + &pg_krb5_client); + if (retval) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_init: krb5_cc_get_principal: %s", + error_message(retval)); + krb5_cc_close(pg_krb5_context, pg_krb5_ccache); + krb5_free_context(pg_krb5_context); + return STATUS_ERROR; + } + + retval = krb5_unparse_name(pg_krb5_context, pg_krb5_client, &pg_krb5_name); + if (retval) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_init: krb5_unparse_name: %s", + error_message(retval)); + krb5_free_principal(pg_krb5_context, pg_krb5_client); + krb5_cc_close(pg_krb5_context, pg_krb5_ccache); + krb5_free_context(pg_krb5_context); + return STATUS_ERROR; + } + + pg_krb5_name = pg_an_to_ln(pg_krb5_name); + + pg_krb5_initialised = 1; + return STATUS_OK; } + /* * pg_krb5_authname -- returns a pointer to static space containing whatever * name the user has authenticated to the system - * - * We obtain this information by digging around in the ticket file. - */ + */ static const char * -pg_krb5_authname(const char *PQerrormsg) +pg_krb5_authname(char *PQerrormsg) { - krb5_ccache ccache; - krb5_principal principal; - krb5_error_code code; - static char *authname = (char *) NULL; + if (pg_krb5_init(PQerrormsg) != STATUS_OK) + return NULL; - if (authname) - return authname; - - ccache = pg_krb5_init(); /* don't free this */ - - if (code = krb5_cc_get_principal(ccache, &principal)) - { - (void) sprintf(PQerrormsg, - "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code); - com_err("pg_krb5_authname", code, "in krb5_cc_get_principal"); - return (char *) NULL; - } - if (code = krb5_unparse_name(principal, &authname)) - { - (void) sprintf(PQerrormsg, - "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code); - com_err("pg_krb5_authname", code, "in krb5_unparse_name"); - krb5_free_principal(principal); - return (char *) NULL; - } - krb5_free_principal(principal); - return pg_an_to_ln(authname); + return pg_krb5_name; } + /* * pg_krb5_sendauth -- client routine to send authentication information to * the server - * - * This routine does not do mutual authentication, nor does it return enough - * information to do encrypted connections. But then, if we want to do - * encrypted connections, we'll have to redesign the whole RPC mechanism - * anyway. - * - * Server hostnames are canonicalized v4-style, i.e., all domain suffixes - * are simply chopped off. Hence, we are assuming that you've entered your - * server instances as - * / - * in the PGREALM (or local) database. This is probably a bad assumption. */ static int -pg_krb5_sendauth(const char *PQerrormsg, int sock, +pg_krb5_sendauth(char *PQerrormsg, int sock, struct sockaddr_in * laddr, struct sockaddr_in * raddr, const char *hostname) { - char servbuf[MAXHOSTNAMELEN + 1 + - sizeof(PG_KRB_SRVNAM)]; - const char *hostp; - const char *realm; - krb5_error_code code; - krb5_principal client, - server; - krb5_ccache ccache; - krb5_error *error = (krb5_error *) NULL; + krb5_error_code retval; + int ret; + krb5_principal server; + krb5_auth_context auth_context = NULL; + krb5_error *err_ret = NULL; + int flags; - ccache = pg_krb5_init(); /* don't free this */ + ret = pg_krb5_init(PQerrormsg); + if (ret != STATUS_OK) + return ret; - /* - * set up client -- this is easy, we can get it out of the ticket - * file. - */ - if (code = krb5_cc_get_principal(ccache, &client)) - { - (void) sprintf(PQerrormsg, - "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code); - com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal"); + retval = krb5_sname_to_principal(pg_krb5_context, hostname, PG_KRB_SRVNAM, + KRB5_NT_SRV_HST, &server); + if (retval) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_sendauth: krb5_sname_to_principal: %s", + error_message(retval)); return STATUS_ERROR; } - /* - * set up server -- canonicalize as described above + /* + * libpq uses a non-blocking socket. But kerberos needs a blocking + * socket, and we have to block somehow to do mutual authentication + * anyway. So we temporarily make it blocking. */ - strcpy(servbuf, PG_KRB_SRVNAM); - *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/'; - if (hostname || *hostname) - strncpy(++hostp, hostname, MAXHOSTNAMELEN); - else - { - if (gethostname(++hostp, MAXHOSTNAMELEN) < 0) - strcpy(hostp, "localhost"); - } - if (hostp = strchr(hostp, '.')) - *hostp = '\0'; - if (realm = getenv("PGREALM")) - { - strcat(servbuf, "@"); - strcat(servbuf, realm); - } - if (code = krb5_parse_name(servbuf, &server)) - { - (void) sprintf(PQerrormsg, - "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code); - com_err("pg_krb5_sendauth", code, "in krb5_parse_name"); - krb5_free_principal(client); + flags = fcntl(sock, F_GETFL); + if (flags < 0 || fcntl(sock, F_SETFL, (long)(flags & ~O_NONBLOCK))) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_sendauth: fcntl: %s", strerror(errno)); + krb5_free_principal(pg_krb5_context, server); return STATUS_ERROR; } - /* - * The only thing we want back from krb5_sendauth is an error status - * and any error messages. - */ - if (code = krb5_sendauth((krb5_pointer) & sock, - PG_KRB5_VERSION, - client, - server, - (krb5_flags) 0, - (krb5_checksum *) NULL, - (krb5_creds *) NULL, - ccache, - (krb5_int32 *) NULL, - (krb5_keyblock **) NULL, - &error, - (krb5_ap_rep_enc_part **) NULL)) - { - if ((code == KRB5_SENDAUTH_REJECTED) && error) - { - (void) sprintf(PQerrormsg, - "pg_krb5_sendauth: authentication rejected: \"%*s\"\n", - error->text.length, error->text.data); + retval = krb5_sendauth(pg_krb5_context, &auth_context, + (krb5_pointer) &sock, PG_KRB_SRVNAM, + pg_krb5_client, server, + AP_OPTS_MUTUAL_REQUIRED, + NULL, 0, /* no creds, use ccache instead */ + pg_krb5_ccache, &err_ret, NULL, NULL); + if (retval) { + if (retval == KRB5_SENDAUTH_REJECTED && err_ret) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_sendauth: authentication rejected: \"%*s\"", + err_ret->text.length, err_ret->text.data); } - else - { - (void) sprintf(PQerrormsg, - "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code); - com_err("pg_krb5_sendauth", code, "in krb5_sendauth"); + else { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_sendauth: krb5_sendauth: %s", + error_message(retval)); } + + if (err_ret) + krb5_free_error(pg_krb5_context, err_ret); + + ret = STATUS_ERROR; } - krb5_free_principal(client); - krb5_free_principal(server); - return code ? STATUS_ERROR : STATUS_OK; + + krb5_free_principal(pg_krb5_context, server); + + if (fcntl(sock, F_SETFL, (long)flags)) { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "pg_krb5_sendauth: fcntl: %s", strerror(errno)); + ret = STATUS_ERROR; + } + + return ret; } #endif /* KRB5 */ diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index f83f52a62c..b7d16d6d39 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.25 2000/05/27 03:58:20 momjian Exp $ + * $Id: libpq-int.h,v 1.26 2000/05/27 04:13:05 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -50,6 +50,7 @@ * POSTGRES backend dependent Constants. */ +#define PQERRORMSG_LENGTH 1024 #define CMDSTATUS_LEN 40 /*