]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/fe-auth.c
The attached patch implements some changes that were discussed a
[postgresql] / src / interfaces / libpq / fe-auth.c
1 /*-------------------------------------------------------------------------
2  *
3  * fe-auth.c--
4  *         The front-end (client) authorization routines
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.21 1998/08/09 02:59:25 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14
15 /*
16  * INTERFACE ROUTINES
17  *         frontend (client) routines:
18  *              fe_sendauth                             send authentication information
19  *              fe_getauthname                  get user's name according to the client side
20  *                                                              of the authentication system
21  *              fe_setauthsvc                   set frontend authentication service
22  *              fe_getauthsvc                   get current frontend authentication service
23  *
24  *
25  *
26  */
27 #ifdef WIN32
28 #include "win32.h"
29 #else
30 #include <stdio.h>
31 #include <string.h>
32 #include <sys/param.h>                  /* for MAXHOSTNAMELEN on most */
33 #include <sys/types.h>
34 #ifndef  MAXHOSTNAMELEN
35 #include <netdb.h>                              /* for MAXHOSTNAMELEN on some */
36 #endif
37 #include <unistd.h>
38 #include <pwd.h>
39 #endif /* WIN32 */
40
41 #include "postgres.h"
42
43 #include "libpq/pqcomm.h"
44
45 #include "libpq-fe.h"
46 #include "fe-auth.h"
47 #include "fe-connect.h"
48
49 #ifdef HAVE_CRYPT_H
50 #include <crypt.h>
51 #endif
52
53
54 /*----------------------------------------------------------------
55  * common definitions for generic fe/be routines
56  *----------------------------------------------------------------
57  */
58
59 struct authsvc
60 {
61         char            name[NAMEDATALEN];      /* service nickname (for command line) */
62         MsgType         msgtype;                /* startup packet header type */
63         int                     allowed;                /* initially allowed (before command line
64                                                                  * option parsing)? */
65 };
66
67 /*
68  * Command-line parsing routines use this structure to map nicknames
69  * onto service types (and the startup packets to use with them).
70  *
71  * Programs receiving an authentication request use this structure to
72  * decide which authentication service types are currently permitted.
73  * By default, all authentication systems compiled into the system are
74  * allowed.  Unauthenticated connections are disallowed unless there
75  * isn't any authentication system.
76  */
77 static struct authsvc authsvcs[] = {
78 #ifdef KRB4
79         {"krb4", STARTUP_KRB4_MSG, 1},
80         {"kerberos", STARTUP_KRB4_MSG, 1},
81 #endif                                                  /* KRB4 */
82 #ifdef KRB5
83         {"krb5", STARTUP_KRB5_MSG, 1},
84         {"kerberos", STARTUP_KRB5_MSG, 1},
85 #endif                                                  /* KRB5 */
86         {UNAUTHNAME, STARTUP_MSG,
87 #if defined(KRB4) || defined(KRB5)
88                 0
89 #else                                                   /* !(KRB4 || KRB5) */
90                 1
91 #endif                                                  /* !(KRB4 || KRB5) */
92         },
93         {"password", STARTUP_PASSWORD_MSG, 0}
94 };
95
96 static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
97
98 #ifdef KRB4
99 /*----------------------------------------------------------------
100  * MIT Kerberos authentication system - protocol version 4
101  *----------------------------------------------------------------
102  */
103
104 #include "krb.h"
105
106 /* for some reason, this is not defined in krb.h ... */
107 extern char *tkt_string(void);
108
109 /*
110  * pg_krb4_init -- initialization performed before any Kerberos calls are made
111  *
112  * For v4, all we need to do is make sure the library routines get the right
113  * ticket file if we want them to see a special one.  (They will open the file
114  * themselves.)
115  */
116 static void
117 pg_krb4_init()
118 {
119         char       *realm;
120         static          init_done = 0;
121
122         if (init_done)
123                 return;
124         init_done = 1;
125
126         /*
127          * If the user set PGREALM, then we use a ticket file with a special
128          * name: <usual-ticket-file-name>@<PGREALM-value>
129          */
130         if (realm = getenv("PGREALM"))
131         {
132                 char            tktbuf[MAXPATHLEN];
133
134                 (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
135                 krb_set_tkt_string(tktbuf);
136         }
137 }
138
139 /*
140  * pg_krb4_authname -- returns a pointer to static space containing whatever
141  *                                         name the user has authenticated to the system
142  *
143  * We obtain this information by digging around in the ticket file.
144  */
145 static char *
146 pg_krb4_authname(char *PQerrormsg)
147 {
148         char            instance[INST_SZ];
149         char            realm[REALM_SZ];
150         int                     status;
151         static char name[SNAME_SZ + 1] = "";
152
153         if (name[0])
154                 return (name);
155
156         pg_krb4_init();
157
158         name[SNAME_SZ] = '\0';
159         status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
160         if (status != KSUCCESS)
161         {
162                 (void) sprintf(PQerrormsg,
163                                            "pg_krb4_authname: krb_get_tf_fullname: %s\n",
164                                            krb_err_txt[status]);
165                 return ((char *) NULL);
166         }
167         return (name);
168 }
169
170 /*
171  * pg_krb4_sendauth -- client routine to send authentication information to
172  *                                         the server
173  *
174  * This routine does not do mutual authentication, nor does it return enough
175  * information to do encrypted connections.  But then, if we want to do
176  * encrypted connections, we'll have to redesign the whole RPC mechanism
177  * anyway.
178  *
179  * If the user is too lazy to feed us a hostname, we try to come up with
180  * something other than "localhost" since the hostname is used as an
181  * instance and instance names in v4 databases are usually actual hostnames
182  * (canonicalized to omit all domain suffixes).
183  */
184 static int
185 pg_krb4_sendauth(const char *PQerrormsg, int sock,
186                                  struct sockaddr_in * laddr,
187                                  struct sockaddr_in * raddr,
188                                  const char *hostname)
189 {
190         long            krbopts = 0;    /* one-way authentication */
191         KTEXT_ST        clttkt;
192         int                     status;
193         char            hostbuf[MAXHOSTNAMELEN];
194         const char *realm = getenv("PGREALM");          /* NULL == current realm */
195
196         if (!hostname || !(*hostname))
197         {
198                 if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
199                         strcpy(hostbuf, "localhost");
200                 hostname = hostbuf;
201         }
202
203         pg_krb4_init();
204
205         status = krb_sendauth(krbopts,
206                                                   sock,
207                                                   &clttkt,
208                                                   PG_KRB_SRVNAM,
209                                                   hostname,
210                                                   realm,
211                                                   (u_long) 0,
212                                                   (MSG_DAT *) NULL,
213                                                   (CREDENTIALS *) NULL,
214                                                   (Key_schedule *) NULL,
215                                                   laddr,
216                                                   raddr,
217                                                   PG_KRB4_VERSION);
218         if (status != KSUCCESS)
219         {
220                 (void) sprintf(PQerrormsg,
221                                            "pg_krb4_sendauth: kerberos error: %s\n",
222                                            krb_err_txt[status]);
223                 return (STATUS_ERROR);
224         }
225         return (STATUS_OK);
226 }
227
228 #endif                                                  /* KRB4 */
229
230 #ifdef KRB5
231 /*----------------------------------------------------------------
232  * MIT Kerberos authentication system - protocol version 5
233  *----------------------------------------------------------------
234  */
235
236 #include "krb5/krb5.h"
237
238 /*
239  * pg_an_to_ln -- return the local name corresponding to an authentication
240  *                                name
241  *
242  * XXX Assumes that the first aname component is the user name.  This is NOT
243  *         necessarily so, since an aname can actually be something out of your
244  *         worst X.400 nightmare, like
245  *                ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
246  *         Note that the MIT an_to_ln code does the same thing if you don't
247  *         provide an aname mapping database...it may be a better idea to use
248  *         krb5_an_to_ln, except that it punts if multiple components are found,
249  *         and we can't afford to punt.
250  */
251 static char *
252 pg_an_to_ln(const char *aname)
253 {
254         char       *p;
255
256         if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
257                 *p = '\0';
258         return (aname);
259 }
260
261
262 /*
263  * pg_krb5_init -- initialization performed before any Kerberos calls are made
264  *
265  * With v5, we can no longer set the ticket (credential cache) file name;
266  * we now have to provide a file handle for the open (well, "resolved")
267  * ticket file everywhere.
268  *
269  */
270 static int
271                         krb5_ccache
272 pg_krb5_init(void)
273 {
274         krb5_error_code code;
275         char       *realm,
276                            *defname;
277         char            tktbuf[MAXPATHLEN];
278         static krb5_ccache ccache = (krb5_ccache) NULL;
279
280         if (ccache)
281                 return (ccache);
282
283         /*
284          * If the user set PGREALM, then we use a ticket file with a special
285          * name: <usual-ticket-file-name>@<PGREALM-value>
286          */
287         if (!(defname = krb5_cc_default_name()))
288         {
289                 (void) sprintf(PQerrormsg,
290                                            "pg_krb5_init: krb5_cc_default_name failed\n");
291                 return ((krb5_ccache) NULL);
292         }
293         strcpy(tktbuf, defname);
294         if (realm = getenv("PGREALM"))
295         {
296                 strcat(tktbuf, "@");
297                 strcat(tktbuf, realm);
298         }
299
300         if (code = krb5_cc_resolve(tktbuf, &ccache))
301         {
302                 (void) sprintf(PQerrormsg,
303                                   "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n",
304                                            code);
305                 com_err("pg_krb5_init", code, "in krb5_cc_resolve");
306                 return ((krb5_ccache) NULL);
307         }
308         return (ccache);
309 }
310
311 /*
312  * pg_krb5_authname -- returns a pointer to static space containing whatever
313  *                                         name the user has authenticated to the system
314  *
315  * We obtain this information by digging around in the ticket file.
316  */
317 static const char *
318 pg_krb5_authname(const char *PQerrormsg)
319 {
320         krb5_ccache ccache;
321         krb5_principal principal;
322         krb5_error_code code;
323         static char *authname = (char *) NULL;
324
325         if (authname)
326                 return (authname);
327
328         ccache = pg_krb5_init();        /* don't free this */
329
330         if (code = krb5_cc_get_principal(ccache, &principal))
331         {
332                 (void) sprintf(PQerrormsg,
333                 "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n",
334                                            code);
335                 com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
336                 return ((char *) NULL);
337         }
338         if (code = krb5_unparse_name(principal, &authname))
339         {
340                 (void) sprintf(PQerrormsg,
341                         "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n",
342                                            code);
343                 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
344                 krb5_free_principal(principal);
345                 return ((char *) NULL);
346         }
347         krb5_free_principal(principal);
348         return (pg_an_to_ln(authname));
349 }
350
351 /*
352  * pg_krb5_sendauth -- client routine to send authentication information to
353  *                                         the server
354  *
355  * This routine does not do mutual authentication, nor does it return enough
356  * information to do encrypted connections.  But then, if we want to do
357  * encrypted connections, we'll have to redesign the whole RPC mechanism
358  * anyway.
359  *
360  * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
361  * are simply chopped off.      Hence, we are assuming that you've entered your
362  * server instances as
363  *              <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
364  * in the PGREALM (or local) database.  This is probably a bad assumption.
365  */
366 static int
367 pg_krb5_sendauth(const char *PQerrormsg, int sock,
368                                  struct sockaddr_in * laddr,
369                                  struct sockaddr_in * raddr,
370                                  const char *hostname)
371 {
372         char            servbuf[MAXHOSTNAMELEN + 1 +
373                                                                         sizeof(PG_KRB_SRVNAM)];
374         const char *hostp;
375         const char *realm;
376         krb5_error_code code;
377         krb5_principal client,
378                                 server;
379         krb5_ccache ccache;
380         krb5_error *error = (krb5_error *) NULL;
381
382         ccache = pg_krb5_init();        /* don't free this */
383
384         /*
385          * set up client -- this is easy, we can get it out of the ticket
386          * file.
387          */
388         if (code = krb5_cc_get_principal(ccache, &client))
389         {
390                 (void) sprintf(PQerrormsg,
391                 "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n",
392                                            code);
393                 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
394                 return (STATUS_ERROR);
395         }
396
397         /*
398          * set up server -- canonicalize as described above
399          */
400         strcpy(servbuf, PG_KRB_SRVNAM);
401         *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
402         if (hostname || *hostname)
403                 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
404         else
405         {
406                 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
407                         strcpy(hostp, "localhost");
408         }
409         if (hostp = strchr(hostp, '.'))
410                 *hostp = '\0';
411         if (realm = getenv("PGREALM"))
412         {
413                 strcat(servbuf, "@");
414                 strcat(servbuf, realm);
415         }
416         if (code = krb5_parse_name(servbuf, &server))
417         {
418                 (void) sprintf(PQerrormsg,
419                           "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n",
420                                            code);
421                 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
422                 krb5_free_principal(client);
423                 return (STATUS_ERROR);
424         }
425
426         /*
427          * The only thing we want back from krb5_sendauth is an error status
428          * and any error messages.
429          */
430         if (code = krb5_sendauth((krb5_pointer) & sock,
431                                                          PG_KRB5_VERSION,
432                                                          client,
433                                                          server,
434                                                          (krb5_flags) 0,
435                                                          (krb5_checksum *) NULL,
436                                                          (krb5_creds *) NULL,
437                                                          ccache,
438                                                          (krb5_int32 *) NULL,
439                                                          (krb5_keyblock **) NULL,
440                                                          &error,
441                                                          (krb5_ap_rep_enc_part **) NULL))
442         {
443                 if ((code == KRB5_SENDAUTH_REJECTED) && error)
444                 {
445                         (void) sprintf(PQerrormsg,
446                                   "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
447                                                    error->text.length, error->text.data);
448                 }
449                 else
450                 {
451                         (void) sprintf(PQerrormsg,
452                                 "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n",
453                                                    code);
454                         com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
455                 }
456         }
457         krb5_free_principal(client);
458         krb5_free_principal(server);
459         return (code ? STATUS_ERROR : STATUS_OK);
460 }
461
462 #endif                                                  /* KRB5 */
463
464 static int
465 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
466 {
467         /* Encrypt the password if needed. */
468
469         if (areq == AUTH_REQ_CRYPT)
470                 password = crypt(password, conn->salt);
471
472         return packetSend(conn, password, strlen(password) + 1);
473 }
474
475 /*
476  * fe_sendauth -- client demux routine for outgoing authentication information
477  */
478 int
479 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
480                         const char *password, char *PQerrormsg)
481 {
482         switch (areq)
483         {
484                         case AUTH_REQ_OK:
485                         break;
486
487                 case AUTH_REQ_KRB4:
488 #ifdef KRB4
489                         if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
490                                                                  &conn->raddr.in,
491                                                                  hostname) != STATUS_OK)
492                         {
493                                 (void) sprintf(PQerrormsg,
494                                                         "fe_sendauth: krb4 authentication failed\n");
495                                 return (STATUS_ERROR);
496                         }
497                         break;
498 #else
499                         (void) sprintf(PQerrormsg,
500                                          "fe_sendauth: krb4 authentication not supported\n");
501                         return (STATUS_ERROR);
502 #endif
503
504                 case AUTH_REQ_KRB5:
505 #ifdef KRB5
506                         if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
507                                                                  &conn->raddr.in,
508                                                                  hostname) != STATUS_OK)
509                         {
510                                 (void) sprintf(PQerrormsg,
511                                                         "fe_sendauth: krb5 authentication failed\n");
512                                 return (STATUS_ERROR);
513                         }
514                         break;
515 #else
516                         (void) sprintf(PQerrormsg,
517                                          "fe_sendauth: krb5 authentication not supported\n");
518                         return (STATUS_ERROR);
519 #endif
520
521                 case AUTH_REQ_PASSWORD:
522                 case AUTH_REQ_CRYPT:
523                         if (password == NULL || *password == '\0')
524                         {
525                                 (void) sprintf(PQerrormsg,
526                                  "fe_sendauth: no password supplied\n");
527                                 return (STATUS_ERROR);
528                         }
529                         if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
530                         {
531                                 (void) sprintf(PQerrormsg,
532                                  "fe_sendauth: error sending password authentication\n");
533                                 return (STATUS_ERROR);
534                         }
535
536                         break;
537
538                 default:
539                         (void) sprintf(PQerrormsg,
540                         "fe_sendauth: authentication type %u not supported\n", areq);
541                         return (STATUS_ERROR);
542         }
543
544         return (STATUS_OK);
545 }
546
547 /*
548  * fe_setauthsvc
549  * fe_getauthsvc
550  *
551  * Set/return the authentication service currently selected for use by the
552  * frontend. (You can only use one in the frontend, obviously.)
553  */
554 static pg_authsvc = -1;
555
556 void
557 fe_setauthsvc(const char *name, char *PQerrormsg)
558 {
559         int                     i;
560
561         for (i = 0; i < n_authsvcs; ++i)
562                 if (!strcmp(name, authsvcs[i].name))
563                 {
564                         pg_authsvc = i;
565                         break;
566                 }
567         if (i == n_authsvcs)
568         {
569                 (void) sprintf(PQerrormsg,
570                                            "fe_setauthsvc: invalid name: %s, ignoring...\n",
571                                            name);
572         }
573         return;
574 }
575
576 MsgType
577 fe_getauthsvc(char *PQerrormsg)
578 {
579         if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
580                 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
581         return (authsvcs[pg_authsvc].msgtype);
582 }
583
584 /*
585  * fe_getauthname -- returns a pointer to dynamic space containing whatever
586  *                                       name the user has authenticated to the system
587  * if there is an error, return the error message in PQerrormsg
588  */
589 char *
590 fe_getauthname(char *PQerrormsg)
591 {
592         char       *name = (char *) NULL;
593         char       *authn = (char *) NULL;
594         MsgType         authsvc;
595
596         authsvc = fe_getauthsvc(PQerrormsg);
597         switch ((int) authsvc)
598         {
599 #ifdef KRB4
600                 case STARTUP_KRB4_MSG:
601                         name = pg_krb4_authname(PQerrormsg);
602                         break;
603 #endif
604 #ifdef KRB5
605                 case STARTUP_KRB5_MSG:
606                         name = pg_krb5_authname(PQerrormsg);
607                         break;
608 #endif
609                 case STARTUP_MSG:
610                         {
611 #ifdef WIN32
612                                 char username[128];
613                                 DWORD namesize = sizeof(username) - 1;
614
615                                 if (GetUserName(username,&namesize)) 
616                                         name = username;
617 #else
618                                 struct passwd *pw = getpwuid(geteuid());
619
620                                 if (pw)
621                                         name = pw->pw_name;
622 #endif
623                         }
624                         break;
625                 default:
626                         (void) sprintf(PQerrormsg,
627                                    "fe_getauthname: invalid authentication system: %d\n",
628                                                    authsvc);
629                         break;
630         }
631
632         if (name && (authn = (char *) malloc(strlen(name) + 1)))
633                 strcpy(authn, name);
634         return (authn);
635 }