]> granicus.if.org Git - postgresql/blob - src/backend/libpq/auth.c
From: Phil Thompson <phil@river-bank.demon.co.uk>
[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.21 1998/01/26 01:41:04 scrappy Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 /*
15  * INTERFACE ROUTINES
16  *
17  *         backend (postmaster) routines:
18  *              be_recvauth                             receive authentication information
19  */
20 #include <stdio.h>
21 #include <string.h>
22 #include <sys/param.h>                  /* for MAXHOSTNAMELEN on most */
23 #ifndef  MAXHOSTNAMELEN
24 #include <netdb.h>                              /* for MAXHOSTNAMELEN on some */
25 #endif
26 #include <pwd.h>
27 #include <ctype.h>                              /* isspace() declaration */
28
29 #include <sys/types.h>                  /* needed by in.h on Ultrix */
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32
33 #include <postgres.h>
34 #include <miscadmin.h>
35
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>
41
42
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);
52
53
54 #ifdef KRB4
55 /* This has to be ifdef'd out because krb.h does exist.  This needs
56    to be fixed.
57 */
58 /*----------------------------------------------------------------
59  * MIT Kerberos authentication system - protocol version 4
60  *----------------------------------------------------------------
61  */
62
63 #include <krb.h>
64
65 /*
66  * pg_krb4_recvauth -- server routine to receive authentication information
67  *                                         from the client
68  *
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.)
73  */
74 static int
75 pg_krb4_recvauth(Port *)
76 {
77         long            krbopts = 0;    /* one-way authentication */
78         KTEXT_ST        clttkt;
79         char            instance[INST_SZ];
80         AUTH_DAT        auth_data;
81         Key_schedule key_sched;
82         char            version[KRB_SENDAUTH_VLEN];
83         int                     status;
84
85         strcpy(instance, "*");          /* don't care, but arg gets expanded
86                                                                  * anyway */
87         status = krb_recvauth(krbopts,
88                                                   port->sock,
89                                                   &clttkt,
90                                                   PG_KRB_SRVNAM,
91                                                   instance,
92                                                   &port->raddr.in,
93                                                   &port->laddr.in,
94                                                   &auth_data,
95                                                   PG_KRB_SRVTAB,
96                                                   key_sched,
97                                                   version);
98         if (status != KSUCCESS)
99         {
100                 sprintf(PQerrormsg,
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);
106         }
107         if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN))
108         {
109                 sprintf(PQerrormsg,
110                                 "pg_krb4_recvauth: protocol version != \"%s\"\n",
111                                 PG_KRB4_VERSION);
112                 fputs(PQerrormsg, stderr);
113                 pqdebug("%s", PQerrormsg);
114                 return (STATUS_ERROR);
115         }
116         if (strncmp(port->user, auth_data.pname, SM_USER))
117         {
118                 sprintf(PQerrormsg,
119                                 "pg_krb4_recvauth: name \"%s\" != \"%s\"\n",
120                                 port->username,
121                                 auth_data.pname);
122                 fputs(PQerrormsg, stderr);
123                 pqdebug("%s", PQerrormsg);
124                 return (STATUS_ERROR);
125         }
126         return (STATUS_OK);
127 }
128
129 #else
130 static int
131 pg_krb4_recvauth(Port *port)
132 {
133         sprintf(PQerrormsg,
134                         "pg_krb4_recvauth: Kerberos not implemented on this "
135                         "server.\n");
136         fputs(PQerrormsg, stderr);
137         pqdebug("%s", PQerrormsg);
138
139         return (STATUS_ERROR);
140 }
141
142 #endif                                                  /* KRB4 */
143
144
145 #ifdef KRB5
146 /* This needs to be ifdef'd out because krb5.h doesn't exist.  This needs
147    to be fixed.
148 */
149 /*----------------------------------------------------------------
150  * MIT Kerberos authentication system - protocol version 5
151  *----------------------------------------------------------------
152  */
153
154 #include <krb5/krb5.h>
155
156 /*
157  * pg_an_to_ln -- return the local name corresponding to an authentication
158  *                                name
159  *
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.
168  */
169 static char *
170 pg_an_to_ln(char *aname)
171 {
172         char       *p;
173
174         if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
175                 *p = '\0';
176         return (aname);
177 }
178
179 /*
180  * pg_krb5_recvauth -- server routine to receive authentication information
181  *                                         from the client
182  *
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.
186  *
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.
190  *
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!)
200  */
201 static int
202 pg_krb5_recvauth(Port *port)
203 {
204         char            servbuf[MAXHOSTNAMELEN + 1 +
205                                                                         sizeof(PG_KRB_SRVNAM)];
206         char       *hostp,
207                            *kusername = (char *) NULL;
208         krb5_error_code code;
209         krb5_principal client,
210                                 server;
211         krb5_address sender_addr;
212         krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL;
213         krb5_pointer keyprocarg = (krb5_pointer) NULL;
214
215         /*
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.
219          */
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, '.'))
225                 *hostp = '\0';
226         if (code = krb5_parse_name(servbuf, &server))
227         {
228                 sprintf(PQerrormsg,
229                           "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n",
230                                 code);
231                 com_err("pg_krb5_recvauth", code, "in krb5_parse_name");
232                 return (STATUS_ERROR);
233         }
234
235         /*
236          * krb5_sendauth needs this to verify the address in the client
237          * authenticator.
238          */
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);
242
243         if (strcmp(PG_KRB_SRVTAB, ""))
244         {
245                 keyproc = krb5_kt_read_service_key;
246                 keyprocarg = PG_KRB_SRVTAB;
247         }
248
249         if (code = krb5_recvauth((krb5_pointer) & port->sock,
250                                                          PG_KRB5_VERSION,
251                                                          server,
252                                                          &sender_addr,
253                                                          (krb5_pointer) NULL,
254                                                          keyproc,
255                                                          keyprocarg,
256                                                          (char *) NULL,
257                                                          (krb5_int32 *) NULL,
258                                                          &client,
259                                                          (krb5_ticket **) NULL,
260                                                          (krb5_authenticator **) NULL))
261         {
262                 sprintf(PQerrormsg,
263                                 "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n",
264                                 code);
265                 com_err("pg_krb5_recvauth", code, "in krb5_recvauth");
266                 krb5_free_principal(server);
267                 return (STATUS_ERROR);
268         }
269         krb5_free_principal(server);
270
271         /*
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.
275          */
276         if ((code = krb5_unparse_name(client, &kusername)))
277         {
278                 sprintf(PQerrormsg,
279                         "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n",
280                                 code);
281                 com_err("pg_krb5_recvauth", code, "in krb5_unparse_name");
282                 krb5_free_principal(client);
283                 return (STATUS_ERROR);
284         }
285         krb5_free_principal(client);
286         if (!kusername)
287         {
288                 sprintf(PQerrormsg,
289                                 "pg_krb5_recvauth: could not decode username\n");
290                 fputs(PQerrormsg, stderr);
291                 pqdebug("%s", PQerrormsg);
292                 return (STATUS_ERROR);
293         }
294         kusername = pg_an_to_ln(kusername);
295         if (strncmp(username, kusername, SM_USER))
296         {
297                 sprintf(PQerrormsg,
298                                 "pg_krb5_recvauth: name \"%s\" != \"%s\"\n",
299                                 port->username, kusername);
300                 fputs(PQerrormsg, stderr);
301                 pqdebug("%s", PQerrormsg);
302                 pfree(kusername);
303                 return (STATUS_ERROR);
304         }
305         pfree(kusername);
306         return (STATUS_OK);
307 }
308
309 #else
310 static int
311 pg_krb5_recvauth(Port *port)
312 {
313         sprintf(PQerrormsg,
314                         "pg_krb5_recvauth: Kerberos not implemented on this "
315                         "server.\n");
316         fputs(PQerrormsg, stderr);
317         pqdebug("%s", PQerrormsg);
318
319         return (STATUS_ERROR);
320 }
321
322 #endif                                                  /* KRB5 */
323
324
325 /*
326  * Handle a v0 password packet.
327  */
328
329 static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt)
330 {
331         Port *port;
332         PasswordPacketV0 *pp;
333         char *user, *password, *cp, *start;
334
335         port = (Port *)arg;
336         pp = (PasswordPacketV0 *)pkt;
337
338         /*
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.
341          */
342
343         user = password = NULL;
344
345         len -= sizeof (pp->unused);
346
347         cp = start = pp->data;
348
349         while (len > 0)
350         if (*cp++ == '\0')
351         {
352                 if (user == NULL)
353                         user = start;
354                 else
355                 {
356                         password = start;
357                         break;
358                 }
359
360                 start = cp;
361         }
362
363         if (user == NULL || password == NULL)
364         {
365                 sprintf(PQerrormsg,
366                                 "pg_password_recvauth: badly formed password packet.\n");
367                 fputs(PQerrormsg, stderr);
368                 pqdebug("%s", PQerrormsg);
369
370                 auth_failed(port);
371         }
372         else if (map_old_to_new(port, uaPassword,
373                                 verify_password(port->auth_arg, user, password)) != STATUS_OK)
374                 auth_failed(port);
375 }
376
377
378 /*
379  * Tell the user the authentication failed, but not why.
380  */
381
382 void auth_failed(Port *port)
383 {
384         PacketSendError(&port->pktInfo, "User authentication failed");
385 }
386
387
388 /*
389  * be_recvauth -- server demux routine for incoming authentication information
390  */
391 void be_recvauth(Port *port)
392 {
393         AuthRequest areq;
394         void (*auth_handler)();
395
396         /*
397          * Get the authentication method to use for this frontend/database
398          * combination.
399          */
400
401         if (hba_getauthmethod(&port->raddr, port->database, port->auth_arg,
402                                 &port->auth_method) != STATUS_OK)
403         {
404                 PacketSendError(&port->pktInfo, "Error getting authentication method");
405                 return;
406         }
407
408         /* Handle old style authentication. */
409
410         if (PG_PROTOCOL_MAJOR(port->proto) == 0)
411         {
412                 if (old_be_recvauth(port) != STATUS_OK)
413                         auth_failed(port);
414
415                 return;
416         }
417
418         /* Handle new style authentication. */
419
420         switch (port->auth_method)
421         {
422         case uaReject:
423                 auth_failed(port);
424                 return;
425  
426         case uaKrb4:
427                 areq = AUTH_REQ_KRB4;
428                 auth_handler = handle_krb4_auth;
429                 break;
430
431         case uaKrb5:
432                 areq = AUTH_REQ_KRB5;
433                 auth_handler = handle_krb5_auth;
434                 break;
435
436         case uaTrust:
437                 areq = AUTH_REQ_OK;
438                 auth_handler = handle_done_auth;
439                 break;
440
441         case uaIdent:
442                 if (authident(&port->raddr.in, &port->laddr.in, port->user,
443                                 port->auth_arg) != STATUS_OK)
444                 {
445                         auth_failed(port);
446                         return;
447                 }
448
449                 areq = AUTH_REQ_OK;
450                 auth_handler = handle_done_auth;
451                 break;
452
453         case uaPassword:
454                 areq = AUTH_REQ_PASSWORD;
455                 auth_handler = handle_password_auth;
456                 break;
457
458         case uaCrypt:
459                 areq = AUTH_REQ_CRYPT;
460                 auth_handler = handle_password_auth;
461                 break;
462         }
463
464         /* Tell the frontend what we want next. */
465
466         sendAuthRequest(port, areq, auth_handler);
467 }
468  
469
470 /*
471  * Send an authentication request packet to the frontend.
472  */
473
474 static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler)())
475 {
476         char *dp, *sp;
477         int i;
478         uint32 net_areq;
479
480         /* Convert to a byte stream. */
481
482         net_areq = htonl(areq);
483
484         dp = port->pktInfo.pkt.ar.data;
485         sp = (char *)&net_areq;
486
487         *dp++ = 'R';
488
489         for (i = 1; i <= 4; ++i)
490                 *dp++ = *sp++;
491
492         /* Add the salt for encrypted passwords. */
493
494         if (areq == AUTH_REQ_CRYPT)
495         {
496                 *dp++ = port->salt[0];
497                 *dp++ = port->salt[1];
498                 i += 2;
499         }
500
501         PacketSendSetup(&port -> pktInfo, i, handler, (char *)port);
502 }
503
504
505 /*
506  * Called when we have told the front end that it is authorised.
507  */
508
509 static void handle_done_auth(Port *port)
510 {
511         /*
512          * Don't generate any more traffic.  This will cause the backend to
513          * start.
514          */
515
516         return;
517 }
518
519
520 /*
521  * Called when we have told the front end that it should use Kerberos V4
522  * authentication.
523  */
524
525 static void handle_krb4_auth(Port *port)
526 {
527         if (pg_krb4_recvauth(port) != STATUS_OK)
528                 auth_failed(port);
529         else
530                 sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
531 }
532
533
534 /*
535  * Called when we have told the front end that it should use Kerberos V5
536  * authentication.
537  */
538
539 static void handle_krb5_auth(Port *port)
540 {
541         if (pg_krb5_recvauth(port) != STATUS_OK)
542                 auth_failed(port);
543         else
544                 sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
545 }
546
547
548 /*
549  * Called when we have told the front end that it should use password
550  * authentication.
551  */
552
553 static void handle_password_auth(Port *port)
554 {
555         /* Set up the read of the password packet. */
556
557         PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (char *)port);
558 }
559
560
561 /*
562  * Called when we have received the password packet.
563  */
564
565 static void readPasswordPacket(char *arg, PacketLen len, char *pkt)
566 {
567         char password[sizeof (PasswordPacket) + 1];
568         Port *port;
569
570         port = (Port *)arg;
571
572         /* Silently truncate a password that is too big. */
573
574         if (len > sizeof (PasswordPacket))
575                 len = sizeof (PasswordPacket);
576                 
577         StrNCpy(password, ((PasswordPacket *)pkt)->passwd, len);
578
579         /*
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,
582          * encrypted or not.
583          */
584
585         if (port->auth_method == uaPassword && port->auth_arg[0] != '\0')
586         {
587                 if (verify_password(port->auth_arg, port->user, password) != STATUS_OK)
588                         auth_failed(port);
589         }
590         else if (crypt_verify(port, port->user, password) != STATUS_OK)
591                 auth_failed(port);
592         else
593                 sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
594 }
595
596
597 /*
598  * Server demux routine for incoming authentication information for protocol
599  * version 0.
600  */
601 static int old_be_recvauth(Port *port)
602 {
603         int status;
604         MsgType msgtype = (MsgType)port->proto;
605
606         /* Handle the authentication that's offered. */
607
608         switch (msgtype)
609         {
610         case STARTUP_KRB4_MSG:
611                 status = map_old_to_new(port,uaKrb4,pg_krb4_recvauth(port));
612                 break;
613
614         case STARTUP_KRB5_MSG:
615                 status = map_old_to_new(port,uaKrb5,pg_krb5_recvauth(port));
616                 break;
617
618         case STARTUP_MSG:
619                 status = map_old_to_new(port,uaTrust,STATUS_OK);
620                 break;
621
622         case STARTUP_PASSWORD_MSG:
623                 PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
624                                         (char *)port);
625
626                 return STATUS_OK;
627
628         default:
629                 fprintf(stderr, "Invalid startup message type: %u\n", msgtype);
630
631                 return STATUS_OK;
632         }
633
634         return status;
635 }
636  
637
638 /*
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.
642  */
643
644 static int map_old_to_new(Port *port, UserAuth old, int status)
645 {
646         switch (port->auth_method)
647         {
648         case uaCrypt:
649         case uaReject:
650                 status = STATUS_ERROR;
651                 break;
652
653         case uaKrb4:
654                 if (old != uaKrb4)
655                         status = STATUS_ERROR;
656                 break;
657
658         case uaKrb5:
659                 if (old != uaKrb5)
660                         status = STATUS_ERROR;
661                 break;
662
663         case uaTrust:
664                 status = STATUS_OK;
665                 break;
666
667         case uaIdent:
668                 status = authident(&port->raddr.in, &port->laddr.in,
669                                         port->user, port->auth_arg);
670                 break;
671
672         case uaPassword:
673                 if (old != uaPassword)
674                         status = STATUS_ERROR;
675
676                 break;
677         }
678  
679         return status;
680 }