OpenSolaris_b135/lib/libidmap/common/namemaps.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <errno.h>
#include <ldap.h>
#include <sasl/sasl.h>
#include <libintl.h>
#include <strings.h>
#include <syslog.h>

#include "addisc.h"
#include "libadutils.h"
#include "idmap_impl.h"
#include "ns_sldap.h"

/*
 * syslog is the default logger.
 * It can be overwritten by supplying a logger
 * with  idmap_set_logger()
 */
idmap_logger logger = syslog;


void
idmap_set_logger(idmap_logger funct)
{
	logger = funct;
	adutils_set_logger(funct);
}


/* From adutils.c: */

/* A single DS */
struct idmap_nm_handle {
	LDAP			*ad;		/* LDAP connection */
	/* LDAP DS info */
	char			*ad_host;
	int			ad_port;

	/* hardwired to SASL GSSAPI only for now */
	char			*saslmech;
	unsigned		saslflags;
	char			*windomain;
	char			*ad_unixuser_attr;
	char			*ad_unixgroup_attr;
	char			*nldap_winname_attr;
	char			*default_domain;
	bool_t			is_nldap;
	bool_t			is_ad;
	int			direction;
	ns_cred_t		nsc;
};

static
idmap_stat
string2auth(const char *from, ns_auth_t *na)
{
	if (from == NULL) {
		na->type = NS_LDAP_AUTH_SASL;
		na->tlstype = NS_LDAP_TLS_SASL;
		na->saslmech = NS_LDAP_SASL_GSSAPI;
		na->saslopt = NS_LDAP_SASLOPT_PRIV |
		    NS_LDAP_SASLOPT_INT;
		return (IDMAP_SUCCESS);
	}

	if (strcasecmp(from, "simple") == 0) {
		na->type = NS_LDAP_AUTH_SIMPLE;
		na->tlstype = NS_LDAP_TLS_NONE;
		na->saslmech = NS_LDAP_SASL_NONE;
		na->saslopt = NS_LDAP_SASLOPT_NONE;
	} else if (strcasecmp(from, "sasl/CRAM-MD5") == 0) {
		na->type = NS_LDAP_AUTH_SASL;
		na->tlstype = NS_LDAP_TLS_SASL;
		na->saslmech = NS_LDAP_SASL_CRAM_MD5;
		na->saslopt = NS_LDAP_SASLOPT_NONE;
	} else if (strcasecmp(from, "sasl/DIGEST-MD5") == 0) {
		na->type = NS_LDAP_AUTH_SASL;
		na->tlstype = NS_LDAP_TLS_SASL;
		na->saslmech = NS_LDAP_SASL_DIGEST_MD5;
		na->saslopt = NS_LDAP_SASLOPT_NONE;
	} else if (strcasecmp(from, "sasl/GSSAPI") == 0) {
		na->type = NS_LDAP_AUTH_SASL;
		na->tlstype = NS_LDAP_TLS_SASL;
		na->saslmech = NS_LDAP_SASL_GSSAPI;
		na->saslopt = NS_LDAP_SASLOPT_PRIV |
		    NS_LDAP_SASLOPT_INT;
	} else if (strcasecmp(from, "tls:simple") == 0) {
		na->type = NS_LDAP_AUTH_TLS;
		na->tlstype = NS_LDAP_TLS_SIMPLE;
		na->saslmech = NS_LDAP_SASL_NONE;
		na->saslopt = NS_LDAP_SASLOPT_NONE;
	} else if (strcasecmp(from, "tls:sasl/CRAM-MD5") == 0) {
		na->type = NS_LDAP_AUTH_TLS;
		na->tlstype = NS_LDAP_TLS_SASL;
		na->saslmech = NS_LDAP_SASL_CRAM_MD5;
		na->saslopt = NS_LDAP_SASLOPT_NONE;
	} else if (strcasecmp(from, "tls:sasl/DIGEST-MD5") == 0) {
		na->type = NS_LDAP_AUTH_TLS;
		na->tlstype = NS_LDAP_TLS_SASL;
		na->saslmech = NS_LDAP_SASL_DIGEST_MD5;
		na->saslopt = NS_LDAP_SASLOPT_NONE;
	} else {
		logger(LOG_ERR,
		    gettext("Invalid authentication method \"%s\" specified\n"),
		    from);
		return (IDMAP_ERR_ARG);
	}

	return (IDMAP_SUCCESS);
}



static
idmap_stat
strings2cred(ns_cred_t *nsc, char *user, char *passwd, char *auth)
{
	idmap_stat rc;
	(void) memset(nsc, 0, sizeof (ns_cred_t));

	if ((rc = string2auth(auth, &nsc->auth)) != IDMAP_SUCCESS)
		return (rc);

	if (user != NULL) {
		nsc->cred.unix_cred.userID = strdup(user);
		if (nsc->cred.unix_cred.userID == NULL)
			return (IDMAP_ERR_MEMORY);
	}

	if (passwd != NULL) {
		nsc->cred.unix_cred.passwd = strdup(passwd);
		if (nsc->cred.unix_cred.passwd == NULL) {
			free(nsc->cred.unix_cred.userID);
			return (IDMAP_ERR_MEMORY);
		}
	}

	return (IDMAP_SUCCESS);
}





/*ARGSUSED*/
static int
idmap_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
{
	sasl_interact_t	*interact;

	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
		return (LDAP_PARAM_ERROR);

	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
	for (interact = prompts; interact->id != SASL_CB_LIST_END;
	    interact++) {
		interact->result = NULL;
		interact->len = 0;
	}
	return (LDAP_SUCCESS);
}

static
idmap_stat
idmap_open_ad_conn(idmap_nm_handle_t *adh)
{
	int zero = 0;
	int timeoutms = 30 * 1000;
	int ldversion, ldap_rc;
	idmap_stat rc = IDMAP_SUCCESS;

	/* Open and bind an LDAP connection */
	adh->ad = ldap_init(adh->ad_host, adh->ad_port);
	if (adh->ad == NULL) {
		logger(LOG_INFO, "ldap_init() to server "
		    "%s port %d failed. (%s)", CHECK_NULL(adh->ad_host),
		    adh->ad_port, strerror(errno));
		rc = IDMAP_ERR_INTERNAL;
		goto out;
	}
	ldversion = LDAP_VERSION3;
	(void) ldap_set_option(adh->ad, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
	(void) ldap_set_option(adh->ad, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
	(void) ldap_set_option(adh->ad, LDAP_OPT_TIMELIMIT, &zero);
	(void) ldap_set_option(adh->ad, LDAP_OPT_SIZELIMIT, &zero);
	(void) ldap_set_option(adh->ad, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
	(void) ldap_set_option(adh->ad, LDAP_OPT_RESTART, LDAP_OPT_ON);
	ldap_rc = ldap_sasl_interactive_bind_s(adh->ad, "" /* binddn */,
	    adh->saslmech, NULL, NULL, adh->saslflags, &idmap_saslcallback,
	    NULL);

	if (ldap_rc != LDAP_SUCCESS) {
		(void) ldap_unbind(adh->ad);
		adh->ad = NULL;
		logger(LOG_INFO, "ldap_sasl_interactive_bind_s() to server "
		    "%s port %d failed. (%s)", CHECK_NULL(adh->ad_host),
		    adh->ad_port, ldap_err2string(ldap_rc));
		rc = IDMAP_ERR_INTERNAL;
	}

out:
	return (rc);
}

static
idmap_stat
idmap_init_nldap(idmap_nm_handle_t *p)
{
/*
 * For now, there is nothing to initialize in nldap. This is just to
 * make it future-proof, especially standalone libsldap-proof
 */
	p->is_nldap = TRUE;
	return (0);
}

static
idmap_stat
idmap_init_ad(idmap_nm_handle_t *p)
{
	idmap_stat	rc = IDMAP_SUCCESS;
	idmap_ad_disc_ds_t	*dc = NULL;
	ad_disc_t	ad_ctx;

	ad_ctx = ad_disc_init();
	if (ad_ctx == NULL) {
		logger(LOG_ERR,
		    gettext("AD autodiscovery initialization failed"));
		return (IDMAP_ERR_INTERNAL);
	}
	ad_disc_refresh(ad_ctx);


	/* Based on the supplied or default domain, find the proper AD: */
	if (ad_disc_set_DomainName(ad_ctx, p->windomain)) {
		rc = IDMAP_ERR_INTERNAL;
		logger(LOG_ERR,
		    gettext("Setting a domain name \"%s\" for autodiscovery"
		    " failed, most likely not enough memory"), p->windomain);
		goto cleanup;
	}

	dc = ad_disc_get_DomainController(ad_ctx, AD_DISC_GLOBAL, NULL);
	if (dc == NULL) {
		rc = IDMAP_ERR_ARG;
		logger(LOG_ERR,
		    gettext("A domain controller for the "
		    "domain \"%s\" not found."), p->windomain);
		goto cleanup;
	}


	p->ad_port = dc->port;
	p->ad_host = strdup(dc->host);

	if (p->ad_host == NULL) {
		rc = IDMAP_ERR_MEMORY;
		goto cleanup;
	}

	p->saslflags = LDAP_SASL_INTERACTIVE;
	p->saslmech = strdup("GSSAPI");

	if (p->saslmech == NULL) {
		rc = IDMAP_ERR_MEMORY;
		goto cleanup;
	}

	rc = idmap_open_ad_conn(p);

	if (rc != IDMAP_SUCCESS)
		goto cleanup;

	p->is_ad = TRUE;

cleanup:
	ad_disc_fini(ad_ctx);
	free(dc);
	return (rc);
}

void
idmap_fini_namemaps(idmap_nm_handle_t *p)
{
	if (p == NULL)
		return;

	if (p->ad_unixgroup_attr != NULL)
		free(p->ad_unixgroup_attr);

	if (p->ad_unixuser_attr != NULL)
		free(p->ad_unixuser_attr);

	if (p->nldap_winname_attr)
		free(p->nldap_winname_attr);

	if (p->windomain != NULL)
		free(p->windomain);

	if (p->default_domain != NULL)
		free(p->default_domain);

	if (p->saslmech != NULL)
		free(p->saslmech);

	if (p->ad_host != NULL)
		free(p->ad_host);

	if (p->nsc.cred.unix_cred.userID != NULL) {
		free(p->nsc.cred.unix_cred.userID);
	}

	if (p->nsc.cred.unix_cred.passwd != NULL) {
		/* No archeology: */
		(void) memset(p->nsc.cred.unix_cred.passwd, 0,
		    strlen(p->nsc.cred.unix_cred.passwd));
		free(p->nsc.cred.unix_cred.passwd);
	}

	if (p->ad)
		(void) ldap_unbind(p->ad);
	free(p);

}



idmap_stat
idmap_init_namemaps(idmap_handle_t *handle, idmap_nm_handle_t **adh,
    char *user, char *passwd, char *auth, char *windomain,
    int direction)
{
	idmap_stat rc;
	idmap_nm_handle_t *p;

	p = (idmap_nm_handle_t *)calloc(1, sizeof (idmap_nm_handle_t));
	if (p == NULL)
		return (IDMAP_ERR_MEMORY);

	rc = idmap_get_prop_str(handle, PROP_DEFAULT_DOMAIN,
	    &p->default_domain);
	if (rc != IDMAP_SUCCESS) {
		logger(LOG_ERR,
		    gettext("Error obtaining default domain from idmapd (%s)"),
		    idmap_stat2string(NULL, rc));
		goto cleanup;
	}

	rc = idmap_get_prop_str(handle, PROP_AD_UNIXUSER_ATTR,
	    &p->ad_unixuser_attr);
	if (rc != IDMAP_SUCCESS) {
		logger(LOG_ERR,
		    gettext("Error obtaining AD unixuser attribute (%s)"),
		    idmap_stat2string(NULL, rc));
		goto cleanup;
	}

	rc = idmap_get_prop_str(handle, PROP_AD_UNIXGROUP_ATTR,
	    &p->ad_unixgroup_attr);
	if (rc != IDMAP_SUCCESS) {
		logger(LOG_ERR,
		    gettext("Error obtaining AD unixgroup attribute (%s)"),
		    idmap_stat2string(NULL, rc));
		goto cleanup;
	}


	rc = idmap_get_prop_str(handle, PROP_NLDAP_WINNAME_ATTR,
	    &p->nldap_winname_attr);
	if (rc != IDMAP_SUCCESS) {
		logger(LOG_ERR,
		    gettext("Error obtaining AD unixgroup attribute (%s)"),
		    idmap_stat2string(NULL, rc));
		goto cleanup;
	}

	if (windomain != NULL) {
		p->windomain = strdup(windomain);
		if (p->windomain == NULL) {
			rc = IDMAP_ERR_MEMORY;
			goto cleanup;
		}
	} else if (!EMPTY_STRING(p->default_domain)) {
		p->windomain = strdup(p->default_domain);
		if (p->windomain == NULL) {
			rc = IDMAP_ERR_MEMORY;
			goto cleanup;
		}
	} else if (direction == IDMAP_DIRECTION_W2U) {
		logger(LOG_ERR,
		    gettext("Windows domain not given and idmapd daemon"
		    " didn't provide a default one"));
		rc = IDMAP_ERR_ARG;
		goto cleanup;
	}

	p->direction = direction;

	if ((p->ad_unixuser_attr != NULL || p->ad_unixgroup_attr != NULL) &&
	    direction != IDMAP_DIRECTION_U2W) {
		rc = idmap_init_ad(p);
		if (rc != IDMAP_SUCCESS) {
			goto cleanup;
		}
	}

	if (p->nldap_winname_attr != NULL && direction != IDMAP_DIRECTION_W2U) {
		rc = idmap_init_nldap(p);
		if (rc != IDMAP_SUCCESS) {
			goto cleanup;
		}

		rc = strings2cred(&p->nsc, user, passwd, auth);
		if (rc != IDMAP_SUCCESS) {
			goto cleanup;
		}
	}

cleanup:

	if (rc == IDMAP_SUCCESS) {
		*adh = p;
		return (IDMAP_SUCCESS);
	}

	/* There was an error: */
	idmap_fini_namemaps(*adh);
	return (rc);
}

static
char *
dns2dn(const char *dns, const char *prefix)
{
	int num_lvl = 1;
	char *buf;
	const char *it, *new_it;

	for (it = dns; it != NULL; it = strchr(it, '.')) {
		it ++;
		num_lvl ++;
	}

	buf = (char *)malloc(strlen(prefix) + strlen(dns) + 4 * num_lvl);
	(void) strcpy(buf, prefix);


	it = dns;
	for (;;) {
		new_it = strchr(it, '.');
		(void) strcat(buf, "DC=");
		if (new_it == NULL) {
			(void) strcat(buf, it);
			break;
		} else {
			(void) strncat(buf, it, new_it - it);
			(void) strcat(buf, ",");
		}

		it = new_it + 1;
	}

	return (buf);
}


static
idmap_stat
extract_attribute(idmap_nm_handle_t *p, LDAPMessage *entry, char *name,
    char **value)
{
	char	**values = NULL;
	idmap_stat rc = IDMAP_SUCCESS;
	/* No value means it is not requested */
	if (value == NULL)
		return (IDMAP_SUCCESS);

	values = ldap_get_values(p->ad, entry, name);
	if (values == NULL || values[0] == NULL)
		*value = NULL;
	else {
		*value = strdup(values[0]);
		if (*value == NULL)
			rc = IDMAP_ERR_MEMORY;
	}
errout:
	ldap_value_free(values);
	return (rc);
}


/* Split winname to its name and domain part */
static
idmap_stat
split_fqwn(char *fqwn, char **name, char **domain)
{
	char *at;

	*name = NULL;
	*domain = NULL;

	at = strchr(fqwn, '@');
	if (at == NULL) {
		at = strchr(fqwn, '\\');
	}
	if (at == NULL) {
	/* There is no domain - leave domain NULL */
		*name = strdup(fqwn);
		if (*name == NULL)
			goto errout;
		return (IDMAP_SUCCESS);
	}


	*domain = strdup(at+1);
	if (*domain == NULL)
		goto errout;
	*name = (char *)malloc(at - fqwn + 1);
	if (*name == NULL)
		goto errout;
	(void) strlcpy(*name, fqwn, at - fqwn + 1);

	if (*at == '\\') {
		char *it = *name;
		*name = *domain;
		*domain = it;
	}

	return (IDMAP_SUCCESS);


errout:
	free(*name);
	*name = NULL;
	free(*domain);
	*domain = NULL;
	return (IDMAP_ERR_MEMORY);
}

static
idmap_stat
unixname2dn(idmap_nm_handle_t *p, char *unixname, int is_user, char **dn,
    char **winname, char **windomain)
{
	idmap_stat rc = IDMAP_SUCCESS;
	int rc_ns;


	char filter[255];
	static const char *attribs[3];
	ns_ldap_result_t *res;
	ns_ldap_error_t *errorp = NULL;
	char **attrs;


	attribs[0] = p->nldap_winname_attr;
	attribs[1] = "dn";
	attribs[2] = NULL;

	(void) snprintf(filter, sizeof (filter), is_user ? "uid=%s" : "cn=%s",
	    unixname);

	rc_ns = __ns_ldap_list(is_user ? "passwd" : "group",
	    filter, NULL, attribs, NULL, 0, &res, &errorp, NULL, NULL);


	if (rc_ns == NS_LDAP_NOTFOUND) {
		logger(LOG_ERR, is_user ? gettext("User \"%s\" not found.")
		    : gettext("Group %s not found."),  unixname);
		return (IDMAP_ERR_NOTFOUND);
	} else if (rc_ns != NS_LDAP_SUCCESS) {
		char *msg = "Cause unidentified";
		if (errorp != NULL) {
			(void) __ns_ldap_err2str(errorp->status, &msg);
		}
		logger(LOG_ERR, gettext("Ldap list failed (%s)."), msg);
		return (IDMAP_ERR_ARG);
	}

	if (res == NULL) {
		logger(LOG_ERR, gettext("User %s not found"), unixname);
		return (IDMAP_ERR_ARG);
	}

	if (winname != NULL && windomain != NULL) {
		attrs = __ns_ldap_getAttr(&res->entry[0],
		    p->nldap_winname_attr);
		if (attrs != NULL && attrs[0] != NULL) {
			rc = split_fqwn(attrs[0], winname, windomain);
		} else {
			*winname = *windomain = NULL;
		}
	}

	if (dn != NULL) {
		attrs = __ns_ldap_getAttr(&res->entry[0], "dn");
		if (attrs == NULL || attrs[0] == NULL) {
			logger(LOG_ERR, gettext("dn for %s not found"),
			    unixname);
			return (IDMAP_ERR_ARG);
		}
		*dn = strdup(attrs[0]);
	}


	return (rc);

}

#define	FILTER	"(sAMAccountName=%s)"

/* Puts the values of attributes to unixuser and unixgroup, unless NULL */

static
idmap_stat
winname2dn(idmap_nm_handle_t *p, char *winname,
    int *is_wuser, char **dn, char **unixuser, char **unixgroup)
{
	idmap_stat rc = IDMAP_SUCCESS;
	char *base;
	char *filter;
	int flen;
	char *attribs[4];
	int i;
	LDAPMessage *results = NULL;
	LDAPMessage *entry;
	int ldap_rc;

	/* Query: */

	base = dns2dn(p->windomain, "");
	if (base == NULL) {
		return (IDMAP_ERR_MEMORY);
	}

	i = 0;
	attribs[i++] = "objectClass";
	if (unixuser != NULL)
		attribs[i++] = p->ad_unixuser_attr;
	if (unixgroup != NULL)
		attribs[i++] = p->ad_unixgroup_attr;
	attribs[i] = NULL;

	flen = snprintf(NULL, 0, FILTER, winname) + 1;
	if ((filter = (char *)malloc(flen)) == NULL) {
		free(base);
		return (IDMAP_ERR_MEMORY);
	}
	(void) snprintf(filter, flen, FILTER, winname);

	ldap_rc = ldap_search_s(p->ad, base, LDAP_SCOPE_SUBTREE, filter,
	    attribs, 0, &results);

	free(base);
	free(filter);

	if (ldap_rc != LDAP_SUCCESS) {
		logger(LOG_ERR,
		    "Ldap query to server %s port %d failed. (%s)",
		    p->ad_host, p->ad_port, ldap_err2string(ldap_rc));
		(void) ldap_msgfree(results);
		return (IDMAP_ERR_OTHER);
	}


	for (entry = ldap_first_entry(p->ad, results), *dn = NULL;
	    entry != NULL;
	    entry = ldap_next_entry(p->ad, entry)) {
		char	**values = NULL;
		int i = 0;
		values = ldap_get_values(p->ad, entry, "objectClass");

		if (values == NULL) {
			(void) ldap_msgfree(results);
			return (IDMAP_ERR_MEMORY);
		}

		for (i = 0; i < ldap_count_values(values); i++) {
		/*
		 * is_wuser can be IDMAP_UNKNOWN, in that case we accept
		 * both User/Group
		 */
			if (*is_wuser != IDMAP_NO &&
			    strcasecmp(values[i], "User") == 0 ||
			    *is_wuser != IDMAP_YES &&
			    strcasecmp(values[i], "Group") == 0) {
				*dn = ldap_get_dn(p->ad, entry);
				if (*dn == NULL) {
					ldap_value_free(values);
					(void) ldap_msgfree(results);
					return (IDMAP_ERR_MEMORY);
				}
				*is_wuser = strcasecmp(values[i], "User") == 0
				    ? IDMAP_YES : IDMAP_NO;
				break;
			}
		}

		ldap_value_free(values);
		if (*dn != NULL)
			break;
	}

	if (*dn == NULL) {
		logger(LOG_ERR,
		    *is_wuser == IDMAP_YES ? gettext("User %s@%s not found") :
		    *is_wuser == IDMAP_NO ? gettext("Group %s@%s not found") :
		    gettext("%s@%s not found"), winname, p->windomain);
		return (IDMAP_ERR_NOTFOUND);
	}

	if (unixuser != NULL)
		rc = extract_attribute(p, entry, p->ad_unixuser_attr,
		    unixuser);

	if (rc == IDMAP_SUCCESS && unixgroup != NULL)
		rc = extract_attribute(p, entry, p->ad_unixgroup_attr,
		    unixgroup);

	(void) ldap_msgfree(results);

	return (rc);
}


/* set the given attribute to the given value. If value is NULL, unset it */
static
idmap_stat
idmap_ad_set(idmap_nm_handle_t *p, char *dn, char *attr, char *value)
{
	idmap_stat rc = IDMAP_SUCCESS;
	int ldap_rc;
	char *new_values[2] = {NULL, NULL};
	LDAPMod *mods[2] = {NULL, NULL};

	mods[0] = (LDAPMod *)calloc(1, sizeof (LDAPMod));
	mods[0]->mod_type = strdup(attr);
	if (value != NULL) {
		mods[0]->mod_op = LDAP_MOD_REPLACE;
		new_values[0] = strdup(value);
		mods[0]->mod_values = new_values;
	} else {
		mods[0]->mod_op = LDAP_MOD_DELETE;
		mods[0]->mod_values = NULL;
	}

	ldap_rc = ldap_modify_s(p->ad, dn, mods);
	if (ldap_rc != LDAP_SUCCESS) {
		logger(LOG_ERR,
		    "Ldap modify of %s, attribute %s failed. (%s)",
		    dn, attr, ldap_err2string(ldap_rc));
		rc = IDMAP_ERR_INTERNAL;
	}


	ldap_mods_free(mods, 0);
	return (rc);
}


/*
 * This function takes the p argument just for the beauty of the symmetry
 * with idmap_ad_set (and for future enhancements).
 */
static
idmap_stat
/* LINTED E_FUNC_ARG_UNUSED */
idmap_nldap_set(idmap_nm_handle_t *p, ns_cred_t *nsc, char *dn, char *attr,
    char *value, bool_t is_new, int is_user)
{
	int ldaprc;
	ns_ldap_error_t *errorp = NULL;
	ns_ldap_attr_t	*attrs[2];



	attrs[0] = (ns_ldap_attr_t *)malloc(sizeof (ns_ldap_attr_t));
	if (attrs == NULL)
		return (IDMAP_ERR_MEMORY);

	attrs[0]->attrname = attr;

	if (value != NULL) {
		char **newattr = (char **)calloc(2, sizeof (char *));
		if (newattr == NULL) {
			free(attrs[0]);
			return (IDMAP_ERR_MEMORY);
		}
		newattr[0] = value;
		newattr[1] = NULL;

		attrs[0]->attrvalue = newattr;
		attrs[0]->value_count = 1;
	} else {
		attrs[0]->attrvalue = NULL;
		attrs[0]->value_count = 0;
	}


	attrs[1] = NULL;

	if (value == NULL) {
		ldaprc = __ns_ldap_delAttr(
		    is_user == IDMAP_YES ? "passwd": "group",
		    dn, (const ns_ldap_attr_t * const *)attrs,
		    nsc, 0, &errorp);
	} else if (is_new)
		ldaprc = __ns_ldap_addAttr(
		    is_user == IDMAP_YES ? "passwd": "group",
		    dn, (const ns_ldap_attr_t * const *)attrs,
		    nsc, 0, &errorp);
	else
		ldaprc = __ns_ldap_repAttr(
		    is_user == IDMAP_YES ? "passwd": "group",
		    dn, (const ns_ldap_attr_t * const *)attrs,
		    nsc, 0, &errorp);

	if (ldaprc != NS_LDAP_SUCCESS) {
		char *msg = "Cause unidentified";
		if (errorp != NULL) {
			(void) __ns_ldap_err2str(errorp->status, &msg);
		}
		logger(LOG_ERR, gettext("__ns_ldap_addAttr/rep/delAttr"
		    " failed (%s)"), msg);
		return (IDMAP_ERR_ARG);
	}

	return (IDMAP_SUCCESS);
}

idmap_stat
idmap_set_namemap(idmap_nm_handle_t *p, char *winname, char *unixname,
    int is_user, int is_wuser, int direction)
{
	idmap_stat	rc = IDMAP_SUCCESS;
	char		*dn = NULL;
	char		*oldwinname = NULL;
	char		*oldwindomain = NULL;

	if (direction == IDMAP_DIRECTION_W2U) {
		if (!p->is_ad) {
			rc = IDMAP_ERR_ARG;
			logger(LOG_ERR,
			    gettext("AD namemaps aren't set up."));
			goto cleanup;
		}

		rc = winname2dn(p, winname, &is_wuser,
		    &dn, NULL, NULL);
		if (rc != IDMAP_SUCCESS)
			goto cleanup;

		rc = idmap_ad_set(p, dn, is_user ? p->ad_unixuser_attr :
		    p->ad_unixgroup_attr, unixname);
		if (rc != IDMAP_SUCCESS)
			goto cleanup;

	}


	if (direction == IDMAP_DIRECTION_U2W) {
		char *fullname;

		if (!p->is_nldap) {
			rc = IDMAP_ERR_ARG;
			logger(LOG_ERR,
			    gettext("Native ldap namemaps aren't set up."));
			goto cleanup;
		}


		rc = unixname2dn(p, unixname, is_user, &dn,
		    &oldwinname, &oldwindomain);
		if (rc != IDMAP_SUCCESS)
			goto cleanup;

		if (p->windomain == NULL) {
			fullname = strdup(winname);
			if (fullname == NULL)
				rc = IDMAP_ERR_MEMORY;
				goto cleanup;
		} else {
			fullname = malloc(strlen(winname) +
			    strlen(p->windomain) + 2);
			if (fullname == NULL) {
				rc = IDMAP_ERR_MEMORY;
				goto cleanup;
			}

			(void) snprintf(fullname,
			    strlen(winname) + strlen(p->windomain) + 2,
			    "%s\\%s", p->windomain, winname);
		}
		rc = idmap_nldap_set(p, &p->nsc, dn, p->nldap_winname_attr,
		    fullname, oldwinname == NULL ? TRUE : FALSE, is_user);

		free(fullname);
		free(oldwindomain);
		free(oldwinname);

		if (rc != IDMAP_SUCCESS)
			goto cleanup;

	}

cleanup:
	if (dn != NULL)
		free(dn);

	if (oldwindomain != NULL)
		free(oldwindomain);

	if (oldwinname != NULL)
		free(oldwinname);

	return (rc);

}


idmap_stat
idmap_unset_namemap(idmap_nm_handle_t *p, char *winname, char *unixname,
    int is_user, int is_wuser, int direction)
{
	idmap_stat	rc = IDMAP_SUCCESS;
	char		*dn = NULL;
	char		*oldwinname = NULL;
	char		*oldwindomain = NULL;

	if (direction == IDMAP_DIRECTION_W2U) {
		if (!p->is_ad) {
			rc = IDMAP_ERR_ARG;
			logger(LOG_ERR,
			    gettext("AD namemaps aren't set up."));
			goto cleanup;
		}

		rc = winname2dn(p, winname, &is_wuser,
		    &dn, NULL, NULL);
		if (rc != IDMAP_SUCCESS)
			goto cleanup;

		rc = idmap_ad_set(p, dn, is_user ? p->ad_unixuser_attr :
		    p->ad_unixgroup_attr, unixname);
		if (rc != IDMAP_SUCCESS)
			goto cleanup;

	} else { /* direction == IDMAP_DIRECTION_U2W */
		if (!p->is_nldap) {
			rc = IDMAP_ERR_ARG;
			logger(LOG_ERR,
			    gettext("Native ldap namemaps aren't set up."));
			goto cleanup;
		}

		rc = unixname2dn(p, unixname, is_user, &dn, NULL, NULL);
		if (rc != IDMAP_SUCCESS)
			goto cleanup;

		rc = idmap_nldap_set(p, &p->nsc, dn, p->nldap_winname_attr,
		    NULL, TRUE, is_user);
		if (rc != IDMAP_SUCCESS)
			goto cleanup;

	}

cleanup:
	if (oldwindomain != NULL)
		free(oldwindomain);
	if (oldwinname != NULL)
		free(oldwinname);
	if (dn != NULL)
		free(dn);
	return (rc);
}

idmap_stat
idmap_get_namemap(idmap_nm_handle_t *p, int *is_source_ad, char **winname,
    char **windomain, int *is_wuser, char **unixuser, char **unixgroup)
{
	idmap_stat	rc = IDMAP_SUCCESS;
	char		*dn = NULL;

	*is_source_ad = IDMAP_UNKNOWN;
	if (*winname != NULL) {
		*is_source_ad = IDMAP_YES;

		if (p->is_ad == NULL) {
			rc = IDMAP_ERR_ARG;
			logger(LOG_ERR,
			    gettext("AD namemaps are not active."));
			goto cleanup;
			/* In future maybe resolve winname and try nldap? */
		}

		rc = winname2dn(p, *winname, is_wuser, &dn, unixuser,
		    unixgroup);
		if (rc != IDMAP_SUCCESS) {
			logger(LOG_ERR,
			    gettext("Winname %s@%s not found in AD."),
			    *winname, p->windomain);
		}
	} else if (*unixuser != NULL ||	*unixgroup != NULL) {
		char *unixname;
		int is_user;

		*is_source_ad = IDMAP_NO;

		if (p->is_nldap == NULL) {
			rc = IDMAP_ERR_ARG;
			logger(LOG_ERR,
			    gettext("Native ldap namemaps aren't active."));
			goto cleanup;
			/* In future maybe resolve unixname and try AD? */
		}

		if (*unixuser != NULL) {
			is_user = IDMAP_YES;
			unixname = *unixuser;
		} else if (*unixgroup != NULL) {
			is_user = IDMAP_NO;
			unixname = *unixgroup;
		}

		rc = unixname2dn(p, unixname, is_user, NULL, winname,
		    windomain);
		if (rc != IDMAP_SUCCESS) {
			logger(LOG_ERR,
			    gettext("%s %s not found in native ldap."),
			    is_user == IDMAP_YES ? "UNIX user" : "UNIX group",
			    unixname);
			goto cleanup;
		}
	} else {
		rc = IDMAP_ERR_ARG;
		goto cleanup;
	}

cleanup:
	return (rc);
}