mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +03:00
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.
This commit is contained in:
@ -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
|
||||
*----------------------------------------------------------------
|
||||
*/
|
||||
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);
|
||||
|
||||
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)? */
|
||||
};
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
PacketBuf buf;
|
||||
char *user,
|
||||
*password;
|
||||
|
||||
if (PacketReceive(port, &buf, BLOCKING) != STATUS_OK)
|
||||
/*
|
||||
* Handle a v0 password packet.
|
||||
*/
|
||||
|
||||
static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt)
|
||||
{
|
||||
Port *port;
|
||||
PasswordPacketV0 *pp;
|
||||
char *user, *password, *cp, *start;
|
||||
|
||||
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')
|
||||
{
|
||||
sprintf(PQerrormsg,
|
||||
"pg_password_recvauth: failed to receive authentication packet.\n");
|
||||
fputs(PQerrormsg, stderr);
|
||||
pqdebug("%s", PQerrormsg);
|
||||
return STATUS_ERROR;
|
||||
if (user == NULL)
|
||||
user = start;
|
||||
else
|
||||
{
|
||||
password = start;
|
||||
break;
|
||||
}
|
||||
|
||||
start = cp;
|
||||
}
|
||||
|
||||
user = buf.data;
|
||||
password = buf.data + strlen(user) + 1;
|
||||
if (user == NULL || password == NULL)
|
||||
{
|
||||
sprintf(PQerrormsg,
|
||||
"pg_password_recvauth: badly formed password packet.\n");
|
||||
fputs(PQerrormsg, stderr);
|
||||
pqdebug("%s", PQerrormsg);
|
||||
|
||||
return verify_password(user, password, port, database, DataDir);
|
||||
auth_failed(port);
|
||||
}
|
||||
else if (map_old_to_new(port, uaPassword,
|
||||
verify_password(port->auth_arg, user, password)) != STATUS_OK)
|
||||
auth_failed(port);
|
||||
}
|
||||
|
||||
static int
|
||||
crypt_recvauth(Port *port)
|
||||
|
||||
/*
|
||||
* Tell the user the authentication failed, but not why.
|
||||
*/
|
||||
|
||||
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 (useHostBasedAuth)
|
||||
msgtype = STARTUP_HBA_MSG;
|
||||
else
|
||||
msgtype = STARTUP_UNAUTH_MSG;
|
||||
}
|
||||
else
|
||||
msgtype = msgtype_arg;
|
||||
|
||||
|
||||
if (!username)
|
||||
if (hba_getauthmethod(&port->raddr, port->database, port->auth_arg,
|
||||
&port->auth_method) != STATUS_OK)
|
||||
{
|
||||
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);
|
||||
PacketSendError(&port->pktInfo, "Error getting authentication method");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msgtype)
|
||||
{
|
||||
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)
|
||||
{
|
||||
/* Handle old style authentication. */
|
||||
|
||||
/*
|
||||
* 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);
|
||||
if (PG_PROTOCOL_MAJOR(port->proto) == 0)
|
||||
{
|
||||
if (old_be_recvauth(port) != STATUS_OK)
|
||||
auth_failed(port);
|
||||
|
||||
return;
|
||||
}
|
||||
return (STATUS_OK);
|
||||
|
||||
/* Handle new style authentication. */
|
||||
|
||||
switch (port->auth_method)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int i,
|
||||
j;
|
||||
int turnon = 1;
|
||||
|
||||
if (!name)
|
||||
return;
|
||||
if (!strncmp("no", name, 2))
|
||||
static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler)())
|
||||
{
|
||||
char *dp, *sp;
|
||||
int i;
|
||||
uint32 net_areq;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
sprintf(PQerrormsg,
|
||||
"be_setauthsvc: invalid name %s, ignoring...\n",
|
||||
name);
|
||||
fputs(PQerrormsg, stderr);
|
||||
pqdebug("%s", PQerrormsg);
|
||||
*dp++ = port->salt[0];
|
||||
*dp++ = port->salt[1];
|
||||
i += 2;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int
|
||||
be_getauthsvc(MsgType msgtype)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_authsvcs; ++i)
|
||||
if (msgtype == authsvcs[i].msgtype)
|
||||
return (authsvcs[i].allowed);
|
||||
return (0);
|
||||
/*
|
||||
* 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')
|
||||
{
|
||||
if (verify_password(port->auth_arg, port->user, password) != STATUS_OK)
|
||||
auth_failed(port);
|
||||
}
|
||||
else if (crypt_verify(port, port->user, password) != STATUS_OK)
|
||||
auth_failed(port);
|
||||
else
|
||||
sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Server demux routine for incoming authentication information for protocol
|
||||
* version 0.
|
||||
*/
|
||||
static int old_be_recvauth(Port *port)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user