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