]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/fe-auth.c
Rename pg_make_encrypted_password to PQencryptPassword.
[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.110 2005/12/26 14:58:05 petere 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/crypt.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 static int      pg_krb5_initialised;
105 static krb5_context pg_krb5_context;
106 static krb5_ccache pg_krb5_ccache;
107 static krb5_principal pg_krb5_client;
108 static char *pg_krb5_name;
109
110
111 static int
112 pg_krb5_init(char *PQerrormsg)
113 {
114         krb5_error_code retval;
115
116         if (pg_krb5_initialised)
117                 return STATUS_OK;
118
119         retval = krb5_init_context(&pg_krb5_context);
120         if (retval)
121         {
122                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
123                                  "pg_krb5_init: krb5_init_context: %s\n",
124                                  error_message(retval));
125                 return STATUS_ERROR;
126         }
127
128         retval = krb5_cc_default(pg_krb5_context, &pg_krb5_ccache);
129         if (retval)
130         {
131                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
132                                  "pg_krb5_init: krb5_cc_default: %s\n",
133                                  error_message(retval));
134                 krb5_free_context(pg_krb5_context);
135                 return STATUS_ERROR;
136         }
137
138         retval = krb5_cc_get_principal(pg_krb5_context, pg_krb5_ccache,
139                                                                    &pg_krb5_client);
140         if (retval)
141         {
142                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
143                                  "pg_krb5_init: krb5_cc_get_principal: %s\n",
144                                  error_message(retval));
145                 krb5_cc_close(pg_krb5_context, pg_krb5_ccache);
146                 krb5_free_context(pg_krb5_context);
147                 return STATUS_ERROR;
148         }
149
150         retval = krb5_unparse_name(pg_krb5_context, pg_krb5_client, &pg_krb5_name);
151         if (retval)
152         {
153                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
154                                  "pg_krb5_init: krb5_unparse_name: %s\n",
155                                  error_message(retval));
156                 krb5_free_principal(pg_krb5_context, pg_krb5_client);
157                 krb5_cc_close(pg_krb5_context, pg_krb5_ccache);
158                 krb5_free_context(pg_krb5_context);
159                 return STATUS_ERROR;
160         }
161
162         pg_krb5_name = pg_an_to_ln(pg_krb5_name);
163
164         pg_krb5_initialised = 1;
165         return STATUS_OK;
166 }
167
168
169 /*
170  * pg_krb5_authname -- returns a pointer to static space containing whatever
171  *                                         name the user has authenticated to the system
172   */
173 static const char *
174 pg_krb5_authname(char *PQerrormsg)
175 {
176         if (pg_krb5_init(PQerrormsg) != STATUS_OK)
177                 return NULL;
178
179         return pg_krb5_name;
180 }
181
182
183 /*
184  * pg_krb5_sendauth -- client routine to send authentication information to
185  *                                         the server
186  */
187 static int
188 pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *servicename)
189 {
190         krb5_error_code retval;
191         int                     ret;
192         krb5_principal server;
193         krb5_auth_context auth_context = NULL;
194         krb5_error *err_ret = NULL;
195
196         if (!hostname)
197         {
198                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
199                                  "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n");
200                 return STATUS_ERROR;
201         }
202
203         ret = pg_krb5_init(PQerrormsg);
204         if (ret != STATUS_OK)
205                 return ret;
206
207         retval = krb5_sname_to_principal(pg_krb5_context, hostname, servicename,
208                                                                          KRB5_NT_SRV_HST, &server);
209         if (retval)
210         {
211                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
212                                  "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
213                                  error_message(retval));
214                 return STATUS_ERROR;
215         }
216
217         /*
218          * libpq uses a non-blocking socket. But kerberos needs a blocking socket,
219          * and we have to block somehow to do mutual authentication anyway. So we
220          * temporarily make it blocking.
221          */
222         if (!pg_set_block(sock))
223         {
224                 char            sebuf[256];
225
226                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
227                                  libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf)));
228                 krb5_free_principal(pg_krb5_context, server);
229                 return STATUS_ERROR;
230         }
231
232         retval = krb5_sendauth(pg_krb5_context, &auth_context,
233                                                    (krb5_pointer) & sock, (char *) servicename,
234                                                    pg_krb5_client, server,
235                                                    AP_OPTS_MUTUAL_REQUIRED,
236                                                    NULL, 0,             /* no creds, use ccache instead */
237                                                    pg_krb5_ccache, &err_ret, NULL, NULL);
238         if (retval)
239         {
240                 if (retval == KRB5_SENDAUTH_REJECTED && err_ret)
241                 {
242 #if defined(HAVE_KRB5_ERROR_TEXT_DATA)
243                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
244                                   libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
245                                          (int) err_ret->text.length, err_ret->text.data);
246 #elif defined(HAVE_KRB5_ERROR_E_DATA)
247                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
248                                   libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
249                                          (int) err_ret->e_data->length,
250                                          (const char *) err_ret->e_data->data);
251 #else
252 #error "bogus configuration"
253 #endif
254                 }
255                 else
256                 {
257                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
258                                          "krb5_sendauth: %s\n", error_message(retval));
259                 }
260
261                 if (err_ret)
262                         krb5_free_error(pg_krb5_context, err_ret);
263
264                 ret = STATUS_ERROR;
265         }
266
267         krb5_free_principal(pg_krb5_context, server);
268
269         if (!pg_set_noblock(sock))
270         {
271                 char            sebuf[256];
272
273                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
274                 libpq_gettext("could not restore non-blocking mode on socket: %s\n"),
275                                  pqStrerror(errno, sebuf, sizeof(sebuf)));
276                 ret = STATUS_ERROR;
277         }
278
279         return ret;
280 }
281 #endif   /* KRB5 */
282
283
284 /*
285  * Respond to AUTH_REQ_SCM_CREDS challenge.
286  *
287  * Note: current backends will not use this challenge if HAVE_GETPEEREID
288  * or SO_PEERCRED is defined, but pre-7.4 backends might, so compile the
289  * code anyway.
290  */
291 static int
292 pg_local_sendauth(char *PQerrormsg, PGconn *conn)
293 {
294 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
295         (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
296         char            buf;
297         struct iovec iov;
298         struct msghdr msg;
299
300 #ifdef HAVE_STRUCT_CMSGCRED
301         /* Prevent padding */
302         char            cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
303
304         /* Point to start of first structure */
305         struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
306 #endif
307
308         /*
309          * The backend doesn't care what we send here, but it wants exactly one
310          * character to force recvmsg() to block and wait for us.
311          */
312         buf = '\0';
313         iov.iov_base = &buf;
314         iov.iov_len = 1;
315
316         memset(&msg, 0, sizeof(msg));
317         msg.msg_iov = &iov;
318         msg.msg_iovlen = 1;
319
320 #ifdef HAVE_STRUCT_CMSGCRED
321         /* Create control header, FreeBSD */
322         msg.msg_control = cmsg;
323         msg.msg_controllen = sizeof(cmsgmem);
324         memset(cmsg, 0, sizeof(cmsgmem));
325         cmsg->cmsg_len = sizeof(cmsgmem);
326         cmsg->cmsg_level = SOL_SOCKET;
327         cmsg->cmsg_type = SCM_CREDS;
328 #endif
329
330         if (sendmsg(conn->sock, &msg, 0) == -1)
331         {
332                 char            sebuf[256];
333
334                 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
335                                  "pg_local_sendauth: sendmsg: %s\n",
336                                  pqStrerror(errno, sebuf, sizeof(sebuf)));
337                 return STATUS_ERROR;
338         }
339         return STATUS_OK;
340 #else
341         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
342                          libpq_gettext("SCM_CRED authentication method not supported\n"));
343         return STATUS_ERROR;
344 #endif
345 }
346
347 static int
348 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
349 {
350         int                     ret;
351         char       *crypt_pwd;
352
353         /* Encrypt the password if needed. */
354
355         switch (areq)
356         {
357                 case AUTH_REQ_MD5:
358                         {
359                                 char       *crypt_pwd2;
360
361                                 /* Allocate enough space for two MD5 hashes */
362                                 crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
363                                 if (!crypt_pwd)
364                                 {
365                                         fprintf(stderr, libpq_gettext("out of memory\n"));
366                                         return STATUS_ERROR;
367                                 }
368
369                                 crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
370                                 if (!pg_md5_encrypt(password, conn->pguser,
371                                                                         strlen(conn->pguser), crypt_pwd2))
372                                 {
373                                         free(crypt_pwd);
374                                         return STATUS_ERROR;
375                                 }
376                                 if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt,
377                                                                         sizeof(conn->md5Salt), crypt_pwd))
378                                 {
379                                         free(crypt_pwd);
380                                         return STATUS_ERROR;
381                                 }
382                                 break;
383                         }
384                 case AUTH_REQ_CRYPT:
385                         {
386                                 char            salt[3];
387
388                                 StrNCpy(salt, conn->cryptSalt, 3);
389                                 crypt_pwd = crypt(password, salt);
390                                 break;
391                         }
392                 case AUTH_REQ_PASSWORD:
393                         /* discard const so we can assign it */
394                         crypt_pwd = (char *) password;
395                         break;
396                 default:
397                         return STATUS_ERROR;
398         }
399         /* Packet has a message type as of protocol 3.0 */
400         if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
401                 ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
402         else
403                 ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
404         if (areq == AUTH_REQ_MD5)
405                 free(crypt_pwd);
406         return ret;
407 }
408
409 /*
410  * pg_fe_sendauth
411  *              client demux routine for outgoing authentication information
412  */
413 int
414 pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
415                            const char *password, char *PQerrormsg)
416 {
417 #ifndef KRB5
418         (void) hostname;                        /* not used */
419 #endif
420
421         switch (areq)
422         {
423                 case AUTH_REQ_OK:
424                         break;
425
426                 case AUTH_REQ_KRB4:
427                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
428                                  libpq_gettext("Kerberos 4 authentication not supported\n"));
429                         return STATUS_ERROR;
430
431                 case AUTH_REQ_KRB5:
432 #ifdef KRB5
433                         pglock_thread();
434                         if (pg_krb5_sendauth(PQerrormsg, conn->sock,
435                                                                  hostname, conn->krbsrvname) != STATUS_OK)
436                         {
437                                 /* PQerrormsg already filled in */
438                                 pgunlock_thread();
439                                 return STATUS_ERROR;
440                         }
441                         pgunlock_thread();
442                         break;
443 #else
444                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
445                                  libpq_gettext("Kerberos 5 authentication not supported\n"));
446                         return STATUS_ERROR;
447 #endif
448
449                 case AUTH_REQ_MD5:
450                 case AUTH_REQ_CRYPT:
451                 case AUTH_REQ_PASSWORD:
452                         if (password == NULL || *password == '\0')
453                         {
454                                 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
455                                                                 PQnoPasswordSupplied);
456                                 return STATUS_ERROR;
457                         }
458                         if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
459                         {
460                                 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
461                                          "fe_sendauth: error sending password authentication\n");
462                                 return STATUS_ERROR;
463                         }
464                         break;
465
466                 case AUTH_REQ_SCM_CREDS:
467                         if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
468                                 return STATUS_ERROR;
469                         break;
470
471                 default:
472                         snprintf(PQerrormsg, PQERRORMSG_LENGTH,
473                         libpq_gettext("authentication method %u not supported\n"), areq);
474                         return STATUS_ERROR;
475         }
476
477         return STATUS_OK;
478 }
479
480
481 /*
482  * pg_fe_getauthname -- returns a pointer to dynamic space containing whatever
483  *                                       name the user has authenticated to the system
484  *
485  * if there is an error, return NULL with an error message in PQerrormsg
486  */
487 char *
488 pg_fe_getauthname(char *PQerrormsg)
489 {
490         const char *name = NULL;
491         char       *authn;
492
493 #ifdef WIN32
494         char            username[128];
495         DWORD           namesize = sizeof(username) - 1;
496 #else
497         char            pwdbuf[BUFSIZ];
498         struct passwd pwdstr;
499         struct passwd *pw = NULL;
500 #endif
501
502         /*
503          * pglock_thread() really only needs to be called around
504          * pg_krb5_authname(), but some users are using configure
505          * --enable-thread-safety-force, so we might as well do the locking within
506          * our library to protect pqGetpwuid(). In fact, application developers
507          * can use getpwuid() in their application if they use the locking call we
508          * provide, or install their own locking function using
509          * PQregisterThreadLock().
510          */
511         pglock_thread();
512
513 #ifdef KRB5
514         name = pg_krb5_authname(PQerrormsg);
515 #endif
516
517         if (!name)
518         {
519 #ifdef WIN32
520                 if (GetUserName(username, &namesize))
521                         name = username;
522 #else
523                 if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0)
524                         name = pw->pw_name;
525 #endif
526         }
527
528         authn = name ? strdup(name) : NULL;
529
530         pgunlock_thread();
531
532         return authn;
533 }
534
535
536 /*
537  * PQencryptPassword -- exported routine to encrypt a password
538  *
539  * This is intended to be used by client applications that wish to send
540  * commands like ALTER USER joe PASSWORD 'pwd'.  The password need not
541  * be sent in cleartext if it is encrypted on the client side.  This is
542  * good because it ensures the cleartext password won't end up in logs,
543  * pg_stat displays, etc.  We export the function so that clients won't
544  * be dependent on low-level details like whether the enceyption is MD5
545  * or something else.
546  *
547  * Arguments are the cleartext password, and the SQL name of the user it
548  * is for.
549  *
550  * Return value is a malloc'd string, or NULL if out-of-memory.  The client
551  * may assume the string doesn't contain any special characters that would
552  * require escaping.
553  */
554 char *
555 PQencryptPassword(const char *passwd, const char *user)
556 {
557         char       *crypt_pwd;
558
559         crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
560         if (!crypt_pwd)
561                 return NULL;
562
563         if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))
564         {
565                 free(crypt_pwd);
566                 return NULL;
567         }
568
569         return crypt_pwd;
570 }