/* Unix SMB/Netbios implementation. Version 1.9. Groupname handling Copyright (C) Jeremy Allison 1998. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * UNIX gid and Local or Domain SID resolution. This module resolves * only those entries in the map files, it is *NOT* responsible for * resolving UNIX groups not listed: that is an entirely different * matter, altogether... */ /* * * format of the file is: unixname NT Group name unixname Domain Admins (well-known Domain Group) unixname DOMAIN_NAME\NT Group name unixname OTHER_DOMAIN_NAME\NT Group name unixname DOMAIN_NAME\Domain Admins (well-known Domain Group) .... if the DOMAIN_NAME\ component is left off, then your own domain is assumed. * * */ #include "includes.h" extern int DEBUGLEVEL; extern fstring global_sam_name; /* we can map either local aliases or domain groups */ typedef enum { GROUP_LOCAL, GROUP_DOMAIN } GROUP_TYPE; /************************************************************************** Groupname map functionality. The code loads a groupname map file and (currently) loads it into a linked list. This is slow and memory hungry, but can be changed into a more efficient storage format if the demands on it become excessive. ***************************************************************************/ typedef struct group_name_info { char *nt_name; char *nt_domain; char *unix_name; DOM_SID sid; gid_t unix_gid; } GROUP_NAME_INFO; typedef struct name_map { ubi_slNode next; GROUP_NAME_INFO grp; } name_map_entry; static ubi_slList groupname_map_list; static ubi_slList aliasname_map_list; static void delete_name_entry(name_map_entry *gmep) { if (gmep->grp.nt_name) { free(gmep->grp.nt_name); } if (gmep->grp.nt_domain) { free(gmep->grp.nt_domain); } if (gmep->grp.unix_name) { free(gmep->grp.unix_name); } free((char*)gmep); } /************************************************************************** Delete all the entries in the name map list. ***************************************************************************/ static void delete_map_list(ubi_slList *map_list) { name_map_entry *gmep; while ((gmep = (name_map_entry *)ubi_slRemHead(map_list )) != NULL) { delete_name_entry(gmep); } } /************************************************************************** makes a group sid out of a domain sid and a _unix_ gid. ***************************************************************************/ static BOOL make_mydomain_sid(GROUP_NAME_INFO *grp, GROUP_TYPE type) { uint32 tmp_rid; DOM_SID sid; uint8 tmp_type; DEBUG(10,("make_mydomain_sid\n")); if (!map_domain_name_to_sid(&grp->sid, &(grp->nt_domain))) { DEBUG(0,("make_mydomain_sid: unknown domain %s\n", grp->nt_domain)); return False; } if (strequal(grp->nt_domain, global_sam_name) && lookup_builtin_grp_name(grp->nt_name, &sid, &tmp_type) == 0x0) { sid_copy(&grp->sid, &sid); return True; } else { BOOL ret; fstring sid_str; if (type == GROUP_DOMAIN) { tmp_rid = pwdb_gid_to_group_rid(grp->unix_gid); } else { tmp_rid = pwdb_gid_to_alias_rid(grp->unix_gid); } ret = sid_append_rid(&(grp->sid), tmp_rid); sid_to_string(sid_str, &grp->sid); DEBUG(10,("nt name %s gid %d mapped to %s\n", grp->nt_name, grp->unix_gid, sid_str)); return ret; } } /************************************************************************** makes a group sid out of an nt domain, nt group name or a unix group name. ***************************************************************************/ static BOOL unix_name_to_group_info(GROUP_NAME_INFO *grp, GROUP_TYPE type) { struct group *gptr = NULL; /* * Attempt to get the unix gid_t for this name. */ DEBUG(5,("unix_name_to_group_info: unix_name:%s\n", grp->unix_name)); gptr = (struct group *)getgrnam(grp->unix_name); if (gptr == NULL) { DEBUG(0,("unix_name_to_group_info: getgrnam for group %s\ failed. Error was %s.\n", grp->unix_name, strerror(errno) )); return False; } grp->unix_gid = (gid_t)gptr->gr_gid; DEBUG(5,("unix_name_to_group_info: unix gid:%d\n", grp->unix_gid)); /* * Now map the name to an NT SID+RID. */ if (grp->nt_domain != NULL && !strequal(grp->nt_domain, global_sam_name)) { /* Must add client-call lookup code here, to * resolve remote domain's sid and the group's rid, * in that domain. * * NOTE: it is _incorrect_ to put code here that assumes * that we can call pwdb_gid_to_group_rid() or _alias_rid(): * it is a totally different domain for which we are *NOT* * responsible. * for foriegn domains for which we are *NOT* the PDC, all * we can be responsible for is the unix * gid_t to which * the foriegn SID+rid maps to, on this _local_ machine. */ if (!map_domain_name_to_sid(&grp->sid, &(grp->nt_domain))) { DEBUG(0,("unix_name_to_group_info: no known sid for %s\n", grp->nt_domain)); return False; } } return make_mydomain_sid(grp, type); } static BOOL make_name_entry(name_map_entry **new_ep, char *nt_domain, char *nt_group, char *unix_group, GROUP_TYPE type) { /* * Create the list entry and add it onto the list. */ DEBUG(5,("make_name_entry:%s,%s,%s\n", nt_domain, nt_group, unix_group)); (*new_ep) = (name_map_entry *)malloc(sizeof(name_map_entry)); if ((*new_ep) == NULL) { DEBUG(0,("make_name_entry: malloc fail for name_map_entry.\n")); return False; } ZERO_STRUCTP(*new_ep); (*new_ep)->grp.nt_name = strdup(nt_group ); (*new_ep)->grp.nt_domain = strdup(nt_domain ); (*new_ep)->grp.unix_name = strdup(unix_group); if ((*new_ep)->grp.nt_name == NULL || (*new_ep)->grp.unix_name == NULL) { DEBUG(0,("make_name_entry: malloc fail for names in name_map_entry.\n")); delete_name_entry((*new_ep)); return False; } /* * look up the group names, make the Group-SID and unix gid */ if (!unix_name_to_group_info(&(*new_ep)->grp, type)) { delete_name_entry((*new_ep)); return False; } return True; } /************************************************************************** Load a name map file. Sets last accessed timestamp. ***************************************************************************/ static ubi_slList *load_name_map(GROUP_TYPE type) { static time_t groupmap_file_last_modified = (time_t)0; static time_t aliasmap_file_last_modified = (time_t)0; static BOOL initialised_group = False; static BOOL initialised_alias = False; char *groupname_map_file = lp_groupname_map(); char *aliasname_map_file = lp_aliasname_map(); SMB_STRUCT_STAT st; FILE *fp; char *s; pstring buf; name_map_entry *new_ep; time_t *file_last_modified; int *initialised; char *map_file; ubi_slList *map_list; if (type == GROUP_DOMAIN) { file_last_modified = &groupmap_file_last_modified; initialised = &initialised_group; map_file = groupname_map_file; map_list = &groupname_map_list; } else { file_last_modified = &aliasmap_file_last_modified; initialised = &initialised_alias; map_file = aliasname_map_file; map_list = &aliasname_map_list; } if (!(*initialised)) { DEBUG(10,("initialising group map %s\n", map_file)); ubi_slInitList(map_list); (*initialised) = True; } if (!*map_file) { return map_list; } if (sys_stat(map_file, &st) != 0) { DEBUG(0, ("load_name_map: Unable to stat file %s. Error was %s\n", map_file, strerror(errno) )); return map_list; } /* * Check if file has changed. */ if (st.st_mtime <= (*file_last_modified)) { return map_list; } (*file_last_modified) = st.st_mtime; /* * Load the file. */ fp = fopen(map_file,"r"); if (!fp) { DEBUG(0,("load_name_map: can't open name map %s. Error was %s\n", map_file, strerror(errno))); return map_list; } /* * Throw away any previous list. */ delete_map_list(map_list); DEBUG(4,("load_name_map: Scanning name map %s\n",map_file)); while ((s = fgets_slash(buf, sizeof(buf), fp)) != NULL) { pstring unixname; pstring nt_name; fstring nt_domain; fstring nt_group; char *p; DEBUG(10,("Read line |%s|\n", s)); memset(nt_name, 0, sizeof(nt_name)); if (!*s || strchr("#;",*s)) continue; if (!next_token(&s,unixname, "\t\n\r=", sizeof(unixname))) continue; if (!next_token(&s,nt_name, "\t\n\r=", sizeof(nt_name))) continue; trim_string(unixname, " ", " "); trim_string(nt_name, " ", " "); if (!*nt_name) continue; if (!*unixname) continue; DEBUG(5,("unixname = %s, ntname = %s.\n", unixname, nt_name)); p = strchr(nt_name, '\\'); if (p == NULL) { memset(nt_domain, 0, sizeof(nt_domain)); fstrcpy(nt_group, nt_name); } else { *p = 0; p++; fstrcpy(nt_domain, nt_name); fstrcpy(nt_group , p); } if (make_name_entry(&new_ep, nt_domain, nt_name, unixname, type)) { ubi_slAddTail(map_list, (ubi_slNode *)new_ep); } } DEBUG(10,("load_name_map: Added %ld entries to name map.\n", ubi_slCount(map_list))); fclose(fp); return map_list; } /*********************************************************** Lookup by SID ************************************************************/ static BOOL map_sid(GROUP_TYPE type, DOM_SID *psid, gid_t *gid, char *ntname, char *ntdomain) { name_map_entry *gmep; ubi_slList *map_list; /* * Initialise and load if not already loaded. */ map_list = load_name_map(type); for (gmep = (name_map_entry *)ubi_slFirst(map_list); gmep != NULL; gmep = (name_map_entry *)ubi_slNext(gmep )) { if (sid_equal(&gmep->grp.sid, psid)) { if (gid != NULL) { *gid = gmep->grp.unix_gid; } if (ntname != NULL) { fstrcpy(ntname, gmep->grp.nt_name); } if (ntdomain != NULL) { fstrcpy(ntdomain, gmep->grp.nt_domain); } DEBUG(7,("map_sid: Mapping unix group %s to nt group %s.\n", gmep->grp.unix_name, gmep->grp.nt_name )); return True; } } return False; } /*********************************************************** Lookup nt name. ************************************************************/ static BOOL map_ntname(GROUP_TYPE type, char *ntname, char *ntdomain, DOM_SID *psid, char *unixname, gid_t *gid) { name_map_entry *gmep; ubi_slList *map_list; /* * Initialise and load if not already loaded. */ map_list = load_name_map(type); for (gmep = (name_map_entry *)ubi_slFirst(map_list); gmep != NULL; gmep = (name_map_entry *)ubi_slNext(gmep )) { if (strequal(gmep->grp.nt_name , ntname) && strequal(gmep->grp.nt_domain, ntdomain)) { if (psid != NULL) { sid_copy(psid, &gmep->grp.sid); } if (gid != NULL) { *gid = gmep->grp.unix_gid; } if (unixname != NULL) { fstrcpy(unixname, gmep->grp.unix_name); } DEBUG(7,("map_ntname: Mapping unix group %s to nt group %s.\n", gmep->grp.unix_name, gmep->grp.nt_name )); return True; } } return False; } /*********************************************************** Lookup unix name. ************************************************************/ static BOOL map_unixname(GROUP_TYPE type, char *unixname, DOM_SID *psid, char *ntname, char *ntdomain) { name_map_entry *gmep; ubi_slList *map_list; /* * Initialise and load if not already loaded. */ map_list = load_name_map(type); for (gmep = (name_map_entry *)ubi_slFirst(map_list); gmep != NULL; gmep = (name_map_entry *)ubi_slNext(gmep )) { if (strequal(gmep->grp.unix_name, unixname)) { if (psid != NULL) { sid_copy(psid, &gmep->grp.sid); } if (ntname != NULL) { fstrcpy(ntname, gmep->grp.nt_name); } if (ntdomain != NULL) { fstrcpy(ntdomain, gmep->grp.nt_domain); } DEBUG(7,("map_unixname: Mapping unix group %s to nt group %s.\n", gmep->grp.unix_name, gmep->grp.nt_name )); return True; } } return False; } /*********************************************************** Lookup by gid_t. ************************************************************/ static BOOL map_gid(GROUP_TYPE type, gid_t gid, DOM_SID *psid, char *ntname, char *ntdomain) { name_map_entry *gmep; ubi_slList *map_list; /* * Initialise and load if not already loaded. */ map_list = load_name_map(type); for (gmep = (name_map_entry *)ubi_slFirst(map_list); gmep != NULL; gmep = (name_map_entry *)ubi_slNext(gmep )) { fstring sid_str; sid_to_string(sid_str, &gmep->grp.sid); DEBUG(10,("map_gid: enum entry unix group %s %d nt %s %s\n", gmep->grp.unix_name, gmep->grp.unix_gid, gmep->grp.nt_name, sid_str)); if (gmep->grp.unix_gid == gid) { if (psid != NULL) { sid_copy(psid, &gmep->grp.sid); } if (ntname != NULL) { fstrcpy(ntname, gmep->grp.nt_name); } if (ntdomain != NULL) { fstrcpy(ntdomain, gmep->grp.nt_domain); } DEBUG(7,("map_gid: Mapping unix group %s to nt group %s.\n", gmep->grp.unix_name, gmep->grp.nt_name )); return True; } } return False; } /*********************************************************** * * Call four functions to resolve unix group ids and either * local group SIDs or domain group SIDs listed in the local group * or domain group map files. * * Note that it is *NOT* the responsibility of these functions to * resolve entries that are not in the map files. * * Any SID can be in the map files (i.e from any Domain). * ***********************************************************/ /*********************************************************** Lookup a Group entry by sid. ************************************************************/ BOOL map_group_sid(DOM_SID *psid, gid_t *gid, char *group_name, char *nt_domain) { return map_sid(GROUP_DOMAIN, psid, gid, group_name, nt_domain); } /*********************************************************** Lookup an Alias SID entry by name. ************************************************************/ BOOL map_alias_sid(DOM_SID *psid, gid_t *gid, char *alias_name, char *nt_domain) { return map_sid(GROUP_LOCAL, psid, gid, alias_name, nt_domain); } /*********************************************************** Lookup a UNIX Group entry by name. ************************************************************/ BOOL map_unix_group_name(char *group_name, DOM_SID *psid, char *ntgroup_name, char *nt_domain) { return map_unixname(GROUP_DOMAIN, group_name, psid, ntgroup_name, nt_domain); } /*********************************************************** Lookup a UNIX Alias entry by name. ************************************************************/ BOOL map_unix_alias_name(char *alias_name, DOM_SID *psid, char *ntalias_name, char *nt_domain) { return map_unixname(GROUP_LOCAL, alias_name, psid, ntalias_name, nt_domain); } /*********************************************************** Lookup a Group entry ************************************************************/ BOOL map_nt_group_name(char *ntgroup_name, char *nt_domain, DOM_SID *psid, char *group_name, gid_t *gid) { return map_ntname(GROUP_DOMAIN, ntgroup_name, nt_domain, psid, group_name, gid); } /*********************************************************** Lookup an Alias name entry ************************************************************/ BOOL map_nt_alias_name(char *ntalias_name, char *nt_domain, DOM_SID *psid, char *alias_name, gid_t *gid) { return map_ntname(GROUP_LOCAL, ntalias_name, nt_domain, psid, alias_name, gid); } /*********************************************************** Lookup an Alias SID entry by gid_t. ************************************************************/ BOOL map_alias_gid(gid_t gid, DOM_SID *psid, char *nt_als_name, char *nt_domain) { return map_gid(GROUP_LOCAL, gid, psid, nt_als_name, nt_domain); } /*********************************************************** Lookup a Group SID entry by gid_t. ************************************************************/ BOOL map_group_gid( gid_t gid, DOM_SID *psid, char *nt_grp_name, char *nt_domain) { return map_gid(GROUP_DOMAIN, gid, psid, nt_grp_name, nt_domain); }