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