1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-12 05:01:15 +03:00
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:
Marc G. Fournier
1998-01-26 01:42:53 +00:00
parent 91d983aa11
commit d5bbe2aca5
41 changed files with 1611 additions and 2288 deletions

View File

@@ -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;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-dumpdata.c,v 1.9 1997/09/12 04:07:50 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-dumpdata.c,v 1.10 1998/01/26 01:41:05 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,7 +34,7 @@
#include <postgres.h>
#include <lib/dllist.h>
#include <libpq/libpq-be.h>
#include <libpq/libpq.h>
#include <access/heapam.h>
#include <access/htup.h>
#include <storage/buf.h>

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.13 1998/01/07 21:03:16 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.14 1998/01/26 01:41:06 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,7 +27,7 @@
#include <tcop/fastpath.h>
#include <tcop/tcopprot.h>
#include <lib/dllist.h>
#include <libpq/libpq-be.h>
#include <libpq/libpq.h>
#include <fmgr.h>
#include <utils/exc.h>
#include <utils/builtins.h>

View File

@@ -17,9 +17,6 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#include "postgres.h"
#include "miscadmin.h"
@@ -27,6 +24,10 @@
#include "storage/fd.h"
#include "libpq/crypt.h"
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
char** pwd_cache = NULL;
int pwd_cache_count = 0;
@@ -219,6 +220,7 @@ int crypt_getloginfo(const char* user, char** passwd, char** valuntil) {
/*-------------------------------------------------------------------------*/
#ifdef 0
MsgType crypt_salt(const char* user) {
char* passwd;
@@ -237,6 +239,7 @@ MsgType crypt_salt(const char* user) {
if (valuntil) free((void*)valuntil);
return STARTUP_SALT_MSG;
}
#endif
/*-------------------------------------------------------------------------*/
@@ -258,7 +261,13 @@ int crypt_verify(Port* port, const char* user, const char* pgpass) {
return STATUS_ERROR;
}
crypt_pwd = crypt(passwd, port->salt);
/*
* Compare with the encrypted or plain password depending on the
* authentication method being used for this connection.
*/
crypt_pwd = (port->auth_method == uaCrypt ? crypt(passwd, port->salt) : passwd);
if (!strcmp(pgpass, crypt_pwd)) {
/* check here to be sure we are not past valuntil
*/

View File

@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.25 1997/12/09 03:10:38 scrappy Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.26 1998/01/26 01:41:08 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -97,84 +97,56 @@ read_through_eol(FILE *file)
static void
read_hba_entry2(FILE *file, enum Userauth * userauth_p, char usermap_name[],
bool *error_p, bool *matches_p, bool find_password_entries)
read_hba_entry2(FILE *file, UserAuth * userauth_p, char auth_arg[],
bool *error_p)
{
/*--------------------------------------------------------------------------
Read from file FILE the rest of a host record, after the mask field,
and return the interpretation of it as *userauth_p, usermap_name, and
and return the interpretation of it as *userauth_p, auth_arg, and
*error_p.
---------------------------------------------------------------------------*/
char buf[MAX_TOKEN];
bool userauth_valid;
/* Get authentication type token. */
next_token(file, buf, sizeof(buf));
userauth_valid = false;
if (buf[0] == '\0')
if (strcmp(buf, "trust") == 0)
*userauth_p = uaTrust;
else if (strcmp(buf, "ident") == 0)
*userauth_p = uaIdent;
else if (strcmp(buf, "password") == 0)
*userauth_p = uaPassword;
else if (strcmp(buf, "krb4") == 0)
*userauth_p = uaKrb4;
else if (strcmp(buf, "krb5") == 0)
*userauth_p = uaKrb5;
else if (strcmp(buf, "reject") == 0)
*userauth_p = uaReject;
else if (strcmp(buf, "crypt") == 0)
*userauth_p = uaCrypt;
else
{
*error_p = true;
}
else
{
userauth_valid = true;
if (strcmp(buf, "trust") == 0)
{
*userauth_p = Trust;
}
else if (strcmp(buf, "ident") == 0)
{
*userauth_p = Ident;
}
else if (strcmp(buf, "password") == 0)
{
*userauth_p = Password;
}
else
{
userauth_valid = false;
}
if ((find_password_entries && strcmp(buf, "password") == 0) ||
(!find_password_entries && strcmp(buf, "password") != 0))
{
*matches_p = true;
}
else
{
*matches_p = false;
}
if (buf[0] != '\0')
read_through_eol(file);
}
if (!userauth_valid || !*matches_p || *error_p)
if (!*error_p)
{
if (!userauth_valid)
{
*error_p = true;
}
read_through_eol(file);
}
else
{
/* Get the map name token, if any */
/* Get the authentication argument token, if any */
next_token(file, buf, sizeof(buf));
if (buf[0] == '\0')
{
*error_p = false;
usermap_name[0] = '\0';
}
auth_arg[0] = '\0';
else
{
strncpy(usermap_name, buf, USERMAP_NAME_SIZE);
StrNCpy(auth_arg, buf, MAX_AUTH_ARG - 1);
next_token(file, buf, sizeof(buf));
if (buf[0] != '\0')
{
*error_p = true;
read_through_eol(file);
}
else
*error_p = false;
}
}
}
@@ -182,139 +154,150 @@ read_hba_entry2(FILE *file, enum Userauth * userauth_p, char usermap_name[],
static void
process_hba_record(FILE *file,
const struct in_addr ip_addr, const char database[],
process_hba_record(FILE *file, SockAddr *raddr, const char database[],
bool *matches_p, bool *error_p,
enum Userauth * userauth_p, char usermap_name[],
bool find_password_entries)
UserAuth * userauth_p, char auth_arg[])
{
/*---------------------------------------------------------------------------
Process the non-comment record in the config file that is next on the file.
See if it applies to a connection to a host with IP address "ip_addr"
See if it applies to a connection to a host with IP address "*raddr"
to a database named "database[]". If so, return *matches_p true
and *userauth_p and usermap_name[] as the values from the entry.
If not, return matches_p false. If the record has a syntax error,
and *userauth_p and auth_arg[] as the values from the entry.
If not, leave *matches_p as it was. If the record has a syntax error,
return *error_p true, after issuing a message to stderr. If no error,
leave *error_p as it was.
---------------------------------------------------------------------------*/
char buf[MAX_TOKEN]; /* A token from the record */
char db[MAX_TOKEN], buf[MAX_TOKEN];
/* Read the record type field. */
/* Read the record type field */
next_token(file, buf, sizeof(buf));
if (buf[0] == '\0')
*matches_p = false;
return;
/* Check the record type. */
if (strcmp(buf, "local") == 0)
{
/* Get the database. */
next_token(file, db, sizeof(db));
if (db[0] == '\0')
goto syntax;
/* Read the rest of the line. */
read_hba_entry2(file, userauth_p, auth_arg, error_p);
/*
* For now, disallow methods that need AF_INET sockets to work.
*/
if (!*error_p &&
(*userauth_p == uaIdent ||
*userauth_p == uaKrb4 ||
*userauth_p == uaKrb5))
*error_p = true;
if (*error_p)
goto syntax;
/*
* If this record isn't for our database, or this is the wrong
* sort of connection, ignore it.
*/
if ((strcmp(db, database) != 0 && strcmp(db, "all") != 0) ||
raddr->sa.sa_family != AF_UNIX)
return;
}
else if (strcmp(buf, "host") == 0)
{
struct in_addr file_ip_addr, mask;
/* Get the database. */
next_token(file, db, sizeof(db));
if (db[0] == '\0')
goto syntax;
/* Read the IP address field. */
next_token(file, buf, sizeof(buf));
if (buf[0] == '\0')
goto syntax;
/* Remember the IP address field and go get mask field. */
if (!inet_aton(buf, &file_ip_addr))
{
read_through_eol(file);
goto syntax;
}
/* Read the mask field. */
next_token(file, buf, sizeof(buf));
if (buf[0] == '\0')
goto syntax;
if (!inet_aton(buf, &mask))
{
read_through_eol(file);
goto syntax;
}
/*
* This is the record we're looking for. Read the rest of the
* info from it.
*/
read_hba_entry2(file, userauth_p, auth_arg, error_p);
if (*error_p)
goto syntax;
/*
* If this record isn't for our database, or this is the wrong
* sort of connection, ignore it.
*/
if ((strcmp(db, database) != 0 && strcmp(db, "all") != 0) ||
raddr->sa.sa_family != AF_INET ||
((file_ip_addr.s_addr ^ raddr->in.sin_addr.s_addr) & mask.s_addr) != 0x0000)
return;
}
else
{
/* if this isn't a "host" record, it can't match. */
if (strcmp(buf, "host") != 0)
{
*matches_p = false;
read_through_eol(file);
}
else
{
/* It's a "host" record. Read the database name field. */
next_token(file, buf, sizeof(buf));
if (buf[0] == '\0')
*matches_p = false;
else
{
/* If this record isn't for our database, ignore it. */
if (strcmp(buf, database) != 0 && strcmp(buf, "all") != 0)
{
*matches_p = false;
read_through_eol(file);
}
else
{
/* Read the IP address field */
next_token(file, buf, sizeof(buf));
if (buf[0] == '\0')
*matches_p = false;
else
{
int valid; /* Field is valid dotted
* decimal */
/*
* Remember the IP address field and go get mask
* field
*/
struct in_addr file_ip_addr; /* IP address field
* value */
valid = inet_aton(buf, &file_ip_addr);
if (!valid)
{
*matches_p = false;
read_through_eol(file);
}
else
{
/* Read the mask field */
next_token(file, buf, sizeof(buf));
if (buf[0] == '\0')
*matches_p = false;
else
{
struct in_addr mask;
/*
* Got mask. Now see if this record is
* for our host.
*/
valid = inet_aton(buf, &mask);
if (!valid)
{
*matches_p = false;
read_through_eol(file);
}
else
{
if (((file_ip_addr.s_addr ^ ip_addr.s_addr) & mask.s_addr)
!= 0x0000)
{
*matches_p = false;
read_through_eol(file);
}
else
{
/*
* This is the record we're
* looking for. Read the rest of
* the info from it.
*/
read_hba_entry2(file, userauth_p, usermap_name,
error_p, matches_p, find_password_entries);
if (*error_p)
{
sprintf(PQerrormsg,
"process_hba_record: invalid syntax in "
"hba config file "
"for host record for IP address %s\n",
inet_ntoa(file_ip_addr));
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
}
}
}
}
}
}
}
}
}
read_through_eol(file);
goto syntax;
}
*matches_p = true;
return;
syntax:
sprintf(PQerrormsg,
"process_hba_record: invalid syntax in pg_hba.conf file\n");
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
*error_p = true;
}
static void
process_open_config_file(FILE *file,
const struct in_addr ip_addr, const char database[],
bool *host_ok_p, enum Userauth * userauth_p,
char usermap_name[], bool find_password_entries)
process_open_config_file(FILE *file, SockAddr *raddr, const char database[],
bool *host_ok_p, UserAuth * userauth_p,
char auth_arg[])
{
/*---------------------------------------------------------------------------
This function does the same thing as find_hba_entry, only with
@@ -348,36 +331,26 @@ process_open_config_file(FILE *file,
read_through_eol(file);
else
{
process_hba_record(file, ip_addr, database,
&found_entry, &error, userauth_p, usermap_name,
find_password_entries);
process_hba_record(file, raddr, database,
&found_entry, &error, userauth_p, auth_arg);
}
}
}
if (found_entry)
{
if (error)
*host_ok_p = false;
else
*host_ok_p = true;
}
else
*host_ok_p = false;
if (found_entry && !error)
*host_ok_p = true;
}
void
find_hba_entry(const char DataDir[], const struct in_addr ip_addr,
const char database[],
bool *host_ok_p, enum Userauth * userauth_p,
char usermap_name[], bool find_password_entries)
static void
find_hba_entry(SockAddr *raddr, const char database[], bool *host_ok_p,
UserAuth * userauth_p, char auth_arg[])
{
/*--------------------------------------------------------------------------
Read the config file and find an entry that allows connection from
host "ip_addr" to database "database". If not found, return
*host_ok_p == false. If found, return *userauth_p and *usermap_name
representing the contents of that entry.
host "*raddr" to database "database". If found, return *host_ok_p == true
and *userauth_p and *auth_arg representing the contents of that entry.
When a record has invalid syntax, we either ignore it or reject the
connection (depending on where it's invalid). No message or anything.
@@ -436,8 +409,6 @@ find_hba_entry(const char DataDir[], const struct in_addr ip_addr,
{
/* The open of the config file failed. */
*host_ok_p = false;
sprintf(PQerrormsg,
"find_hba_entry: Host-based authentication config file "
"does not exist or permissions are not setup correctly! "
@@ -448,8 +419,8 @@ find_hba_entry(const char DataDir[], const struct in_addr ip_addr,
}
else
{
process_open_config_file(file, ip_addr, database, host_ok_p, userauth_p,
usermap_name, find_password_entries);
process_open_config_file(file, raddr, database, host_ok_p, userauth_p,
auth_arg);
FreeFile(file);
}
pfree(conf_file);
@@ -754,8 +725,7 @@ verify_against_open_usermap(FILE *file,
static void
verify_against_usermap(const char DataDir[],
const char pguser[],
verify_against_usermap(const char pguser[],
const char ident_username[],
const char usermap_name[],
bool *checks_out_p)
@@ -834,20 +804,20 @@ verify_against_usermap(const char DataDir[],
static void
authident(const char DataDir[],
const Port port, const char postgres_username[],
const char usermap_name[],
bool *authentic_p)
int
authident(struct sockaddr_in *raddr, struct sockaddr_in *laddr,
const char postgres_username[],
const char auth_arg[])
{
/*---------------------------------------------------------------------------
Talk to the ident server on the remote host and find out who owns the
connection described by "port". Then look in the usermap file under
the usermap usermap_name[] and see if that user is equivalent to
the usermap auth_arg[] and see if that user is equivalent to
Postgres user user[].
Return *authentic_p true iff yes.
Return STATUS_OK if yes.
---------------------------------------------------------------------------*/
bool checks_out;
bool ident_failed;
/* We were unable to get ident to give us a username */
@@ -855,120 +825,35 @@ authident(const char DataDir[],
/* The username returned by ident */
ident(port.raddr.in.sin_addr, port.laddr.in.sin_addr,
port.raddr.in.sin_port, port.laddr.in.sin_port,
ident(raddr->sin_addr, laddr->sin_addr,
raddr->sin_port, laddr->sin_port,
&ident_failed, ident_username);
if (ident_failed)
*authentic_p = false;
else
{
bool checks_out;
return STATUS_ERROR;
verify_against_usermap(DataDir,
postgres_username, ident_username, usermap_name,
verify_against_usermap(postgres_username, ident_username, auth_arg,
&checks_out);
if (checks_out)
*authentic_p = true;
else
*authentic_p = false;
}
return (checks_out ? STATUS_OK : STATUS_ERROR);
}
extern int
hba_recvauth(const Port *port, const char database[], const char user[],
const char DataDir[])
hba_getauthmethod(SockAddr *raddr, char *database, char *auth_arg,
UserAuth *auth_method)
{
/*---------------------------------------------------------------------------
Determine if the TCP connection described by "port" is with someone
allowed to act as user "user" and access database "database". Return
STATUS_OK if yes; STATUS_ERROR if not.
Determine what authentication method should be used when accessing database
"database" from frontend "raddr". Return the method, an optional argument,
and STATUS_OK.
----------------------------------------------------------------------------*/
bool host_ok;
/*
* There's an entry for this database and remote host in the pg_hba
* file
*/
char usermap_name[USERMAP_NAME_SIZE + 1];
host_ok = false;
/*
* The name of the map pg_hba specifies for this connection (or
* special value "SAMEUSER")
*/
enum Userauth userauth;
find_hba_entry(raddr, database, &host_ok, auth_method, auth_arg);
/*
* The type of user authentication pg_hba specifies for this
* connection
*/
int retvalue;
/* UNIX socket always OK, for now */
if (port->raddr.in.sin_family == AF_UNIX)
return STATUS_OK;
/* Our eventual return value */
find_hba_entry(DataDir, port->raddr.in.sin_addr, database,
&host_ok, &userauth, usermap_name,
false /* don't find password entries of type
'password' */ );
if (!host_ok)
retvalue = STATUS_ERROR;
else
{
switch (userauth)
{
case Trust:
retvalue = STATUS_OK;
break;
case Ident:
{
/*
* Here's where we need to call up ident and
* authenticate the user
*/
bool authentic; /* He is who he says he
* is. */
authident(DataDir, *port, user, usermap_name, &authentic);
if (authentic)
retvalue = STATUS_OK;
else
retvalue = STATUS_ERROR;
}
break;
default:
retvalue = STATUS_ERROR;
Assert(false);
}
}
return (retvalue);
return (host_ok ? STATUS_OK : STATUS_ERROR);
}
/*----------------------------------------------------------------
* This version of hba was written by Bryan Henderson
* in September 1996 for Release 6.0. It changed the format of the
* hba file and added ident function.
*
* Here are some notes about the original host based authentication
* the preceded this one.
*
* based on the securelib package originally written by William
* LeFebvre, EECS Department, Northwestern University
* (phil@eecs.nwu.edu) - orginal configuration file code handling
* by Sam Horrocks (sam@ics.uci.edu)
*
* modified and adapted for use with Postgres95 by Paul Fisher
* (pnfisher@unity.ncsu.edu)
*
-----------------------------------------------------------------*/

View File

@@ -1,6 +1,6 @@
#include <postgres.h>
#include <miscadmin.h>
#include <libpq/password.h>
#include <libpq/hba.h>
#include <libpq/libpq.h>
#include <storage/fd.h>
#include <string.h>
@@ -10,56 +10,15 @@
#endif
int
verify_password(char *user, char *password, Port *port,
char *database, char *DataDir)
verify_password(char *auth_arg, char *user, char *password)
{
bool host_ok;
enum Userauth userauth;
char pw_file_name[PWFILE_NAME_SIZE + 1];
char *pw_file_fullname;
FILE *pw_file;
char *pw_file_fullname;
FILE *pw_file;
char pw_file_line[255];
char *p,
*test_user,
*test_pw;
find_hba_entry(DataDir, port->raddr.in.sin_addr, database,
&host_ok, &userauth, pw_file_name, true);
if (!host_ok)
{
sprintf(PQerrormsg,
"verify_password: couldn't find entry for connecting host\n");
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
return STATUS_ERROR;
}
if (userauth != Password)
{
sprintf(PQerrormsg,
"verify_password: couldn't find entry of type 'password' "
"for this host\n");
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
return STATUS_ERROR;
}
if (!pw_file_name || pw_file_name[0] == '\0')
{
sprintf(PQerrormsg,
"verify_password: no password file specified\n");
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
return STATUS_ERROR;
}
pw_file_fullname = (char *) palloc(strlen(DataDir) + strlen(pw_file_name) + 2);
pw_file_fullname = (char *) palloc(strlen(DataDir) + strlen(auth_arg) + 2);
strcpy(pw_file_fullname, DataDir);
strcat(pw_file_fullname, "/");
strcat(pw_file_fullname, pw_file_name);
strcat(pw_file_fullname, auth_arg);
pw_file = AllocateFile(pw_file_fullname, "r");
if (!pw_file)
@@ -69,12 +28,17 @@ verify_password(char *user, char *password, Port *port,
pw_file_fullname);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
pfree(pw_file_fullname);
return STATUS_ERROR;
}
while (!feof(pw_file))
{
fgets(pw_file_line, 255, pw_file);
char pw_file_line[255], *p, *test_user, *test_pw;
fgets(pw_file_line, sizeof (pw_file_line), pw_file);
p = pw_file_line;
test_user = strtok(p, ":");
@@ -97,6 +61,9 @@ verify_password(char *user, char *password, Port *port,
if (strcmp(crypt(password, test_pw), test_pw) == 0)
{
/* it matched. */
pfree(pw_file_fullname);
return STATUS_OK;
}
@@ -105,6 +72,9 @@ verify_password(char *user, char *password, Port *port,
user);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
pfree(pw_file_fullname);
return STATUS_ERROR;
}
}
@@ -114,5 +84,8 @@ verify_password(char *user, char *password, Port *port,
user);
fputs(PQerrormsg, stderr);
pqdebug("%s", PQerrormsg);
pfree(pw_file_fullname);
return STATUS_ERROR;
}

View File

@@ -4,6 +4,7 @@
#
# This file controls what hosts are allowed to connect to what databases
# and specifies some options on how users on a particular host are identified.
# It is read each time a host tries to make a connection to a database.
#
# Each line (terminated by a newline character) is a record. A record cannot
# be continued across two lines.
@@ -29,13 +30,14 @@
# Record type "host"
# ------------------
#
# This record identifies a set of hosts that are permitted to connect to
# databases. No hosts are permitted to connect except as specified by a
# "host" record.
# This record identifies a set of network hosts that are permitted to connect
# to databases. No network hosts are permitted to connect except as specified
# by a "host" record. See the record type "local" to specify permitted
# connections using UNIX sockets.
#
# Format:
#
# host DBNAME IP_ADDRESS ADDRESS_MASK USERAUTH [MAP]
# host DBNAME IP_ADDRESS ADDRESS_MASK USERAUTH [AUTH_ARGUMENT]
#
# DBNAME is the name of a Postgres database, or "all" to indicate all
# databases.
@@ -49,33 +51,54 @@
# under the Postgres username he supplies in his connection parameters.
#
# ident: Authentication is done by the ident server on the remote
# host, via the ident (RFC 1413) protocol.
# host, via the ident (RFC 1413) protocol. AUTH_ARGUMENT, if
# specified, is a map name to be found in the pg_ident.conf file.
# That table maps from ident usernames to Postgres usernames. The
# special map name "sameuser" indicates an implied map (not found
# in pg_ident.conf) that maps every ident username to the identical
# Postgres username.
#
# trust: No authentication is done. Trust that the user has the
# authority to user whatever username he says he does.
# Before Postgres Version 6, all authentication was this way.
#
# MAP is the name of a map that matches an authenticated principal with
# a Postgres username. If USERNAME is "trust", this value is ignored and
# may be absent.
# reject: Reject the connection.
#
# In the case of USERAUTH=ident, this is a map name to be found in the
# pg_ident.conf file. That table maps from ident usernames to Postgres
# usernames. The special map name "sameuser" indicates an implied map
# (not found in pg_ident.conf) that maps every ident username to the identical
# Postgres username.
# password: Authentication is done by matching a password supplied in clear
# by the host. If AUTH_ARGUMENT is specified then the password is
# compared with the user's entry in that file (in the $PGDATA
# directory). See pg_passwd(1). If it is omitted then the
# password is compared with the user's entry in the pg_user table.
#
# crypt: Authentication is done by matching an encrypted password supplied
# by the host with that held for the user in the pg_user table.
#
# krb4: Kerberos V4 authentication is used.
#
# krb5: Kerberos V5 authentication is used.
# Record type "local"
# ------------------
#
# This record identifies the authentication to use when connecting to a
# particular database via a local UNIX socket.
#
# Format:
#
# local DBNAME USERAUTH [AUTH_ARGUMENT]
#
# The format is the same as that of the "host" record type except that the
# IP_ADDRESS and ADDRESS_MASK are omitted and the "ident", "krb4" and "krb5"
# values of USERAUTH are no allowed.
# For backwards compatibility, PostgreSQL also accepts pre-Version 6 records,
# which look like:
#
# all 127.0.0.1 0.0.0.0
#
#
# TYPE DATABASE IP_ADDRESS MASK USERAUTH MAP
host all 127.0.0.1 255.255.255.255 trust
#host all 127.0.0.1 255.255.255.255 trust
# The above allows any user on the local system to connect to any database
# under any username.
@@ -86,10 +109,11 @@ host all 127.0.0.1 255.255.255.255 trust
# connect to database template1 as the same username that ident on that host
# identifies him as (typically his Unix username).
#host all 192.168.0.1 255.255.255.255 reject
#host all 0.0.0.0 0.0.0.0 trust
# The above would allow anyone anywhere to connect to any database under
# any username.
# The above would allow anyone anywhere except from 192.168.0.1 to connect to
# any database under any username.
#host all 192.168.0.0 255.255.255.0 ident omicron
#

View File

@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.34 1998/01/25 05:13:18 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.35 1998/01/26 01:41:11 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,6 +43,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -269,28 +270,6 @@ int
pq_getnchar(char *s, int off, int maxlen)
{
return pqGetNBytes(s + off, maxlen, Pfin);
#if 0
int c = '\0';
if (Pfin == (FILE *) NULL)
{
/* elog(DEBUG, "Input descriptor is null"); */
return (EOF);
}
s += off;
while (maxlen-- && (c = pq_getc(Pfin)) != EOF)
*s++ = c;
/* -----------------
* If EOF reached let caller know
* -----------------
*/
if (c == EOF)
return (EOF);
return (!EOF);
#endif
}
/* --------------------------------
@@ -591,11 +570,7 @@ do_unlink()
int
StreamServerPort(char *hostName, short portName, int *fdP)
{
union
{
struct sockaddr_in in;
struct sockaddr_un un;
} saddr;
SockAddr saddr;
int fd,
err,
family;
@@ -624,20 +599,19 @@ StreamServerPort(char *hostName, short portName, int *fdP)
return (STATUS_ERROR);
}
bzero(&saddr, sizeof(saddr));
saddr.sa.sa_family = family;
if (family == AF_UNIX)
{
saddr.un.sun_family = family;
len = UNIXSOCK_PATH(saddr.un, portName);
strcpy(sock_path, saddr.un.sun_path);
}
else
{
saddr.in.sin_family = family;
saddr.in.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.in.sin_port = htons(portName);
len = sizeof saddr.in;
len = sizeof (struct sockaddr_in);
}
err = bind(fd, (struct sockaddr *) & saddr, len);
err = bind(fd, &saddr.sa, len);
if (err < 0)
{
sprintf(PQerrormsg,
@@ -685,7 +659,7 @@ StreamConnection(int server_fd, Port *port)
{
int len,
addrlen;
int family = port->raddr.in.sin_family;
int family = port->raddr.sa.sa_family;
/* accept connection (and fill in the client (remote) address) */
len = family == AF_INET ?
@@ -726,8 +700,6 @@ StreamConnection(int server_fd, Port *port)
}
}
port->mask = 1 << port->sock;
/* reset to non-blocking */
fcntl(port->sock, F_SETFL, 1);
@@ -788,7 +760,7 @@ StreamOpen(char *hostName, short portName, Port *port)
len = UNIXSOCK_PATH(port->raddr.un, portName);
}
/* connect to the server */
if ((port->sock = socket(port->raddr.in.sin_family, SOCK_STREAM, 0)) < 0)
if ((port->sock = socket(port->raddr.sa.sa_family, SOCK_STREAM, 0)) < 0)
{
sprintf(PQerrormsg,
"FATAL: StreamOpen: socket() failed: errno=%d\n",
@@ -797,7 +769,7 @@ StreamOpen(char *hostName, short portName, Port *port)
pqdebug("%s", PQerrormsg);
return (STATUS_ERROR);
}
err = connect(port->sock, (struct sockaddr *) & port->raddr, len);
err = connect(port->sock, &port->raddr.sa, len);
if (err < 0)
{
sprintf(PQerrormsg,
@@ -809,8 +781,7 @@ StreamOpen(char *hostName, short portName, Port *port)
}
/* fill in the client address */
if (getsockname(port->sock, (struct sockaddr *) & port->laddr,
&len) < 0)
if (getsockname(port->sock, &port->laddr.sa, &len) < 0)
{
sprintf(PQerrormsg,
"FATAL: StreamOpen: getsockname() failed: errno=%d\n",
@@ -822,32 +793,3 @@ StreamOpen(char *hostName, short portName, Port *port)
return (STATUS_OK);
}
static char *authentication_type_name[] = {
0, 0, 0, 0, 0, 0, 0,
"the default authentication type",
0, 0,
"Kerberos v4",
"Kerberos v5",
"host-based authentication",
"unauthenication",
"plaintext password authentication"
};
char *
name_of_authentication_type(int type)
{
char *result = 0;
if (type >= 1 && type <= LAST_AUTHENTICATION_TYPE)
{
result = authentication_type_name[type];
}
if (result == 0)
{
result = "<unknown authentication type>";
}
return result;
}

View File

@@ -1,122 +1,150 @@
#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include "postgres.h"
#include "libpq/pqcomm.h"
#ifdef HAVE_ENDIAN_H
#include <endian.h>
#endif
/* --------------------------------------------------------------------- */
/* These definitions for ntoh/hton are the other way around from the
* default system definitions, so we roll our own here.
/*
* The backend supports the old little endian byte order and the current
* network byte order.
*/
#ifndef FRONTEND
#include "libpq/libpq-be.h"
#ifdef HAVE_ENDIAN_H
#include <endian.h>
#endif
#ifndef BYTE_ORDER
#error BYTE_ORDER must be defined as LITTLE_ENDIAN, BIG_ENDIAN or PDP_ENDIAN
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
#define ntoh_s(n) n
#define ntoh_l(n) n
#define hton_s(n) n
#define hton_l(n) n
#else /* BYTE_ORDER != LITTLE_ENDIAN */
#define ntoh_s(n) n
#define ntoh_l(n) n
#define hton_s(n) n
#define hton_l(n) n
#else
#if BYTE_ORDER == BIG_ENDIAN
#define ntoh_s(n) (u_short)(((u_char *)&n)[1] << 8 \
| ((u_char *)&n)[0])
#define ntoh_l(n) (uint32) (((u_char *)&n)[3] << 24 \
| ((u_char *)&n)[2] << 16 \
| ((u_char *)&n)[1] << 8 \
| ((u_char *)&n)[0])
#define hton_s(n) (ntoh_s(n))
#define hton_l(n) (ntoh_l(n))
#define ntoh_s(n) (uint16)(((u_char *)&n)[1] << 8 \
| ((u_char *)&n)[0])
#define ntoh_l(n) (uint32)(((u_char *)&n)[3] << 24 \
| ((u_char *)&n)[2] << 16 \
| ((u_char *)&n)[1] << 8 \
| ((u_char *)&n)[0])
#define hton_s(n) (ntoh_s(n))
#define hton_l(n) (ntoh_l(n))
#else
/* BYTE_ORDER != BIG_ENDIAN */
#if BYTE_ORDER == PDP_ENDIAN
#error PDP_ENDIAN macros not written yet
#else
/* BYTE_ORDER != anything known */
#error BYTE_ORDER not defined as anything understood
#endif /* BYTE_ORDER == PDP_ENDIAN */
#endif /* BYTE_ORDER == BIG_ENDIAN */
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
#endif
#endif
#endif
#endif
/* --------------------------------------------------------------------- */
int
pqPutShort(int integer, FILE *f)
{
int retval = 0;
u_short n,
s;
uint16 n;
s = integer;
n = hton_s(s);
if (fwrite(&n, sizeof(u_short), 1, f) != 1)
retval = EOF;
#ifdef FRONTEND
n = htons((uint16)integer);
#else
n = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_s(integer) : htons((uint16)integer));
#endif
return retval;
if (fwrite(&n, 2, 1, f) != 1)
return EOF;
return 0;
}
/* --------------------------------------------------------------------- */
int
pqPutLong(int integer, FILE *f)
{
int retval = 0;
uint32 n;
uint32 n;
n = hton_l(integer);
if (fwrite(&n, sizeof(uint32), 1, f) != 1)
retval = EOF;
#ifdef FRONTEND
n = htonl((uint32)integer);
#else
n = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_l(integer) : htonl((uint32)integer));
#endif
return retval;
if (fwrite(&n, 4, 1, f) != 1)
return EOF;
return 0;
}
/* --------------------------------------------------------------------- */
int
pqGetShort(int *result, FILE *f)
{
int retval = 0;
u_short n;
uint16 n;
if (fread(&n, sizeof(u_short), 1, f) != 1)
retval = EOF;
if (fread(&n, 2, 1, f) != 1)
return EOF;
*result = ntoh_s(n);
return retval;
#ifdef FRONTEND
*result = (int)ntohs(n);
#else
*result = (int)((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? ntoh_s(n) : ntohs(n));
#endif
return 0;
}
/* --------------------------------------------------------------------- */
int
pqGetLong(int *result, FILE *f)
{
int retval = 0;
uint32 n;
uint32 n;
if (fread(&n, sizeof(uint32), 1, f) != 1)
retval = EOF;
if (fread(&n, 4, 1, f) != 1)
return EOF;
*result = ntoh_l(n);
return retval;
#ifdef FRONTEND
*result = (int)ntohl(n);
#else
*result = (int)((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? ntoh_l(n) : ntohl(n));
#endif
return 0;
}
/* --------------------------------------------------------------------- */
/* pqGetNBytes: Read a chunk of exactly len bytes in buffer s.
/* pqGetNBytes: Read a chunk of exactly len bytes in buffer s (which must be 1
byte longer) and terminate it with a '\0'.
Return 0 if ok.
*/
int
pqGetNBytes(char *s, size_t len, FILE *f)
{
int cnt;
int cnt;
if (f == NULL)
return EOF;
cnt = fread(s, 1, len, f);
s[cnt] = '\0';
/* mjl: actually needs up to len+1 bytes, is this okay? XXX */
return (cnt == len) ? 0 : EOF;
}
@@ -126,7 +154,7 @@ int
pqPutNBytes(const char *s, size_t len, FILE *f)
{
if (f == NULL)
return 0;
return EOF;
if (fwrite(s, 1, len, f) != len)
return EOF;
@@ -138,15 +166,27 @@ pqPutNBytes(const char *s, size_t len, FILE *f)
int
pqGetString(char *s, size_t len, FILE *f)
{
int c;
int c;
if (f == NULL)
return EOF;
while (len-- && (c = getc(f)) != EOF && c)
*s++ = c;
/*
* Keep on reading until we get the terminating '\0' and discard those
* bytes we don't have room for.
*/
while ((c = getc(f)) != EOF && c != '\0')
if (len > 1)
{
*s++ = c;
len--;
}
*s = '\0';
/* mjl: actually needs up to len+1 bytes, is this okay? XXX */
if (c == EOF)
return EOF;
return 0;
}
@@ -183,5 +223,3 @@ pqPutByte(int c, FILE *f)
return (putc(c, f) == c) ? 0 : EOF;
}
/* --------------------------------------------------------------------- */

View File

@@ -8,36 +8,14 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.12 1997/12/09 03:10:51 scrappy Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.13 1998/01/26 01:41:12 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
/* NOTES
* This is the module that understands the lowest-level part
* of the communication protocol. All of the trickiness in
* this module is for making sure that non-blocking I/O in
* the Postmaster works correctly. Check the notes in PacketRecv
* on non-blocking I/O.
*
* Data Structures:
* Port has two important functions. (1) It records the
* sock/addr used in communication. (2) It holds partially
* read in messages. This is especially important when
* we haven't seen enough to construct a complete packet
* header.
*
* PacketBuf -- None of the clients of this module should know
* what goes into a packet hdr (although they know how big
* it is). This routine is in charge of host to net order
* conversion for headers. Data conversion is someone elses
* responsibility.
*
* IMPORTANT: these routines are called by backends, clients, and
* the Postmaster.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
@@ -50,260 +28,158 @@
#include <storage/ipc.h>
#include <libpq/libpq.h>
/*
* PacketReceive -- receive a packet on a port.
*
* RETURNS: connection id of the packet sender, if one
* is available.
*
* Set up a packet read for the postmaster event loop.
*/
int
PacketReceive(Port *port, /* receive port */
PacketBuf *buf, /* MAX_PACKET_SIZE-worth of buffer space */
bool nonBlocking) /* NON_BLOCKING or BLOCKING i/o */
void PacketReceiveSetup(Packet *pkt, void (*iodone)(), char *arg)
{
PacketLen max_size = sizeof(PacketBuf);
PacketLen cc; /* character count -- bytes recvd */
PacketLen packetLen; /* remaining packet chars to read */
Addr tmp; /* curr recv buf pointer */
int hdrLen;
int flag;
int decr;
pkt->nrtodo = sizeof (pkt->len);
pkt->ptr = (char *)&pkt->len;
pkt->iodone = iodone;
pkt->arg = arg;
pkt->state = ReadingPacketLength;
}
hdrLen = sizeof(buf->len);
if (nonBlocking == NON_BLOCKING)
/*
* Read a packet fragment. Return STATUS_OK if the connection should stay
* open.
*/
int PacketReceiveFragment(Packet *pkt, int sock)
{
int got;
if ((got = read(sock,pkt->ptr,pkt->nrtodo)) > 0)
{
flag = MSG_PEEK;
decr = 0;
pkt->nrtodo -= got;
pkt->ptr += got;
/* See if we have got what we need for the packet length. */
if (pkt->nrtodo == 0 && pkt->state == ReadingPacketLength)
{
pkt->len = ntohl(pkt->len);
if (pkt->len < sizeof (pkt->len) ||
pkt->len > sizeof (pkt->len) + sizeof (pkt->pkt))
{
PacketSendError(pkt,"Invalid packet length");
return STATUS_OK;
}
/* Set up for the rest of the packet. */
pkt->nrtodo = pkt->len - sizeof (pkt->len);
pkt->ptr = (char *)&pkt->pkt;
pkt->state = ReadingPacket;
}
/* See if we have got what we need for the packet. */
if (pkt->nrtodo == 0 && pkt->state == ReadingPacket)
{
pkt->state = Idle;
/* Special case to close the connection. */
if (pkt->iodone == NULL)
return STATUS_ERROR;
(*pkt->iodone)(pkt->arg, pkt->len - sizeof (pkt->len),
(char *)&pkt->pkt);
}
return STATUS_OK;
}
else
if (got == 0)
return STATUS_ERROR;
if (errno == EINTR)
return STATUS_OK;
fprintf(stderr, "read() system call failed\n");
return STATUS_ERROR;
}
/*
* Set up a packet write for the postmaster event loop.
*/
void PacketSendSetup(Packet *pkt, int nbytes, void (*iodone)(), char *arg)
{
pkt->nrtodo = nbytes;
pkt->ptr = (char *)&pkt->pkt;
pkt->iodone = iodone;
pkt->arg = arg;
pkt->state = WritingPacket;
}
/*
* Write a packet fragment. Return STATUS_OK if the connection should stay
* open.
*/
int PacketSendFragment(Packet *pkt, int sock)
{
int done;
if ((done = write(sock,pkt->ptr,pkt->nrtodo)) > 0)
{
flag = 0;
decr = hdrLen;
pkt->nrtodo -= done;
pkt->ptr += done;
/* See if we have written the whole packet. */
if (pkt->nrtodo == 0)
{
pkt->state = Idle;
/* Special case to close the connection. */
if (pkt->iodone == NULL)
return STATUS_ERROR;
(*pkt->iodone)(pkt->arg);
}
return STATUS_OK;
}
if (done == 0)
return STATUS_ERROR;
if (errno == EINTR)
return STATUS_OK;
fprintf(stderr, "write() system call failed\n");
return STATUS_ERROR;
}
/*
* Send an error message from the postmaster to the frontend.
*/
void PacketSendError(Packet *pkt, char *errormsg)
{
fprintf(stderr, "%s\n", errormsg);
pkt->pkt.em.data[0] = 'E';
StrNCpy(&pkt->pkt.em.data[1], errormsg, sizeof (pkt->pkt.em.data) - 2);
/*
* Assume port->nBytes is zero unless we were interrupted during
* non-blocking I/O. This first recv() is to get the hdr information
* so we know how many bytes to read. Life would be very complicated
* if we read too much data (buffering).
* The NULL i/o callback will cause the connection to be broken when
* the error message has been sent.
*/
tmp = ((Addr) buf) + port->nBytes;
if (port->nBytes >= hdrLen)
{
packetLen = ntohl(buf->len) - port->nBytes;
}
else
{
/* peeking into the incoming message */
cc = recv(port->sock, (char *) &(buf->len), hdrLen, flag);
if (cc < hdrLen)
{
/* if cc is negative, the system call failed */
if (cc < 0)
{
return (STATUS_ERROR);
}
/*
* cc == 0 means the connection was broken at the other end.
*/
else if (!cc)
{
return (STATUS_INVALID);
}
else
{
/*
* Worst case. We didn't even read in enough data to get
* the header length. since we are using a data stream,
* this happens only if the client is mallicious.
*
* Don't save the number of bytes we've read so far. Since we
* only peeked at the incoming message, the kernel is
* going to keep it for us.
*/
return (STATUS_NOT_DONE);
}
}
else
{
/*
* This is an attempt to shield the Postmaster from mallicious
* attacks by placing tighter restrictions on the reported
* packet length.
*
* Check for negative packet length
*/
if ((buf->len) <= 0)
{
return (STATUS_INVALID);
}
/*
* Check for oversize packet
*/
if ((ntohl(buf->len)) > max_size)
{
return (STATUS_INVALID);
}
/*
* great. got the header. now get the true length (including
* header size).
*/
packetLen = ntohl(buf->len);
/*
* if someone is sending us junk, close the connection
*/
if (packetLen > max_size)
{
port->nBytes = packetLen;
return (STATUS_BAD_PACKET);
}
packetLen -= decr;
tmp += decr - port->nBytes;
}
}
/*
* Now that we know how big it is, read the packet. We read the
* entire packet, since the last call was just a peek.
*/
while (packetLen)
{
cc = read(port->sock, tmp, packetLen);
if (cc < 0)
return (STATUS_ERROR);
/*
* cc == 0 means the connection was broken at the other end.
*/
else if (!cc)
return (STATUS_INVALID);
/*
fprintf(stderr,"expected packet of %d bytes, got %d bytes\n",
packetLen, cc);
*/
tmp += cc;
packetLen -= cc;
/* if non-blocking, we're done. */
if (nonBlocking && packetLen)
{
port->nBytes += cc;
return (STATUS_NOT_DONE);
}
}
port->nBytes = 0;
return (STATUS_OK);
PacketSendSetup(pkt, strlen(pkt->pkt.em.data) + 1, NULL, NULL);
}
/*
* PacketSend -- send a single-packet message.
*
* RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
* SIDE_EFFECTS: may block.
* NOTES: Non-blocking writes would significantly complicate
* buffer management. For now, we're not going to do it.
*
*/
int
PacketSend(Port *port,
PacketBuf *buf,
PacketLen len,
bool nonBlocking)
{
PacketLen doneLen;
Assert(!nonBlocking);
Assert(buf);
doneLen = write(port->sock, buf, len);
if (doneLen < len)
{
sprintf(PQerrormsg,
"FATAL: PacketSend: couldn't send complete packet: errno=%d\n",
errno);
fputs(PQerrormsg, stderr);
return (STATUS_ERROR);
}
return (STATUS_OK);
}
/*
* StartupInfo2PacketBuf -
* convert the fields of the StartupInfo to a PacketBuf
*
*/
/* moved to src/libpq/fe-connect.c */
/*
PacketBuf*
StartupInfo2PacketBuf(StartupInfo* s)
{
PacketBuf* res;
char* tmp;
res = (PacketBuf*)palloc(sizeof(PacketBuf));
res->len = htonl(sizeof(PacketBuf));
res->data[0] = '\0';
tmp= res->data;
strncpy(tmp, s->database, sizeof(s->database));
tmp += sizeof(s->database);
strncpy(tmp, s->user, sizeof(s->user));
tmp += sizeof(s->user);
strncpy(tmp, s->options, sizeof(s->options));
tmp += sizeof(s->options);
strncpy(tmp, s->execFile, sizeof(s->execFile));
tmp += sizeof(s->execFile);
strncpy(tmp, s->tty, sizeof(s->execFile));
return res;
}
*/
/*
* PacketBuf2StartupInfo -
* convert the fields of the StartupInfo to a PacketBuf
*
*/
/* moved to postmaster.c
StartupInfo*
PacketBuf2StartupInfo(PacketBuf* p)
{
StartupInfo* res;
char* tmp;
res = (StartupInfo*)palloc(sizeof(StartupInfo));
res->database[0]='\0';
res->user[0]='\0';
res->options[0]='\0';
res->execFile[0]='\0';
res->tty[0]='\0';
tmp= p->data;
strncpy(res->database,tmp,sizeof(res->database));
tmp += sizeof(res->database);
strncpy(res->user,tmp, sizeof(res->user));
tmp += sizeof(res->user);
strncpy(res->options,tmp, sizeof(res->options));
tmp += sizeof(res->options);
strncpy(res->execFile,tmp, sizeof(res->execFile));
tmp += sizeof(res->execFile);
strncpy(res->tty,tmp, sizeof(res->tty));
return res;
}
*/