summaryrefslogtreecommitdiff
path: root/usr/src/lib/nsswitch/ad/common/getpwnam.c
diff options
context:
space:
mode:
authorBaban Kenkre <Baban.Kenkre@Sun.COM>2008-11-07 12:09:53 -0800
committerBaban Kenkre <Baban.Kenkre@Sun.COM>2008-11-07 12:09:53 -0800
commit2b4a78020b9c38d1b95e2f3fefa6d6e4be382d1f (patch)
treeb9f0bc817d950cefb1af4653dad8de547a17e061 /usr/src/lib/nsswitch/ad/common/getpwnam.c
parent0a2b1d27cac02f57e17b310f8baeb1dda082c83a (diff)
downloadillumos-joyent-2b4a78020b9c38d1b95e2f3fefa6d6e4be382d1f.tar.gz
PSARC/2008/441 Active Directory name service module (nss_ad)
6722476 name service switch module for AD (nss_ad) needed
Diffstat (limited to 'usr/src/lib/nsswitch/ad/common/getpwnam.c')
-rw-r--r--usr/src/lib/nsswitch/ad/common/getpwnam.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/usr/src/lib/nsswitch/ad/common/getpwnam.c b/usr/src/lib/nsswitch/ad/common/getpwnam.c
new file mode 100644
index 0000000000..ece50a54b0
--- /dev/null
+++ b/usr/src/lib/nsswitch/ad/common/getpwnam.c
@@ -0,0 +1,518 @@
+/*
+ * 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));
+}