diff options
| author | Marc G. Fournier | 1998-01-26 01:42:53 +0000 |
|---|---|---|
| committer | Marc G. Fournier | 1998-01-26 01:42:53 +0000 |
| commit | d5bbe2aca55bc833e38c768d7f82c129b8b70c83 (patch) | |
| tree | 47f4e1ecb3277869bb276e5433df335d920d1baf /src/backend/libpq | |
| parent | 91d983aa1140e3ae109684ff7c916583ed059e0e (diff) | |
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')
| -rw-r--r-- | src/backend/libpq/auth.c | 668 | ||||
| -rw-r--r-- | src/backend/libpq/be-dumpdata.c | 4 | ||||
| -rw-r--r-- | src/backend/libpq/be-pqexec.c | 4 | ||||
| -rw-r--r-- | src/backend/libpq/crypt.c | 17 | ||||
| -rw-r--r-- | src/backend/libpq/hba.c | 477 | ||||
| -rw-r--r-- | src/backend/libpq/password.c | 69 | ||||
| -rw-r--r-- | src/backend/libpq/pg_hba.conf.sample | 60 | ||||
| -rw-r--r-- | src/backend/libpq/pqcomm.c | 78 | ||||
| -rw-r--r-- | src/backend/libpq/pqcomprim.c | 160 | ||||
| -rw-r--r-- | src/backend/libpq/pqpacket.c | 356 |
10 files changed, 825 insertions, 1068 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 8aa3efca9b..7464303728 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; } diff --git a/src/backend/libpq/be-dumpdata.c b/src/backend/libpq/be-dumpdata.c index f34be7cdab..7a20e26e54 100644 --- a/src/backend/libpq/be-dumpdata.c +++ b/src/backend/libpq/be-dumpdata.c @@ -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> diff --git a/src/backend/libpq/be-pqexec.c b/src/backend/libpq/be-pqexec.c index 69d8675a46..a63ff43161 100644 --- a/src/backend/libpq/be-pqexec.c +++ b/src/backend/libpq/be-pqexec.c @@ -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> diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 6e6555503a..7a72275bc4 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -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 */ diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 3390f38f9a..048add7771 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -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') - { - *error_p = true; - } + + 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 { - 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; - } + *error_p = true; - 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 (!userauth_valid) - { - *error_p = true; - } - read_through_eol(file); - } - else + if (!*error_p) { - /* 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; - else + 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) { - /* if this isn't a "host" record, it can't match. */ - 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)) { - *matches_p = false; read_through_eol(file); + goto syntax; } - else + + /* Read the mask field. */ + + next_token(file, buf, sizeof(buf)); + + if (buf[0] == '\0') + goto syntax; + + if (!inet_aton(buf, &mask)) { - /* 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; } + + /* + * 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 + { + 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]; - - /* - * The name of the map pg_hba specifies for this connection (or - * special value "SAMEUSER") - */ - enum Userauth userauth; - - /* - * 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' */ ); + host_ok = false; - 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; + find_hba_entry(raddr, database, &host_ok, auth_method, auth_arg); - 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) - * - -----------------------------------------------------------------*/ diff --git a/src/backend/libpq/password.c b/src/backend/libpq/password.c index 8f26597f6a..c77b065e33 100644 --- a/src/backend/libpq/password.c +++ b/src/backend/libpq/password.c @@ -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; } diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index bb0d34f401..c746c752e4 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -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 # diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index 8014ae14f8..2b4e25f873 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -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; -} diff --git a/src/backend/libpq/pqcomprim.c b/src/backend/libpq/pqcomprim.c index 53742190f3..0dd46d9205 100644 --- a/src/backend/libpq/pqcomprim.c +++ b/src/backend/libpq/pqcomprim.c @@ -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; + +#ifdef FRONTEND + n = htons((uint16)integer); +#else + n = ((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? hton_s(integer) : htons((uint16)integer)); +#endif - s = integer; - n = hton_s(s); - if (fwrite(&n, sizeof(u_short), 1, f) != 1) - retval = EOF; + if (fwrite(&n, 2, 1, f) != 1) + return EOF; - return retval; + 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; + +#ifdef FRONTEND + *result = (int)ntohs(n); +#else + *result = (int)((PG_PROTOCOL_MAJOR(FrontendProtocol) == 0) ? ntoh_s(n) : ntohs(n)); +#endif - *result = ntoh_s(n); - return retval; + 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; } - -/* --------------------------------------------------------------------- */ diff --git a/src/backend/libpq/pqpacket.c b/src/backend/libpq/pqpacket.c index b62eb3ee36..0a00a97ec6 100644 --- a/src/backend/libpq/pqpacket.c +++ b/src/backend/libpq/pqpacket.c @@ -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) - { - flag = MSG_PEEK; - decr = 0; - } - else - { - flag = 0; - decr = hdrLen; - } +/* + * Read a packet fragment. Return STATUS_OK if the connection should stay + * open. + */ - /* - * 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). - */ - tmp = ((Addr) buf) + port->nBytes; +int PacketReceiveFragment(Packet *pkt, int sock) +{ + int got; - if (port->nBytes >= hdrLen) + if ((got = read(sock,pkt->ptr,pkt->nrtodo)) > 0) { - packetLen = ntohl(buf->len) - port->nBytes; - } - else - { - /* peeking into the incoming message */ - cc = recv(port->sock, (char *) &(buf->len), hdrLen, flag); - if (cc < hdrLen) + 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) { - /* if cc is negative, the system call failed */ - if (cc < 0) - { - return (STATUS_ERROR); - } + pkt->len = ntohl(pkt->len); - /* - * cc == 0 means the connection was broken at the other end. - */ - else if (!cc) + if (pkt->len < sizeof (pkt->len) || + pkt->len > sizeof (pkt->len) + sizeof (pkt->pkt)) { - return (STATUS_INVALID); + PacketSendError(pkt,"Invalid packet length"); + return STATUS_OK; } - 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); - } + /* Set up for the rest of the packet. */ + + pkt->nrtodo = pkt->len - sizeof (pkt->len); + pkt->ptr = (char *)&pkt->pkt; + pkt->state = ReadingPacket; } - 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); - } + /* See if we have got what we need for the packet. */ - /* - * Check for oversize packet - */ - if ((ntohl(buf->len)) > max_size) - { - return (STATUS_INVALID); - } + if (pkt->nrtodo == 0 && pkt->state == ReadingPacket) + { + pkt->state = Idle; - /* - * great. got the header. now get the true length (including - * header size). - */ - packetLen = ntohl(buf->len); + /* Special case to close the connection. */ - /* - * 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; + if (pkt->iodone == NULL) + return STATUS_ERROR; + + (*pkt->iodone)(pkt->arg, pkt->len - sizeof (pkt->len), + (char *)&pkt->pkt); } + + return STATUS_OK; } - /* - * 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); + if (got == 0) + return STATUS_ERROR; - /* - * cc == 0 means the connection was broken at the other end. - */ - else if (!cc) - return (STATUS_INVALID); + if (errno == EINTR) + return STATUS_OK; -/* - 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); - } - } + fprintf(stderr, "read() system call failed\n"); - port->nBytes = 0; - return (STATUS_OK); + return STATUS_ERROR; } + /* - * 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. - * + * Set up a packet write for the postmaster event loop. */ -int -PacketSend(Port *port, - PacketBuf *buf, - PacketLen len, - bool nonBlocking) + +void PacketSendSetup(Packet *pkt, int nbytes, void (*iodone)(), char *arg) { - PacketLen doneLen; + 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. + */ - Assert(!nonBlocking); - Assert(buf); +int PacketSendFragment(Packet *pkt, int sock) +{ + int done; - doneLen = write(port->sock, buf, len); - if (doneLen < len) + if ((done = write(sock,pkt->ptr,pkt->nrtodo)) > 0) { - sprintf(PQerrormsg, - "FATAL: PacketSend: couldn't send complete packet: errno=%d\n", - errno); - fputs(PQerrormsg, stderr); - return (STATUS_ERROR); + 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; } - return (STATUS_OK); -} + if (done == 0) + return STATUS_ERROR; -/* - * 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; + if (errno == EINTR) + return STATUS_OK; + + fprintf(stderr, "write() system call failed\n"); + + return STATUS_ERROR; } -*/ + /* - * PacketBuf2StartupInfo - - * convert the fields of the StartupInfo to a PacketBuf - * + * Send an error message from the postmaster to the frontend. */ -/* moved to postmaster.c -StartupInfo* -PacketBuf2StartupInfo(PacketBuf* p) + +void PacketSendError(Packet *pkt, char *errormsg) { - 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; + fprintf(stderr, "%s\n", errormsg); + + pkt->pkt.em.data[0] = 'E'; + StrNCpy(&pkt->pkt.em.data[1], errormsg, sizeof (pkt->pkt.em.data) - 2); + + /* + * The NULL i/o callback will cause the connection to be broken when + * the error message has been sent. + */ + + PacketSendSetup(pkt, strlen(pkt->pkt.em.data) + 1, NULL, NULL); } -*/ |
