diff options
author | Marc G. Fournier <scrappy@hub.org> | 1998-01-26 01:42:53 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1998-01-26 01:42:53 +0000 |
commit | d5bbe2aca55bc833e38c768d7f82c129b8b70c83 (patch) | |
tree | 47f4e1ecb3277869bb276e5433df335d920d1baf /src/interfaces/libpq/fe-connect.c | |
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/interfaces/libpq/fe-connect.c')
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 354 |
1 files changed, 116 insertions, 238 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 37dd88d425d..ee95ac59de5 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.58 1998/01/23 02:31:18 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.59 1998/01/26 01:42:28 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -21,14 +21,14 @@ #include <ctype.h> #include <string.h> #include <netdb.h> +#include <sys/un.h> +#include <netinet/in.h> #include <netinet/tcp.h> #include <errno.h> #include <signal.h> #include <ctype.h> /* for isspace() */ #include "postgres.h" -#include "libpq/pqcomm.h" /* for decls of MsgType, PacketBuf, - * StartupInfo */ #include "fe-auth.h" #include "fe-connect.h" #include "libpq-fe.h" @@ -44,7 +44,6 @@ /* use a local version instead of the one found in pqpacket.c */ static ConnStatusType connectDB(PGconn *conn); -static void startup2PacketBuf(StartupInfo *s, PacketBuf *res); static void freePGconn(PGconn *conn); static void closePGconn(PGconn *conn); static int conninfo_parse(const char *conninfo, char *errorMessage); @@ -78,6 +77,7 @@ static PQconninfoOption PQconninfoOptions[] = { /* Option-name Environment-Var Compiled-in Current value */ /* Label Disp-Char */ /* ----------------- --------------- --------------- --------------- */ + /* "authtype" is ignored as it is no longer used. */ {"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL, "Database-Authtype", "", 20}, @@ -183,7 +183,6 @@ PQconnectdb(const char *conninfo) conn->Pfout = NULL; conn->Pfin = NULL; conn->Pfdebug = NULL; - conn->port = NULL; conn->notifyList = DLNewList(); tmp = conninfo_getval("host"); @@ -198,8 +197,6 @@ PQconnectdb(const char *conninfo) conn->pguser = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval("password"); conn->pgpass = tmp ? strdup(tmp) : NULL; - tmp = conninfo_getval("authtype"); - conn->pgauth = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval("dbname"); conn->dbName = tmp ? strdup(tmp) : NULL; @@ -209,14 +206,6 @@ PQconnectdb(const char *conninfo) */ conninfo_free(); - /* - * try to set the auth service if one was specified - */ - if (conn->pgauth) - { - fe_setauthsvc(conn->pgauth, conn->errorMessage); - } - /* ---------- * Connect to the database * ---------- @@ -326,7 +315,6 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons conn->Pfout = NULL; conn->Pfin = NULL; conn->Pfdebug = NULL; - conn->port = NULL; conn->notifyList = DLNewList(); if ((pghost == NULL) || pghost[0] == '\0') @@ -467,44 +455,31 @@ connectDB(PGconn *conn) { struct hostent *hp; - StartupInfo startup; - PacketBuf pacBuf; - PacketLen pacLen; - int status; - MsgType msgtype; - int laddrlen = sizeof(struct sockaddr); - Port *port = conn->port; + StartupPacket sp; + AuthRequest areq; + int laddrlen = sizeof(SockAddr); int portno, family, len; - bool salted = false; - char* tmp; /* * Initialize the startup packet. - * - * This data structure is used for the seq-packet protocol. It describes - * the frontend-backend connection. - * - * */ - strncpy(startup.user, conn->pguser, sizeof(startup.user)); - strncpy(startup.database, conn->dbName, sizeof(startup.database)); - strncpy(startup.tty, conn->pgtty, sizeof(startup.tty)); + + MemSet((char *)&sp, 0, sizeof (StartupPacket)); + + sp.protoVersion = (ProtocolVersion)htonl(PG_PROTOCOL_LATEST); + + strncpy(sp.user, conn->pguser, SM_USER); + strncpy(sp.database, conn->dbName, SM_DATABASE); + strncpy(sp.tty, conn->pgtty, SM_TTY); + if (conn->pgoptions) - { - strncpy(startup.options, conn->pgoptions, sizeof(startup.options)); - } - else - startup.options[0] = '\0'; - startup.execFile[0] = '\0'; /* not used */ + strncpy(sp.options, conn->pgoptions, SM_OPTIONS); /* * Open a connection to postmaster/backend. - * */ - port = (Port *) malloc(sizeof(Port)); - MemSet((char *) port, 0, sizeof(Port)); if (conn->pghost != NULL) { @@ -524,28 +499,28 @@ connectDB(PGconn *conn) #endif portno = atoi(conn->pgport); family = (conn->pghost != NULL) ? AF_INET : AF_UNIX; - port->raddr.in.sin_family = family; + conn->raddr.sa.sa_family = family; if (family == AF_INET) { - memmove((char *) &(port->raddr.in.sin_addr), + memmove((char *) &(conn->raddr.in.sin_addr), (char *) hp->h_addr, hp->h_length); - port->raddr.in.sin_port = htons((unsigned short) (portno)); + conn->raddr.in.sin_port = htons((unsigned short) (portno)); len = sizeof(struct sockaddr_in); } else { - len = UNIXSOCK_PATH(port->raddr.un, portno); + len = UNIXSOCK_PATH(conn->raddr.un, portno); } /* connect to the server */ - if ((port->sock = socket(family, SOCK_STREAM, 0)) < 0) + if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0) { (void) sprintf(conn->errorMessage, "connectDB() -- socket() failed: errno=%d\n%s\n", errno, strerror(errno)); goto connect_errReturn; } - if (connect(port->sock, (struct sockaddr *) & port->raddr, len) < 0) + if (connect(conn->sock, &conn->raddr.sa, len) < 0) { (void) sprintf(conn->errorMessage, "connectDB() failed: Is the postmaster running and accepting%s connections at '%s' on port '%s'?\n", @@ -566,7 +541,7 @@ connectDB(PGconn *conn) "connectDB(): getprotobyname failed\n"); goto connect_errReturn; } - if (setsockopt(port->sock, pe->p_proto, TCP_NODELAY, + if (setsockopt(conn->sock, pe->p_proto, TCP_NODELAY, &on, sizeof(on)) < 0) { (void) sprintf(conn->errorMessage, @@ -576,8 +551,7 @@ connectDB(PGconn *conn) } /* fill in the client address */ - if (getsockname(port->sock, (struct sockaddr *) & port->laddr, - &laddrlen) < 0) + if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0) { (void) sprintf(conn->errorMessage, "connectDB() -- getsockname() failed: errno=%d\n%s\n", @@ -585,81 +559,93 @@ connectDB(PGconn *conn) goto connect_errReturn; } - /* by this point, connection has been opened */ - - /* This section of code is new as of Nov 19, 1997. It sends just the - * user's login to the backend. This allows the backend to search - * pg_user to see if the user has a password defined. If the user - * does have a password in pg_user, then the backend will send a - * packet back with a randomly generated salt, so the user's password - * can be encrypted. - */ - pacLen = sizeof(pacBuf.len) + sizeof(pacBuf.msgtype) + strlen(startup.user) + 1; - pacBuf.len = htonl(pacLen); - pacBuf.msgtype = htonl(STARTUP_USER_MSG); - strcpy(pacBuf.data, startup.user); - status = packetSend(port, &pacBuf, pacLen, BLOCKING); - if (status == STATUS_ERROR) { - sprintf(conn->errorMessage, "connectDB() -- couldn't send complete packet: errno=%d\n%s\n", errno, strerror(errno)); - goto connect_errReturn; - } - - /* Check to see if the server sent us a salt back to encrypt the - * password to send for authentication. --TAB - */ - status = packetReceive(port, &pacBuf, BLOCKING); - - if (status != STATUS_OK) { - sprintf(conn->errorMessage, "connectDB() -- couldn't receive un/salt packet: errno=%d\n%s\n", errno, strerror(errno)); - goto connect_errReturn; - } - pacBuf.msgtype = ntohl(pacBuf.msgtype); - switch (pacBuf.msgtype) { - case STARTUP_SALT_MSG: - salted = true; - if (!conn->pgpass) { - sprintf(conn->errorMessage, "connectDB() -- backend requested a password, but none was given\n"); - goto connect_errReturn; - } - tmp = crypt(conn->pgpass, pacBuf.data); - free((void*)conn->pgpass); - conn->pgpass = strdup(tmp); - break; - case STARTUP_UNSALT_MSG: - salted = false; - break; - default: - sprintf(conn->errorMessage, "connectDB() -- backend did not supply a salt packet\n"); - goto connect_errReturn; - } - - if (salted) - msgtype = STARTUP_CRYPT_MSG; - else - msgtype = fe_getauthsvc(conn->errorMessage); - -/* pacBuf = startup2PacketBuf(&startup);*/ - startup2PacketBuf(&startup, &pacBuf); - pacBuf.msgtype = (MsgType) htonl(msgtype); - status = packetSend(port, &pacBuf, sizeof(PacketBuf), BLOCKING); - - if (status == STATUS_ERROR) + /* set up the socket file descriptors */ + conn->Pfout = fdopen(conn->sock, "w"); + conn->Pfin = fdopen(dup(conn->sock), "r"); + if ((conn->Pfout == NULL) || (conn->Pfin == NULL)) + { + (void) sprintf(conn->errorMessage, + "connectDB() -- fdopen() failed: errno=%d\n%s\n", + errno, strerror(errno)); + goto connect_errReturn; + } + + /* Send the startup packet. */ + + if (packetSend(conn, (char *)&sp, sizeof(StartupPacket)) != STATUS_OK) { sprintf(conn->errorMessage, "connectDB() -- couldn't send complete packet: errno=%d\n%s\n", errno, strerror(errno)); goto connect_errReturn; } - /* authenticate as required */ - if (fe_sendauth(msgtype, port, conn->pghost, - conn->pguser, conn->pgpass, - conn->errorMessage) != STATUS_OK) + /* + * Get the response from the backend, either an error message or an + * authentication request. + */ + + do { - (void) sprintf(conn->errorMessage, - "connectDB() -- authentication failed with %s\n", - conn->pghost); - goto connect_errReturn; + int beresp; + + if ((beresp = pqGetc(conn->Pfin, conn->Pfdebug)) == EOF) + { + (void)sprintf(conn->errorMessage, + "connectDB() -- error getting authentication request\n"); + + goto connect_errReturn; + } + + /* Handle errors. */ + + if (beresp == 'E') + { + pqGets(conn->errorMessage, sizeof (conn->errorMessage), + conn->Pfin, conn->Pfdebug); + + goto connect_errReturn; + } + + /* Check it was an authentication request. */ + + if (beresp != 'R') + { + (void)sprintf(conn->errorMessage, + "connectDB() -- expected authentication request\n"); + + goto connect_errReturn; + } + + /* Get the type of request. */ + + if (pqGetInt((int *)&areq, 4, conn->Pfin, conn->Pfdebug)) + { + (void)sprintf(conn->errorMessage, + "connectDB() -- error getting authentication request type\n"); + + goto connect_errReturn; + } + + /* Get the password salt if there is one. */ + + if (areq == AUTH_REQ_CRYPT && + pqGetnchar(conn->salt, sizeof (conn->salt), + conn->Pfin, conn->Pfdebug)) + { + (void)sprintf(conn->errorMessage, + "connectDB() -- error getting password salt\n"); + + goto connect_errReturn; + } + + + /* Respond to the request. */ + + if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass, + conn->errorMessage) != STATUS_OK) + goto connect_errReturn; } + while (areq != AUTH_REQ_OK); /* free the password so it's not hanging out in memory forever */ if (conn->pgpass != NULL) @@ -667,30 +653,9 @@ connectDB(PGconn *conn) free(conn->pgpass); } - /* set up the socket file descriptors */ - conn->Pfout = fdopen(port->sock, "w"); - conn->Pfin = fdopen(dup(port->sock), "r"); - if ((conn->Pfout == NULL) || (conn->Pfin == NULL)) - { - (void) sprintf(conn->errorMessage, - "connectDB() -- fdopen() failed: errno=%d\n%s\n", - errno, strerror(errno)); - goto connect_errReturn; - } - - conn->port = port; - return CONNECTION_OK; connect_errReturn: - - /* - * Igor/6/3/97 - We need to free it here...otherwise the function - * returns without setting conn->port to port. Because of that any way - * of referencing this variable will be lost and it's allocated memory - * will not be freed. - */ - free(port); /* PURIFY */ return CONNECTION_BAD; } @@ -746,8 +711,6 @@ freePGconn(PGconn *conn) free(conn->pguser); if (conn->notifyList) DLFreeList(conn->notifyList); - if (conn->port) - free(conn->port); free(conn); } @@ -845,113 +808,28 @@ PQreset(PGconn *conn) * * 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) +packetSend(PGconn *conn, + char *buf, + size_t len) { - PacketLen doneLen = write(port->sock, buf, len); + /* Send the total packet size. */ - if (doneLen < len) - { - return (STATUS_ERROR); - } - return (STATUS_OK); -} + if (pqPutInt(4 + len, 4, conn->Pfout, conn->Pfdebug)) + return STATUS_ERROR; -/* - * packetReceive() - * - This is a less stringent PacketReceive(), defined in backend/libpq/pqpacket.c - We define it here to avoid linking in all of libpq.a + /* Send the packet itself. */ - * packetReceive -- receive a packet on a port - * - * RETURNS: STATUS_ERROR if the read fails, STATUS_OK otherwise. - * SIDE_EFFECTS: may block. - * NOTES: Non-blocking reads would significantly complicate - * buffer management. For now, we're not going to do it. - * -*/ -int -packetReceive(Port *port, PacketBuf *buf, bool nonBlocking) { - - PacketLen max_size = sizeof(PacketBuf); - PacketLen cc; /* character count -- recvd */ - PacketLen packetLen; - int addrLen = sizeof(struct sockaddr_in); - int hdrLen; - int msgLen; - - /* Read the packet length into the PacketBuf - */ - hdrLen = sizeof(PacketLen); - cc = recvfrom(port->sock, (char*)&packetLen, hdrLen, 0, (struct sockaddr*)&port->raddr, &addrLen); - if (cc < 0) - return STATUS_ERROR; - else if (!cc) - return STATUS_INVALID; - else if (cc < hdrLen) - return STATUS_NOT_DONE; - - /* convert to local form of integer and check for oversized packet - */ - buf->len = packetLen; - if ((packetLen = ntohl(packetLen)) > max_size) { - port->nBytes = packetLen; - return STATUS_INVALID; - } - - /* fetch the rest of the message - */ - msgLen = packetLen - cc; - cc = recvfrom(port->sock, (char*)&buf->msgtype, msgLen, 0, (struct sockaddr*)&port->raddr, &addrLen); - if (cc < 0) - return STATUS_ERROR; - else if (!cc) - return STATUS_INVALID; - else if (cc < msgLen) - return STATUS_NOT_DONE; - - return STATUS_OK; -} + if (pqPutnchar(buf, len, conn->Pfout, conn->Pfdebug)) + return STATUS_ERROR; -/* - * startup2PacketBuf() - * - * this is just like StartupInfo2Packet(), defined in backend/libpq/pqpacket.c - * but we repeat it here so we don't have to link in libpq.a - * - * converts a StartupInfo structure to a PacketBuf - */ -static void -startup2PacketBuf(StartupInfo *s, PacketBuf *res) -{ - char *tmp; + pqFlush(conn->Pfout, conn->Pfdebug); -/* res = (PacketBuf*)malloc(sizeof(PacketBuf)); */ - res->len = htonl(sizeof(PacketBuf)); - /* use \n to delimit the strings */ - 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->tty)); + return STATUS_OK; } + /* ---------------- * Conninfo parser routine * ---------------- |