]> granicus.if.org Git - postgresql/blob - src/backend/libpq/auth.c
The following patch makes postmaster -D work. -D specifies a different PGDATA
[postgresql] / src / backend / libpq / auth.c
1 /*-------------------------------------------------------------------------
2  *
3  * auth.c--
4  *    Routines to handle network authentication
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *    $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.2 1996/08/14 04:51:02 scrappy Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 /*
15  * INTERFACE ROUTINES
16  *
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?
21  *
22  *   NOTES
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
35  *         to separate them.
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.
44  *
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.
47  *
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...
51  *
52  */
53 #include <stdio.h>
54 #include <string.h>
55 #include <sys/param.h>  /* for MAX{HOSTNAME,PATH}LEN, NOFILE */
56 #include <pwd.h>
57 #include <ctype.h>                      /* isspace() declaration */
58
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"
65
66 /*----------------------------------------------------------------
67  * common definitions for generic fe/be routines
68  *----------------------------------------------------------------
69  */
70
71 struct authsvc {
72     char        name[16];       /* service nickname (for command line) */
73     MsgType     msgtype;        /* startup packet header type */
74     int         allowed;        /* initially allowed (before command line
75                                  * option parsing)?
76                                  */
77 };
78
79 /*
80  * Command-line parsing routines use this structure to map nicknames
81  * onto service types (and the startup packets to use with them).
82  *
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.
88  */
89 static struct authsvc authsvcs[] = {
90 #ifdef KRB4
91     { "krb4",     STARTUP_KRB4_MSG, 1 },
92     { "kerberos", STARTUP_KRB4_MSG, 1 },
93 #endif /* KRB4 */
94 #ifdef KRB5
95     { "krb5",     STARTUP_KRB5_MSG, 1 },
96     { "kerberos", STARTUP_KRB5_MSG, 1 },
97 #endif /* KRB5 */
98     { UNAUTHNAME, STARTUP_MSG,
99 #if defined(KRB4) || defined(KRB5)
100           0
101 #else /* !(KRB4 || KRB5) */
102           1
103 #endif /* !(KRB4 || KRB5) */
104     }
105 };
106
107 static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
108
109 #ifdef KRB4
110 /*----------------------------------------------------------------
111  * MIT Kerberos authentication system - protocol version 4
112  *----------------------------------------------------------------
113  */
114
115 #include "krb.h"
116
117 #ifdef FRONTEND
118 /* moves to src/libpq/fe-auth.c  */
119 #else /* !FRONTEND */
120
121 /*
122  * pg_krb4_recvauth -- server routine to receive authentication information
123  *                     from the client
124  *
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.)
129  */
130 static int
131 pg_krb4_recvauth(int sock,
132                  struct sockaddr_in *laddr,
133                  struct sockaddr_in *raddr,
134                  char *username)
135 {
136     long                krbopts = 0;    /* one-way authentication */
137     KTEXT_ST    clttkt;
138     char                instance[INST_SZ];
139     AUTH_DAT    auth_data;
140     Key_schedule        key_sched;
141     char                version[KRB_SENDAUTH_VLEN];
142     int         status;
143     
144     strcpy(instance, "*");      /* don't care, but arg gets expanded anyway */
145     status = krb_recvauth(krbopts,
146                           sock,
147                           &clttkt,
148                           PG_KRB_SRVNAM,
149                           instance,
150                           raddr,
151                           laddr,
152                           &auth_data,
153                           PG_KRB_SRVTAB,
154                           key_sched,
155                           version);
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);
163     }
164     if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN)) {
165         (void) sprintf(PQerrormsg,
166                        "pg_krb4_recvauth: protocol version != \"%s\"\n",
167                        PG_KRB4_VERSION);
168         fputs(PQerrormsg, stderr);
169         pqdebug("%s", PQerrormsg);
170         return(STATUS_ERROR);
171     }
172     if (username && *username &&
173         strncmp(username, auth_data.pname, NAMEDATALEN)) {
174         (void) sprintf(PQerrormsg,
175                        "pg_krb4_recvauth: name \"%s\" != \"%s\"\n",
176                        username,
177                        auth_data.pname);
178         fputs(PQerrormsg, stderr);
179         pqdebug("%s", PQerrormsg);
180         return(STATUS_ERROR);
181     }
182     return(STATUS_OK);
183 }
184
185 #endif /* !FRONTEND */
186
187 #endif /* KRB4 */
188
189 #ifdef KRB5
190 /*----------------------------------------------------------------
191  * MIT Kerberos authentication system - protocol version 5
192  *----------------------------------------------------------------
193  */
194
195 #include "krb5/krb5.h"
196
197 /*
198  * pg_an_to_ln -- return the local name corresponding to an authentication
199  *                name
200  *
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.
209  */
210 static char *
211 pg_an_to_ln(char *aname)
212 {
213     char        *p;
214     
215     if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
216         *p = '\0';
217     return(aname);
218 }
219
220 #ifdef FRONTEND
221 /* moves to src/libpq/fe-auth.c  */
222 #else /* !FRONTEND */
223
224 /*
225  * pg_krb4_recvauth -- server routine to receive authentication information
226  *                     from the client
227  *
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.
231  *
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.
235  *
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!)
245  */
246 static int
247 pg_krb5_recvauth(int sock,
248                  struct sockaddr_in *laddr,
249                  struct sockaddr_in *raddr,
250                  char *username)
251 {
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;
260     
261     /*
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.
265      */
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, '.'))
271         *hostp = '\0';
272     if (code = krb5_parse_name(servbuf, &server)) {
273         (void) sprintf(PQerrormsg,
274                        "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n",
275                        code);
276         com_err("pg_krb5_recvauth", code, "in krb5_parse_name");
277         return(STATUS_ERROR);
278     }
279     
280     /*
281      * krb5_sendauth needs this to verify the address in the client
282      * authenticator.
283      */
284     sender_addr.addrtype = raddr->sin_family;
285     sender_addr.length = sizeof(raddr->sin_addr);
286     sender_addr.contents = (krb5_octet *) &(raddr->sin_addr);
287     
288     if (strcmp(PG_KRB_SRVTAB, "")) {
289         keyproc = krb5_kt_read_service_key;
290         keyprocarg = PG_KRB_SRVTAB;
291     }
292     
293     if (code = krb5_recvauth((krb5_pointer) &sock,
294                              PG_KRB5_VERSION,
295                              server,
296                              &sender_addr,
297                              (krb5_pointer) NULL,
298                              keyproc,
299                              keyprocarg,
300                              (char *) NULL,
301                              (krb5_int32 *) NULL,
302                              &client,
303                              (krb5_ticket **) NULL,
304                              (krb5_authenticator **) NULL)) {
305         (void) sprintf(PQerrormsg,
306                        "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n",
307                        code);
308         com_err("pg_krb5_recvauth", code, "in krb5_recvauth");
309         krb5_free_principal(server);
310         return(STATUS_ERROR);
311     }
312     krb5_free_principal(server);
313     
314     /*
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.
318      */
319     if ((code = krb5_unparse_name(client, &kusername))) {
320         (void) sprintf(PQerrormsg,
321                        "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n",
322                        code);
323         com_err("pg_krb5_recvauth", code, "in krb5_unparse_name");
324         krb5_free_principal(client);
325         return(STATUS_ERROR);
326     }
327     krb5_free_principal(client);
328     if (!kusername) {
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);
334     }
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);
342         free(kusername);
343         return(STATUS_ERROR);
344     }
345     free(kusername);
346     return(STATUS_OK);
347 }
348
349 #endif /* !FRONTEND */
350
351 #endif /* KRB5 */
352
353
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)
361  *
362  * modified and adapted for use with Postgres95 by Paul Fisher
363  * (pnfisher@unity.ncsu.edu)
364  */
365
366 #define CONF_FILE "pg_hba"              /* Name of the config file           */
367
368 #define MAX_LINES 255                    /* Maximum number of config lines    *
369                                          * that can apply to one database    */
370
371 #define ALL_NAME "all"                  /* Name used in config file for      *
372                                          * lines that apply to all databases */
373
374 #define MAX_TOKEN 80                    /* Maximum size of one token in the  *
375                                          * configuration file                */
376  
377 struct conf_line {                      /* Info about config file line */
378   u_long adr, mask;
379 };
380  
381 static int next_token(FILE *, char *, int);
382
383 /* hba_recvauth */
384 /* check for host-based authentication */
385 /*
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.
390  */
391
392 static int
393 hba_recvauth(struct sockaddr_in *addr, PacketBuf *pbuf, StartupInfo *sp)
394 {
395     u_long ip_addr;
396     static struct conf_line conf[MAX_LINES];
397     static int nconf;
398     int i;
399
400     char buf[MAX_TOKEN];
401     FILE *file;
402
403     char *conf_file;
404
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);
408
409     /* Open the config file. */
410     file = fopen(conf_file, "r");
411     if (file)
412     {
413         free(conf_file);
414         nconf = 0;
415
416         /* Grab the "name" */
417         while ((i = next_token(file, buf, sizeof(buf))) != EOF)
418         {
419             /* If only token on the line, ignore */
420             if (i == '\n') continue;
421             
422             /* Comment -- read until end of line then next line */
423             if (buf[0] == '#')
424             {
425                 while (next_token(file, buf, sizeof(buf)) == 0) ;
426                 continue;
427             }
428
429             /*
430              * Check to make sure this says "all" or that it matches
431              * the database name.
432              */
433             
434             if (strcmp(buf, ALL_NAME) == 0 || (strcmp(buf, sp->database) == 0))
435             {
436                 /* Get next token, if last on line, ignore */
437                 if (next_token(file, buf, sizeof(buf)) != 0)
438                     continue;
439
440                 /* Got address */
441                 conf[nconf].adr = inet_addr(buf);
442                     
443                 /* Get next token (mask) */
444                 i = next_token(file, buf, sizeof(buf));
445
446                 /* Only ignore if we got no text at all */
447                 if (i != EOF)
448                 {
449                     /* Add to list, quit if array is full */
450                     conf[nconf++].mask = inet_addr(buf);
451                     if (nconf == MAX_LINES) break;
452                 }
453
454                 /* If not at end-of-line, keep reading til we are */
455                 while (i == 0)
456                     i = next_token(file, buf, sizeof(buf));
457             }
458         }
459         fclose(file);
460     }
461     else 
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", 
466                       conf_file);
467             fputs(PQerrormsg, stderr);
468             pqdebug("%s", PQerrormsg);
469         free(conf_file);
470         return(STATUS_ERROR); 
471     }
472
473
474     /* Config lines now in memory so start checking address */
475     /* grab just the address */
476     ip_addr = addr->sin_addr.s_addr;
477
478     /*
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.
482      */
483     for (i = 0; i < nconf; ++i)
484         if ((ip_addr & ~conf[i].mask) == conf[i].adr) return(STATUS_OK);
485     
486     /* no match, so we can't approve the address */
487     return(STATUS_ERROR);
488 }
489
490 /*
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.
496  */
497 static int 
498 next_token(FILE *fp, char *buf, int bufsz)
499 {
500     int c;
501     char *eb = buf+(bufsz-1);
502
503     /* Discard inital whitespace */
504     while (isspace(c = getc(fp))) ;
505
506     /* EOF seen before any token so return EOF */
507     if (c == EOF) return -1;
508
509     /* Form a token in buf */
510     do {
511         if (buf < eb) *buf++ = c;
512         c = getc(fp);
513     } while (!isspace(c) && c != EOF);
514     *buf = '\0';
515
516     /* Discard trailing tabs and spaces */
517     while (c == ' ' || c == '\t') c = getc(fp);
518
519     /* Put back the char that was non-whitespace (putting back EOF is ok) */
520     (void) ungetc(c, fp);
521
522     /* If we ended with a newline, return that, otherwise return 0 */
523     return (c == '\n' ? '\n' : 0);
524 }
525
526 /*
527  * be_recvauth -- server demux routine for incoming authentication information
528  */
529 int
530 be_recvauth(MsgType msgtype, Port *port, char *username, StartupInfo* sp)
531 {
532     if (!username) {
533         (void) sprintf(PQerrormsg,
534                        "be_recvauth: no user name passed\n");
535         fputs(PQerrormsg, stderr);
536         pqdebug("%s", PQerrormsg);
537         return(STATUS_ERROR);
538     }
539     if (!port) {
540         (void) sprintf(PQerrormsg,
541                        "be_recvauth: no port structure passed\n");
542         fputs(PQerrormsg, stderr);
543         pqdebug("%s", PQerrormsg);
544         return(STATUS_ERROR);
545     }
546     
547     switch (msgtype) {
548 #ifdef KRB4
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);
556         }
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);
564         }
565         break;
566 #endif
567 #ifdef KRB5
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);
575         }
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);
583         }
584         break;
585 #endif
586     case STARTUP_MSG:
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);
593         }
594         break;
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);
602         }
603         break;
604     default:
605         (void) sprintf(PQerrormsg,
606                        "be_recvauth: unrecognized message type: %d\n",
607                        msgtype);
608         fputs(PQerrormsg, stderr);
609         pqdebug("%s", PQerrormsg);
610         return(STATUS_ERROR);
611     }
612     return(STATUS_OK);
613 }
614
615 /*
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
620  *                  current selections
621  *
622  * be_setauthsvc encodes the command-line syntax that
623  *      -a "<service-name>"
624  * enables a service, whereas
625  *      -a "no<service-name>"
626  * disables it.
627  */
628 void
629 be_setauthsvc(char *name)
630 {
631     int i, j;
632     int turnon = 1;
633     
634     if (!name)
635         return;
636     if (!strncmp("no", name, 2)) {
637         turnon = 0;
638         name += 2;
639     }
640     if (name[0] == '\0')
641         return;
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;
647             break;
648         }
649     if (i == n_authsvcs) {
650         (void) sprintf(PQerrormsg,
651                        "be_setauthsvc: invalid name %s, ignoring...\n",
652                        name);
653         fputs(PQerrormsg, stderr);
654         pqdebug("%s", PQerrormsg);
655     }
656     return;
657 }
658
659 int
660 be_getauthsvc(MsgType msgtype)
661 {
662     int i;
663     
664     for (i = 0; i < n_authsvcs; ++i)
665         if (msgtype == authsvcs[i].msgtype)
666             return(authsvcs[i].allowed);
667     return(0);
668 }