]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/fe-auth.c
Hi!
[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  * NOTE: the error message strings returned by this module must not
9  * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
10  *
11  * IDENTIFICATION
12  *        $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.35 2000/01/18 19:05:31 momjian Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16
17 /*
18  * INTERFACE ROUTINES
19  *         frontend (client) routines:
20  *              fe_sendauth                             send authentication information
21  *              fe_getauthname                  get user's name according to the client side
22  *                                                              of the authentication system
23  *              fe_setauthsvc                   set frontend authentication service
24  *              fe_getauthsvc                   get current frontend authentication service
25  *
26  *
27  *
28  */
29
30 #ifndef WIN32
31 #include "postgres.h"
32 #endif
33 #include "libpq-fe.h"
34 #include "libpq-int.h"
35 #include "fe-auth.h"
36
37 #ifdef WIN32
38 #include "win32.h"
39 #else
40 #include <unistd.h>
41 #include <sys/param.h>                  /* for MAXHOSTNAMELEN on most */
42 #ifndef  MAXHOSTNAMELEN
43 #include <netdb.h>                              /* for MAXHOSTNAMELEN on some */
44 #endif
45 #include <pwd.h>
46 #endif
47
48 #ifdef HAVE_CRYPT_H
49 #include <crypt.h>
50 #endif
51
52
53 /*----------------------------------------------------------------
54  * common definitions for generic fe/be routines
55  *----------------------------------------------------------------
56  */
57
58 struct authsvc
59 {
60         char            name[NAMEDATALEN];              /* service nickname (for command
61                                                                                  * 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 int      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[MAXPGPATH];
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 + 1];
149         char            realm[REALM_SZ + 1];
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[MAXPGPATH];
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", code);
304                 com_err("pg_krb5_init", code, "in krb5_cc_resolve");
305                 return (krb5_ccache) NULL;
306         }
307         return ccache;
308 }
309
310 /*
311  * pg_krb5_authname -- returns a pointer to static space containing whatever
312  *                                         name the user has authenticated to the system
313  *
314  * We obtain this information by digging around in the ticket file.
315  */
316 static const char *
317 pg_krb5_authname(const char *PQerrormsg)
318 {
319         krb5_ccache ccache;
320         krb5_principal principal;
321         krb5_error_code code;
322         static char *authname = (char *) NULL;
323
324         if (authname)
325                 return authname;
326
327         ccache = pg_krb5_init();        /* don't free this */
328
329         if (code = krb5_cc_get_principal(ccache, &principal))
330         {
331                 (void) sprintf(PQerrormsg,
332                                            "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code);
333                 com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
334                 return (char *) NULL;
335         }
336         if (code = krb5_unparse_name(principal, &authname))
337         {
338                 (void) sprintf(PQerrormsg,
339                                            "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code);
340                 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
341                 krb5_free_principal(principal);
342                 return (char *) NULL;
343         }
344         krb5_free_principal(principal);
345         return pg_an_to_ln(authname);
346 }
347
348 /*
349  * pg_krb5_sendauth -- client routine to send authentication information to
350  *                                         the server
351  *
352  * This routine does not do mutual authentication, nor does it return enough
353  * information to do encrypted connections.  But then, if we want to do
354  * encrypted connections, we'll have to redesign the whole RPC mechanism
355  * anyway.
356  *
357  * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
358  * are simply chopped off.      Hence, we are assuming that you've entered your
359  * server instances as
360  *              <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
361  * in the PGREALM (or local) database.  This is probably a bad assumption.
362  */
363 static int
364 pg_krb5_sendauth(const char *PQerrormsg, int sock,
365                                  struct sockaddr_in * laddr,
366                                  struct sockaddr_in * raddr,
367                                  const char *hostname)
368 {
369         char            servbuf[MAXHOSTNAMELEN + 1 +
370                                                                         sizeof(PG_KRB_SRVNAM)];
371         const char *hostp;
372         const char *realm;
373         krb5_error_code code;
374         krb5_principal client,
375                                 server;
376         krb5_ccache ccache;
377         krb5_error *error = (krb5_error *) NULL;
378
379         ccache = pg_krb5_init();        /* don't free this */
380
381         /*
382          * set up client -- this is easy, we can get it out of the ticket
383          * file.
384          */
385         if (code = krb5_cc_get_principal(ccache, &client))
386         {
387                 (void) sprintf(PQerrormsg,
388                                            "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code);
389                 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
390                 return STATUS_ERROR;
391         }
392
393         /*
394          * set up server -- canonicalize as described above
395          */
396         strcpy(servbuf, PG_KRB_SRVNAM);
397         *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
398         if (hostname || *hostname)
399                 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
400         else
401         {
402                 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
403                         strcpy(hostp, "localhost");
404         }
405         if (hostp = strchr(hostp, '.'))
406                 *hostp = '\0';
407         if (realm = getenv("PGREALM"))
408         {
409                 strcat(servbuf, "@");
410                 strcat(servbuf, realm);
411         }
412         if (code = krb5_parse_name(servbuf, &server))
413         {
414                 (void) sprintf(PQerrormsg,
415                 "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code);
416                 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
417                 krb5_free_principal(client);
418                 return STATUS_ERROR;
419         }
420
421         /*
422          * The only thing we want back from krb5_sendauth is an error status
423          * and any error messages.
424          */
425         if (code = krb5_sendauth((krb5_pointer) & sock,
426                                                          PG_KRB5_VERSION,
427                                                          client,
428                                                          server,
429                                                          (krb5_flags) 0,
430                                                          (krb5_checksum *) NULL,
431                                                          (krb5_creds *) NULL,
432                                                          ccache,
433                                                          (krb5_int32 *) NULL,
434                                                          (krb5_keyblock **) NULL,
435                                                          &error,
436                                                          (krb5_ap_rep_enc_part **) NULL))
437         {
438                 if ((code == KRB5_SENDAUTH_REJECTED) && error)
439                 {
440                         (void) sprintf(PQerrormsg,
441                                   "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
442                                                    error->text.length, error->text.data);
443                 }
444                 else
445                 {
446                         (void) sprintf(PQerrormsg,
447                                                    "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code);
448                         com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
449                 }
450         }
451         krb5_free_principal(client);
452         krb5_free_principal(server);
453         return code ? STATUS_ERROR : STATUS_OK;
454 }
455
456 #endif   /* KRB5 */
457
458 static int
459 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
460 {
461         /* Encrypt the password if needed. */
462
463         if (areq == AUTH_REQ_CRYPT)
464                 password = crypt(password, conn->salt);
465
466         return pqPacketSend(conn, password, strlen(password) + 1);
467 }
468
469 /*
470  * fe_sendauth -- client demux routine for outgoing authentication information
471  */
472 int
473 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
474                         const char *password, char *PQerrormsg)
475 {
476         switch (areq)
477         {
478                         case AUTH_REQ_OK:
479                         break;
480
481                 case AUTH_REQ_KRB4:
482 #ifdef KRB4
483                         if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
484                                                                  &conn->raddr.in,
485                                                                  hostname) != STATUS_OK)
486                         {
487                                 (void) sprintf(PQerrormsg,
488                                                         "fe_sendauth: krb4 authentication failed\n");
489                                 return STATUS_ERROR;
490                         }
491                         break;
492 #else
493                         (void) sprintf(PQerrormsg,
494                                          "fe_sendauth: krb4 authentication not supported\n");
495                         return STATUS_ERROR;
496 #endif
497
498                 case AUTH_REQ_KRB5:
499 #ifdef KRB5
500                         if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
501                                                                  &conn->raddr.in,
502                                                                  hostname) != STATUS_OK)
503                         {
504                                 (void) sprintf(PQerrormsg,
505                                                         "fe_sendauth: krb5 authentication failed\n");
506                                 return STATUS_ERROR;
507                         }
508                         break;
509 #else
510                         (void) sprintf(PQerrormsg,
511                                          "fe_sendauth: krb5 authentication not supported\n");
512                         return STATUS_ERROR;
513 #endif
514
515                 case AUTH_REQ_PASSWORD:
516                 case AUTH_REQ_CRYPT:
517                         if (password == NULL || *password == '\0')
518                         {
519                                 (void) sprintf(PQerrormsg,
520                                                            "fe_sendauth: no password supplied\n");
521                                 return STATUS_ERROR;
522                         }
523                         if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
524                         {
525                                 (void) sprintf(PQerrormsg,
526                                  "fe_sendauth: error sending password authentication\n");
527                                 return STATUS_ERROR;
528                         }
529
530                         break;
531
532                 default:
533                         (void) sprintf(PQerrormsg,
534                         "fe_sendauth: authentication type %u not supported\n", areq);
535                         return STATUS_ERROR;
536         }
537
538         return STATUS_OK;
539 }
540
541 /*
542  * fe_setauthsvc
543  * fe_getauthsvc
544  *
545  * Set/return the authentication service currently selected for use by the
546  * frontend. (You can only use one in the frontend, obviously.)
547  */
548 static int      pg_authsvc = -1;
549
550 void
551 fe_setauthsvc(const char *name, char *PQerrormsg)
552 {
553         int                     i;
554
555         for (i = 0; i < n_authsvcs; ++i)
556                 if (!strcmp(name, authsvcs[i].name))
557                 {
558                         pg_authsvc = i;
559                         break;
560                 }
561         if (i == n_authsvcs)
562         {
563                 (void) sprintf(PQerrormsg,
564                                            "fe_setauthsvc: invalid name: %s, ignoring...\n",
565                                            name);
566         }
567         return;
568 }
569
570 MsgType
571 fe_getauthsvc(char *PQerrormsg)
572 {
573         if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
574                 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
575         return authsvcs[pg_authsvc].msgtype;
576 }
577
578 /*
579  * fe_getauthname -- returns a pointer to dynamic space containing whatever
580  *                                       name the user has authenticated to the system
581  * if there is an error, return the error message in PQerrormsg
582  */
583 char *
584 fe_getauthname(char *PQerrormsg)
585 {
586         char       *name = (char *) NULL;
587         char       *authn = (char *) NULL;
588         MsgType         authsvc;
589
590         authsvc = fe_getauthsvc(PQerrormsg);
591         switch ((int) authsvc)
592         {
593 #ifdef KRB4
594                 case STARTUP_KRB4_MSG:
595                         name = pg_krb4_authname(PQerrormsg);
596                         break;
597 #endif
598 #ifdef KRB5
599                 case STARTUP_KRB5_MSG:
600                         name = pg_krb5_authname(PQerrormsg);
601                         break;
602 #endif
603                 case STARTUP_MSG:
604                         {
605 #ifdef WIN32
606                                 char            username[128];
607                                 DWORD           namesize = sizeof(username) - 1;
608
609                                 if (GetUserName(username, &namesize))
610                                         name = username;
611 #else
612                                 struct passwd *pw = getpwuid(geteuid());
613
614                                 if (pw)
615                                         name = pw->pw_name;
616 #endif
617                         }
618                         break;
619                 default:
620                         (void) sprintf(PQerrormsg,
621                                    "fe_getauthname: invalid authentication system: %d\n",
622                                                    authsvc);
623                         break;
624         }
625
626         if (name && (authn = (char *) malloc(strlen(name) + 1)))
627                 strcpy(authn, name);
628         return authn;
629 }