]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/fe-auth.c
1b8ac726ddb25639d90ca255a0242bd9156937dd
[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-2006, 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.116 2006/07/12 02:31:56 joe Exp $
14  *
15  *-------------------------------------------------------------------------
16  */
17
18 /*
19  * INTERFACE ROUTINES
20  *         frontend (client) routines:
21  *              pg_fe_sendauth                  send authentication information
22  *              pg_fe_getauthname               get user's name according to the client side
23  *                                                              of the authentication system
24  */
25
26 #include "postgres_fe.h"
27
28 #ifdef WIN32
29 #include "win32.h"
30 #else
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/types.h>
34 #include <sys/param.h>                  /* for MAXHOSTNAMELEN on most */
35 #include <sys/socket.h>
36 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
37 #include <sys/uio.h>
38 #include <sys/ucred.h>
39 #endif
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 #include "libpq-fe.h"
51 #include "libpq-int.h"
52 #include "fe-auth.h"
53 #include "libpq/md5.h"
54
55
56 #ifdef KRB5
57 /*
58  * MIT Kerberos authentication system - protocol version 5
59  */
60
61 #include <krb5.h>
62 /* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
63 #if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)
64 #include <com_err.h>
65 #endif
66
67 /*
68  * pg_an_to_ln -- return the local name corresponding to an authentication
69  *                                name
70  *
71  * XXX Assumes that the first aname component is the user name.  This is NOT
72  *         necessarily so, since an aname can actually be something out of your
73  *         worst X.400 nightmare, like
74  *                ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
75  *         Note that the MIT an_to_ln code does the same thing if you don't
76  *         provide an aname mapping database...it may be a better idea to use
77  *         krb5_an_to_ln, except that it punts if multiple components are found,
78  *         and we can't afford to punt.
79  *
80  * For WIN32, convert username to lowercase because the Win32 kerberos library
81  * generates tickets with the username as the user entered it instead of as
82  * it is entered in the directory.
83  */
84 static char *
85 pg_an_to_ln(char *aname)
86 {
87         char       *p;
88
89         if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
90                 *p = '\0';
91 #ifdef WIN32
92         for (p = aname; *p; p++)
93                 *p = pg_tolower(*p);
94 #endif
95
96         return aname;
97 }
98
99
100 /*
101  * Various krb5 state which is not connection specific, and a flag to
102  * indicate whether we have initialised it yet.
103  */
104 /* 
105 static int      pg_krb5_initialised;
106 static krb5_context pg_krb5_context;
107 static krb5_ccache pg_krb5_ccache;
108 static krb5_principal pg_krb5_client;
109 static char *pg_krb5_name;
110 */
111
112 struct krb5_info
113 {
114         int             pg_krb5_initialised;
115         krb5_context    pg_krb5_context;
116         krb5_ccache     pg_krb5_ccache;
117         krb5_principal  pg_krb5_client;
118         char            *pg_krb5_name;
119 };
120
121
122 static int
123 pg_krb5_init(char *PQerrormsg, struct krb5_info *info)
124 {
125         krb5_error_code retval;
126
127         if (info->pg_krb5_initialised)
128                 return STATUS_OK;
129
130         retval = krb5_init_context(&(info->pg_krb5_context));
131         if (retval)
132         {
133                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
134                                  "pg_krb5_init: krb5_init_context: %s\n",
135                                  error_message(retval));
136                 return STATUS_ERROR;
137         }
138
139         retval = krb5_cc_default(info->pg_krb5_context, &(info->pg_krb5_ccache));
140         if (retval)
141         {
142                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
143                                  "pg_krb5_init: krb5_cc_default: %s\n",
144                                  error_message(retval));
145                 krb5_free_context(info->pg_krb5_context);
146                 return STATUS_ERROR;
147         }
148
149         retval = krb5_cc_get_principal(info->pg_krb5_context, info->pg_krb5_ccache,
150                                                                    &(info->pg_krb5_client));
151         if (retval)
152         {
153                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
154                                  "pg_krb5_init: krb5_cc_get_principal: %s\n",
155                                  error_message(retval));
156                 krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache);
157                 krb5_free_context(info->pg_krb5_context);
158                 return STATUS_ERROR;
159         }
160
161         retval = krb5_unparse_name(info->pg_krb5_context, info->pg_krb5_client, &(info->pg_krb5_name));
162         if (retval)
163         {
164                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
165                                  "pg_krb5_init: krb5_unparse_name: %s\n",
166                                  error_message(retval));
167                 krb5_free_principal(info->pg_krb5_context, info->pg_krb5_client);
168                 krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache);
169                 krb5_free_context(info->pg_krb5_context);
170                 return STATUS_ERROR;
171         }
172
173         info->pg_krb5_name = pg_an_to_ln(info->pg_krb5_name);
174
175         info->pg_krb5_initialised = 1;
176         return STATUS_OK;
177 }
178
179 static void 
180 pg_krb5_destroy(struct krb5_info *info)
181 {
182         krb5_free_principal(info->pg_krb5_context, info->pg_krb5_client);
183         krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache);
184         krb5_free_context(info->pg_krb5_context);
185         free(info->pg_krb5_name);
186 }
187
188
189
190 /*
191  * pg_krb5_authname -- returns a copy of whatever name the user
192  *                                         has authenticated to the system, or NULL
193  */
194 static char *
195 pg_krb5_authname(char *PQerrormsg)
196 {
197         char *tmp_name;
198         struct krb5_info info;
199         info.pg_krb5_initialised = 0;
200
201         if (pg_krb5_init(PQerrormsg, &info) != STATUS_OK)
202                 return NULL;
203         tmp_name = strdup(info.pg_krb5_name);
204         pg_krb5_destroy(&info);
205
206         return tmp_name;
207 }
208
209
210 /*
211  * pg_krb5_sendauth -- client routine to send authentication information to
212  *                                         the server
213  */
214 static int
215 pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *servicename)
216 {
217         krb5_error_code retval;
218         int                     ret;
219         krb5_principal server;
220         krb5_auth_context auth_context = NULL;
221         krb5_error *err_ret = NULL;
222         struct krb5_info info;
223         info.pg_krb5_initialised = 0;
224
225         if (!hostname)
226         {
227                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
228                                  "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n");
229                 return STATUS_ERROR;
230         }
231
232         ret = pg_krb5_init(PQerrormsg, &info);
233         if (ret != STATUS_OK)
234                 return ret;
235
236         retval = krb5_sname_to_principal(info.pg_krb5_context, hostname, servicename,
237                                                                          KRB5_NT_SRV_HST, &server);
238         if (retval)
239         {
240                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
241                                  "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
242                                  error_message(retval));
243                 pg_krb5_destroy(&info);
244                 return STATUS_ERROR;
245         }
246
247         /*
248          * libpq uses a non-blocking socket. But kerberos needs a blocking socket,
249          * and we have to block somehow to do mutual authentication anyway. So we
250          * temporarily make it blocking.
251          */
252         if (!pg_set_block(sock))
253         {
254                 char            sebuf[256];
255
256                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
257                                  libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf)));
258                 krb5_free_principal(info.pg_krb5_context, server);
259                 pg_krb5_destroy(&info);
260                 return STATUS_ERROR;
261         }
262
263         retval = krb5_sendauth(info.pg_krb5_context, &auth_context,
264                                                    (krb5_pointer) & sock, (char *) servicename,
265                                                    info.pg_krb5_client, server,
266                                                    AP_OPTS_MUTUAL_REQUIRED,
267                                                    NULL, 0,             /* no creds, use ccache instead */
268                                                    info.pg_krb5_ccache, &err_ret, NULL, NULL);
269         if (retval)
270         {
271                 if (retval == KRB5_SENDAUTH_REJECTED && err_ret)
272                 {
273 #if defined(HAVE_KRB5_ERROR_TEXT_DATA)
274                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
275                                   libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
276                                          (int) err_ret->text.length, err_ret->text.data);
277 #elif defined(HAVE_KRB5_ERROR_E_DATA)
278                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
279                                   libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
280                                          (int) err_ret->e_data->length,
281                                          (const char *) err_ret->e_data->data);
282 #else
283 #error "bogus configuration"
284 #endif
285                 }
286                 else
287                 {
288                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
289                                          "krb5_sendauth: %s\n", error_message(retval));
290                 }
291
292                 if (err_ret)
293                         krb5_free_error(info.pg_krb5_context, err_ret);
294
295                 ret = STATUS_ERROR;
296         }
297
298         krb5_free_principal(info.pg_krb5_context, server);
299
300         if (!pg_set_noblock(sock))
301         {
302                 char            sebuf[256];
303
304                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
305                 libpq_gettext("could not restore non-blocking mode on socket: %s\n"),
306                                  pqStrerror(errno, sebuf, sizeof(sebuf)));
307                 ret = STATUS_ERROR;
308         }
309         pg_krb5_destroy(&info);
310
311         return ret;
312 }
313 #endif   /* KRB5 */
314
315
316 /*
317  * Respond to AUTH_REQ_SCM_CREDS challenge.
318  *
319  * Note: current backends will not use this challenge if HAVE_GETPEEREID
320  * or SO_PEERCRED is defined, but pre-7.4 backends might, so compile the
321  * code anyway.
322  */
323 static int
324 pg_local_sendauth(char *PQerrormsg, PGconn *conn)
325 {
326 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
327         (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
328         char            buf;
329         struct iovec iov;
330         struct msghdr msg;
331
332 #ifdef HAVE_STRUCT_CMSGCRED
333         /* Prevent padding */
334         char            cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
335
336         /* Point to start of first structure */
337         struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
338 #endif
339
340         /*
341          * The backend doesn't care what we send here, but it wants exactly one
342          * character to force recvmsg() to block and wait for us.
343          */
344         buf = '\0';
345         iov.iov_base = &buf;
346         iov.iov_len = 1;
347
348         memset(&msg, 0, sizeof(msg));
349         msg.msg_iov = &iov;
350         msg.msg_iovlen = 1;
351
352 #ifdef HAVE_STRUCT_CMSGCRED
353         /* Create control header, FreeBSD */
354         msg.msg_control = cmsg;
355         msg.msg_controllen = sizeof(cmsgmem);
356         memset(cmsg, 0, sizeof(cmsgmem));
357         cmsg->cmsg_len = sizeof(cmsgmem);
358         cmsg->cmsg_level = SOL_SOCKET;
359         cmsg->cmsg_type = SCM_CREDS;
360 #endif
361
362         if (sendmsg(conn->sock, &msg, 0) == -1)
363         {
364                 char            sebuf[256];
365
366                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
367                                  "pg_local_sendauth: sendmsg: %s\n",
368                                  pqStrerror(errno, sebuf, sizeof(sebuf)));
369                 return STATUS_ERROR;
370         }
371         return STATUS_OK;
372 #else
373         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
374                          libpq_gettext("SCM_CRED authentication method not supported\n"));
375         return STATUS_ERROR;
376 #endif
377 }
378
379 static int
380 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
381 {
382         int                     ret;
383         char       *crypt_pwd;
384
385         /* Encrypt the password if needed. */
386
387         switch (areq)
388         {
389                 case AUTH_REQ_MD5:
390                         {
391                                 char       *crypt_pwd2;
392
393                                 /* Allocate enough space for two MD5 hashes */
394                                 crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
395                                 if (!crypt_pwd)
396                                 {
397                                         fprintf(stderr, libpq_gettext("out of memory\n"));
398                                         return STATUS_ERROR;
399                                 }
400
401                                 crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
402                                 if (!pg_md5_encrypt(password, conn->pguser,
403                                                                         strlen(conn->pguser), crypt_pwd2))
404                                 {
405                                         free(crypt_pwd);
406                                         return STATUS_ERROR;
407                                 }
408                                 if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt,
409                                                                         sizeof(conn->md5Salt), crypt_pwd))
410                                 {
411                                         free(crypt_pwd);
412                                         return STATUS_ERROR;
413                                 }
414                                 break;
415                         }
416                 case AUTH_REQ_CRYPT:
417                         {
418                                 char            salt[3];
419
420                                 StrNCpy(salt, conn->cryptSalt, 3);
421                                 crypt_pwd = crypt(password, salt);
422                                 break;
423                         }
424                 case AUTH_REQ_PASSWORD:
425                         /* discard const so we can assign it */
426                         crypt_pwd = (char *) password;
427                         break;
428                 default:
429                         return STATUS_ERROR;
430         }
431         /* Packet has a message type as of protocol 3.0 */
432         if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
433                 ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
434         else
435                 ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
436         if (areq == AUTH_REQ_MD5)
437                 free(crypt_pwd);
438         return ret;
439 }
440
441 /*
442  * pg_fe_sendauth
443  *              client demux routine for outgoing authentication information
444  */
445 int
446 pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
447                            const char *password, char *PQerrormsg)
448 {
449 #ifndef KRB5
450         (void) hostname;                        /* not used */
451 #endif
452
453         switch (areq)
454         {
455                 case AUTH_REQ_OK:
456                         break;
457
458                 case AUTH_REQ_KRB4:
459                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
460                                  libpq_gettext("Kerberos 4 authentication not supported\n"));
461                         return STATUS_ERROR;
462
463                 case AUTH_REQ_KRB5:
464 #ifdef KRB5
465                         pglock_thread();
466                         if (pg_krb5_sendauth(PQerrormsg, conn->sock,
467                                                                  hostname, conn->krbsrvname) != STATUS_OK)
468                         {
469                                 /* PQerrormsg already filled in */
470                                 pgunlock_thread();
471                                 return STATUS_ERROR;
472                         }
473                         pgunlock_thread();
474                         break;
475 #else
476                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
477                                  libpq_gettext("Kerberos 5 authentication not supported\n"));
478                         return STATUS_ERROR;
479 #endif
480
481                 case AUTH_REQ_MD5:
482                 case AUTH_REQ_CRYPT:
483                 case AUTH_REQ_PASSWORD:
484                         if (password == NULL || *password == '\0')
485                         {
486                                 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
487                                                                 PQnoPasswordSupplied);
488                                 return STATUS_ERROR;
489                         }
490                         if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
491                         {
492                                 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
493                                          "fe_sendauth: error sending password authentication\n");
494                                 return STATUS_ERROR;
495                         }
496                         break;
497
498                 case AUTH_REQ_SCM_CREDS:
499                         if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
500                                 return STATUS_ERROR;
501                         break;
502
503                 default:
504                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
505                         libpq_gettext("authentication method %u not supported\n"), areq);
506                         return STATUS_ERROR;
507         }
508
509         return STATUS_OK;
510 }
511
512
513 /*
514  * pg_fe_getauthname -- returns a pointer to dynamic space containing whatever
515  *                                       name the user has authenticated to the system
516  *
517  * if there is an error, return NULL with an error message in PQerrormsg
518  */
519 char *
520 pg_fe_getauthname(char *PQerrormsg)
521 {
522 #ifdef KRB5
523         char       *krb5_name = NULL;
524 #endif
525         const char *name = NULL;
526         char       *authn;
527
528 #ifdef WIN32
529         char            username[128];
530         DWORD           namesize = sizeof(username) - 1;
531 #else
532         char            pwdbuf[BUFSIZ];
533         struct passwd pwdstr;
534         struct passwd *pw = NULL;
535 #endif
536
537         /*
538          * pglock_thread() really only needs to be called around
539          * pg_krb5_authname(), but some users are using configure
540          * --enable-thread-safety-force, so we might as well do the locking within
541          * our library to protect pqGetpwuid(). In fact, application developers
542          * can use getpwuid() in their application if they use the locking call we
543          * provide, or install their own locking function using
544          * PQregisterThreadLock().
545          */
546         pglock_thread();
547
548 #ifdef KRB5
549         /* pg_krb5_authname gives us a strdup'd value that we need
550          * to free later, however, we don't want to free 'name' directly
551          * in case it's *not* a Kerberos login and we fall through to
552          * name = pw->pw_name; */
553         krb5_name = pg_krb5_authname(PQerrormsg);
554         name = krb5_name;
555 #endif
556
557         if (!name)
558         {
559 #ifdef WIN32
560                 if (GetUserName(username, &namesize))
561                         name = username;
562 #else
563                 if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0)
564                         name = pw->pw_name;
565 #endif
566         }
567
568         authn = name ? strdup(name) : NULL;
569
570 #ifdef KRB5
571         /* Free the strdup'd string from pg_krb5_authname, if we got one */
572         if (krb5_name)
573                 free(krb5_name);
574 #endif
575
576         pgunlock_thread();
577
578         return authn;
579 }
580
581
582 /*
583  * PQencryptPassword -- exported routine to encrypt a password
584  *
585  * This is intended to be used by client applications that wish to send
586  * commands like ALTER USER joe PASSWORD 'pwd'.  The password need not
587  * be sent in cleartext if it is encrypted on the client side.  This is
588  * good because it ensures the cleartext password won't end up in logs,
589  * pg_stat displays, etc.  We export the function so that clients won't
590  * be dependent on low-level details like whether the enceyption is MD5
591  * or something else.
592  *
593  * Arguments are the cleartext password, and the SQL name of the user it
594  * is for.
595  *
596  * Return value is a malloc'd string, or NULL if out-of-memory.  The client
597  * may assume the string doesn't contain any special characters that would
598  * require escaping.
599  */
600 char *
601 PQencryptPassword(const char *passwd, const char *user)
602 {
603         char       *crypt_pwd;
604
605         crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
606         if (!crypt_pwd)
607                 return NULL;
608
609         if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))
610         {
611                 free(crypt_pwd);
612                 return NULL;
613         }
614
615         return crypt_pwd;
616 }