Blame modules/cyrus-sasl.c

Packit Service a4b2a9
/*
Packit Service a4b2a9
   Copyright 2005 Red Hat, Inc.
Packit Service a4b2a9
   All rights reserved.
Packit Service a4b2a9
Packit Service a4b2a9
   Redistribution and use in source and binary forms, with or without
Packit Service a4b2a9
   modification, are permitted provided that the following conditions are met:
Packit Service a4b2a9
Packit Service a4b2a9
Packit Service a4b2a9
    * Redistributions of source code must retain the above copyright
Packit Service a4b2a9
      notice, this list of conditions and the following disclaimer.
Packit Service a4b2a9
    * Redistributions in binary form must reproduce the above copyright
Packit Service a4b2a9
      notice, this list of conditions and the following disclaimer in
Packit Service a4b2a9
      the documentation and/or other materials provided with the
Packit Service a4b2a9
      distribution.
Packit Service a4b2a9
    * Neither the name of Red Hat, Inc., nor the names of its
Packit Service a4b2a9
      contributors may be used to endorse or promote products derived
Packit Service a4b2a9
      from this software without specific prior written permission.
Packit Service a4b2a9
Packit Service a4b2a9
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
Packit Service a4b2a9
   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
Packit Service a4b2a9
   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
Packit Service a4b2a9
   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
Packit Service a4b2a9
   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
Packit Service a4b2a9
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
Packit Service a4b2a9
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
Packit Service a4b2a9
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
Packit Service a4b2a9
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit Service a4b2a9
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Packit Service a4b2a9
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service a4b2a9
 */
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  cyrus-sasl.c
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Description:
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  This file implements SASL authentication to an LDAP server for the
Packit Service a4b2a9
 *  following mechanisms:
Packit Service a4b2a9
 *    GSSAPI, EXTERNAL, ANONYMOUS, PLAIN, DIGEST-MD5, KERBEROS_V5, LOGIN
Packit Service a4b2a9
 *  The mechanism to use is specified in an external file,
Packit Service a4b2a9
 *  LDAP_AUTH_CONF_FILE.  See the samples directory in the autofs
Packit Service a4b2a9
 *  distribution for an example configuration file.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  This file is written with the intent that it will work with both the
Packit Service a4b2a9
 *  openldap and the netscape ldap client libraries.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Author: Nalin Dahyabhai <nalin@redhat.com>
Packit Service a4b2a9
 *  Modified by Jeff Moyer <jmoyer@redhat.com> to adapt it to autofs.
Packit Service a4b2a9
 */
Packit Service a4b2a9
#include <sys/types.h>
Packit Service a4b2a9
#include <sys/wait.h>
Packit Service a4b2a9
#include <stdio.h>
Packit Service a4b2a9
#include <stdlib.h>
Packit Service a4b2a9
#include <string.h>
Packit Service a4b2a9
#include <sasl/sasl.h>
Packit Service a4b2a9
Packit Service a4b2a9
#include "automount.h"
Packit Service a4b2a9
#include "lookup_ldap.h"
Packit Service a4b2a9
Packit Service a4b2a9
#ifndef LDAP_OPT_RESULT_CODE
Packit Service a4b2a9
#ifdef  LDAP_OPT_ERROR_NUMBER
Packit Service a4b2a9
#define LDAP_OPT_RESULT_CODE LDAP_OPT_ERROR_NUMBER
Packit Service a4b2a9
#else
Packit Service a4b2a9
#error "Could not determine the proper value for LDAP_OPT_RESULT_CODE."
Packit Service a4b2a9
#endif
Packit Service a4b2a9
#endif
Packit Service a4b2a9
Packit Service a4b2a9
#ifdef HAVE_KRB5_PRINCIPAL_GET_REALM
Packit Service a4b2a9
void _krb5_princ_realm(krb5_context context, krb5_const_principal princ,
Packit Service a4b2a9
                          const char **realm, int *len)
Packit Service a4b2a9
{
Packit Service a4b2a9
	*realm = krb5_principal_get_realm(context, princ);
Packit Service a4b2a9
	if (*realm)
Packit Service a4b2a9
		*len = strlen(*realm);
Packit Service a4b2a9
	else
Packit Service a4b2a9
		*len = 0;
Packit Service a4b2a9
	return;
Packit Service a4b2a9
}
Packit Service a4b2a9
#else
Packit Service a4b2a9
void _krb5_princ_realm(krb5_context context, krb5_const_principal princ,
Packit Service a4b2a9
                          const char **realm, int *len)
Packit Service a4b2a9
{
Packit Service a4b2a9
	const krb5_data *data;
Packit Service a4b2a9
Packit Service a4b2a9
	data = krb5_princ_realm(context, princ);
Packit Service a4b2a9
	if (data) {
Packit Service a4b2a9
		*realm = data->data;
Packit Service a4b2a9
		*len = data->length;
Packit Service a4b2a9
	} else {
Packit Service a4b2a9
		*realm = NULL;
Packit Service a4b2a9
		*len = 0;
Packit Service a4b2a9
	}
Packit Service a4b2a9
	return;
Packit Service a4b2a9
}
Packit Service a4b2a9
#endif
Packit Service a4b2a9
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Once a krb5 credentials cache is setup, we need to set the KRB5CCNAME
Packit Service a4b2a9
 *  environment variable so that the library knows where to find it.
Packit Service a4b2a9
 */
Packit Service a4b2a9
static const char *krb5ccenv = "KRB5CCNAME";
Packit Service a4b2a9
static const char *krb5ccval = "MEMORY:_autofstkt";
Packit Service a4b2a9
static const char *default_client = "autofsclient";
Packit Service a4b2a9
static pthread_mutex_t krb5cc_mutex = PTHREAD_MUTEX_INITIALIZER;
Packit Service a4b2a9
static unsigned int krb5cc_in_use = 0;
Packit Service a4b2a9
Packit Service a4b2a9
static int sasl_log_func(void *, int, const char *);
Packit Service a4b2a9
static int getpass_func(sasl_conn_t *, void *, int, sasl_secret_t **);
Packit Service a4b2a9
static int getuser_func(void *, int, const char **, unsigned *);
Packit Service a4b2a9
Packit Service a4b2a9
static sasl_callback_t callbacks[] = {
Packit Service a4b2a9
	{ SASL_CB_LOG, &sasl_log_func, NULL },
Packit Service a4b2a9
	{ SASL_CB_USER, &getuser_func, NULL },
Packit Service a4b2a9
	{ SASL_CB_AUTHNAME, &getuser_func, NULL },
Packit Service a4b2a9
	{ SASL_CB_PASS, &getpass_func, NULL },
Packit Service a4b2a9
	{ SASL_CB_LIST_END, NULL, NULL },
Packit Service a4b2a9
};
Packit Service a4b2a9
Packit Service a4b2a9
static char *sasl_auth_id = NULL;
Packit Service a4b2a9
static char *sasl_auth_secret = NULL;
Packit Service a4b2a9
Packit Service a4b2a9
static int
Packit Service a4b2a9
sasl_log_func(void *context, int level, const char *message)
Packit Service a4b2a9
{
Packit Service a4b2a9
	switch (level) {
Packit Service a4b2a9
	case SASL_LOG_ERR:
Packit Service a4b2a9
	case SASL_LOG_FAIL:
Packit Service a4b2a9
		logerr("%s", message);
Packit Service a4b2a9
		break;
Packit Service a4b2a9
	case SASL_LOG_WARN:
Packit Service a4b2a9
		logmsg("%s", message);
Packit Service a4b2a9
		break;
Packit Service a4b2a9
	case SASL_LOG_NOTE:
Packit Service a4b2a9
		logmsg("%s", message);
Packit Service a4b2a9
		break;
Packit Service a4b2a9
	case SASL_LOG_DEBUG:
Packit Service a4b2a9
	case SASL_LOG_TRACE:
Packit Service a4b2a9
	case SASL_LOG_PASS:
Packit Service a4b2a9
		debug(LOGOPT_DEBUG, "%s", message);
Packit Service a4b2a9
		break;
Packit Service a4b2a9
	default:
Packit Service a4b2a9
		break;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	return SASL_OK;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
static int
Packit Service a4b2a9
getuser_func(void *context, int id, const char **result, unsigned *len)
Packit Service a4b2a9
{
Packit Service a4b2a9
	debug(LOGOPT_NONE, "called with context %p, id %d.", context, id);
Packit Service a4b2a9
Packit Service a4b2a9
	switch (id) {
Packit Service a4b2a9
	case SASL_CB_USER:
Packit Service a4b2a9
	case SASL_CB_AUTHNAME:
Packit Service a4b2a9
		*result = sasl_auth_id;
Packit Service a4b2a9
		if (len)
Packit Service a4b2a9
			*len = strlen(sasl_auth_id);
Packit Service a4b2a9
		break;
Packit Service a4b2a9
	default:
Packit Service a4b2a9
		error(LOGOPT_VERBOSE, "unknown id in request: %d", id);
Packit Service a4b2a9
		return SASL_FAIL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	return SASL_OK;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  This function creates a sasl_secret_t from the credentials specified in
Packit Service a4b2a9
 *  the configuration file.  sasl_client_auth can return SASL_OK or
Packit Service a4b2a9
 *  SASL_NOMEM.  We simply propagate this return value to the caller.
Packit Service a4b2a9
 */
Packit Service a4b2a9
static int
Packit Service a4b2a9
getpass_func(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
Packit Service a4b2a9
{
Packit Service a4b2a9
	int len = strlen(sasl_auth_secret);
Packit Service a4b2a9
Packit Service a4b2a9
	debug(LOGOPT_NONE, "context %p, id %d", context, id);
Packit Service a4b2a9
Packit Service a4b2a9
	*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
Packit Service a4b2a9
	if (!*psecret)
Packit Service a4b2a9
		return SASL_NOMEM;
Packit Service a4b2a9
Packit Service a4b2a9
	(*psecret)->len = strlen(sasl_auth_secret);
Packit Service a4b2a9
	strncpy((char *)(*psecret)->data, sasl_auth_secret, len);
Packit Service a4b2a9
Packit Service a4b2a9
	return SASL_OK;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  retrieves the supportedSASLmechanisms from the LDAP server.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Return Value: the result of ldap_get_values on success, NULL on failure.
Packit Service a4b2a9
 *                The caller is responsible for calling ldap_value_free on
Packit Service a4b2a9
 *                the returned data.
Packit Service a4b2a9
 */
Packit Service a4b2a9
char **
Packit Service a4b2a9
get_server_SASL_mechanisms(unsigned logopt, LDAP *ld)
Packit Service a4b2a9
{
Packit Service a4b2a9
	int ret;
Packit Service a4b2a9
	const char *saslattrlist[] = {"supportedSASLmechanisms", NULL};
Packit Service a4b2a9
	LDAPMessage *results = NULL, *entry;
Packit Service a4b2a9
	char **mechanisms;
Packit Service a4b2a9
Packit Service a4b2a9
	ret = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
Packit Service a4b2a9
				(char **)saslattrlist, 0,
Packit Service a4b2a9
				NULL, NULL,
Packit Service a4b2a9
				NULL, LDAP_NO_LIMIT, &results);
Packit Service a4b2a9
	if (ret != LDAP_SUCCESS) {
Packit Service a4b2a9
		error(logopt, "%s", ldap_err2string(ret));
Packit Service a4b2a9
		return NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	entry = ldap_first_entry(ld, results);
Packit Service a4b2a9
	if (entry == NULL) {
Packit Service a4b2a9
		/* No root DSE. (!) */
Packit Service a4b2a9
		ldap_msgfree(results);
Packit Service a4b2a9
		debug(logopt,
Packit Service a4b2a9
		      "a lookup of \"supportedSASLmechanisms\" returned "
Packit Service a4b2a9
		      "no results.");
Packit Service a4b2a9
		return NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	mechanisms = ldap_get_values(ld, entry, "supportedSASLmechanisms");
Packit Service a4b2a9
	ldap_msgfree(results);
Packit Service a4b2a9
	if (mechanisms == NULL) {
Packit Service a4b2a9
		/* Well, that was a waste of time. */
Packit Service a4b2a9
		info(logopt, "No SASL authentication mechanisms are supported"
Packit Service a4b2a9
		    " by the LDAP server.");
Packit Service a4b2a9
		return NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	return mechanisms;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Returns 0 upon successful connect, -1 on failure.
Packit Service a4b2a9
 */
Packit Service a4b2a9
int
Packit Service a4b2a9
do_sasl_bind(unsigned logopt, LDAP *ld, sasl_conn_t *conn, const char **clientout,
Packit Service a4b2a9
	     unsigned int *clientoutlen, const char *auth_mech, int sasl_result)
Packit Service a4b2a9
{
Packit Service a4b2a9
	int ret, msgid, bind_result = LDAP_OTHER;
Packit Service a4b2a9
	struct berval client_cred, *server_cred, temp_cred;
Packit Service a4b2a9
	LDAPMessage *results;
Packit Service a4b2a9
	int have_data, expected_data;
Packit Service a4b2a9
Packit Service a4b2a9
	do {
Packit Service a4b2a9
		/* Take whatever client data we have and send it to the
Packit Service a4b2a9
		 * server. */
Packit Service a4b2a9
		client_cred.bv_val = (char *)*clientout;
Packit Service a4b2a9
		client_cred.bv_len = *clientoutlen;
Packit Service a4b2a9
		ret = ldap_sasl_bind(ld, NULL, auth_mech,
Packit Service a4b2a9
				     (client_cred.bv_len > 0) ?
Packit Service a4b2a9
				     &client_cred : NULL,
Packit Service a4b2a9
				     NULL, NULL, &msgid);
Packit Service a4b2a9
		if (ret != LDAP_SUCCESS) {
Packit Service a4b2a9
			crit(logopt,
Packit Service a4b2a9
			     "Error sending sasl_bind request to "
Packit Service a4b2a9
			     "the server: %s", ldap_err2string(ret));
Packit Service a4b2a9
			return -1;
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
		/* Wait for a result message for this bind request. */
Packit Service a4b2a9
		results = NULL;
Packit Service a4b2a9
		ret = ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &results);
Packit Service a4b2a9
		if (ret != LDAP_RES_BIND) {
Packit Service a4b2a9
			crit(logopt,
Packit Service a4b2a9
			     "Error while waiting for response to "
Packit Service a4b2a9
			     "sasl_bind request: %s", ldap_err2string(ret));
Packit Service a4b2a9
			return -1;
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
		/* Retrieve the result code for the bind request and
Packit Service a4b2a9
		 * any data which the server sent. */
Packit Service a4b2a9
		server_cred = NULL;
Packit Service a4b2a9
		ret = ldap_parse_sasl_bind_result(ld, results,
Packit Service a4b2a9
						  &server_cred, 0);
Packit Service a4b2a9
		ldap_msgfree(results);
Packit Service a4b2a9
Packit Service a4b2a9
		/* Okay, here's where things get tricky.  Both
Packit Service a4b2a9
		 * Mozilla's LDAP SDK and OpenLDAP store the result
Packit Service a4b2a9
		 * code which was returned by the server in the
Packit Service a4b2a9
		 * handle's ERROR_NUMBER option.  Mozilla returns
Packit Service a4b2a9
		 * LDAP_SUCCESS if the data was parsed correctly, even
Packit Service a4b2a9
		 * if the result was an error, while OpenLDAP returns
Packit Service a4b2a9
		 * the result code.  I'm leaning toward Mozilla being
Packit Service a4b2a9
		 * more correct.
Packit Service a4b2a9
		 * In either case, we stuff the result into bind_result.
Packit Service a4b2a9
		 */
Packit Service a4b2a9
		if (ret == LDAP_SUCCESS) {
Packit Service a4b2a9
			/* Mozilla? */
Packit Service a4b2a9
			ret = ldap_get_option(ld, LDAP_OPT_RESULT_CODE,
Packit Service a4b2a9
					      &bind_result);
Packit Service a4b2a9
			if (ret != LDAP_SUCCESS) {
Packit Service a4b2a9
				crit(logopt,
Packit Service a4b2a9
				     "Error retrieving response to sasl_bind "
Packit Service a4b2a9
				     "request: %s", ldap_err2string(ret));
Packit Service a4b2a9
				ret = -1;
Packit Service a4b2a9
				break;
Packit Service a4b2a9
			}
Packit Service a4b2a9
		} else {
Packit Service a4b2a9
			/* OpenLDAP? */
Packit Service a4b2a9
			switch (ret) {
Packit Service a4b2a9
			case LDAP_SASL_BIND_IN_PROGRESS:
Packit Service a4b2a9
				bind_result = ret;
Packit Service a4b2a9
				break;
Packit Service a4b2a9
			default:
Packit Service a4b2a9
				warn(logopt,
Packit Service a4b2a9
				     "Error parsing response to sasl_bind "
Packit Service a4b2a9
				     "request: %s.", ldap_err2string(ret));
Packit Service a4b2a9
				break;
Packit Service a4b2a9
			}
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
		/*
Packit Service a4b2a9
		 * The LDAP server is supposed to send a NULL value for
Packit Service a4b2a9
		 * server_cred if there was no data.  However, *some*
Packit Service a4b2a9
		 * server implementations get this wrong, and instead send
Packit Service a4b2a9
		 * an empty string.  We check for both.
Packit Service a4b2a9
		 */
Packit Service a4b2a9
		have_data = server_cred != NULL && server_cred->bv_len > 0;
Packit Service a4b2a9
Packit Service a4b2a9
		/*
Packit Service a4b2a9
		 * If the result of the sasl_client_start is SASL_CONTINUE,
Packit Service a4b2a9
		 * then the server should have sent us more data.
Packit Service a4b2a9
		 */
Packit Service a4b2a9
		expected_data = sasl_result == SASL_CONTINUE;
Packit Service a4b2a9
Packit Service a4b2a9
		if (have_data && !expected_data) {
Packit Service a4b2a9
			warn(logopt,
Packit Service a4b2a9
			     "The LDAP server sent data in response to our "
Packit Service a4b2a9
			     "bind request, but indicated that the bind was "
Packit Service a4b2a9
			     "complete. LDAP SASL bind with mechansim %s "
Packit Service a4b2a9
			     "failed.", auth_mech);
Packit Service a4b2a9
			ret = -1;
Packit Service a4b2a9
			break;
Packit Service a4b2a9
		}
Packit Service a4b2a9
		if (expected_data && !have_data) {
Packit Service a4b2a9
			warn(logopt,
Packit Service a4b2a9
			     "The LDAP server indicated that the LDAP SASL "
Packit Service a4b2a9
			     "bind was incomplete, but did not provide the "
Packit Service a4b2a9
			     "required data to proceed. LDAP SASL bind with "
Packit Service a4b2a9
			     "mechanism %s failed.", auth_mech);
Packit Service a4b2a9
			ret = -1;
Packit Service a4b2a9
			break;
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
		/* If we need another round trip, process whatever we
Packit Service a4b2a9
		 * received and prepare data to be transmitted back. */
Packit Service a4b2a9
		if ((sasl_result == SASL_CONTINUE) &&
Packit Service a4b2a9
		    ((bind_result == LDAP_SUCCESS) ||
Packit Service a4b2a9
		     (bind_result == LDAP_SASL_BIND_IN_PROGRESS))) {
Packit Service a4b2a9
			if (server_cred != NULL) {
Packit Service a4b2a9
				temp_cred = *server_cred;
Packit Service a4b2a9
			} else {
Packit Service a4b2a9
				temp_cred.bv_len = 0;
Packit Service a4b2a9
				temp_cred.bv_val = NULL;
Packit Service a4b2a9
			}
Packit Service a4b2a9
			sasl_result = sasl_client_step(conn,
Packit Service a4b2a9
						       temp_cred.bv_val,
Packit Service a4b2a9
						       temp_cred.bv_len,
Packit Service a4b2a9
						       NULL,
Packit Service a4b2a9
						       clientout,
Packit Service a4b2a9
						       clientoutlen);
Packit Service a4b2a9
			/* If we have data to send, then the server
Packit Service a4b2a9
			 * had better be expecting it.  (It's valid
Packit Service a4b2a9
			 * to send the server no data with a request.)
Packit Service a4b2a9
			 */
Packit Service a4b2a9
			if ((*clientoutlen > 0) &&
Packit Service a4b2a9
			    (bind_result != LDAP_SASL_BIND_IN_PROGRESS)) {
Packit Service a4b2a9
				warn(logopt,
Packit Service a4b2a9
				     "We have data for the server, "
Packit Service a4b2a9
				     "but it thinks we are done!");
Packit Service a4b2a9
				/* XXX should print out debug data here */
Packit Service a4b2a9
				ret = -1;
Packit Service a4b2a9
			}
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
		if (server_cred && server_cred->bv_len > 0) {
Packit Service a4b2a9
			ber_bvfree(server_cred);
Packit Service a4b2a9
			server_cred = NULL;
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
	} while ((bind_result == LDAP_SASL_BIND_IN_PROGRESS) ||
Packit Service a4b2a9
		 (sasl_result == SASL_CONTINUE));
Packit Service a4b2a9
Packit Service a4b2a9
	if (server_cred && server_cred->bv_len > 0)
Packit Service a4b2a9
		ber_bvfree(server_cred);
Packit Service a4b2a9
Packit Service a4b2a9
	return ret;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Read client credentials from the default keytab, create a credentials
Packit Service a4b2a9
 *  cache, add the TGT to that cache, and set the environment variable so
Packit Service a4b2a9
 *  that the sasl/krb5 libraries can find our credentials.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Returns 0 upon success.  ctxt->kinit_done and ctxt->kinit_successful
Packit Service a4b2a9
 *  are set for cleanup purposes.  The krb5 context and ccache entries in
Packit Service a4b2a9
 *  the lookup_context are also filled in.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Upon failure, -1 is returned.
Packit Service a4b2a9
 */
Packit Service a4b2a9
int
Packit Service a4b2a9
sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt)
Packit Service a4b2a9
{
Packit Service a4b2a9
	krb5_error_code ret;
Packit Service a4b2a9
	krb5_principal tgs_princ, krb5_client_princ;
Packit Service a4b2a9
	krb5_creds my_creds;
Packit Service a4b2a9
	char *tgs_name;
Packit Service a4b2a9
	const char *realm_name;
Packit Service a4b2a9
	int status, realm_length;
Packit Service a4b2a9
Packit Service a4b2a9
	if (ctxt->kinit_done)
Packit Service a4b2a9
		return 0;
Packit Service a4b2a9
	ctxt->kinit_done = 1;
Packit Service a4b2a9
Packit Service a4b2a9
	debug(logopt,
Packit Service a4b2a9
	      "initializing kerberos ticket: client principal %s",
Packit Service a4b2a9
	      ctxt->client_princ ? ctxt->client_princ : default_client);
Packit Service a4b2a9
Packit Service a4b2a9
	ret = krb5_init_context(&ctxt->krb5ctxt);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt, "krb5_init_context failed with %d", ret);
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	ret = krb5_cc_resolve(ctxt->krb5ctxt, krb5ccval, &ctxt->krb5_ccache);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt, "krb5_cc_resolve failed with error %d",
Packit Service a4b2a9
		      ret);
Packit Service a4b2a9
		krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	if (ctxt->client_princ) {
Packit Service a4b2a9
		debug(logopt,
Packit Service a4b2a9
		      "calling krb5_parse_name on client principal %s",
Packit Service a4b2a9
		      ctxt->client_princ);
Packit Service a4b2a9
Packit Service a4b2a9
		ret = krb5_parse_name(ctxt->krb5ctxt, ctxt->client_princ,
Packit Service a4b2a9
				      &krb5_client_princ);
Packit Service a4b2a9
		if (ret) {
Packit Service a4b2a9
			error(logopt,
Packit Service a4b2a9
			      "krb5_parse_name failed for "
Packit Service a4b2a9
			      "specified client principal %s",
Packit Service a4b2a9
			      ctxt->client_princ);
Packit Service a4b2a9
			goto out_cleanup_cc;
Packit Service a4b2a9
		}
Packit Service a4b2a9
	} else {
Packit Service a4b2a9
		char *tmp_name = NULL;
Packit Service a4b2a9
Packit Service a4b2a9
		debug(logopt,
Packit Service a4b2a9
		      "calling krb5_sname_to_principal using defaults");
Packit Service a4b2a9
Packit Service a4b2a9
		ret = krb5_sname_to_principal(ctxt->krb5ctxt, NULL,
Packit Service a4b2a9
					default_client, KRB5_NT_SRV_HST, 
Packit Service a4b2a9
					&krb5_client_princ);
Packit Service a4b2a9
		if (ret) {
Packit Service a4b2a9
			error(logopt,
Packit Service a4b2a9
			      "krb5_sname_to_principal failed for "
Packit Service a4b2a9
			      "%s with error %d", default_client, ret);
Packit Service a4b2a9
			goto out_cleanup_cc;
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
Packit Service a4b2a9
		ret = krb5_unparse_name(ctxt->krb5ctxt,
Packit Service a4b2a9
					krb5_client_princ, &tmp_name);
Packit Service a4b2a9
		if (ret) {
Packit Service a4b2a9
			debug(logopt,
Packit Service a4b2a9
			      "krb5_unparse_name failed with error %d",
Packit Service a4b2a9
			      ret);
Packit Service a4b2a9
			goto out_cleanup_client_princ;
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
		debug(logopt,
Packit Service a4b2a9
		      "principal used for authentication: %s", tmp_name);
Packit Service a4b2a9
Packit Service a4b2a9
		krb5_free_unparsed_name(ctxt->krb5ctxt, tmp_name);
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	/* setup a principal for the ticket granting service */
Packit Service a4b2a9
	_krb5_princ_realm(ctxt->krb5ctxt, krb5_client_princ, &realm_name, &realm_length);
Packit Service a4b2a9
	ret = krb5_build_principal_ext(ctxt->krb5ctxt, &tgs_princ,
Packit Service a4b2a9
		realm_length, realm_name,
Packit Service a4b2a9
		strlen(KRB5_TGS_NAME), KRB5_TGS_NAME,
Packit Service a4b2a9
		realm_length, realm_name,
Packit Service a4b2a9
		0);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt,
Packit Service a4b2a9
		      "krb5_build_principal failed with error %d", ret);
Packit Service a4b2a9
		goto out_cleanup_client_princ;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	ret = krb5_unparse_name(ctxt->krb5ctxt, tgs_princ, &tgs_name);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt, "krb5_unparse_name failed with error %d",
Packit Service a4b2a9
		      ret);
Packit Service a4b2a9
		goto out_cleanup_client_princ;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	debug(logopt, "Using tgs name %s", tgs_name);
Packit Service a4b2a9
Packit Service a4b2a9
	memset(&my_creds, 0, sizeof(my_creds));
Packit Service a4b2a9
	ret = krb5_get_init_creds_keytab(ctxt->krb5ctxt, &my_creds,
Packit Service a4b2a9
					 krb5_client_princ,
Packit Service a4b2a9
					 NULL /*keytab*/,
Packit Service a4b2a9
					 0 /* relative start time */,
Packit Service a4b2a9
					 tgs_name, NULL);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt,
Packit Service a4b2a9
		      "krb5_get_init_creds_keytab failed with error %d",
Packit Service a4b2a9
		      ret);
Packit Service a4b2a9
		goto out_cleanup_unparse;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	status = pthread_mutex_lock(&krb5cc_mutex);
Packit Service a4b2a9
	if (status)
Packit Service a4b2a9
		fatal(status);
Packit Service a4b2a9
Packit Service a4b2a9
	if (krb5cc_in_use++ == 0)
Packit Service a4b2a9
		/* tell the cache what the default principal is */
Packit Service a4b2a9
		ret = krb5_cc_initialize(ctxt->krb5ctxt,
Packit Service a4b2a9
				 ctxt->krb5_ccache, krb5_client_princ);
Packit Service a4b2a9
Packit Service a4b2a9
	status = pthread_mutex_unlock(&krb5cc_mutex);
Packit Service a4b2a9
	if (status)
Packit Service a4b2a9
		fatal(status);
Packit Service a4b2a9
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt,
Packit Service a4b2a9
		      "krb5_cc_initialize failed with error %d", ret);
Packit Service a4b2a9
		goto out_cleanup_creds;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	/* and store credentials for that principal */
Packit Service a4b2a9
	ret = krb5_cc_store_cred(ctxt->krb5ctxt, ctxt->krb5_ccache, &my_creds);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt,
Packit Service a4b2a9
		      "krb5_cc_store_cred failed with error %d", ret);
Packit Service a4b2a9
		goto out_cleanup_creds;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	/* finally, set the environment variable to point to our
Packit Service a4b2a9
	 * credentials cache */
Packit Service a4b2a9
	if (setenv(krb5ccenv, krb5ccval, 1) != 0) {
Packit Service a4b2a9
		error(logopt, "setenv failed with %d", errno);
Packit Service a4b2a9
		goto out_cleanup_creds;
Packit Service a4b2a9
	}
Packit Service a4b2a9
	ctxt->kinit_successful = 1;
Packit Service a4b2a9
Packit Service a4b2a9
	debug(logopt, "Kerberos authentication was successful!");
Packit Service a4b2a9
Packit Service a4b2a9
	krb5_free_unparsed_name(ctxt->krb5ctxt, tgs_name);
Packit Service a4b2a9
	krb5_free_cred_contents(ctxt->krb5ctxt, &my_creds);
Packit Service a4b2a9
	krb5_free_principal(ctxt->krb5ctxt, tgs_princ);
Packit Service a4b2a9
	krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
Packit Service a4b2a9
Packit Service a4b2a9
	return 0;
Packit Service a4b2a9
Packit Service a4b2a9
out_cleanup_creds:
Packit Service a4b2a9
	krb5cc_in_use--;
Packit Service a4b2a9
	krb5_free_cred_contents(ctxt->krb5ctxt, &my_creds);
Packit Service a4b2a9
out_cleanup_unparse:
Packit Service a4b2a9
	krb5_free_principal(ctxt->krb5ctxt, tgs_princ);
Packit Service a4b2a9
	krb5_free_unparsed_name(ctxt->krb5ctxt, tgs_name);
Packit Service a4b2a9
out_cleanup_client_princ:
Packit Service a4b2a9
	krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
Packit Service a4b2a9
out_cleanup_cc:
Packit Service a4b2a9
	status = pthread_mutex_lock(&krb5cc_mutex);
Packit Service a4b2a9
	if (status)
Packit Service a4b2a9
		fatal(status);
Packit Service a4b2a9
Packit Service a4b2a9
	if (krb5cc_in_use)
Packit Service a4b2a9
		ret = krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
	else
Packit Service a4b2a9
		ret = krb5_cc_destroy(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
	if (ret)
Packit Service a4b2a9
		warn(logopt,
Packit Service a4b2a9
		     "krb5_cc_destroy failed with non-fatal error %d", ret);
Packit Service a4b2a9
Packit Service a4b2a9
	status = pthread_mutex_unlock(&krb5cc_mutex);
Packit Service a4b2a9
	if (status)
Packit Service a4b2a9
		fatal(status);
Packit Service a4b2a9
Packit Service a4b2a9
	krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
Packit Service a4b2a9
	return -1;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Check a client given external credential cache.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Returns 0 upon success.  ctxt->kinit_done and ctxt->kinit_successful
Packit Service a4b2a9
 *  are set for cleanup purposes.  The krb5 context and ccache entries in
Packit Service a4b2a9
 *  the lookup_context are also filled in.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Upon failure, -1 is returned.
Packit Service a4b2a9
 */
Packit Service a4b2a9
int
Packit Service a4b2a9
sasl_do_kinit_ext_cc(unsigned logopt, struct lookup_context *ctxt)
Packit Service a4b2a9
{
Packit Service a4b2a9
	krb5_principal def_princ;
Packit Service a4b2a9
	krb5_principal krb5_client_princ;
Packit Service a4b2a9
	krb5_error_code ret;
Packit Service a4b2a9
	char *cc_princ, *client_princ;
Packit Service a4b2a9
Packit Service a4b2a9
	if (ctxt->kinit_done)
Packit Service a4b2a9
		return 0;
Packit Service a4b2a9
	ctxt->kinit_done = 1;
Packit Service a4b2a9
Packit Service a4b2a9
	debug(logopt,
Packit Service a4b2a9
	      "using external credential cache for auth: client principal %s",
Packit Service a4b2a9
	      ctxt->client_princ ? ctxt->client_princ : default_client);
Packit Service a4b2a9
Packit Service a4b2a9
	ret = krb5_init_context(&ctxt->krb5ctxt);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt, "krb5_init_context failed with %d", ret);
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	ret = krb5_cc_resolve(ctxt->krb5ctxt, ctxt->client_cc, &ctxt->krb5_ccache);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt, "krb5_cc_resolve failed with error %d",
Packit Service a4b2a9
		      ret);
Packit Service a4b2a9
		krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
		krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	ret = krb5_cc_get_principal(ctxt->krb5ctxt, ctxt->krb5_ccache, &def_princ);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt, "krb5_cc_get_principal failed with error %d", ret);
Packit Service a4b2a9
		krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
		krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	ret = krb5_unparse_name(ctxt->krb5ctxt, def_princ, &cc_princ);
Packit Service a4b2a9
	if (ret) {
Packit Service a4b2a9
		error(logopt, "krb5_unparse_name failed with error %d", ret);
Packit Service a4b2a9
		krb5_free_principal(ctxt->krb5ctxt, def_princ);
Packit Service a4b2a9
		krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
		krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	debug(logopt, "external credential cache default principal %s", cc_princ);
Packit Service a4b2a9
Packit Service a4b2a9
	/*
Packit Service a4b2a9
	 * If the principal isn't set in the config construct the default
Packit Service a4b2a9
	 * so we can check against the default principal of the external
Packit Service a4b2a9
	 * cred cache.
Packit Service a4b2a9
	 */
Packit Service a4b2a9
	if (ctxt->client_princ)
Packit Service a4b2a9
		client_princ = ctxt->client_princ;
Packit Service a4b2a9
	else {
Packit Service a4b2a9
		debug(logopt,
Packit Service a4b2a9
		      "calling krb5_sname_to_principal using defaults");
Packit Service a4b2a9
Packit Service a4b2a9
		ret = krb5_sname_to_principal(ctxt->krb5ctxt, NULL,
Packit Service a4b2a9
					default_client, KRB5_NT_SRV_HST, 
Packit Service a4b2a9
					&krb5_client_princ);
Packit Service a4b2a9
		if (ret) {
Packit Service a4b2a9
			error(logopt,
Packit Service a4b2a9
			      "krb5_sname_to_principal failed for "
Packit Service a4b2a9
			      "%s with error %d", default_client, ret);
Packit Service a4b2a9
			krb5_free_principal(ctxt->krb5ctxt, def_princ);
Packit Service a4b2a9
			krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
			krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
			return -1;
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
Packit Service a4b2a9
		ret = krb5_unparse_name(ctxt->krb5ctxt,
Packit Service a4b2a9
					krb5_client_princ, &client_princ);
Packit Service a4b2a9
		if (ret) {
Packit Service a4b2a9
			debug(logopt,
Packit Service a4b2a9
			      "krb5_unparse_name failed with error %d",
Packit Service a4b2a9
			      ret);
Packit Service a4b2a9
			krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
Packit Service a4b2a9
			krb5_free_principal(ctxt->krb5ctxt, def_princ);
Packit Service a4b2a9
			krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
			krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
			return -1;
Packit Service a4b2a9
		}
Packit Service a4b2a9
Packit Service a4b2a9
		debug(logopt,
Packit Service a4b2a9
		      "principal used for authentication: %s", client_princ);
Packit Service a4b2a9
Packit Service a4b2a9
		krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	/*
Packit Service a4b2a9
	 * Check if the principal to be used matches the default principal in
Packit Service a4b2a9
	 * the external cred cache.
Packit Service a4b2a9
	 */
Packit Service a4b2a9
	if (strcmp(cc_princ, client_princ)) {
Packit Service a4b2a9
		error(logopt,
Packit Service a4b2a9
		      "configured client principal %s ",
Packit Service a4b2a9
		      ctxt->client_princ);
Packit Service a4b2a9
		error(logopt,
Packit Service a4b2a9
		      "external credential cache default principal %s",
Packit Service a4b2a9
		      cc_princ);
Packit Service a4b2a9
		error(logopt, 
Packit Service a4b2a9
		      "cannot use credential cache, external "
Packit Service a4b2a9
		      "default principal does not match configured "
Packit Service a4b2a9
		      "principal");
Packit Service a4b2a9
		if (!ctxt->client_princ)
Packit Service a4b2a9
			krb5_free_unparsed_name(ctxt->krb5ctxt, client_princ);
Packit Service a4b2a9
		krb5_free_unparsed_name(ctxt->krb5ctxt, cc_princ);
Packit Service a4b2a9
		krb5_free_principal(ctxt->krb5ctxt, def_princ);
Packit Service a4b2a9
		krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
		krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	if (!ctxt->client_princ)
Packit Service a4b2a9
		krb5_free_unparsed_name(ctxt->krb5ctxt, client_princ);
Packit Service a4b2a9
	krb5_free_unparsed_name(ctxt->krb5ctxt, cc_princ);
Packit Service a4b2a9
	krb5_free_principal(ctxt->krb5ctxt, def_princ);
Packit Service a4b2a9
Packit Service a4b2a9
	/* Set the environment variable to point to the external cred cache */
Packit Service a4b2a9
	if (setenv(krb5ccenv, ctxt->client_cc, 1) != 0) {
Packit Service a4b2a9
		error(logopt, "setenv failed with %d", errno);
Packit Service a4b2a9
		krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
		krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
	}
Packit Service a4b2a9
	ctxt->kinit_successful = 1;
Packit Service a4b2a9
Packit Service a4b2a9
	debug(logopt, "Kerberos authentication was successful!");
Packit Service a4b2a9
Packit Service a4b2a9
	return 0;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Attempt to bind to the ldap server using a given authentication
Packit Service a4b2a9
 *  mechanism.  ldap should be a properly initialzed ldap pointer.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Returns a valid sasl_conn_t pointer upon success, NULL on failure.
Packit Service a4b2a9
 */
Packit Service a4b2a9
sasl_conn_t *
Packit Service a4b2a9
sasl_bind_mech(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt, const char *mech)
Packit Service a4b2a9
{
Packit Service a4b2a9
	sasl_conn_t *conn;
Packit Service a4b2a9
	char *tmp, *host = NULL;
Packit Service a4b2a9
	const char *clientout;
Packit Service a4b2a9
	unsigned int clientoutlen;
Packit Service a4b2a9
	const char *chosen_mech;
Packit Service a4b2a9
	int result;
Packit Service a4b2a9
Packit Service a4b2a9
	if (!strncmp(mech, "GSSAPI", 6)) {
Packit Service a4b2a9
		if (ctxt->client_cc)
Packit Service a4b2a9
			result = sasl_do_kinit_ext_cc(logopt, ctxt);
Packit Service a4b2a9
		else
Packit Service a4b2a9
			result = sasl_do_kinit(logopt, ctxt);
Packit Service a4b2a9
		if (result != 0)
Packit Service a4b2a9
			return NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	debug(logopt, "Attempting sasl bind with mechanism %s", mech);
Packit Service a4b2a9
Packit Service a4b2a9
	result = ldap_get_option(ldap, LDAP_OPT_HOST_NAME, &host);
Packit Service a4b2a9
	if (result != LDAP_OPT_SUCCESS || !host) {
Packit Service a4b2a9
		debug(logopt, "failed to get hostname for connection");
Packit Service a4b2a9
		return NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	/*
Packit Service a4b2a9
	 * We need a host name to start the client.
Packit Service a4b2a9
	 * But the ldap library can return a list of host names so
Packit Service a4b2a9
	 * just use the first one.
Packit Service a4b2a9
	 */
Packit Service a4b2a9
	if ((tmp = strchr(host, ' ')))
Packit Service a4b2a9
		*tmp = '\0';
Packit Service a4b2a9
	if ((tmp = strrchr(host, ':'))) {
Packit Service a4b2a9
		if (*(tmp - 1) != ']') {
Packit Service a4b2a9
			*tmp = '\0';
Packit Service a4b2a9
			tmp = host;
Packit Service a4b2a9
		} else {
Packit Service a4b2a9
			*(tmp - 1) = '\0';
Packit Service a4b2a9
			tmp = host;
Packit Service a4b2a9
			if (*tmp == '[')
Packit Service a4b2a9
				tmp++;
Packit Service a4b2a9
		}
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	/* Create a new authentication context for the service. */
Packit Service a4b2a9
	result = sasl_client_new("ldap", tmp, NULL, NULL, NULL, 0, &conn;;
Packit Service a4b2a9
	if (result != SASL_OK) {
Packit Service a4b2a9
		error(logopt, "sasl_client_new failed with error %d",
Packit Service a4b2a9
		      result);
Packit Service a4b2a9
		ldap_memfree(host);
Packit Service a4b2a9
		return NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	chosen_mech = NULL;
Packit Service a4b2a9
	result = sasl_client_start(conn, mech, NULL,
Packit Service a4b2a9
				&clientout, &clientoutlen, &chosen_mech);
Packit Service a4b2a9
Packit Service a4b2a9
	/* OK and CONTINUE are the only non-fatal return codes here. */
Packit Service a4b2a9
	if ((result != SASL_OK) && (result != SASL_CONTINUE)) {
Packit Service a4b2a9
		warn(logopt, "sasl_client_start failed for %s", host);
Packit Service a4b2a9
		debug(logopt, "sasl_client_start: %s", sasl_errdetail(conn));
Packit Service a4b2a9
		ldap_memfree(host);
Packit Service a4b2a9
		sasl_dispose(&conn;;
Packit Service a4b2a9
		return NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	result = do_sasl_bind(logopt, ldap, conn,
Packit Service a4b2a9
			 &clientout, &clientoutlen, chosen_mech, result);
Packit Service a4b2a9
	if (result == 0) {
Packit Service a4b2a9
		ldap_memfree(host);
Packit Service a4b2a9
		debug(logopt, "sasl bind with mechanism %s succeeded",
Packit Service a4b2a9
		      chosen_mech);
Packit Service a4b2a9
		return conn;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	info(logopt, "sasl bind with mechanism %s failed", mech);
Packit Service a4b2a9
Packit Service a4b2a9
	/* sasl bind failed */
Packit Service a4b2a9
	ldap_memfree(host);
Packit Service a4b2a9
	sasl_dispose(&conn;;
Packit Service a4b2a9
Packit Service a4b2a9
	return NULL;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Returns 0 if a suitable authentication mechanism is available.  Returns
Packit Service a4b2a9
 *  -1 on error or if no mechanism is supported by both client and server.
Packit Service a4b2a9
 */
Packit Service a4b2a9
sasl_conn_t *
Packit Service a4b2a9
sasl_choose_mech(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt)
Packit Service a4b2a9
{
Packit Service a4b2a9
	sasl_conn_t *conn = NULL;
Packit Service a4b2a9
	int authenticated;
Packit Service a4b2a9
	int i;
Packit Service a4b2a9
	char **mechanisms;
Packit Service a4b2a9
Packit Service a4b2a9
	mechanisms = get_server_SASL_mechanisms(logopt, ldap);
Packit Service a4b2a9
	if (!mechanisms)
Packit Service a4b2a9
		return NULL;
Packit Service a4b2a9
Packit Service a4b2a9
	/* Try each supported mechanism in turn. */
Packit Service a4b2a9
	authenticated = 0;
Packit Service a4b2a9
	for (i = 0; mechanisms[i] != NULL; i++) {
Packit Service a4b2a9
		/*
Packit Service a4b2a9
		 *  This routine is called if there is no configured
Packit Service a4b2a9
		 *  mechanism.  As such, we can skip over any auth
Packit Service a4b2a9
		 *  mechanisms that require user credentials.  These include
Packit Service a4b2a9
		 *  PLAIN, LOGIN, and DIGEST-MD5.
Packit Service a4b2a9
		 */
Packit Service a4b2a9
		if (authtype_requires_creds(mechanisms[i]))
Packit Service a4b2a9
			continue;
Packit Service a4b2a9
Packit Service a4b2a9
		conn = sasl_bind_mech(logopt, ldap, ctxt, mechanisms[i]);
Packit Service a4b2a9
		if (conn) {
Packit Service a4b2a9
			ctxt->sasl_mech = strdup(mechanisms[i]);
Packit Service a4b2a9
			if (!ctxt->sasl_mech) {
Packit Service a4b2a9
				crit(logopt, "Successfully authenticated with "
Packit Service a4b2a9
				     "mechanism %s, but failed to allocate "
Packit Service a4b2a9
				     "memory to hold the mechanism type.",
Packit Service a4b2a9
				     mechanisms[i]);
Packit Service a4b2a9
				sasl_dispose(&conn;;
Packit Service a4b2a9
				ldap_value_free(mechanisms);
Packit Service a4b2a9
				return NULL;
Packit Service a4b2a9
			}
Packit Service a4b2a9
			authenticated = 1;
Packit Service a4b2a9
			break;
Packit Service a4b2a9
		}
Packit Service a4b2a9
		debug(logopt, "Failed to authenticate with mech %s",
Packit Service a4b2a9
		      mechanisms[i]);
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	debug(logopt, "authenticated: %d, sasl_mech: %s",
Packit Service a4b2a9
	      authenticated, ctxt->sasl_mech);
Packit Service a4b2a9
Packit Service a4b2a9
	ldap_value_free(mechanisms);
Packit Service a4b2a9
	return conn;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Routine called when unbinding an ldap connection.
Packit Service a4b2a9
 */
Packit Service a4b2a9
void
Packit Service a4b2a9
autofs_sasl_unbind(struct ldap_conn *conn, struct lookup_context *ctxt)
Packit Service a4b2a9
{
Packit Service a4b2a9
	if (ctxt->sasl_mech && !strncmp(ctxt->sasl_mech, "EXTERNAL", 8)) {
Packit Service a4b2a9
		if (conn->ldap) {
Packit Service a4b2a9
			ldap_unbind_s(conn->ldap);
Packit Service a4b2a9
			conn->ldap = NULL;
Packit Service a4b2a9
		}
Packit Service a4b2a9
		return;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	if (conn->sasl_conn) {
Packit Service a4b2a9
		sasl_dispose(&conn->sasl_conn);
Packit Service a4b2a9
		conn->sasl_conn = NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Given a lookup context that has been initialized with any user-specified
Packit Service a4b2a9
 *  parameters, figure out which sasl mechanism to use.  Then, initialize
Packit Service a4b2a9
 *  the necessary parameters to authenticate with the chosen mechanism.
Packit Service a4b2a9
 *
Packit Service a4b2a9
 *  Return Values:
Packit Service a4b2a9
 *  0  -  Success
Packit Service a4b2a9
 * -1  -  Failure
Packit Service a4b2a9
 */
Packit Service a4b2a9
int
Packit Service a4b2a9
autofs_sasl_bind(unsigned logopt,
Packit Service a4b2a9
		 struct ldap_conn *conn, struct lookup_context *ctxt)
Packit Service a4b2a9
{
Packit Service a4b2a9
	sasl_conn_t *sasl_conn = NULL;
Packit Service a4b2a9
Packit Service a4b2a9
	if (ctxt->sasl_mech && !strncmp(ctxt->sasl_mech, "EXTERNAL", 8)) {
Packit Service a4b2a9
		int result;
Packit Service a4b2a9
Packit Service a4b2a9
		debug(logopt,
Packit Service a4b2a9
		      "Attempting sasl bind with mechanism %s",
Packit Service a4b2a9
		      ctxt->sasl_mech);
Packit Service a4b2a9
Packit Service a4b2a9
		result = do_sasl_extern(conn->ldap, ctxt);
Packit Service a4b2a9
		if (result)
Packit Service a4b2a9
			debug(logopt,
Packit Service a4b2a9
			      "Failed to authenticate with mech %s",
Packit Service a4b2a9
			      ctxt->sasl_mech);
Packit Service a4b2a9
		else
Packit Service a4b2a9
			debug(logopt,
Packit Service a4b2a9
			      "sasl bind with mechanism %s succeeded",
Packit Service a4b2a9
			      ctxt->sasl_mech);
Packit Service a4b2a9
Packit Service a4b2a9
		return result;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	sasl_auth_id = ctxt->user;
Packit Service a4b2a9
	sasl_auth_secret = ctxt->secret;
Packit Service a4b2a9
Packit Service a4b2a9
	if (ctxt->auth_required & LDAP_AUTH_AUTODETECT) {
Packit Service a4b2a9
		if (ctxt->sasl_mech) {
Packit Service a4b2a9
			free(ctxt->sasl_mech);
Packit Service a4b2a9
			ctxt->sasl_mech = NULL;
Packit Service a4b2a9
		}
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	/*
Packit Service a4b2a9
	 *  If LDAP_AUTH_AUTODETECT is set, it means that there was no
Packit Service a4b2a9
	 *  mechanism specified in the configuration file or auto
Packit Service a4b2a9
	 *  selection has been requested, so try to auto-select an
Packit Service a4b2a9
	 *  auth mechanism.
Packit Service a4b2a9
	 */
Packit Service a4b2a9
	if (ctxt->sasl_mech)
Packit Service a4b2a9
		sasl_conn = sasl_bind_mech(logopt,
Packit Service a4b2a9
					   conn->ldap, ctxt, ctxt->sasl_mech);
Packit Service a4b2a9
	else
Packit Service a4b2a9
		sasl_conn = sasl_choose_mech(logopt, conn->ldap, ctxt);
Packit Service a4b2a9
Packit Service a4b2a9
	if (!sasl_conn)
Packit Service a4b2a9
		return -1;
Packit Service a4b2a9
Packit Service a4b2a9
	conn->sasl_conn = sasl_conn;
Packit Service a4b2a9
Packit Service a4b2a9
	return 0;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 *  Destructor routine.  This should be called when finished with an ldap
Packit Service a4b2a9
 *  session.
Packit Service a4b2a9
 */
Packit Service a4b2a9
void autofs_sasl_dispose(struct ldap_conn *conn, struct lookup_context *ctxt)
Packit Service a4b2a9
{
Packit Service a4b2a9
	int status, ret;
Packit Service a4b2a9
Packit Service a4b2a9
	if (ctxt->sasl_mech && !strncmp(ctxt->sasl_mech, "EXTERNAL", 8)) {
Packit Service a4b2a9
		if (conn && conn->ldap) {
Packit Service a4b2a9
			ldap_unbind_s(conn->ldap);
Packit Service a4b2a9
			conn->ldap = NULL;
Packit Service a4b2a9
		}
Packit Service a4b2a9
		return;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	if (conn && conn->sasl_conn) {
Packit Service a4b2a9
		sasl_dispose(&conn->sasl_conn);
Packit Service a4b2a9
		conn->sasl_conn = NULL;
Packit Service a4b2a9
	}
Packit Service a4b2a9
Packit Service a4b2a9
	if (ctxt->kinit_successful) {
Packit Service a4b2a9
		status = pthread_mutex_lock(&krb5cc_mutex);
Packit Service a4b2a9
		if (status)
Packit Service a4b2a9
			fatal(status);
Packit Service a4b2a9
Packit Service a4b2a9
		if (--krb5cc_in_use || ctxt->client_cc)
Packit Service a4b2a9
			ret = krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
		else 
Packit Service a4b2a9
			ret = krb5_cc_destroy(ctxt->krb5ctxt, ctxt->krb5_ccache);
Packit Service a4b2a9
		if (ret)
Packit Service a4b2a9
			logmsg("krb5_cc_destroy failed with non-fatal error %d",
Packit Service a4b2a9
			     ret);
Packit Service a4b2a9
Packit Service a4b2a9
		status = pthread_mutex_unlock(&krb5cc_mutex);
Packit Service a4b2a9
		if (status)
Packit Service a4b2a9
			fatal(status);
Packit Service a4b2a9
Packit Service a4b2a9
		krb5_free_context(ctxt->krb5ctxt);
Packit Service a4b2a9
		if (unsetenv(krb5ccenv) != 0)
Packit Service a4b2a9
			logerr("unsetenv failed with error %d", errno);
Packit Service a4b2a9
Packit Service a4b2a9
		ctxt->krb5ctxt = NULL;
Packit Service a4b2a9
		ctxt->krb5_ccache = NULL;
Packit Service a4b2a9
		ctxt->kinit_done = 0;
Packit Service a4b2a9
		ctxt->kinit_successful = 0;
Packit Service a4b2a9
	}
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
static void *sasl_mutex_new(void)
Packit Service a4b2a9
{
Packit Service a4b2a9
	pthread_mutex_t* mutex;
Packit Service a4b2a9
Packit Service a4b2a9
	mutex = malloc(sizeof(pthread_mutex_t));
Packit Service a4b2a9
	if (!mutex)
Packit Service a4b2a9
		return 0;
Packit Service a4b2a9
		
Packit Service a4b2a9
	pthread_mutex_init(mutex, NULL);
Packit Service a4b2a9
Packit Service a4b2a9
	return (void *) mutex;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
static int sasl_mutex_lock(void *mutex __attribute__((unused)))
Packit Service a4b2a9
{
Packit Service a4b2a9
	int rc;
Packit Service a4b2a9
Packit Service a4b2a9
	if (!mutex)
Packit Service a4b2a9
		return SASL_FAIL;
Packit Service a4b2a9
Packit Service a4b2a9
	rc = pthread_mutex_lock((pthread_mutex_t *) mutex);
Packit Service a4b2a9
Packit Service a4b2a9
	return (rc==0 ? SASL_OK : SASL_FAIL);
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
static int sasl_mutex_unlock(void *mutex __attribute__((unused)))
Packit Service a4b2a9
{
Packit Service a4b2a9
	int rc;
Packit Service a4b2a9
Packit Service a4b2a9
	if (!mutex)
Packit Service a4b2a9
		return SASL_FAIL;
Packit Service a4b2a9
Packit Service a4b2a9
	rc = pthread_mutex_unlock((pthread_mutex_t *) mutex);
Packit Service a4b2a9
Packit Service a4b2a9
	return (rc==0 ? SASL_OK : SASL_FAIL);
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
static void sasl_mutex_dispose(void *mutex __attribute__((unused)))
Packit Service a4b2a9
{
Packit Service a4b2a9
	int rc;
Packit Service a4b2a9
Packit Service a4b2a9
	if (!mutex)
Packit Service a4b2a9
		return;
Packit Service a4b2a9
Packit Service a4b2a9
	rc = pthread_mutex_destroy((pthread_mutex_t *) mutex);
Packit Service a4b2a9
	if (rc == 0)
Packit Service a4b2a9
		free(mutex);
Packit Service a4b2a9
Packit Service a4b2a9
	return;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 * Initialize the sasl callbacks, which increments the global
Packit Service a4b2a9
 * use counter.
Packit Service a4b2a9
 */
Packit Service a4b2a9
int autofs_sasl_client_init(unsigned logopt)
Packit Service a4b2a9
{
Packit Service a4b2a9
Packit Service a4b2a9
	sasl_set_mutex(sasl_mutex_new,
Packit Service a4b2a9
		       sasl_mutex_lock,
Packit Service a4b2a9
		       sasl_mutex_unlock,
Packit Service a4b2a9
		       sasl_mutex_dispose);
Packit Service a4b2a9
Packit Service a4b2a9
	/* Start up Cyrus SASL--only needs to be done at library load. */
Packit Service a4b2a9
	if (sasl_client_init(callbacks) != SASL_OK) {
Packit Service a4b2a9
		error(logopt, "sasl_client_init failed");
Packit Service a4b2a9
		return 0;
Packit Service a4b2a9
	}
Packit Service a4b2a9
	return 1;
Packit Service a4b2a9
}
Packit Service a4b2a9
Packit Service a4b2a9
/*
Packit Service a4b2a9
 * Decrement the library reference count and free resources if
Packit Service a4b2a9
 * we are the last to close the library.
Packit Service a4b2a9
 */
Packit Service a4b2a9
void autofs_sasl_done(void)
Packit Service a4b2a9
{
Packit Service a4b2a9
	sasl_done();
Packit Service a4b2a9
	return;
Packit Service a4b2a9
}
Packit Service a4b2a9