1 /*-------------------------------------------------------------------------
4 * The front-end (client) authorization routines
6 * Portions Copyright (c) 1996-2005, 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.110 2005/12/26 14:58:05 petere 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/crypt.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.
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;
112 pg_krb5_init(char *PQerrormsg)
114 krb5_error_code retval;
116 if (pg_krb5_initialised)
119 retval = krb5_init_context(&pg_krb5_context);
122 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
123 "pg_krb5_init: krb5_init_context: %s\n",
124 error_message(retval));
128 retval = krb5_cc_default(pg_krb5_context, &pg_krb5_ccache);
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);
138 retval = krb5_cc_get_principal(pg_krb5_context, pg_krb5_ccache,
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);
150 retval = krb5_unparse_name(pg_krb5_context, pg_krb5_client, &pg_krb5_name);
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);
162 pg_krb5_name = pg_an_to_ln(pg_krb5_name);
164 pg_krb5_initialised = 1;
170 * pg_krb5_authname -- returns a pointer to static space containing whatever
171 * name the user has authenticated to the system
174 pg_krb5_authname(char *PQerrormsg)
176 if (pg_krb5_init(PQerrormsg) != STATUS_OK)
184 * pg_krb5_sendauth -- client routine to send authentication information to
188 pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname, const char *servicename)
190 krb5_error_code retval;
192 krb5_principal server;
193 krb5_auth_context auth_context = NULL;
194 krb5_error *err_ret = NULL;
198 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
199 "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n");
203 ret = pg_krb5_init(PQerrormsg);
204 if (ret != STATUS_OK)
207 retval = krb5_sname_to_principal(pg_krb5_context, hostname, servicename,
208 KRB5_NT_SRV_HST, &server);
211 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
212 "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
213 error_message(retval));
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.
222 if (!pg_set_block(sock))
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);
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);
240 if (retval == KRB5_SENDAUTH_REJECTED && err_ret)
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);
252 #error "bogus configuration"
257 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
258 "krb5_sendauth: %s\n", error_message(retval));
262 krb5_free_error(pg_krb5_context, err_ret);
267 krb5_free_principal(pg_krb5_context, server);
269 if (!pg_set_noblock(sock))
273 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
274 libpq_gettext("could not restore non-blocking mode on socket: %s\n"),
275 pqStrerror(errno, sebuf, sizeof(sebuf)));
285 * Respond to AUTH_REQ_SCM_CREDS challenge.
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
292 pg_local_sendauth(char *PQerrormsg, PGconn *conn)
294 #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
295 (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
300 #ifdef HAVE_STRUCT_CMSGCRED
301 /* Prevent padding */
302 char cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
304 /* Point to start of first structure */
305 struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
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.
316 memset(&msg, 0, sizeof(msg));
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;
330 if (sendmsg(conn->sock, &msg, 0) == -1)
334 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
335 "pg_local_sendauth: sendmsg: %s\n",
336 pqStrerror(errno, sebuf, sizeof(sebuf)));
341 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
342 libpq_gettext("SCM_CRED authentication method not supported\n"));
348 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
353 /* Encrypt the password if needed. */
361 /* Allocate enough space for two MD5 hashes */
362 crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
365 fprintf(stderr, libpq_gettext("out of memory\n"));
369 crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
370 if (!pg_md5_encrypt(password, conn->pguser,
371 strlen(conn->pguser), crypt_pwd2))
376 if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt,
377 sizeof(conn->md5Salt), crypt_pwd))
388 StrNCpy(salt, conn->cryptSalt, 3);
389 crypt_pwd = crypt(password, salt);
392 case AUTH_REQ_PASSWORD:
393 /* discard const so we can assign it */
394 crypt_pwd = (char *) password;
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);
403 ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
404 if (areq == AUTH_REQ_MD5)
411 * client demux routine for outgoing authentication information
414 pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
415 const char *password, char *PQerrormsg)
418 (void) hostname; /* not used */
427 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
428 libpq_gettext("Kerberos 4 authentication not supported\n"));
434 if (pg_krb5_sendauth(PQerrormsg, conn->sock,
435 hostname, conn->krbsrvname) != STATUS_OK)
437 /* PQerrormsg already filled in */
444 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
445 libpq_gettext("Kerberos 5 authentication not supported\n"));
451 case AUTH_REQ_PASSWORD:
452 if (password == NULL || *password == '\0')
454 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
455 PQnoPasswordSupplied);
458 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
460 (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
461 "fe_sendauth: error sending password authentication\n");
466 case AUTH_REQ_SCM_CREDS:
467 if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
472 snprintf(PQerrormsg, PQERRORMSG_LENGTH,
473 libpq_gettext("authentication method %u not supported\n"), areq);
482 * pg_fe_getauthname -- returns a pointer to dynamic space containing whatever
483 * name the user has authenticated to the system
485 * if there is an error, return NULL with an error message in PQerrormsg
488 pg_fe_getauthname(char *PQerrormsg)
490 const char *name = NULL;
495 DWORD namesize = sizeof(username) - 1;
498 struct passwd pwdstr;
499 struct passwd *pw = NULL;
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().
514 name = pg_krb5_authname(PQerrormsg);
520 if (GetUserName(username, &namesize))
523 if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0)
528 authn = name ? strdup(name) : NULL;
537 * PQencryptPassword -- exported routine to encrypt a password
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
547 * Arguments are the cleartext password, and the SQL name of the user it
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
555 PQencryptPassword(const char *passwd, const char *user)
559 crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
563 if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))