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