]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/fe-auth.c
Remove support for Kerberos V4. It seems no one is using this, it has
[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-2005, 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  *        $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.102 2005/06/27 02:04:26 neilc 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 #ifdef WIN32
34 #include "win32.h"
35 #else
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/param.h>                  /* for MAXHOSTNAMELEN on most */
41 #include <sys/socket.h>
42 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
43 #include <sys/uio.h>
44 #include <sys/ucred.h>
45 #endif
46 #ifndef  MAXHOSTNAMELEN
47 #include <netdb.h>                              /* for MAXHOSTNAMELEN on some */
48 #endif
49 #include <pwd.h>
50 #endif
51
52 #ifdef HAVE_CRYPT_H
53 #include <crypt.h>
54 #endif
55
56 #include "libpq-fe.h"
57 #include "libpq-int.h"
58 #include "fe-auth.h"
59 #include "libpq/crypt.h"
60
61
62 /*
63  * common definitions for generic fe/be routines
64  */
65
66 #define STARTUP_MSG             7               /* Initialise a connection */
67 #define STARTUP_KRB4_MSG        10      /* krb4 session follows. Not supported any more. */
68 #define STARTUP_KRB5_MSG        11      /* krb5 session follows */
69 #define STARTUP_PASSWORD_MSG    14              /* Password follows */
70
71 struct authsvc
72 {
73         const char *name;                       /* service nickname (for command line) */
74         MsgType         msgtype;                /* startup packet header type */
75         int                     allowed;                /* initially allowed (before command line
76                                                                  * option parsing)? */
77 };
78
79 /*
80  * Command-line parsing routines use this structure to map nicknames
81  * onto service types (and the startup packets to use with them).
82  *
83  * Programs receiving an authentication request use this structure to
84  * decide which authentication service types are currently permitted.
85  * By default, all authentication systems compiled into the system are
86  * allowed.  Unauthenticated connections are disallowed unless there
87  * isn't any authentication system.
88  */
89 static const struct authsvc authsvcs[] = {
90 #ifdef KRB5
91         {"krb5", STARTUP_KRB5_MSG, 1},
92         {"kerberos", STARTUP_KRB5_MSG, 1},
93 #endif   /* KRB5 */
94         {UNAUTHNAME, STARTUP_MSG,
95 #ifdef KRB5
96                 0
97 #else                                                   /* !KRB5 */
98                 1
99 #endif   /* !KRB5 */
100         },
101         {"password", STARTUP_PASSWORD_MSG, 0}
102 };
103
104 static const int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
105
106 #ifdef KRB5
107 /*
108  * MIT Kerberos authentication system - protocol version 5
109  */
110
111 #include <krb5.h>
112 /* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
113 #if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)
114 #include <com_err.h>
115 #endif
116
117 /*
118  * pg_an_to_ln -- return the local name corresponding to an authentication
119  *                                name
120  *
121  * XXX Assumes that the first aname component is the user name.  This is NOT
122  *         necessarily so, since an aname can actually be something out of your
123  *         worst X.400 nightmare, like
124  *                ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
125  *         Note that the MIT an_to_ln code does the same thing if you don't
126  *         provide an aname mapping database...it may be a better idea to use
127  *         krb5_an_to_ln, except that it punts if multiple components are found,
128  *         and we can't afford to punt.
129  *
130  * For WIN32, convert username to lowercase because the Win32 kerberos library
131  * generates tickets with the username as the user entered it instead of as
132  * it is entered in the directory.
133  */
134 static char *
135 pg_an_to_ln(char *aname)
136 {
137         char       *p;
138
139         if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
140                 *p = '\0';
141 #ifdef WIN32
142         for (p = aname; *p ; p++)
143                 *p = pg_tolower(*p);
144 #endif
145
146         return aname;
147 }
148
149
150 /*
151  * Various krb5 state which is not connection specific, and a flag to
152  * indicate whether we have initialised it yet.
153  */
154 static int      pg_krb5_initialised;
155 static krb5_context pg_krb5_context;
156 static krb5_ccache pg_krb5_ccache;
157 static krb5_principal pg_krb5_client;
158 static char *pg_krb5_name;
159
160
161 static int
162 pg_krb5_init(char *PQerrormsg)
163 {
164         krb5_error_code retval;
165
166         if (pg_krb5_initialised)
167                 return STATUS_OK;
168
169         retval = krb5_init_context(&pg_krb5_context);
170         if (retval)
171         {
172                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
173                                  "pg_krb5_init: krb5_init_context: %s\n",
174                                  error_message(retval));
175                 return STATUS_ERROR;
176         }
177
178         retval = krb5_cc_default(pg_krb5_context, &pg_krb5_ccache);
179         if (retval)
180         {
181                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
182                                  "pg_krb5_init: krb5_cc_default: %s\n",
183                                  error_message(retval));
184                 krb5_free_context(pg_krb5_context);
185                 return STATUS_ERROR;
186         }
187
188         retval = krb5_cc_get_principal(pg_krb5_context, pg_krb5_ccache,
189                                                                    &pg_krb5_client);
190         if (retval)
191         {
192                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
193                                  "pg_krb5_init: krb5_cc_get_principal: %s\n",
194                                  error_message(retval));
195                 krb5_cc_close(pg_krb5_context, pg_krb5_ccache);
196                 krb5_free_context(pg_krb5_context);
197                 return STATUS_ERROR;
198         }
199
200         retval = krb5_unparse_name(pg_krb5_context, pg_krb5_client, &pg_krb5_name);
201         if (retval)
202         {
203                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
204                                  "pg_krb5_init: krb5_unparse_name: %s\n",
205                                  error_message(retval));
206                 krb5_free_principal(pg_krb5_context, pg_krb5_client);
207                 krb5_cc_close(pg_krb5_context, pg_krb5_ccache);
208                 krb5_free_context(pg_krb5_context);
209                 return STATUS_ERROR;
210         }
211
212         pg_krb5_name = pg_an_to_ln(pg_krb5_name);
213
214         pg_krb5_initialised = 1;
215         return STATUS_OK;
216 }
217
218
219 /*
220  * pg_krb5_authname -- returns a pointer to static space containing whatever
221  *                                         name the user has authenticated to the system
222   */
223 static const char *
224 pg_krb5_authname(char *PQerrormsg)
225 {
226         if (pg_krb5_init(PQerrormsg) != STATUS_OK)
227                 return NULL;
228
229         return pg_krb5_name;
230 }
231
232
233 /*
234  * pg_krb5_sendauth -- client routine to send authentication information to
235  *                                         the server
236  */
237 static int
238 pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *servicename)
239 {
240         krb5_error_code retval;
241         int                     ret;
242         krb5_principal server;
243         krb5_auth_context auth_context = NULL;
244         krb5_error *err_ret = NULL;
245
246         if (!hostname)
247         {
248                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
249                                  "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n");
250                 return STATUS_ERROR;
251         }
252
253         ret = pg_krb5_init(PQerrormsg);
254         if (ret != STATUS_OK)
255                 return ret;
256
257         retval = krb5_sname_to_principal(pg_krb5_context, hostname, servicename,
258                                                                          KRB5_NT_SRV_HST, &server);
259         if (retval)
260         {
261                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
262                                  "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
263                                  error_message(retval));
264                 return STATUS_ERROR;
265         }
266
267         /*
268          * libpq uses a non-blocking socket. But kerberos needs a blocking
269          * socket, and we have to block somehow to do mutual authentication
270          * anyway. So we temporarily make it blocking.
271          */
272         if (!pg_set_block(sock))
273         {
274                 char            sebuf[256];
275
276                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
277                                  libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf)));
278                 krb5_free_principal(pg_krb5_context, server);
279                 return STATUS_ERROR;
280         }
281
282         retval = krb5_sendauth(pg_krb5_context, &auth_context,
283                                                    (krb5_pointer) & sock, "postgres",
284                                                    pg_krb5_client, server,
285                                                    AP_OPTS_MUTUAL_REQUIRED,
286                                                    NULL, 0,             /* no creds, use ccache instead */
287                                                    pg_krb5_ccache, &err_ret, NULL, NULL);
288         if (retval)
289         {
290                 if (retval == KRB5_SENDAUTH_REJECTED && err_ret)
291                 {
292 #if defined(HAVE_KRB5_ERROR_TEXT_DATA)
293                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
294                           libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
295                                          (int) err_ret->text.length, err_ret->text.data);
296 #elif defined(HAVE_KRB5_ERROR_E_DATA)
297                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
298                           libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
299                                          (int) err_ret->e_data->length,
300                                          (const char *) err_ret->e_data->data);
301 #else
302 #error "bogus configuration"
303 #endif
304                 }
305                 else
306                 {
307                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
308                                          "krb5_sendauth: %s\n", error_message(retval));
309                 }
310
311                 if (err_ret)
312                         krb5_free_error(pg_krb5_context, err_ret);
313
314                 ret = STATUS_ERROR;
315         }
316
317         krb5_free_principal(pg_krb5_context, server);
318
319         if (!pg_set_noblock(sock))
320         {
321                 char            sebuf[256];
322
323                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
324                                  libpq_gettext("could not restore non-blocking mode on socket: %s\n"),
325                                  pqStrerror(errno, sebuf, sizeof(sebuf)));
326                 ret = STATUS_ERROR;
327         }
328
329         return ret;
330 }
331 #endif   /* KRB5 */
332
333 /*
334  * Respond to AUTH_REQ_SCM_CREDS challenge.
335  *
336  * Note: current backends will not use this challenge if HAVE_GETPEEREID
337  * or SO_PEERCRED is defined, but pre-7.4 backends might, so compile the
338  * code anyway.
339  */
340 static int
341 pg_local_sendauth(char *PQerrormsg, PGconn *conn)
342 {
343 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
344         (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
345         char            buf;
346         struct iovec iov;
347         struct msghdr msg;
348
349 #ifdef HAVE_STRUCT_CMSGCRED
350         /* Prevent padding */
351         char            cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
352
353         /* Point to start of first structure */
354         struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
355 #endif
356
357         /*
358          * The backend doesn't care what we send here, but it wants exactly
359          * one character to force recvmsg() to block and wait for us.
360          */
361         buf = '\0';
362         iov.iov_base = &buf;
363         iov.iov_len = 1;
364
365         memset(&msg, 0, sizeof(msg));
366         msg.msg_iov = &iov;
367         msg.msg_iovlen = 1;
368
369 #ifdef HAVE_STRUCT_CMSGCRED
370         /* Create control header, FreeBSD */
371         msg.msg_control = cmsg;
372         msg.msg_controllen = sizeof(cmsgmem);
373         memset(cmsg, 0, sizeof(cmsgmem));
374         cmsg->cmsg_len = sizeof(cmsgmem);
375         cmsg->cmsg_level = SOL_SOCKET;
376         cmsg->cmsg_type = SCM_CREDS;
377 #endif
378
379         if (sendmsg(conn->sock, &msg, 0) == -1)
380         {
381                 char            sebuf[256];
382
383                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
384                                  "pg_local_sendauth: sendmsg: %s\n",
385                                  pqStrerror(errno, sebuf, sizeof(sebuf)));
386                 return STATUS_ERROR;
387         }
388         return STATUS_OK;
389 #else
390         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
391                 libpq_gettext("SCM_CRED authentication method not supported\n"));
392         return STATUS_ERROR;
393 #endif
394 }
395
396 static int
397 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
398 {
399         int                     ret;
400         char       *crypt_pwd;
401
402         /* Encrypt the password if needed. */
403
404         switch (areq)
405         {
406                 case AUTH_REQ_MD5:
407                         {
408                                 char       *crypt_pwd2;
409
410                                 if (!(crypt_pwd = malloc(MD5_PASSWD_LEN + 1)) ||
411                                         !(crypt_pwd2 = malloc(MD5_PASSWD_LEN + 1)))
412                                 {
413                                         fprintf(stderr, libpq_gettext("out of memory\n"));
414                                         return STATUS_ERROR;
415                                 }
416                                 if (!EncryptMD5(password, conn->pguser,
417                                                                 strlen(conn->pguser), crypt_pwd2))
418                                 {
419                                         free(crypt_pwd);
420                                         free(crypt_pwd2);
421                                         return STATUS_ERROR;
422                                 }
423                                 if (!EncryptMD5(crypt_pwd2 + strlen("md5"), conn->md5Salt,
424                                                                 sizeof(conn->md5Salt), crypt_pwd))
425                                 {
426                                         free(crypt_pwd);
427                                         free(crypt_pwd2);
428                                         return STATUS_ERROR;
429                                 }
430                                 free(crypt_pwd2);
431                                 break;
432                         }
433                 case AUTH_REQ_CRYPT:
434                         {
435                                 char            salt[3];
436
437                                 StrNCpy(salt, conn->cryptSalt, 3);
438                                 crypt_pwd = crypt(password, salt);
439                                 break;
440                         }
441                 case AUTH_REQ_PASSWORD:
442                         /* discard const so we can assign it */
443                         crypt_pwd = (char *) password;
444                         break;
445                 default:
446                         return STATUS_ERROR;
447         }
448         /* Packet has a message type as of protocol 3.0 */
449         if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
450                 ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
451         else
452                 ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
453         if (areq == AUTH_REQ_MD5)
454                 free(crypt_pwd);
455         return ret;
456 }
457
458 /*
459  * fe_sendauth -- client demux routine for outgoing authentication information
460  */
461 int
462 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
463                         const char *password, char *PQerrormsg)
464 {
465 #ifndef KRB5
466         (void) hostname;                        /* not used */
467 #endif
468
469         switch (areq)
470         {
471                 case AUTH_REQ_OK:
472                         break;
473
474                 case AUTH_REQ_KRB4:
475                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
476                          libpq_gettext("Kerberos 4 authentication not supported\n"));
477                         return STATUS_ERROR;
478
479                 case AUTH_REQ_KRB5:
480 #ifdef KRB5
481                         pglock_thread();
482                         if (pg_krb5_sendauth(PQerrormsg, conn->sock,
483                                                                  hostname, conn->krbsrvname) != STATUS_OK)
484                         {
485                                 /* PQerrormsg already filled in */
486                                 pgunlock_thread();
487                                 return STATUS_ERROR;
488                         }
489                         pgunlock_thread();
490                         break;
491 #else
492                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
493                          libpq_gettext("Kerberos 5 authentication not supported\n"));
494                         return STATUS_ERROR;
495 #endif
496
497                 case AUTH_REQ_MD5:
498                 case AUTH_REQ_CRYPT:
499                 case AUTH_REQ_PASSWORD:
500                         if (password == NULL || *password == '\0')
501                         {
502                                 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
503                                                                 PQnoPasswordSupplied);
504                                 return STATUS_ERROR;
505                         }
506                         if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
507                         {
508                                 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
509                                  "fe_sendauth: error sending password authentication\n");
510                                 return STATUS_ERROR;
511                         }
512                         break;
513
514                 case AUTH_REQ_SCM_CREDS:
515                         if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
516                                 return STATUS_ERROR;
517                         break;
518
519                 default:
520                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
521                                          libpq_gettext("authentication method %u not supported\n"), areq);
522                         return STATUS_ERROR;
523         }
524
525         return STATUS_OK;
526 }
527
528 /*
529  * fe_setauthsvc
530  * fe_getauthsvc
531  *
532  * Set/return the authentication service currently selected for use by the
533  * frontend. (You can only use one in the frontend, obviously.)
534  *
535  * NB: This is not thread-safe if different threads try to select different
536  * authentication services!  It's OK for fe_getauthsvc to select the default,
537  * since that will be the same for all threads, but direct application use
538  * of fe_setauthsvc is not thread-safe.  However, use of fe_setauthsvc is
539  * deprecated anyway...
540  */
541
542 static int      pg_authsvc = -1;
543
544 void
545 fe_setauthsvc(const char *name, char *PQerrormsg)
546 {
547         int                     i;
548
549         for (i = 0; i < n_authsvcs; ++i)
550                 if (strcmp(name, authsvcs[i].name) == 0)
551                 {
552                         pg_authsvc = i;
553                         break;
554                 }
555         if (i == n_authsvcs)
556         {
557                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
558                                  libpq_gettext("invalid authentication service name \"%s\", ignored\n"),
559                                  name);
560         }
561         return;
562 }
563
564 MsgType
565 fe_getauthsvc(char *PQerrormsg)
566 {
567         if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
568         {
569                 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
570                 if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
571                 {
572                         /* Can only get here if DEFAULT_CLIENT_AUTHSVC is misdefined */
573                         return 0;
574                 }
575         }
576         return authsvcs[pg_authsvc].msgtype;
577 }
578
579 /*
580  * fe_getauthname -- returns a pointer to dynamic space containing whatever
581  *                                       name the user has authenticated to the system
582  * if there is an error, return the error message in PQerrormsg
583  */
584 char *
585 fe_getauthname(char *PQerrormsg)
586 {
587         const char *name = NULL;
588         char       *authn;
589         MsgType         authsvc;
590 #ifdef WIN32
591         char            username[128];
592         DWORD           namesize = sizeof(username) - 1;
593 #else
594         char            pwdbuf[BUFSIZ];
595         struct passwd pwdstr;
596         struct passwd *pw = NULL;
597 #endif
598
599         authsvc = fe_getauthsvc(PQerrormsg);
600
601         /* this just guards against broken DEFAULT_CLIENT_AUTHSVC, see above */
602         if (authsvc == 0)
603                 return NULL;                    /* leave original error message in place */
604
605         pglock_thread();
606
607 #ifdef KRB5
608         if (authsvc == STARTUP_KRB5_MSG)
609                 name = pg_krb5_authname(PQerrormsg);
610 #endif
611
612         if (authsvc == STARTUP_MSG
613                 || (authsvc == STARTUP_KRB5_MSG && !name))
614         {
615 #ifdef WIN32
616                 if (GetUserName(username, &namesize))
617                         name = username;
618 #else
619                 if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0)
620                         name = pw->pw_name;
621 #endif
622         }
623
624         if (authsvc != STARTUP_MSG && authsvc != STARTUP_KRB5_MSG)
625                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
626                                  libpq_gettext("fe_getauthname: invalid authentication system: %d\n"),
627                                  authsvc);
628
629         authn = name ? strdup(name) : NULL;
630
631         pgunlock_thread();
632
633         return authn;
634 }