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