]> granicus.if.org Git - postgresql/blob - src/backend/libpq/auth.c
Add #include <sys/types.h> so it works with Ultrix's in.h
[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.8 1996/11/16 08:09:15 bryanh 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 MAXHOSTNAMELEN on most */
56 #ifndef  MAXHOSTNAMELEN
57 #include <netdb.h>      /* for MAXHOSTNAMELEN on some */
58 #endif
59 #include <pwd.h>
60 #include <ctype.h>                      /* isspace() declaration */
61
62 #include <sys/types.h>    /* needed by in.h on Ultrix */
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
65
66 #include <postgres.h>
67 #include <miscadmin.h>
68
69 #include <libpq/auth.h>
70 #include <libpq/libpq.h>
71 #include <libpq/libpq-be.h>
72 #include <libpq/hba.h>
73
74 /*----------------------------------------------------------------
75  * common definitions for generic fe/be routines
76  *----------------------------------------------------------------
77  */
78
79 struct authsvc {
80     char        name[16];       /* service nickname (for command line) */
81     MsgType     msgtype;        /* startup packet header type */
82     int         allowed;        /* initially allowed (before command line
83                                  * option parsing)?
84                                  */
85 };
86
87 /*
88  * Command-line parsing routines use this structure to map nicknames
89  * onto service types (and the startup packets to use with them).
90  *
91  * Programs receiving an authentication request use this structure to
92  * decide which authentication service types are currently permitted.
93  * By default, all authentication systems compiled into the system are
94  * allowed.  Unauthenticated connections are disallowed unless there
95  * isn't any authentication system.
96  */
97
98 #if defined(HBA)
99 static int useHostBasedAuth = 1;
100 #else
101 static int useHostBasedAuth = 0;
102 #endif
103
104 #if defined(KRB4) || defined(KRB5) || defined(HBA)
105 #define UNAUTH_ALLOWED 0
106 #else
107 #define UNAUTH_ALLOWED 1
108 #endif
109
110 static struct authsvc authsvcs[] = {
111     { "unauth",   STARTUP_UNAUTH_MSG, UNAUTH_ALLOWED },
112     { "hba",      STARTUP_HBA_MSG,  1 },
113     { "krb4",     STARTUP_KRB4_MSG, 1 },
114     { "krb5",     STARTUP_KRB5_MSG, 1 },
115 #if defined(KRB5) 
116     { "kerberos", STARTUP_KRB5_MSG, 1 }
117 #else
118     { "kerberos", STARTUP_KRB4_MSG, 1 }
119 #endif
120 };
121
122 static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
123
124 #ifdef KRB4
125 /* This has to be ifdef'd out because krb.h does exist.  This needs
126    to be fixed.
127 */
128 /*----------------------------------------------------------------
129  * MIT Kerberos authentication system - protocol version 4
130  *----------------------------------------------------------------
131  */
132
133 #include <krb.h>
134
135 #ifdef FRONTEND
136 /* moves to src/libpq/fe-auth.c  */
137 #else /* !FRONTEND */
138
139 /*
140  * pg_krb4_recvauth -- server routine to receive authentication information
141  *                     from the client
142  *
143  * Nothing unusual here, except that we compare the username obtained from
144  * the client's setup packet to the authenticated name.  (We have to retain
145  * the name in the setup packet since we have to retain the ability to handle
146  * unauthenticated connections.)
147  */
148 static int
149 pg_krb4_recvauth(int sock,
150                  struct sockaddr_in *laddr,
151                  struct sockaddr_in *raddr,
152                  char *username)
153 {
154     long                krbopts = 0;    /* one-way authentication */
155     KTEXT_ST    clttkt;
156     char                instance[INST_SZ];
157     AUTH_DAT    auth_data;
158     Key_schedule        key_sched;
159     char                version[KRB_SENDAUTH_VLEN];
160     int         status;
161     
162     strcpy(instance, "*");      /* don't care, but arg gets expanded anyway */
163     status = krb_recvauth(krbopts,
164                           sock,
165                           &clttkt,
166                           PG_KRB_SRVNAM,
167                           instance,
168                           raddr,
169                           laddr,
170                           &auth_data,
171                           PG_KRB_SRVTAB,
172                           key_sched,
173                           version);
174     if (status != KSUCCESS) {
175         (void) sprintf(PQerrormsg,
176                        "pg_krb4_recvauth: kerberos error: %s\n",
177                        krb_err_txt[status]);
178         fputs(PQerrormsg, stderr);
179         pqdebug("%s", PQerrormsg);
180         return(STATUS_ERROR);
181     }
182     if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN)) {
183         (void) sprintf(PQerrormsg,
184                        "pg_krb4_recvauth: protocol version != \"%s\"\n",
185                        PG_KRB4_VERSION);
186         fputs(PQerrormsg, stderr);
187         pqdebug("%s", PQerrormsg);
188         return(STATUS_ERROR);
189     }
190     if (username && *username &&
191         strncmp(username, auth_data.pname, NAMEDATALEN)) {
192         (void) sprintf(PQerrormsg,
193                        "pg_krb4_recvauth: name \"%s\" != \"%s\"\n",
194                        username,
195                        auth_data.pname);
196         fputs(PQerrormsg, stderr);
197         pqdebug("%s", PQerrormsg);
198         return(STATUS_ERROR);
199     }
200     return(STATUS_OK);
201 }
202
203 #endif /* !FRONTEND */
204
205 #else
206 static int
207 pg_krb4_recvauth(int sock,
208                  struct sockaddr_in *laddr,
209                  struct sockaddr_in *raddr,
210                  char *username)
211 {
212   (void) sprintf(PQerrormsg,
213                  "pg_krb4_recvauth: Kerberos not implemented on this "
214                  "server.\n");
215   fputs(PQerrormsg, stderr);
216   pqdebug("%s", PQerrormsg);
217
218 return(STATUS_ERROR);
219 }
220 #endif /* KRB4 */
221
222
223 #ifdef KRB5
224 /* This needs to be ifdef'd out because krb5.h doesn't exist.  This needs
225    to be fixed.
226 */
227 /*----------------------------------------------------------------
228  * MIT Kerberos authentication system - protocol version 5
229  *----------------------------------------------------------------
230  */
231
232 #include <krb5/krb5.h>
233
234 /*
235  * pg_an_to_ln -- return the local name corresponding to an authentication
236  *                name
237  *
238  * XXX Assumes that the first aname component is the user name.  This is NOT
239  *     necessarily so, since an aname can actually be something out of your
240  *     worst X.400 nightmare, like
241  *        ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
242  *     Note that the MIT an_to_ln code does the same thing if you don't
243  *     provide an aname mapping database...it may be a better idea to use
244  *     krb5_an_to_ln, except that it punts if multiple components are found,
245  *     and we can't afford to punt.
246  */
247 static char *
248 pg_an_to_ln(char *aname)
249 {
250     char        *p;
251     
252     if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
253         *p = '\0';
254     return(aname);
255 }
256
257 #ifdef FRONTEND
258 /* moves to src/libpq/fe-auth.c  */
259 #else /* !FRONTEND */
260
261 /*
262  * pg_krb5_recvauth -- server routine to receive authentication information
263  *                     from the client
264  *
265  * We still need to compare the username obtained from the client's setup
266  * packet to the authenticated name, as described in pg_krb4_recvauth.  This
267  * is a bit more problematic in v5, as described above in pg_an_to_ln.
268  *
269  * In addition, as described above in pg_krb5_sendauth, we still need to
270  * canonicalize the server name v4-style before constructing a principal
271  * from it.  Again, this is kind of iffy.
272  *
273  * Finally, we need to tangle with the fact that v5 doesn't let you explicitly
274  * set server keytab file names -- you have to feed lower-level routines a
275  * function to retrieve the contents of a keytab, along with a single argument
276  * that allows them to open the keytab.  We assume that a server keytab is
277  * always a real file so we can allow people to specify their own filenames.
278  * (This is important because the POSTGRES keytab needs to be readable by
279  * non-root users/groups; the v4 tools used to force you do dump a whole
280  * host's worth of keys into a file, effectively forcing you to use one file,
281  * but kdb5_edit allows you to select which principals to dump.  Yay!)
282  */
283 static int
284 pg_krb5_recvauth(int sock,
285                  struct sockaddr_in *laddr,
286                  struct sockaddr_in *raddr,
287                  char *username)
288 {
289     char                        servbuf[MAXHOSTNAMELEN + 1 +
290                                         sizeof(PG_KRB_SRVNAM)];
291     char                        *hostp, *kusername = (char *) NULL;
292     krb5_error_code             code;
293     krb5_principal              client, server;
294     krb5_address                sender_addr;
295     krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL;
296     krb5_pointer                keyprocarg = (krb5_pointer) NULL;
297     
298     /*
299      * Set up server side -- since we have no ticket file to make this
300      * easy, we construct our own name and parse it.  See note on
301      * canonicalization above.
302      */
303     (void) strcpy(servbuf, PG_KRB_SRVNAM);
304     *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
305     if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
306         (void) strcpy(hostp, "localhost");
307     if (hostp = strchr(hostp, '.'))
308         *hostp = '\0';
309     if (code = krb5_parse_name(servbuf, &server)) {
310         (void) sprintf(PQerrormsg,
311                        "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n",
312                        code);
313         com_err("pg_krb5_recvauth", code, "in krb5_parse_name");
314         return(STATUS_ERROR);
315     }
316     
317     /*
318      * krb5_sendauth needs this to verify the address in the client
319      * authenticator.
320      */
321     sender_addr.addrtype = raddr->sin_family;
322     sender_addr.length = sizeof(raddr->sin_addr);
323     sender_addr.contents = (krb5_octet *) &(raddr->sin_addr);
324     
325     if (strcmp(PG_KRB_SRVTAB, "")) {
326         keyproc = krb5_kt_read_service_key;
327         keyprocarg = PG_KRB_SRVTAB;
328     }
329     
330     if (code = krb5_recvauth((krb5_pointer) &sock,
331                              PG_KRB5_VERSION,
332                              server,
333                              &sender_addr,
334                              (krb5_pointer) NULL,
335                              keyproc,
336                              keyprocarg,
337                              (char *) NULL,
338                              (krb5_int32 *) NULL,
339                              &client,
340                              (krb5_ticket **) NULL,
341                              (krb5_authenticator **) NULL)) {
342         (void) sprintf(PQerrormsg,
343                        "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n",
344                        code);
345         com_err("pg_krb5_recvauth", code, "in krb5_recvauth");
346         krb5_free_principal(server);
347         return(STATUS_ERROR);
348     }
349     krb5_free_principal(server);
350     
351     /*
352      * The "client" structure comes out of the ticket and is therefore
353      * authenticated.  Use it to check the username obtained from the
354      * postmaster startup packet.
355      */
356     if ((code = krb5_unparse_name(client, &kusername))) {
357         (void) sprintf(PQerrormsg,
358                        "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n",
359                        code);
360         com_err("pg_krb5_recvauth", code, "in krb5_unparse_name");
361         krb5_free_principal(client);
362         return(STATUS_ERROR);
363     }
364     krb5_free_principal(client);
365     if (!kusername) {
366         (void) sprintf(PQerrormsg,
367                        "pg_krb5_recvauth: could not decode username\n");
368         fputs(PQerrormsg, stderr);
369         pqdebug("%s", PQerrormsg);
370         return(STATUS_ERROR);
371     }
372     kusername = pg_an_to_ln(kusername);
373     if (username && strncmp(username, kusername, NAMEDATALEN)) {
374         (void) sprintf(PQerrormsg,
375                        "pg_krb5_recvauth: name \"%s\" != \"%s\"\n",
376                        username, kusername);
377         fputs(PQerrormsg, stderr);
378         pqdebug("%s", PQerrormsg);
379         free(kusername);
380         return(STATUS_ERROR);
381     }
382     free(kusername);
383     return(STATUS_OK);
384 }
385
386 #endif /* !FRONTEND */
387
388
389 #else
390 static int
391 pg_krb5_recvauth(int sock,
392                  struct sockaddr_in *laddr,
393                  struct sockaddr_in *raddr,
394                  char *username)
395 {
396   (void) sprintf(PQerrormsg,
397                  "pg_krb5_recvauth: Kerberos not implemented on this "
398                  "server.\n");
399   fputs(PQerrormsg, stderr);
400   pqdebug("%s", PQerrormsg);
401
402 return(STATUS_ERROR);
403 }
404 #endif /* KRB5 */
405
406 /*
407  * be_recvauth -- server demux routine for incoming authentication information
408  */
409 int
410 be_recvauth(MsgType msgtype_arg, Port *port, char *username, StartupInfo* sp)
411 {
412     MsgType msgtype;
413
414     /* A message type of STARTUP_MSG (which once upon a time was the only
415        startup message type) means user wants us to choose.  "unauth" is 
416        what used to be the only choice, but installation may choose "hba"
417        instead.
418        */
419     if (msgtype_arg == STARTUP_MSG && useHostBasedAuth)
420         msgtype = STARTUP_HBA_MSG;
421     else 
422         msgtype = STARTUP_UNAUTH_MSG;
423
424     if (!username) {
425         (void) sprintf(PQerrormsg,
426                        "be_recvauth: no user name passed\n");
427         fputs(PQerrormsg, stderr);
428         pqdebug("%s", PQerrormsg);
429         return(STATUS_ERROR);
430     }
431     if (!port) {
432         (void) sprintf(PQerrormsg,
433         "be_recvauth: no port structure passed\n");
434         fputs(PQerrormsg, stderr);
435         pqdebug("%s", PQerrormsg);
436         return(STATUS_ERROR);
437     }
438     
439     switch (msgtype) {
440     case STARTUP_KRB4_MSG:
441         if (!be_getauthsvc(msgtype)) {
442             (void) sprintf(PQerrormsg,
443                            "be_recvauth: krb4 authentication disallowed\n");
444             fputs(PQerrormsg, stderr);
445             pqdebug("%s", PQerrormsg);
446             return(STATUS_ERROR);
447         }
448         if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr,
449                              username) != STATUS_OK) {
450             (void) sprintf(PQerrormsg,
451                            "be_recvauth: krb4 authentication failed\n");
452             fputs(PQerrormsg, stderr);
453             pqdebug("%s", PQerrormsg);
454             return(STATUS_ERROR);
455         }
456         break;
457     case STARTUP_KRB5_MSG:
458         if (!be_getauthsvc(msgtype)) {
459             (void) sprintf(PQerrormsg,
460                            "be_recvauth: krb5 authentication disallowed\n");
461             fputs(PQerrormsg, stderr);
462             pqdebug("%s", PQerrormsg);
463             return(STATUS_ERROR);
464           }
465         if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr,
466                              username) != STATUS_OK) {
467             (void) sprintf(PQerrormsg,
468                            "be_recvauth: krb5 authentication failed\n");
469             fputs(PQerrormsg, stderr);
470             pqdebug("%s", PQerrormsg);
471             return(STATUS_ERROR);
472         }
473         break;
474     case STARTUP_UNAUTH_MSG:
475         if (!be_getauthsvc(msgtype)) {
476             (void) sprintf(PQerrormsg,
477                            "be_recvauth: "
478                            "unauthenticated connections disallowed\n");
479             fputs(PQerrormsg, stderr);
480             pqdebug("%s", PQerrormsg);
481             return(STATUS_ERROR);
482         }
483         break;
484     case STARTUP_HBA_MSG:
485         if (hba_recvauth(port, sp->database, sp->user, DataDir) != STATUS_OK) {
486             (void) sprintf(PQerrormsg,
487                            "be_recvauth: host-based authentication failed\n");
488             fputs(PQerrormsg, stderr);
489             pqdebug("%s", PQerrormsg);
490             return(STATUS_ERROR);
491           }
492         break;
493     default:
494         (void) sprintf(PQerrormsg,
495                        "be_recvauth: unrecognized message type: %d\n",
496                        msgtype);
497         fputs(PQerrormsg, stderr);
498         pqdebug("%s", PQerrormsg);
499         return(STATUS_ERROR);
500     }
501     return(STATUS_OK);
502 }
503
504 /*
505  * be_setauthsvc -- enable/disable the authentication services currently
506  *                  selected for use by the backend
507  * be_getauthsvc -- returns whether a particular authentication system
508  *                  (indicated by its message type) is permitted by the
509  *                  current selections
510  *
511  * be_setauthsvc encodes the command-line syntax that
512  *      -a "<service-name>"
513  * enables a service, whereas
514  *      -a "no<service-name>"
515  * disables it.
516  */
517 void
518 be_setauthsvc(char *name)
519 {
520     int i, j;
521     int turnon = 1;
522     
523     if (!name)
524         return;
525     if (!strncmp("no", name, 2)) {
526         turnon = 0;
527         name += 2;
528     }
529     if (name[0] == '\0')
530         return;
531     for (i = 0; i < n_authsvcs; ++i)
532         if (!strcmp(name, authsvcs[i].name)) {
533             for (j = 0; j < n_authsvcs; ++j)
534                 if (authsvcs[j].msgtype == authsvcs[i].msgtype)
535                     authsvcs[j].allowed = turnon;
536             break;
537         }
538     if (i == n_authsvcs) {
539         (void) sprintf(PQerrormsg,
540                        "be_setauthsvc: invalid name %s, ignoring...\n",
541                        name);
542         fputs(PQerrormsg, stderr);
543         pqdebug("%s", PQerrormsg);
544     }
545     return;
546 }
547
548 int
549 be_getauthsvc(MsgType msgtype)
550 {
551     int i;
552     
553     for (i = 0; i < n_authsvcs; ++i)
554         if (msgtype == authsvcs[i].msgtype)
555             return(authsvcs[i].allowed);
556     return(0);
557 }