Blame modules/cyrus-sasl.c

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