OpenSolaris_b135/lib/nsswitch/ad/common/getpwnam.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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <pwd.h>
#include <idmap.h>
#include <ctype.h>
#include "ad_common.h"

/* passwd attributes and filters */
#define	_PWD_DN			"dn"
#define	_PWD_SAN		"sAMAccountName"
#define	_PWD_OBJSID		"objectSid"
#define	_PWD_PRIMARYGROUPID	"primaryGroupID"
#define	_PWD_CN			"cn"
#define	_PWD_HOMEDIRECTORY	"homedirectory"
#define	_PWD_LOGINSHELL		"loginshell"
#define	_PWD_OBJCLASS		"objectClass"

#define	_F_GETPWNAM		"(sAMAccountName=%.*s)"
#define	_F_GETPWUID		"(objectSid=%s)"

static const char *pwd_attrs[] = {
	_PWD_SAN,
	_PWD_OBJSID,
	_PWD_PRIMARYGROUPID,
	_PWD_CN,
	_PWD_HOMEDIRECTORY,
	_PWD_LOGINSHELL,
	_PWD_OBJCLASS,
	(char *)NULL
};

static int
update_buffer(ad_backend_ptr be, nss_XbyY_args_t *argp,
		const char *name, const char *domain,
		uid_t uid, gid_t gid, const char *gecos,
		const char *homedir, const char *shell)
{
	int	buflen;
	char	*buffer;

	if (be->db_type == NSS_AD_DB_PASSWD_BYNAME) {
		/*
		 * The canonical name obtained from AD lookup may not match
		 * the case of the name (i.e. key) in the request. Therefore,
		 * use the name from the request to construct the result.
		 */
		buflen = snprintf(NULL, 0, "%s:%s:%u:%u:%s:%s:%s",
		    argp->key.name, "x", uid, gid, gecos, homedir, shell) + 1;
	} else {
		if (domain == NULL)
			domain = WK_DOMAIN;
		buflen = snprintf(NULL, 0, "%s@%s:%s:%u:%u:%s:%s:%s",
		    name, domain, "x", uid, gid, gecos, homedir, shell) + 1;
	}


	if (argp->buf.result != NULL) {
		buffer = be->buffer = malloc(buflen);
		if (be->buffer == NULL)
			return (-1);
		be->buflen = buflen;
	} else {
		if (buflen > argp->buf.buflen)
			return (-1);
		buflen = argp->buf.buflen;
		buffer = argp->buf.buffer;
	}

	if (be->db_type == NSS_AD_DB_PASSWD_BYNAME)
		(void) snprintf(buffer, buflen, "%s:%s:%u:%u:%s:%s:%s",
		    argp->key.name, "x", uid, gid, gecos, homedir, shell);
	else
		(void) snprintf(buffer, buflen, "%s@%s:%s:%u:%u:%s:%s:%s",
		    name, domain, "x", uid, gid, gecos, homedir, shell);
	return (0);
}


#define	NET_SCHEME	"/net"

/*
 * 1) If the homeDirectory string is in UNC format then convert it into
 * a /net format. This needs to be revisited later but is fine for now
 * because Solaris does not support -hosts automount map for CIFS yet.
 *
 * 2) If homeDirectory contains ':' then return NULL because ':' is the
 * delimiter in passwd entries and may break apps that parse these entries.
 *
 * 3) For all other cases return the same string that was passed to
 * this function.
 */
static
char *
process_homedir(char *homedir)
{
	size_t	len, smb_len;
	char	*smb_homedir;
	int	i, slash = 0;

	len = strlen(homedir);

	if (strchr(homedir, ':') != NULL)
		/*
		 * Ignore paths that have colon ':' because ':' is a
		 * delimiter for the passwd entry.
		 */
		return (NULL);

	if (!(len > 1 && homedir[0] == '\\' && homedir[1] == '\\'))
		/* Keep homedir intact if not in UNC format */
		return (homedir);

	/*
	 * Convert UNC string into /net format
	 * Example: \\server\abc -> /net/server/abc
	 */
	smb_len = len + 1 + sizeof (NET_SCHEME);
	if ((smb_homedir = calloc(1, smb_len)) == NULL)
		return (NULL);
	(void) strlcpy(smb_homedir, NET_SCHEME, smb_len);
	for (i = strlen(smb_homedir); *homedir != '\0'; homedir++) {
		if (*homedir == '\\') {
			/* Reduce double backslashes into one */
			if (slash)
				slash = 0;
			else {
				slash = 1;
				smb_homedir[i++] = '/';
			}
		} else {
			smb_homedir[i++] = *homedir;
			slash = 0;
		}
	}
	return (smb_homedir);
}

/*
 * _nss_ad_passwd2str is the data marshaling method for the passwd getXbyY
 * (e.g., getbyuid(), getbyname(), getpwent()) backend processes. This method is
 * called after a successful AD search has been performed. This method will
 * parse the AD search values into the file format.
 * e.g.
 *
 * blue@whale:x:123456:10:Blue Whale:/:
 *
 */
static int
_nss_ad_passwd2str(ad_backend_ptr be, nss_XbyY_args_t *argp)
{
	int			nss_result;
	adutils_result_t	*result = be->result;
	const adutils_entry_t	*entry;
	char			**sid_v, *ptr, **pgid_v, *end;
	ulong_t			tmp;
	uint32_t		urid, grid;
	uid_t			uid;
	gid_t			gid;
	idmap_stat		gstat;
	idmap_get_handle_t 	*ig = NULL;
	char			**name_v, **dn_v, *domain = NULL;
	char			**gecos_v, **shell_v;
	char			**homedir_v = NULL, *homedir = NULL;
	char			*NULL_STR = "";

	if (result == NULL)
		return (NSS_STR_PARSE_PARSE);
	entry = adutils_getfirstentry(result);
	nss_result = NSS_STR_PARSE_PARSE;

	/* Create handles for idmap service */
	if (be->ih == NULL && idmap_init(&be->ih) != 0)
		goto result_pwd2str;
	if (idmap_get_create(be->ih, &ig) != 0)
		goto result_pwd2str;

	/* Get name */
	name_v = adutils_getattr(entry, _PWD_SAN);
	if (name_v == NULL || name_v[0] == NULL || *name_v[0] == '\0')
		goto result_pwd2str;

	/* Get domain */
	dn_v = adutils_getattr(entry, _PWD_DN);
	if (dn_v == NULL || dn_v[0] == NULL || *dn_v[0] == '\0')
		goto result_pwd2str;
	domain = adutils_dn2dns(dn_v[0]);

	/* Get objectSID (in text format) */
	sid_v = adutils_getattr(entry, _PWD_OBJSID);
	if (sid_v == NULL || sid_v[0] == NULL || *sid_v[0] == '\0')
		goto result_pwd2str;

	/* Break SID into prefix and rid */
	if ((ptr = strrchr(sid_v[0], '-')) == NULL)
		goto result_pwd2str;
	*ptr = '\0';
	end = ++ptr;
	tmp = strtoul(ptr, &end, 10);
	if (end == ptr || tmp > UINT32_MAX)
		goto result_pwd2str;
	urid = (uint32_t)tmp;

	/* We already have uid -- no need to call idmapd */
	if (be->db_type == NSS_AD_DB_PASSWD_BYUID)
		uid = argp->key.uid;
	else
		uid = be->uid;

	/* Get primaryGroupID */
	pgid_v = adutils_getattr(entry, _PWD_PRIMARYGROUPID);
	if (pgid_v == NULL || pgid_v[0] == NULL || *pgid_v[0] == '\0')
		/*
		 * If primaryGroupID is not found then we request
		 * a GID to be mapped to the given user's objectSID
		 * (diagonal mapping) and use this GID as the primary
		 * GID for the entry.
		 */
		grid = urid;
	else {
		end = pgid_v[0];
		tmp = strtoul(pgid_v[0], &end, 10);
		if (end == pgid_v[0] || tmp > UINT32_MAX)
			goto result_pwd2str;
		grid = (uint32_t)tmp;
	}

	/* Map group SID to GID using idmap service */
	if (idmap_get_gidbysid(ig, sid_v[0], grid, 0, &gid, &gstat) != 0)
		goto result_pwd2str;
	if (idmap_get_mappings(ig) != 0 || gstat != 0) {
		RESET_ERRNO();
		goto result_pwd2str;
	}

	/* Get gecos, homedirectory and shell information if available */
	gecos_v = adutils_getattr(entry, _PWD_CN);
	if (gecos_v == NULL || gecos_v[0] == NULL || *gecos_v[0] == '\0')
		gecos_v = &NULL_STR;

	homedir_v = adutils_getattr(entry, _PWD_HOMEDIRECTORY);
	if (homedir_v == NULL || homedir_v[0] == NULL || *homedir_v[0] == '\0')
		homedir = NULL_STR;
	else if ((homedir = process_homedir(homedir_v[0])) == NULL)
		homedir = NULL_STR;

	shell_v = adutils_getattr(entry, _PWD_LOGINSHELL);
	if (shell_v == NULL || shell_v[0] == NULL || *shell_v[0] == '\0')
		shell_v = &NULL_STR;

	if (update_buffer(be, argp, name_v[0], domain, uid, gid,
	    gecos_v[0], homedir, shell_v[0]) < 0)
		nss_result = NSS_STR_PARSE_ERANGE;
	else
		nss_result = NSS_STR_PARSE_SUCCESS;

result_pwd2str:
	idmap_get_destroy(ig);
	(void) idmap_fini(be->ih);
	be->ih = NULL;
	(void) adutils_freeresult(&be->result);
	free(domain);
	if (homedir != NULL_STR && homedir_v != NULL &&
	    homedir != homedir_v[0])
		free(homedir);
	return ((int)nss_result);
}

/*
 * getbyname gets a passwd entry by winname. This function constructs an ldap
 * search filter using the name invocation parameter and the getpwnam search
 * filter defined. Once the filter is constructed, we search for a matching
 * entry and marshal the data results into struct passwd for the frontend
 * process. The function _nss_ad_passwd2ent performs the data marshaling.
 */

static nss_status_t
getbyname(ad_backend_ptr be, void *a)
{
	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
	char		*searchfilter;
	char		name[SEARCHFILTERLEN];
	char		*dname;
	int		filterlen, namelen;
	int		flag;
	nss_status_t	stat;
	idmap_stat	idmaprc;
	uid_t		uid;
	gid_t		gid;
	int		is_user, is_wuser, try_idmap;
	idmap_handle_t	*ih;

	be->db_type = NSS_AD_DB_PASSWD_BYNAME;

	/* Sanitize name so that it can be used in our LDAP filter */
	if (_ldap_filter_name(name, argp->key.name, sizeof (name)) != 0)
		return ((nss_status_t)NSS_NOTFOUND);

	if ((dname = strchr(name, '@')) == NULL)
		return ((nss_status_t)NSS_NOTFOUND);

	*dname = '\0';
	dname++;

	/*
	 * Map the given name to UID using idmap service. If idmap
	 * call fails then this will save us doing AD discovery and
	 * AD lookup here.
	 */
	if (idmap_init(&be->ih) != IDMAP_SUCCESS)
		return ((nss_status_t)NSS_NOTFOUND);
	flag = (strcasecmp(dname, WK_DOMAIN) == 0) ?
	    IDMAP_REQ_FLG_WK_OR_LOCAL_SIDS_ONLY : 0;
	is_wuser = -1;
	is_user = 1;
	if (idmap_get_w2u_mapping(be->ih, NULL, NULL, name,
	    dname, flag, &is_user, &is_wuser, &be->uid, NULL,
	    NULL, NULL) != IDMAP_SUCCESS) {
		(void) idmap_fini(be->ih);
		be->ih = NULL;
		RESET_ERRNO();
		return ((nss_status_t)NSS_NOTFOUND);
	}

	/* If this is not a Well-Known SID then try AD lookup. */
	if (strcasecmp(dname, WK_DOMAIN) != 0) {
		/* Assemble filter using the given name */
		namelen = strlen(name);
		filterlen = snprintf(NULL, 0, _F_GETPWNAM, namelen, name) + 1;
		if ((searchfilter = (char *)malloc(filterlen)) == NULL)
			return ((nss_status_t)NSS_NOTFOUND);
		(void) snprintf(searchfilter, filterlen, _F_GETPWNAM,
		    namelen, name);
		stat = _nss_ad_lookup(be, argp, _PASSWD, searchfilter,
		    dname, &try_idmap);
		free(searchfilter);

		if (!try_idmap) {
			(void) idmap_fini(be->ih);
			be->ih = NULL;
			return (stat);
		}

	}

	/*
	 * Either this is a Well-Known SID or AD lookup failed. Map
	 * the given name to GID using idmap service and construct
	 * the passwd entry.
	 */
	is_wuser = -1;
	is_user = 0; /* Map name to primary gid */
	idmaprc = idmap_get_w2u_mapping(be->ih, NULL, NULL, name, dname,
	    flag, &is_user, &is_wuser, &gid, NULL, NULL, NULL);
	(void) idmap_fini(be->ih);
	be->ih = NULL;
	if (idmaprc != IDMAP_SUCCESS) {
		RESET_ERRNO();
		return ((nss_status_t)NSS_NOTFOUND);
	}

	/* Create passwd(4) style string */
	if (update_buffer(be, argp, name, dname,
	    be->uid, gid, "", "", "") < 0)
		return ((nss_status_t)NSS_NOTFOUND);

	/* Marshall the data, sanitize the return status and return */
	stat = _nss_ad_marshall_data(be, argp);
	return (_nss_ad_sanitize_status(be, argp, stat));
}


/*
 * getbyuid gets a passwd entry by uid number. This function constructs an ldap
 * search filter using the uid invocation parameter and the getpwuid search
 * filter defined. Once the filter is constructed, we search for a matching
 * entry and marshal the data results into struct passwd for the frontend
 * process. The function _nss_ad_passwd2ent performs the data marshaling.
 */

static nss_status_t
getbyuid(ad_backend_ptr be, void *a)
{
	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
	char			searchfilter[ADUTILS_MAXHEXBINSID + 14];
	char			*sidprefix = NULL;
	idmap_rid_t		rid;
	char			cbinsid[ADUTILS_MAXHEXBINSID + 1];
	char			*winname = NULL, *windomain = NULL;
	int			is_user, is_wuser;
	gid_t			gid;
	idmap_stat		idmaprc;
	int			ret, try_idmap;
	nss_status_t		stat;

	be->db_type = NSS_AD_DB_PASSWD_BYUID;

	stat = (nss_status_t)NSS_NOTFOUND;

	/* nss_ad does not support non ephemeral uids */
	if (argp->key.uid <= MAXUID)
		goto out;

	/* Map the given UID to a SID using the idmap service */
	if (idmap_init(&be->ih) != 0)
		goto out;
	if (idmap_get_u2w_mapping(be->ih, &argp->key.uid, NULL, 0,
	    1, NULL, &sidprefix, &rid, &winname, &windomain,
	    NULL, NULL) != 0) {
		RESET_ERRNO();
		goto out;
	}

	/*
	 * NULL winname implies a local SID or unresolvable SID both of
	 * which cannot be used to generated passwd(4) entry
	 */
	if (winname == NULL)
		goto out;

	/* If this is not a Well-Known SID try AD lookup */
	if (windomain != NULL && strcasecmp(windomain, WK_DOMAIN) != 0) {
		if (adutils_txtsid2hexbinsid(sidprefix, &rid,
		    &cbinsid[0], sizeof (cbinsid)) != 0)
			goto out;

		ret = snprintf(searchfilter, sizeof (searchfilter),
		    _F_GETPWUID, cbinsid);
		if (ret >= sizeof (searchfilter) || ret < 0)
			goto out;

		stat = _nss_ad_lookup(be, argp, _PASSWD, searchfilter,
		    windomain, &try_idmap);

		if (!try_idmap)
			goto out;
	}

	/* Map winname to primary gid using idmap service */
	is_user = 0;
	is_wuser = -1;
	idmaprc = idmap_get_w2u_mapping(be->ih, NULL, NULL,
	    winname, windomain, 0, &is_user, &is_wuser, &gid,
	    NULL, NULL, NULL);

	(void) idmap_fini(be->ih);
	be->ih = NULL;

	if (idmaprc != IDMAP_SUCCESS) {
		RESET_ERRNO();
		goto out;
	}

	/* Create passwd(4) style string */
	if (update_buffer(be, argp, winname, windomain,
	    argp->key.uid, gid, "", "", "") < 0)
		goto out;

	/* Marshall the data, sanitize the return status and return */
	stat = _nss_ad_marshall_data(be, argp);
	stat = _nss_ad_sanitize_status(be, argp, stat);

out:
	idmap_free(sidprefix);
	idmap_free(winname);
	idmap_free(windomain);
	(void) idmap_fini(be->ih);
	be->ih = NULL;
	return (stat);
}

static ad_backend_op_t passwd_ops[] = {
	_nss_ad_destr,
	_nss_ad_endent,
	_nss_ad_setent,
	_nss_ad_getent,
	getbyname,
	getbyuid
};

/*
 * _nss_ad_passwd_constr is where life begins. This function calls the
 * generic AD constructor function to define and build the abstract
 * data types required to support AD operations.
 */

/*ARGSUSED0*/
nss_backend_t *
_nss_ad_passwd_constr(const char *dummy1, const char *dummy2,
			const char *dummy3)
{

	return ((nss_backend_t *)_nss_ad_constr(passwd_ops,
	    sizeof (passwd_ops)/sizeof (passwd_ops[0]),
	    _PASSWD, pwd_attrs, _nss_ad_passwd2str));
}