1 /*-------------------------------------------------------------------------
4 * The front-end (client) authorization routines
6 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * NOTE: the error message strings returned by this module must not
10 * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
13 * $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.118 2006/07/14 04:59:30 momjian Exp $
15 *-------------------------------------------------------------------------
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
26 #include "postgres_fe.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)
38 #include <sys/ucred.h>
40 #ifndef MAXHOSTNAMELEN
41 #include <netdb.h> /* for MAXHOSTNAMELEN on some */
51 #include "libpq-int.h"
53 #include "libpq/md5.h"
58 * MIT Kerberos authentication system - protocol version 5
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__)
68 * pg_an_to_ln -- return the local name corresponding to an authentication
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.
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.
85 pg_an_to_ln(char *aname)
89 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
92 for (p = aname; *p; p++)
101 * Various krb5 state which is not connection specific, and a flag to
102 * indicate whether we have initialised it yet.
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;
114 int pg_krb5_initialised;
115 krb5_context pg_krb5_context;
116 krb5_ccache pg_krb5_ccache;
117 krb5_principal pg_krb5_client;
123 pg_krb5_init(char *PQerrormsg, struct krb5_info *info)
125 krb5_error_code retval;
127 if (info->pg_krb5_initialised)
130 retval = krb5_init_context(&(info->pg_krb5_context));
133 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
134 "pg_krb5_init: krb5_init_context: %s\n",
135 error_message(retval));
139 retval = krb5_cc_default(info->pg_krb5_context, &(info->pg_krb5_ccache));
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);
149 retval = krb5_cc_get_principal(info->pg_krb5_context, info->pg_krb5_ccache,
150 &(info->pg_krb5_client));
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);
161 retval = krb5_unparse_name(info->pg_krb5_context, info->pg_krb5_client, &(info->pg_krb5_name));
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);
173 info->pg_krb5_name = pg_an_to_ln(info->pg_krb5_name);
175 info->pg_krb5_initialised = 1;
180 pg_krb5_destroy(struct krb5_info *info)
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);
191 * pg_krb5_authname -- returns a copy of whatever name the user
192 * has authenticated to the system, or NULL
195 pg_krb5_authname(char *PQerrormsg)
198 struct krb5_info info;
199 info.pg_krb5_initialised = 0;
201 if (pg_krb5_init(PQerrormsg, &info) != STATUS_OK)
203 tmp_name = strdup(info.pg_krb5_name);
204 pg_krb5_destroy(&info);
211 * pg_krb5_sendauth -- client routine to send authentication information to
215 pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *servicename)
217 krb5_error_code retval;
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;
227 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
228 "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n");
232 ret = pg_krb5_init(PQerrormsg, &info);
233 if (ret != STATUS_OK)
236 retval = krb5_sname_to_principal(info.pg_krb5_context, hostname, servicename,
237 KRB5_NT_SRV_HST, &server);
240 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
241 "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
242 error_message(retval));
243 pg_krb5_destroy(&info);
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.
252 if (!pg_set_block(sock))
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);
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);
271 if (retval == KRB5_SENDAUTH_REJECTED && err_ret)
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);
283 #error "bogus configuration"
288 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
289 "krb5_sendauth: %s\n", error_message(retval));
293 krb5_free_error(info.pg_krb5_context, err_ret);
298 krb5_free_principal(info.pg_krb5_context, server);
300 if (!pg_set_noblock(sock))
304 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
305 libpq_gettext("could not restore non-blocking mode on socket: %s\n"),
306 pqStrerror(errno, sebuf, sizeof(sebuf)));
309 pg_krb5_destroy(&info);
317 * Respond to AUTH_REQ_SCM_CREDS challenge.
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
324 pg_local_sendauth(char *PQerrormsg, PGconn *conn)
326 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
327 (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
332 #ifdef HAVE_STRUCT_CMSGCRED
333 /* Prevent padding */
334 char cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
336 /* Point to start of first structure */
337 struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
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.
348 memset(&msg, 0, sizeof(msg));
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;
362 if (sendmsg(conn->sock, &msg, 0) == -1)
366 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
367 "pg_local_sendauth: sendmsg: %s\n",
368 pqStrerror(errno, sebuf, sizeof(sebuf)));
373 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
374 libpq_gettext("SCM_CRED authentication method not supported\n"));
380 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
385 /* Encrypt the password if needed. */
393 /* Allocate enough space for two MD5 hashes */
394 crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
397 fprintf(stderr, libpq_gettext("out of memory\n"));
401 crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
402 if (!pg_md5_encrypt(password, conn->pguser,
403 strlen(conn->pguser), crypt_pwd2))
408 if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt,
409 sizeof(conn->md5Salt), crypt_pwd))
420 StrNCpy(salt, conn->cryptSalt, 3);
421 crypt_pwd = crypt(password, salt);
424 case AUTH_REQ_PASSWORD:
425 /* discard const so we can assign it */
426 crypt_pwd = (char *) password;
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);
435 ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
436 if (areq == AUTH_REQ_MD5)
443 * client demux routine for outgoing authentication information
446 pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
447 const char *password, char *PQerrormsg)
450 (void) hostname; /* not used */
459 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
460 libpq_gettext("Kerberos 4 authentication not supported\n"));
466 if (pg_krb5_sendauth(PQerrormsg, conn->sock,
467 hostname, conn->krbsrvname) != STATUS_OK)
469 /* PQerrormsg already filled in */
476 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
477 libpq_gettext("Kerberos 5 authentication not supported\n"));
483 case AUTH_REQ_PASSWORD:
484 if (password == NULL || *password == '\0')
486 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
487 PQnoPasswordSupplied);
490 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
492 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
493 "fe_sendauth: error sending password authentication\n");
498 case AUTH_REQ_SCM_CREDS:
499 if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
504 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
505 libpq_gettext("authentication method %u not supported\n"), areq);
514 * pg_fe_getauthname -- returns a pointer to dynamic space containing whatever
515 * name the user has authenticated to the system
517 * if there is an error, return NULL with an error message in PQerrormsg
520 pg_fe_getauthname(char *PQerrormsg)
523 char *krb5_name = NULL;
525 const char *name = NULL;
530 DWORD namesize = sizeof(username) - 1;
533 struct passwd pwdstr;
534 struct passwd *pw = NULL;
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().
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);
560 if (GetUserName(username, &namesize))
563 if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0)
568 authn = name ? strdup(name) : NULL;
571 /* Free the strdup'd string from pg_krb5_authname, if we got one */
583 * PQencryptPassword -- exported routine to encrypt a password
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
593 * Arguments are the cleartext password, and the SQL name of the user it
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
601 PQencryptPassword(const char *passwd, const char *user)
605 crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
609 if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))