diff options
author | Marc G. Fournier <scrappy@hub.org> | 1998-01-26 01:42:53 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1998-01-26 01:42:53 +0000 |
commit | d5bbe2aca55bc833e38c768d7f82c129b8b70c83 (patch) | |
tree | 47f4e1ecb3277869bb276e5433df335d920d1baf /src/backend/libpq/auth.c | |
parent | 91d983aa1140e3ae109684ff7c916583ed059e0e (diff) | |
download | postgresql-d5bbe2aca55bc833e38c768d7f82c129b8b70c83.tar.gz postgresql-d5bbe2aca55bc833e38c768d7f82c129b8b70c83.zip |
From: Phil Thompson <phil@river-bank.demon.co.uk>
I've completed the patch to fix the protocol and authentication issues I
was discussing a couple of weeks ago. The particular changes are:
- the protocol has a version number
- network byte order is used throughout
- the pg_hba.conf file is used to specify what method is used to
authenticate a frontend (either password, ident, trust, reject, krb4
or krb5)
- support for multiplexed backends is removed
- appropriate changes to man pages
- the -a switch to many programs to specify an authentication service
no longer has any effect
- the libpq.so version number has changed to 1.1
The new backend still supports the old protocol so old interfaces won't
break.
Diffstat (limited to 'src/backend/libpq/auth.c')
-rw-r--r-- | src/backend/libpq/auth.c | 668 |
1 files changed, 339 insertions, 329 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 8aa3efca9b3..74643037288 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.20 1997/12/09 03:10:31 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.21 1998/01/26 01:41:04 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -16,39 +16,6 @@ * * backend (postmaster) routines: * be_recvauth receive authentication information - * be_setauthsvc do/do not permit an authentication service - * be_getauthsvc is an authentication service permitted? - * - * NOTES - * To add a new authentication system: - * 0. If you can't do your authentication over an existing socket, - * you lose -- get ready to hack around this framework instead of - * using it. Otherwise, you can assume you have an initialized - * and empty connection to work with. (Please don't leave leftover - * gunk in the connection after the authentication transactions, or - * the POSTGRES routines that follow will be very unhappy.) - * 1. Write a set of routines that: - * let a client figure out what user/principal name to use - * send authentication information (client side) - * receive authentication information (server side) - * You can include both routines in this file, using #ifdef FRONTEND - * to separate them. - * 2. Edit libpq/pqcomm.h and assign a MsgType for your protocol. - * 3. Edit the static "struct authsvc" array and the generic - * {be,fe}_{get,set}auth{name,svc} routines in this file to reflect - * the new service. You may have to change the arguments of these - * routines; they basically just reflect what Kerberos v4 needs. - * 4. Hack on src/{,bin}/Makefile.global and src/{backend,libpq}/Makefile - * to add library and CFLAGS hooks -- basically, grep the Makefile - * hierarchy for KRBVERS to see where you need to add things. - * - * Send mail to post_hackers@postgres.Berkeley.EDU if you have to make - * any changes to arguments, etc. Context diffs would be nice, too. - * - * Someday, this cruft will go away and magically be replaced by a - * nice interface based on the GSS API or something. For now, though, - * there's no (stable) UNIX security API to work with... - * */ #include <stdio.h> #include <string.h> @@ -68,66 +35,21 @@ #include <libpq/auth.h> #include <libpq/libpq.h> -#include <libpq/libpq-be.h> #include <libpq/hba.h> #include <libpq/password.h> #include <libpq/crypt.h> -static int be_getauthsvc(MsgType msgtype); - -/*---------------------------------------------------------------- - * common definitions for generic fe/be routines - *---------------------------------------------------------------- - */ -struct authsvc -{ - char name[16]; /* service nickname (for command line) */ - MsgType msgtype; /* startup packet header type */ - int allowed; /* initially allowed (before command line - * option parsing)? */ -}; +static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler)()); +static void handle_done_auth(Port *port); +static void handle_krb4_auth(Port *port); +static void handle_krb5_auth(Port *port); +static void handle_password_auth(Port *port); +static void readPasswordPacket(char *arg, PacketLen len, char *pkt); +static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt); +static int old_be_recvauth(Port *port); +static int map_old_to_new(Port *port, UserAuth old, int status); -/* - * Command-line parsing routines use this structure to map nicknames - * onto service types (and the startup packets to use with them). - * - * Programs receiving an authentication request use this structure to - * decide which authentication service types are currently permitted. - * By default, all authentication systems compiled into the system are - * allowed. Unauthenticated connections are disallowed unless there - * isn't any authentication system. - */ - -#if defined(HBA) -static int useHostBasedAuth = 1; - -#else -static int useHostBasedAuth = 0; - -#endif - -#if defined(KRB4) || defined(KRB5) || defined(HBA) -#define UNAUTH_ALLOWED 0 -#else -#define UNAUTH_ALLOWED 1 -#endif - -static struct authsvc authsvcs[] = { - {"unauth", STARTUP_UNAUTH_MSG, UNAUTH_ALLOWED}, - {"hba", STARTUP_HBA_MSG, 1}, - {"krb4", STARTUP_KRB4_MSG, 1}, - {"krb5", STARTUP_KRB5_MSG, 1}, -#if defined(KRB5) - {"kerberos", STARTUP_KRB5_MSG, 1}, -#else - {"kerberos", STARTUP_KRB4_MSG, 1}, -#endif - {"password", STARTUP_PASSWORD_MSG, 1}, - {"crypt", STARTUP_CRYPT_MSG, 1} -}; - -static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); #ifdef KRB4 /* This has to be ifdef'd out because krb.h does exist. This needs @@ -140,10 +62,6 @@ static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); #include <krb.h> -#ifdef FRONTEND -/* moves to src/libpq/fe-auth.c */ -#else /* !FRONTEND */ - /* * pg_krb4_recvauth -- server routine to receive authentication information * from the client @@ -154,10 +72,7 @@ static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); * unauthenticated connections.) */ static int -pg_krb4_recvauth(int sock, - struct sockaddr_in * laddr, - struct sockaddr_in * raddr, - char *username) +pg_krb4_recvauth(Port *) { long krbopts = 0; /* one-way authentication */ KTEXT_ST clttkt; @@ -170,12 +85,12 @@ pg_krb4_recvauth(int sock, strcpy(instance, "*"); /* don't care, but arg gets expanded * anyway */ status = krb_recvauth(krbopts, - sock, + port->sock, &clttkt, PG_KRB_SRVNAM, instance, - raddr, - laddr, + &port->raddr.in, + &port->laddr.in, &auth_data, PG_KRB_SRVTAB, key_sched, @@ -198,12 +113,11 @@ pg_krb4_recvauth(int sock, pqdebug("%s", PQerrormsg); return (STATUS_ERROR); } - if (username && *username && - strncmp(username, auth_data.pname, NAMEDATALEN)) + if (strncmp(port->user, auth_data.pname, SM_USER)) { sprintf(PQerrormsg, "pg_krb4_recvauth: name \"%s\" != \"%s\"\n", - username, + port->username, auth_data.pname); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); @@ -212,14 +126,9 @@ pg_krb4_recvauth(int sock, return (STATUS_OK); } -#endif /* !FRONTEND */ - #else static int -pg_krb4_recvauth(int sock, - struct sockaddr_in * laddr, - struct sockaddr_in * raddr, - char *username) +pg_krb4_recvauth(Port *port) { sprintf(PQerrormsg, "pg_krb4_recvauth: Kerberos not implemented on this " @@ -267,10 +176,6 @@ pg_an_to_ln(char *aname) return (aname); } -#ifdef FRONTEND -/* moves to src/libpq/fe-auth.c */ -#else /* !FRONTEND */ - /* * pg_krb5_recvauth -- server routine to receive authentication information * from the client @@ -294,10 +199,7 @@ pg_an_to_ln(char *aname) * but kdb5_edit allows you to select which principals to dump. Yay!) */ static int -pg_krb5_recvauth(int sock, - struct sockaddr_in * laddr, - struct sockaddr_in * raddr, - char *username) +pg_krb5_recvauth(Port *port) { char servbuf[MAXHOSTNAMELEN + 1 + sizeof(PG_KRB_SRVNAM)]; @@ -334,9 +236,9 @@ pg_krb5_recvauth(int sock, * krb5_sendauth needs this to verify the address in the client * authenticator. */ - sender_addr.addrtype = raddr->sin_family; - sender_addr.length = sizeof(raddr->sin_addr); - sender_addr.contents = (krb5_octet *) & (raddr->sin_addr); + 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, "")) { @@ -344,7 +246,7 @@ pg_krb5_recvauth(int sock, keyprocarg = PG_KRB_SRVTAB; } - if (code = krb5_recvauth((krb5_pointer) & sock, + if (code = krb5_recvauth((krb5_pointer) & port->sock, PG_KRB5_VERSION, server, &sender_addr, @@ -390,11 +292,11 @@ pg_krb5_recvauth(int sock, return (STATUS_ERROR); } kusername = pg_an_to_ln(kusername); - if (username && strncmp(username, kusername, NAMEDATALEN)) + if (strncmp(username, kusername, SM_USER)) { sprintf(PQerrormsg, "pg_krb5_recvauth: name \"%s\" != \"%s\"\n", - username, kusername); + port->username, kusername); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); pfree(kusername); @@ -404,15 +306,9 @@ pg_krb5_recvauth(int sock, return (STATUS_OK); } -#endif /* !FRONTEND */ - - #else static int -pg_krb5_recvauth(int sock, - struct sockaddr_in * laddr, - struct sockaddr_in * raddr, - char *username) +pg_krb5_recvauth(Port *port) { sprintf(PQerrormsg, "pg_krb5_recvauth: Kerberos not implemented on this " @@ -425,246 +321,360 @@ pg_krb5_recvauth(int sock, #endif /* KRB5 */ -static int -pg_password_recvauth(Port *port, char *database, char *DataDir) + +/* + * Handle a v0 password packet. + */ + +static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt) { - PacketBuf buf; - char *user, - *password; + Port *port; + PasswordPacketV0 *pp; + char *user, *password, *cp, *start; - if (PacketReceive(port, &buf, BLOCKING) != STATUS_OK) + port = (Port *)arg; + pp = (PasswordPacketV0 *)pkt; + + /* + * The packet is supposed to comprise the user name and the password + * as C strings. Be careful the check that this is the case. + */ + + user = password = NULL; + + len -= sizeof (pp->unused); + + cp = start = pp->data; + + while (len > 0) + if (*cp++ == '\0') + { + if (user == NULL) + user = start; + else + { + password = start; + break; + } + + start = cp; + } + + if (user == NULL || password == NULL) { sprintf(PQerrormsg, - "pg_password_recvauth: failed to receive authentication packet.\n"); + "pg_password_recvauth: badly formed password packet.\n"); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); - return STATUS_ERROR; + + auth_failed(port); } + else if (map_old_to_new(port, uaPassword, + verify_password(port->auth_arg, user, password)) != STATUS_OK) + auth_failed(port); +} - user = buf.data; - password = buf.data + strlen(user) + 1; - return verify_password(user, password, port, database, DataDir); -} +/* + * Tell the user the authentication failed, but not why. + */ -static int -crypt_recvauth(Port *port) +void auth_failed(Port *port) { - PacketBuf buf; - char *user, - *password; - - if (PacketReceive(port, &buf, BLOCKING) != STATUS_OK) - { - sprintf(PQerrormsg, - "crypt_recvauth: failed to receive authentication packet.\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return STATUS_ERROR; - } - - user = buf.data; - password = buf.data + strlen(user) + 1; - - return crypt_verify(port, user, password); + PacketSendError(&port->pktInfo, "User authentication failed"); } + /* * be_recvauth -- server demux routine for incoming authentication information */ -int -be_recvauth(MsgType msgtype_arg, Port *port, char *username, StartupInfo *sp) +void be_recvauth(Port *port) { - MsgType msgtype; + AuthRequest areq; + void (*auth_handler)(); /* - * A message type of STARTUP_MSG (which once upon a time was the only - * startup message type) means user wants us to choose. "unauth" is - * what used to be the only choice, but installation may choose "hba" - * instead. + * Get the authentication method to use for this frontend/database + * combination. */ - if (msgtype_arg == STARTUP_MSG) + + if (hba_getauthmethod(&port->raddr, port->database, port->auth_arg, + &port->auth_method) != STATUS_OK) { - if (useHostBasedAuth) - msgtype = STARTUP_HBA_MSG; - else - msgtype = STARTUP_UNAUTH_MSG; + PacketSendError(&port->pktInfo, "Error getting authentication method"); + return; } - else - msgtype = msgtype_arg; + /* Handle old style authentication. */ - if (!username) + if (PG_PROTOCOL_MAJOR(port->proto) == 0) { - sprintf(PQerrormsg, - "be_recvauth: no user name passed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - if (!port) - { - sprintf(PQerrormsg, - "be_recvauth: no port structure passed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); + if (old_be_recvauth(port) != STATUS_OK) + auth_failed(port); + + return; } - switch (msgtype) + /* Handle new style authentication. */ + + switch (port->auth_method) { - case STARTUP_KRB4_MSG: - if (!be_getauthsvc(msgtype)) - { - sprintf(PQerrormsg, - "be_recvauth: krb4 authentication disallowed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - if (pg_krb4_recvauth(port->sock, (struct sockaddr_in *) &port->laddr, - (struct sockaddr_in *) &port->raddr, - username) != STATUS_OK) - { - sprintf(PQerrormsg, - "be_recvauth: krb4 authentication failed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - break; - case STARTUP_KRB5_MSG: - if (!be_getauthsvc(msgtype)) - { - sprintf(PQerrormsg, - "be_recvauth: krb5 authentication disallowed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - if (pg_krb5_recvauth(port->sock, (struct sockaddr_in *) &port->laddr, - (struct sockaddr_in *) &port->raddr, - username) != STATUS_OK) - { - sprintf(PQerrormsg, - "be_recvauth: krb5 authentication failed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - break; - case STARTUP_UNAUTH_MSG: - if (!be_getauthsvc(msgtype)) - { - sprintf(PQerrormsg, - "be_recvauth: " - "unauthenticated connections disallowed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - break; - case STARTUP_HBA_MSG: - if (hba_recvauth(port, sp->database, sp->user, DataDir) != STATUS_OK) - { - sprintf(PQerrormsg, - "be_recvauth: host-based authentication failed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - break; - case STARTUP_PASSWORD_MSG: - if (!be_getauthsvc(msgtype)) - { - sprintf(PQerrormsg, - "be_recvauth: " - "plaintext password authentication disallowed\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - if (pg_password_recvauth(port, sp->database, DataDir) != STATUS_OK) - { - - /* - * pg_password_recvauth or lower-level routines have - * already set - */ - /* the error message */ - return (STATUS_ERROR); - } - break; - case STARTUP_CRYPT_MSG: - if (crypt_recvauth(port) != STATUS_OK) - return STATUS_ERROR; - break; - default: - sprintf(PQerrormsg, - "be_recvauth: unrecognized message type: %d\n", - msgtype); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - return (STATUS_ERROR); - } - return (STATUS_OK); + case uaReject: + auth_failed(port); + return; + + case uaKrb4: + areq = AUTH_REQ_KRB4; + auth_handler = handle_krb4_auth; + break; + + case uaKrb5: + areq = AUTH_REQ_KRB5; + auth_handler = handle_krb5_auth; + break; + + case uaTrust: + areq = AUTH_REQ_OK; + auth_handler = handle_done_auth; + break; + + case uaIdent: + if (authident(&port->raddr.in, &port->laddr.in, port->user, + port->auth_arg) != STATUS_OK) + { + auth_failed(port); + return; + } + + areq = AUTH_REQ_OK; + auth_handler = handle_done_auth; + break; + + case uaPassword: + areq = AUTH_REQ_PASSWORD; + auth_handler = handle_password_auth; + break; + + case uaCrypt: + areq = AUTH_REQ_CRYPT; + auth_handler = handle_password_auth; + break; + } + + /* Tell the frontend what we want next. */ + + sendAuthRequest(port, areq, auth_handler); } + /* - * be_setauthsvc -- enable/disable the authentication services currently - * selected for use by the backend - * be_getauthsvc -- returns whether a particular authentication system - * (indicated by its message type) is permitted by the - * current selections - * - * be_setauthsvc encodes the command-line syntax that - * -a "<service-name>" - * enables a service, whereas - * -a "no<service-name>" - * disables it. + * Send an authentication request packet to the frontend. */ -void -be_setauthsvc(char *name) + +static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler)()) { - int i, - j; - int turnon = 1; + char *dp, *sp; + int i; + uint32 net_areq; - if (!name) - return; - if (!strncmp("no", name, 2)) + /* Convert to a byte stream. */ + + net_areq = htonl(areq); + + dp = port->pktInfo.pkt.ar.data; + sp = (char *)&net_areq; + + *dp++ = 'R'; + + for (i = 1; i <= 4; ++i) + *dp++ = *sp++; + + /* Add the salt for encrypted passwords. */ + + if (areq == AUTH_REQ_CRYPT) { - turnon = 0; - name += 2; + *dp++ = port->salt[0]; + *dp++ = port->salt[1]; + i += 2; } - if (name[0] == '\0') - return; - for (i = 0; i < n_authsvcs; ++i) - if (!strcmp(name, authsvcs[i].name)) - { - for (j = 0; j < n_authsvcs; ++j) - if (authsvcs[j].msgtype == authsvcs[i].msgtype) - authsvcs[j].allowed = turnon; - break; - } - if (i == n_authsvcs) + + PacketSendSetup(&port -> pktInfo, i, handler, (char *)port); +} + + +/* + * Called when we have told the front end that it is authorised. + */ + +static void handle_done_auth(Port *port) +{ + /* + * Don't generate any more traffic. This will cause the backend to + * start. + */ + + return; +} + + +/* + * Called when we have told the front end that it should use Kerberos V4 + * authentication. + */ + +static void handle_krb4_auth(Port *port) +{ + if (pg_krb4_recvauth(port) != STATUS_OK) + auth_failed(port); + else + sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth); +} + + +/* + * Called when we have told the front end that it should use Kerberos V5 + * authentication. + */ + +static void handle_krb5_auth(Port *port) +{ + if (pg_krb5_recvauth(port) != STATUS_OK) + auth_failed(port); + else + sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth); +} + + +/* + * Called when we have told the front end that it should use password + * authentication. + */ + +static void handle_password_auth(Port *port) +{ + /* Set up the read of the password packet. */ + + PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (char *)port); +} + + +/* + * Called when we have received the password packet. + */ + +static void readPasswordPacket(char *arg, PacketLen len, char *pkt) +{ + char password[sizeof (PasswordPacket) + 1]; + Port *port; + + port = (Port *)arg; + + /* Silently truncate a password that is too big. */ + + if (len > sizeof (PasswordPacket)) + len = sizeof (PasswordPacket); + + StrNCpy(password, ((PasswordPacket *)pkt)->passwd, len); + + /* + * Use the local flat password file if clear passwords are used and the + * file is specified. Otherwise use the password in the pg_user table, + * encrypted or not. + */ + + if (port->auth_method == uaPassword && port->auth_arg[0] != '\0') { - sprintf(PQerrormsg, - "be_setauthsvc: invalid name %s, ignoring...\n", - name); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); + if (verify_password(port->auth_arg, port->user, password) != STATUS_OK) + auth_failed(port); } - return; + else if (crypt_verify(port, port->user, password) != STATUS_OK) + auth_failed(port); + else + sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth); } -static int -be_getauthsvc(MsgType msgtype) + +/* + * Server demux routine for incoming authentication information for protocol + * version 0. + */ +static int old_be_recvauth(Port *port) { - int i; + int status; + MsgType msgtype = (MsgType)port->proto; + + /* Handle the authentication that's offered. */ + + switch (msgtype) + { + case STARTUP_KRB4_MSG: + status = map_old_to_new(port,uaKrb4,pg_krb4_recvauth(port)); + break; + + case STARTUP_KRB5_MSG: + status = map_old_to_new(port,uaKrb5,pg_krb5_recvauth(port)); + break; + + case STARTUP_MSG: + status = map_old_to_new(port,uaTrust,STATUS_OK); + break; + + case STARTUP_PASSWORD_MSG: + PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth, + (char *)port); - for (i = 0; i < n_authsvcs; ++i) - if (msgtype == authsvcs[i].msgtype) - return (authsvcs[i].allowed); - return (0); + return STATUS_OK; + + default: + fprintf(stderr, "Invalid startup message type: %u\n", msgtype); + + return STATUS_OK; + } + + return status; +} + + +/* + * The old style authentication has been done. Modify the result of this (eg. + * allow the connection anyway, disallow it anyway, or use the result) + * depending on what authentication we really want to use. + */ + +static int map_old_to_new(Port *port, UserAuth old, int status) +{ + switch (port->auth_method) + { + case uaCrypt: + case uaReject: + status = STATUS_ERROR; + break; + + case uaKrb4: + if (old != uaKrb4) + status = STATUS_ERROR; + break; + + case uaKrb5: + if (old != uaKrb5) + status = STATUS_ERROR; + break; + + case uaTrust: + status = STATUS_OK; + break; + + case uaIdent: + status = authident(&port->raddr.in, &port->laddr.in, + port->user, port->auth_arg); + break; + + case uaPassword: + if (old != uaPassword) + status = STATUS_ERROR; + + break; + } + + return status; } |