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.2 1996/08/14 04:51:02 scrappy Exp $
12 *-------------------------------------------------------------------------
17 * backend (postmaster) routines:
18 * be_recvauth receive authentication information
19 * be_setauthsvc do/do not permit an authentication service
20 * be_getauthsvc is an authentication service permitted?
23 * To add a new authentication system:
24 * 0. If you can't do your authentication over an existing socket,
25 * you lose -- get ready to hack around this framework instead of
26 * using it. Otherwise, you can assume you have an initialized
27 * and empty connection to work with. (Please don't leave leftover
28 * gunk in the connection after the authentication transactions, or
29 * the POSTGRES routines that follow will be very unhappy.)
30 * 1. Write a set of routines that:
31 * let a client figure out what user/principal name to use
32 * send authentication information (client side)
33 * receive authentication information (server side)
34 * You can include both routines in this file, using #ifdef FRONTEND
36 * 2. Edit libpq/pqcomm.h and assign a MsgType for your protocol.
37 * 3. Edit the static "struct authsvc" array and the generic
38 * {be,fe}_{get,set}auth{name,svc} routines in this file to reflect
39 * the new service. You may have to change the arguments of these
40 * routines; they basically just reflect what Kerberos v4 needs.
41 * 4. Hack on src/{,bin}/Makefile.global and src/{backend,libpq}/Makefile
42 * to add library and CFLAGS hooks -- basically, grep the Makefile
43 * hierarchy for KRBVERS to see where you need to add things.
45 * Send mail to post_hackers@postgres.Berkeley.EDU if you have to make
46 * any changes to arguments, etc. Context diffs would be nice, too.
48 * Someday, this cruft will go away and magically be replaced by a
49 * nice interface based on the GSS API or something. For now, though,
50 * there's no (stable) UNIX security API to work with...
55 #include <sys/param.h> /* for MAX{HOSTNAME,PATH}LEN, NOFILE */
57 #include <ctype.h> /* isspace() declaration */
59 #include <netinet/in.h>
60 #include <arpa/inet.h>
61 #include "libpq/auth.h"
62 #include "libpq/libpq.h"
63 #include "libpq/pqcomm.h"
64 #include "libpq/libpq-be.h"
66 /*----------------------------------------------------------------
67 * common definitions for generic fe/be routines
68 *----------------------------------------------------------------
72 char name[16]; /* service nickname (for command line) */
73 MsgType msgtype; /* startup packet header type */
74 int allowed; /* initially allowed (before command line
80 * Command-line parsing routines use this structure to map nicknames
81 * onto service types (and the startup packets to use with them).
83 * Programs receiving an authentication request use this structure to
84 * decide which authentication service types are currently permitted.
85 * By default, all authentication systems compiled into the system are
86 * allowed. Unauthenticated connections are disallowed unless there
87 * isn't any authentication system.
89 static struct authsvc authsvcs[] = {
91 { "krb4", STARTUP_KRB4_MSG, 1 },
92 { "kerberos", STARTUP_KRB4_MSG, 1 },
95 { "krb5", STARTUP_KRB5_MSG, 1 },
96 { "kerberos", STARTUP_KRB5_MSG, 1 },
98 { UNAUTHNAME, STARTUP_MSG,
99 #if defined(KRB4) || defined(KRB5)
101 #else /* !(KRB4 || KRB5) */
103 #endif /* !(KRB4 || KRB5) */
107 static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
110 /*----------------------------------------------------------------
111 * MIT Kerberos authentication system - protocol version 4
112 *----------------------------------------------------------------
118 /* moves to src/libpq/fe-auth.c */
119 #else /* !FRONTEND */
122 * pg_krb4_recvauth -- server routine to receive authentication information
125 * Nothing unusual here, except that we compare the username obtained from
126 * the client's setup packet to the authenticated name. (We have to retain
127 * the name in the setup packet since we have to retain the ability to handle
128 * unauthenticated connections.)
131 pg_krb4_recvauth(int sock,
132 struct sockaddr_in *laddr,
133 struct sockaddr_in *raddr,
136 long krbopts = 0; /* one-way authentication */
138 char instance[INST_SZ];
140 Key_schedule key_sched;
141 char version[KRB_SENDAUTH_VLEN];
144 strcpy(instance, "*"); /* don't care, but arg gets expanded anyway */
145 status = krb_recvauth(krbopts,
156 if (status != KSUCCESS) {
157 (void) sprintf(PQerrormsg,
158 "pg_krb4_recvauth: kerberos error: %s\n",
159 krb_err_txt[status]);
160 fputs(PQerrormsg, stderr);
161 pqdebug("%s", PQerrormsg);
162 return(STATUS_ERROR);
164 if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN)) {
165 (void) sprintf(PQerrormsg,
166 "pg_krb4_recvauth: protocol version != \"%s\"\n",
168 fputs(PQerrormsg, stderr);
169 pqdebug("%s", PQerrormsg);
170 return(STATUS_ERROR);
172 if (username && *username &&
173 strncmp(username, auth_data.pname, NAMEDATALEN)) {
174 (void) sprintf(PQerrormsg,
175 "pg_krb4_recvauth: name \"%s\" != \"%s\"\n",
178 fputs(PQerrormsg, stderr);
179 pqdebug("%s", PQerrormsg);
180 return(STATUS_ERROR);
185 #endif /* !FRONTEND */
190 /*----------------------------------------------------------------
191 * MIT Kerberos authentication system - protocol version 5
192 *----------------------------------------------------------------
195 #include "krb5/krb5.h"
198 * pg_an_to_ln -- return the local name corresponding to an authentication
201 * XXX Assumes that the first aname component is the user name. This is NOT
202 * necessarily so, since an aname can actually be something out of your
203 * worst X.400 nightmare, like
204 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
205 * Note that the MIT an_to_ln code does the same thing if you don't
206 * provide an aname mapping database...it may be a better idea to use
207 * krb5_an_to_ln, except that it punts if multiple components are found,
208 * and we can't afford to punt.
211 pg_an_to_ln(char *aname)
215 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
221 /* moves to src/libpq/fe-auth.c */
222 #else /* !FRONTEND */
225 * pg_krb4_recvauth -- server routine to receive authentication information
228 * We still need to compare the username obtained from the client's setup
229 * packet to the authenticated name, as described in pg_krb4_recvauth. This
230 * is a bit more problematic in v5, as described above in pg_an_to_ln.
232 * In addition, as described above in pg_krb5_sendauth, we still need to
233 * canonicalize the server name v4-style before constructing a principal
234 * from it. Again, this is kind of iffy.
236 * Finally, we need to tangle with the fact that v5 doesn't let you explicitly
237 * set server keytab file names -- you have to feed lower-level routines a
238 * function to retrieve the contents of a keytab, along with a single argument
239 * that allows them to open the keytab. We assume that a server keytab is
240 * always a real file so we can allow people to specify their own filenames.
241 * (This is important because the POSTGRES keytab needs to be readable by
242 * non-root users/groups; the v4 tools used to force you do dump a whole
243 * host's worth of keys into a file, effectively forcing you to use one file,
244 * but kdb5_edit allows you to select which principals to dump. Yay!)
247 pg_krb5_recvauth(int sock,
248 struct sockaddr_in *laddr,
249 struct sockaddr_in *raddr,
252 char servbuf[MAXHOSTNAMELEN + 1 +
253 sizeof(PG_KRB_SRVNAM)];
254 char *hostp, *kusername = (char *) NULL;
255 krb5_error_code code;
256 krb5_principal client, server;
257 krb5_address sender_addr;
258 krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL;
259 krb5_pointer keyprocarg = (krb5_pointer) NULL;
262 * Set up server side -- since we have no ticket file to make this
263 * easy, we construct our own name and parse it. See note on
264 * canonicalization above.
266 (void) strcpy(servbuf, PG_KRB_SRVNAM);
267 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
268 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
269 (void) strcpy(hostp, "localhost");
270 if (hostp = strchr(hostp, '.'))
272 if (code = krb5_parse_name(servbuf, &server)) {
273 (void) sprintf(PQerrormsg,
274 "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n",
276 com_err("pg_krb5_recvauth", code, "in krb5_parse_name");
277 return(STATUS_ERROR);
281 * krb5_sendauth needs this to verify the address in the client
284 sender_addr.addrtype = raddr->sin_family;
285 sender_addr.length = sizeof(raddr->sin_addr);
286 sender_addr.contents = (krb5_octet *) &(raddr->sin_addr);
288 if (strcmp(PG_KRB_SRVTAB, "")) {
289 keyproc = krb5_kt_read_service_key;
290 keyprocarg = PG_KRB_SRVTAB;
293 if (code = krb5_recvauth((krb5_pointer) &sock,
303 (krb5_ticket **) NULL,
304 (krb5_authenticator **) NULL)) {
305 (void) sprintf(PQerrormsg,
306 "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n",
308 com_err("pg_krb5_recvauth", code, "in krb5_recvauth");
309 krb5_free_principal(server);
310 return(STATUS_ERROR);
312 krb5_free_principal(server);
315 * The "client" structure comes out of the ticket and is therefore
316 * authenticated. Use it to check the username obtained from the
317 * postmaster startup packet.
319 if ((code = krb5_unparse_name(client, &kusername))) {
320 (void) sprintf(PQerrormsg,
321 "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n",
323 com_err("pg_krb5_recvauth", code, "in krb5_unparse_name");
324 krb5_free_principal(client);
325 return(STATUS_ERROR);
327 krb5_free_principal(client);
329 (void) sprintf(PQerrormsg,
330 "pg_krb5_recvauth: could not decode username\n");
331 fputs(PQerrormsg, stderr);
332 pqdebug("%s", PQerrormsg);
333 return(STATUS_ERROR);
335 kusername = pg_an_to_ln(kusername);
336 if (username && strncmp(username, kusername, NAMEDATALEN)) {
337 (void) sprintf(PQerrormsg,
338 "pg_krb5_recvauth: name \"%s\" != \"%s\"\n",
339 username, kusername);
340 fputs(PQerrormsg, stderr);
341 pqdebug("%s", PQerrormsg);
343 return(STATUS_ERROR);
349 #endif /* !FRONTEND */
354 /*----------------------------------------------------------------
355 * host based authentication
356 *----------------------------------------------------------------
357 * based on the securelib package originally written by William
358 * LeFebvre, EECS Department, Northwestern University
359 * (phil@eecs.nwu.edu) - orginal configuration file code handling
360 * by Sam Horrocks (sam@ics.uci.edu)
362 * modified and adapted for use with Postgres95 by Paul Fisher
363 * (pnfisher@unity.ncsu.edu)
366 #define CONF_FILE "pg_hba" /* Name of the config file */
368 #define MAX_LINES 255 /* Maximum number of config lines *
369 * that can apply to one database */
371 #define ALL_NAME "all" /* Name used in config file for *
372 * lines that apply to all databases */
374 #define MAX_TOKEN 80 /* Maximum size of one token in the *
375 * configuration file */
377 struct conf_line { /* Info about config file line */
381 static int next_token(FILE *, char *, int);
384 /* check for host-based authentication */
386 * hba_recvauth - check the sockaddr_in "addr" to see if it corresponds
387 * to an acceptable host for the database that's being
388 * connected to. Return STATUS_OK if acceptable,
389 * otherwise return STATUS_ERROR.
393 hba_recvauth(struct sockaddr_in *addr, PacketBuf *pbuf, StartupInfo *sp)
396 static struct conf_line conf[MAX_LINES];
405 /* put together the full pathname to the config file */
406 conf_file = (char *) malloc((strlen(DataDir)+strlen(CONF_FILE)+2)*sizeof(char));
407 sprintf(conf_file, "%s/%s", DataDir, CONF_FILE);
409 /* Open the config file. */
410 file = fopen(conf_file, "r");
416 /* Grab the "name" */
417 while ((i = next_token(file, buf, sizeof(buf))) != EOF)
419 /* If only token on the line, ignore */
420 if (i == '\n') continue;
422 /* Comment -- read until end of line then next line */
425 while (next_token(file, buf, sizeof(buf)) == 0) ;
430 * Check to make sure this says "all" or that it matches
434 if (strcmp(buf, ALL_NAME) == 0 || (strcmp(buf, sp->database) == 0))
436 /* Get next token, if last on line, ignore */
437 if (next_token(file, buf, sizeof(buf)) != 0)
441 conf[nconf].adr = inet_addr(buf);
443 /* Get next token (mask) */
444 i = next_token(file, buf, sizeof(buf));
446 /* Only ignore if we got no text at all */
449 /* Add to list, quit if array is full */
450 conf[nconf++].mask = inet_addr(buf);
451 if (nconf == MAX_LINES) break;
454 /* If not at end-of-line, keep reading til we are */
456 i = next_token(file, buf, sizeof(buf));
462 { (void) sprintf(PQerrormsg,
463 "hba_recvauth: Host-based authentication config file "
464 "does not exist or permissions are not setup correctly! "
465 "Unable to open file \"%s\".\n",
467 fputs(PQerrormsg, stderr);
468 pqdebug("%s", PQerrormsg);
470 return(STATUS_ERROR);
474 /* Config lines now in memory so start checking address */
475 /* grab just the address */
476 ip_addr = addr->sin_addr.s_addr;
479 * Go through the conf array, turn off the bits given by the mask
480 * and then compare the result with the address. A match means
481 * that this address is ok.
483 for (i = 0; i < nconf; ++i)
484 if ((ip_addr & ~conf[i].mask) == conf[i].adr) return(STATUS_OK);
486 /* no match, so we can't approve the address */
487 return(STATUS_ERROR);
491 * Grab one token out of fp. Defined as the next string of non-whitespace
492 * in the file. After we get the token, continue reading until EOF, end of
493 * line or the next token. If it's the last token on the line, return '\n'
494 * for the value. If we get EOF before reading a token, return EOF. In all
495 * other cases return 0.
498 next_token(FILE *fp, char *buf, int bufsz)
501 char *eb = buf+(bufsz-1);
503 /* Discard inital whitespace */
504 while (isspace(c = getc(fp))) ;
506 /* EOF seen before any token so return EOF */
507 if (c == EOF) return -1;
509 /* Form a token in buf */
511 if (buf < eb) *buf++ = c;
513 } while (!isspace(c) && c != EOF);
516 /* Discard trailing tabs and spaces */
517 while (c == ' ' || c == '\t') c = getc(fp);
519 /* Put back the char that was non-whitespace (putting back EOF is ok) */
520 (void) ungetc(c, fp);
522 /* If we ended with a newline, return that, otherwise return 0 */
523 return (c == '\n' ? '\n' : 0);
527 * be_recvauth -- server demux routine for incoming authentication information
530 be_recvauth(MsgType msgtype, Port *port, char *username, StartupInfo* sp)
533 (void) sprintf(PQerrormsg,
534 "be_recvauth: no user name passed\n");
535 fputs(PQerrormsg, stderr);
536 pqdebug("%s", PQerrormsg);
537 return(STATUS_ERROR);
540 (void) sprintf(PQerrormsg,
541 "be_recvauth: no port structure passed\n");
542 fputs(PQerrormsg, stderr);
543 pqdebug("%s", PQerrormsg);
544 return(STATUS_ERROR);
549 case STARTUP_KRB4_MSG:
550 if (!be_getauthsvc(msgtype)) {
551 (void) sprintf(PQerrormsg,
552 "be_recvauth: krb4 authentication disallowed\n");
553 fputs(PQerrormsg, stderr);
554 pqdebug("%s", PQerrormsg);
555 return(STATUS_ERROR);
557 if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr,
558 username) != STATUS_OK) {
559 (void) sprintf(PQerrormsg,
560 "be_recvauth: krb4 authentication failed\n");
561 fputs(PQerrormsg, stderr);
562 pqdebug("%s", PQerrormsg);
563 return(STATUS_ERROR);
568 case STARTUP_KRB5_MSG:
569 if (!be_getauthsvc(msgtype)) {
570 (void) sprintf(PQerrormsg,
571 "be_recvauth: krb5 authentication disallowed\n");
572 fputs(PQerrormsg, stderr);
573 pqdebug("%s", PQerrormsg);
574 return(STATUS_ERROR);
576 if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr,
577 username) != STATUS_OK) {
578 (void) sprintf(PQerrormsg,
579 "be_recvauth: krb5 authentication failed\n");
580 fputs(PQerrormsg, stderr);
581 pqdebug("%s", PQerrormsg);
582 return(STATUS_ERROR);
587 if (!be_getauthsvc(msgtype)) {
588 (void) sprintf(PQerrormsg,
589 "be_recvauth: unauthenticated connections disallowed failed\n");
590 fputs(PQerrormsg, stderr);
591 pqdebug("%s", PQerrormsg);
592 return(STATUS_ERROR);
595 case STARTUP_HBA_MSG:
596 if (hba_recvauth(&port->raddr, &port->buf, sp) != STATUS_OK) {
597 (void) sprintf(PQerrormsg,
598 "be_recvauth: host-based authentication failed\n");
599 fputs(PQerrormsg, stderr);
600 pqdebug("%s", PQerrormsg);
601 return(STATUS_ERROR);
605 (void) sprintf(PQerrormsg,
606 "be_recvauth: unrecognized message type: %d\n",
608 fputs(PQerrormsg, stderr);
609 pqdebug("%s", PQerrormsg);
610 return(STATUS_ERROR);
616 * be_setauthsvc -- enable/disable the authentication services currently
617 * selected for use by the backend
618 * be_getauthsvc -- returns whether a particular authentication system
619 * (indicated by its message type) is permitted by the
622 * be_setauthsvc encodes the command-line syntax that
623 * -a "<service-name>"
624 * enables a service, whereas
625 * -a "no<service-name>"
629 be_setauthsvc(char *name)
636 if (!strncmp("no", name, 2)) {
642 for (i = 0; i < n_authsvcs; ++i)
643 if (!strcmp(name, authsvcs[i].name)) {
644 for (j = 0; j < n_authsvcs; ++j)
645 if (authsvcs[j].msgtype == authsvcs[i].msgtype)
646 authsvcs[j].allowed = turnon;
649 if (i == n_authsvcs) {
650 (void) sprintf(PQerrormsg,
651 "be_setauthsvc: invalid name %s, ignoring...\n",
653 fputs(PQerrormsg, stderr);
654 pqdebug("%s", PQerrormsg);
660 be_getauthsvc(MsgType msgtype)
664 for (i = 0; i < n_authsvcs; ++i)
665 if (msgtype == authsvcs[i].msgtype)
666 return(authsvcs[i].allowed);