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