1 /*-------------------------------------------------------------------------
4 * Routines to handle network authentication
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.21 1998/01/26 01:41:04 scrappy Exp $
12 *-------------------------------------------------------------------------
17 * backend (postmaster) routines:
18 * be_recvauth receive authentication information
22 #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
23 #ifndef MAXHOSTNAMELEN
24 #include <netdb.h> /* for MAXHOSTNAMELEN on some */
27 #include <ctype.h> /* isspace() declaration */
29 #include <sys/types.h> /* needed by in.h on Ultrix */
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
34 #include <miscadmin.h>
36 #include <libpq/auth.h>
37 #include <libpq/libpq.h>
38 #include <libpq/hba.h>
39 #include <libpq/password.h>
40 #include <libpq/crypt.h>
43 static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler)());
44 static void handle_done_auth(Port *port);
45 static void handle_krb4_auth(Port *port);
46 static void handle_krb5_auth(Port *port);
47 static void handle_password_auth(Port *port);
48 static void readPasswordPacket(char *arg, PacketLen len, char *pkt);
49 static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt);
50 static int old_be_recvauth(Port *port);
51 static int map_old_to_new(Port *port, UserAuth old, int status);
55 /* This has to be ifdef'd out because krb.h does exist. This needs
58 /*----------------------------------------------------------------
59 * MIT Kerberos authentication system - protocol version 4
60 *----------------------------------------------------------------
66 * pg_krb4_recvauth -- server routine to receive authentication information
69 * Nothing unusual here, except that we compare the username obtained from
70 * the client's setup packet to the authenticated name. (We have to retain
71 * the name in the setup packet since we have to retain the ability to handle
72 * unauthenticated connections.)
75 pg_krb4_recvauth(Port *)
77 long krbopts = 0; /* one-way authentication */
79 char instance[INST_SZ];
81 Key_schedule key_sched;
82 char version[KRB_SENDAUTH_VLEN];
85 strcpy(instance, "*"); /* don't care, but arg gets expanded
87 status = krb_recvauth(krbopts,
98 if (status != KSUCCESS)
101 "pg_krb4_recvauth: kerberos error: %s\n",
102 krb_err_txt[status]);
103 fputs(PQerrormsg, stderr);
104 pqdebug("%s", PQerrormsg);
105 return (STATUS_ERROR);
107 if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN))
110 "pg_krb4_recvauth: protocol version != \"%s\"\n",
112 fputs(PQerrormsg, stderr);
113 pqdebug("%s", PQerrormsg);
114 return (STATUS_ERROR);
116 if (strncmp(port->user, auth_data.pname, SM_USER))
119 "pg_krb4_recvauth: name \"%s\" != \"%s\"\n",
122 fputs(PQerrormsg, stderr);
123 pqdebug("%s", PQerrormsg);
124 return (STATUS_ERROR);
131 pg_krb4_recvauth(Port *port)
134 "pg_krb4_recvauth: Kerberos not implemented on this "
136 fputs(PQerrormsg, stderr);
137 pqdebug("%s", PQerrormsg);
139 return (STATUS_ERROR);
146 /* This needs to be ifdef'd out because krb5.h doesn't exist. This needs
149 /*----------------------------------------------------------------
150 * MIT Kerberos authentication system - protocol version 5
151 *----------------------------------------------------------------
154 #include <krb5/krb5.h>
157 * pg_an_to_ln -- return the local name corresponding to an authentication
160 * XXX Assumes that the first aname component is the user name. This is NOT
161 * necessarily so, since an aname can actually be something out of your
162 * worst X.400 nightmare, like
163 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
164 * Note that the MIT an_to_ln code does the same thing if you don't
165 * provide an aname mapping database...it may be a better idea to use
166 * krb5_an_to_ln, except that it punts if multiple components are found,
167 * and we can't afford to punt.
170 pg_an_to_ln(char *aname)
174 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
180 * pg_krb5_recvauth -- server routine to receive authentication information
183 * We still need to compare the username obtained from the client's setup
184 * packet to the authenticated name, as described in pg_krb4_recvauth. This
185 * is a bit more problematic in v5, as described above in pg_an_to_ln.
187 * In addition, as described above in pg_krb5_sendauth, we still need to
188 * canonicalize the server name v4-style before constructing a principal
189 * from it. Again, this is kind of iffy.
191 * Finally, we need to tangle with the fact that v5 doesn't let you explicitly
192 * set server keytab file names -- you have to feed lower-level routines a
193 * function to retrieve the contents of a keytab, along with a single argument
194 * that allows them to open the keytab. We assume that a server keytab is
195 * always a real file so we can allow people to specify their own filenames.
196 * (This is important because the POSTGRES keytab needs to be readable by
197 * non-root users/groups; the v4 tools used to force you do dump a whole
198 * host's worth of keys into a file, effectively forcing you to use one file,
199 * but kdb5_edit allows you to select which principals to dump. Yay!)
202 pg_krb5_recvauth(Port *port)
204 char servbuf[MAXHOSTNAMELEN + 1 +
205 sizeof(PG_KRB_SRVNAM)];
207 *kusername = (char *) NULL;
208 krb5_error_code code;
209 krb5_principal client,
211 krb5_address sender_addr;
212 krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL;
213 krb5_pointer keyprocarg = (krb5_pointer) NULL;
216 * Set up server side -- since we have no ticket file to make this
217 * easy, we construct our own name and parse it. See note on
218 * canonicalization above.
220 strcpy(servbuf, PG_KRB_SRVNAM);
221 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
222 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
223 strcpy(hostp, "localhost");
224 if (hostp = strchr(hostp, '.'))
226 if (code = krb5_parse_name(servbuf, &server))
229 "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n",
231 com_err("pg_krb5_recvauth", code, "in krb5_parse_name");
232 return (STATUS_ERROR);
236 * krb5_sendauth needs this to verify the address in the client
239 sender_addr.addrtype = port->raddr.in.sin_family;
240 sender_addr.length = sizeof(port->raddr.in.sin_addr);
241 sender_addr.contents = (krb5_octet *) & (port->raddr.in.sin_addr);
243 if (strcmp(PG_KRB_SRVTAB, ""))
245 keyproc = krb5_kt_read_service_key;
246 keyprocarg = PG_KRB_SRVTAB;
249 if (code = krb5_recvauth((krb5_pointer) & port->sock,
259 (krb5_ticket **) NULL,
260 (krb5_authenticator **) NULL))
263 "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n",
265 com_err("pg_krb5_recvauth", code, "in krb5_recvauth");
266 krb5_free_principal(server);
267 return (STATUS_ERROR);
269 krb5_free_principal(server);
272 * The "client" structure comes out of the ticket and is therefore
273 * authenticated. Use it to check the username obtained from the
274 * postmaster startup packet.
276 if ((code = krb5_unparse_name(client, &kusername)))
279 "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n",
281 com_err("pg_krb5_recvauth", code, "in krb5_unparse_name");
282 krb5_free_principal(client);
283 return (STATUS_ERROR);
285 krb5_free_principal(client);
289 "pg_krb5_recvauth: could not decode username\n");
290 fputs(PQerrormsg, stderr);
291 pqdebug("%s", PQerrormsg);
292 return (STATUS_ERROR);
294 kusername = pg_an_to_ln(kusername);
295 if (strncmp(username, kusername, SM_USER))
298 "pg_krb5_recvauth: name \"%s\" != \"%s\"\n",
299 port->username, kusername);
300 fputs(PQerrormsg, stderr);
301 pqdebug("%s", PQerrormsg);
303 return (STATUS_ERROR);
311 pg_krb5_recvauth(Port *port)
314 "pg_krb5_recvauth: Kerberos not implemented on this "
316 fputs(PQerrormsg, stderr);
317 pqdebug("%s", PQerrormsg);
319 return (STATUS_ERROR);
326 * Handle a v0 password packet.
329 static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt)
332 PasswordPacketV0 *pp;
333 char *user, *password, *cp, *start;
336 pp = (PasswordPacketV0 *)pkt;
339 * The packet is supposed to comprise the user name and the password
340 * as C strings. Be careful the check that this is the case.
343 user = password = NULL;
345 len -= sizeof (pp->unused);
347 cp = start = pp->data;
363 if (user == NULL || password == NULL)
366 "pg_password_recvauth: badly formed password packet.\n");
367 fputs(PQerrormsg, stderr);
368 pqdebug("%s", PQerrormsg);
372 else if (map_old_to_new(port, uaPassword,
373 verify_password(port->auth_arg, user, password)) != STATUS_OK)
379 * Tell the user the authentication failed, but not why.
382 void auth_failed(Port *port)
384 PacketSendError(&port->pktInfo, "User authentication failed");
389 * be_recvauth -- server demux routine for incoming authentication information
391 void be_recvauth(Port *port)
394 void (*auth_handler)();
397 * Get the authentication method to use for this frontend/database
401 if (hba_getauthmethod(&port->raddr, port->database, port->auth_arg,
402 &port->auth_method) != STATUS_OK)
404 PacketSendError(&port->pktInfo, "Error getting authentication method");
408 /* Handle old style authentication. */
410 if (PG_PROTOCOL_MAJOR(port->proto) == 0)
412 if (old_be_recvauth(port) != STATUS_OK)
418 /* Handle new style authentication. */
420 switch (port->auth_method)
427 areq = AUTH_REQ_KRB4;
428 auth_handler = handle_krb4_auth;
432 areq = AUTH_REQ_KRB5;
433 auth_handler = handle_krb5_auth;
438 auth_handler = handle_done_auth;
442 if (authident(&port->raddr.in, &port->laddr.in, port->user,
443 port->auth_arg) != STATUS_OK)
450 auth_handler = handle_done_auth;
454 areq = AUTH_REQ_PASSWORD;
455 auth_handler = handle_password_auth;
459 areq = AUTH_REQ_CRYPT;
460 auth_handler = handle_password_auth;
464 /* Tell the frontend what we want next. */
466 sendAuthRequest(port, areq, auth_handler);
471 * Send an authentication request packet to the frontend.
474 static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler)())
480 /* Convert to a byte stream. */
482 net_areq = htonl(areq);
484 dp = port->pktInfo.pkt.ar.data;
485 sp = (char *)&net_areq;
489 for (i = 1; i <= 4; ++i)
492 /* Add the salt for encrypted passwords. */
494 if (areq == AUTH_REQ_CRYPT)
496 *dp++ = port->salt[0];
497 *dp++ = port->salt[1];
501 PacketSendSetup(&port -> pktInfo, i, handler, (char *)port);
506 * Called when we have told the front end that it is authorised.
509 static void handle_done_auth(Port *port)
512 * Don't generate any more traffic. This will cause the backend to
521 * Called when we have told the front end that it should use Kerberos V4
525 static void handle_krb4_auth(Port *port)
527 if (pg_krb4_recvauth(port) != STATUS_OK)
530 sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
535 * Called when we have told the front end that it should use Kerberos V5
539 static void handle_krb5_auth(Port *port)
541 if (pg_krb5_recvauth(port) != STATUS_OK)
544 sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
549 * Called when we have told the front end that it should use password
553 static void handle_password_auth(Port *port)
555 /* Set up the read of the password packet. */
557 PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (char *)port);
562 * Called when we have received the password packet.
565 static void readPasswordPacket(char *arg, PacketLen len, char *pkt)
567 char password[sizeof (PasswordPacket) + 1];
572 /* Silently truncate a password that is too big. */
574 if (len > sizeof (PasswordPacket))
575 len = sizeof (PasswordPacket);
577 StrNCpy(password, ((PasswordPacket *)pkt)->passwd, len);
580 * Use the local flat password file if clear passwords are used and the
581 * file is specified. Otherwise use the password in the pg_user table,
585 if (port->auth_method == uaPassword && port->auth_arg[0] != '\0')
587 if (verify_password(port->auth_arg, port->user, password) != STATUS_OK)
590 else if (crypt_verify(port, port->user, password) != STATUS_OK)
593 sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
598 * Server demux routine for incoming authentication information for protocol
601 static int old_be_recvauth(Port *port)
604 MsgType msgtype = (MsgType)port->proto;
606 /* Handle the authentication that's offered. */
610 case STARTUP_KRB4_MSG:
611 status = map_old_to_new(port,uaKrb4,pg_krb4_recvauth(port));
614 case STARTUP_KRB5_MSG:
615 status = map_old_to_new(port,uaKrb5,pg_krb5_recvauth(port));
619 status = map_old_to_new(port,uaTrust,STATUS_OK);
622 case STARTUP_PASSWORD_MSG:
623 PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
629 fprintf(stderr, "Invalid startup message type: %u\n", msgtype);
639 * The old style authentication has been done. Modify the result of this (eg.
640 * allow the connection anyway, disallow it anyway, or use the result)
641 * depending on what authentication we really want to use.
644 static int map_old_to_new(Port *port, UserAuth old, int status)
646 switch (port->auth_method)
650 status = STATUS_ERROR;
655 status = STATUS_ERROR;
660 status = STATUS_ERROR;
668 status = authident(&port->raddr.in, &port->laddr.in,
669 port->user, port->auth_arg);
673 if (old != uaPassword)
674 status = STATUS_ERROR;