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