opendirectory_sam.c [plain text]
#include "includes.h"
#include "opendirectory.h"
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
static CFDictionaryRef sam_searchattr_first(
struct opendirectory_session * session,
const char * type,
const char * attr,
const char * value);
static CFDictionaryRef sam_searchname_first(
struct opendirectory_session * session,
const char * type,
const char * name);
static void get_sid_for_samrecord(void *mem_ctx,
struct opendirectory_session *session,
CFDictionaryRef sam_record,
DOM_SID *record_sid)
{
fstring sidstr;
char * record_name;
char * record_path;
record_path = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDSNAttrMetaNodeLocation);
record_name = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDSNAttrRecordName);
SMB_ASSERT(record_path);
SMB_ASSERT(record_name);
DEBUG (5, ("resolving SID for record=%s within %s\n",
record_name, record_path));
if (opendirectory_node_path_is_local(session, record_path)) {
sid_copy(record_sid, get_global_sam_sid());
DEBUG(4, ("record=%s is local\n", record_name));
return;
}
if (opendirectory_domain_sid_from_path(mem_ctx, session,
record_path, record_sid)) {
DEBUG(4, ("record=%s is relative to domain SID=%s\n",
record_name, sid_to_string(sidstr, record_sid)));
} else {
string_to_sid(record_sid,
"S-1-5-21-987654321-987654321-987654321");
DEBUG(4, ("no domain SID for %s, assuming synthetic SID=%s\n",
record_path, sid_to_string(sidstr, record_sid)));
}
}
static BOOL
memberd_record_ugid_to_sid(
void * mem_ctx,
CFDictionaryRef sam_record,
const char * attr,
int id_type,
DOM_SID * sid)
{
nt_sid_t ntsid;
uuid_t uuid;
id_t ugid;
int err;
char * strval;
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, attr);
if (!strval) {
return False;
}
ugid = (id_t)strtol(strval, NULL, 10 );
switch (id_type) {
case MBR_ID_TYPE_UID:
err = mbr_uid_to_uuid(ugid, uuid);
break;
case MBR_ID_TYPE_GID:
err = mbr_gid_to_uuid(ugid, uuid);
break;
default:
return False;
}
if (err != 0) {
DEBUG(6, ("failed to map %s %d: %s\n",
id_type == MBR_ID_TYPE_UID ? "UID" : "GID",
ugid, strerror(err)));
}
err = mbr_uuid_to_sid(uuid, &ntsid);
if (err != 0) {
uuid_string_t str;
uuid_unparse(uuid, str);
DEBUG(6, ("failed to map %s: %s\n",
str, strerror(err)));
return False;
}
convert_ntsid_to_DOMSID(sid, &ntsid);
return True;
}
static BOOL
memberd_record_uuid_to_sid(
void * mem_ctx,
CFDictionaryRef sam_record,
DOM_SID * sid)
{
nt_sid_t ntsid;
uuid_t uuid;
char * strval;
int err;
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrGeneratedUID);
if (!strval) {
return False;
}
if (uuid_parse(strval, uuid) != 0) {
return False;
}
err = mbr_uuid_to_sid(uuid, &ntsid);
if (err != 0) {
DEBUG(6, ("failed to map %s: %s\n",
strval, strerror(err)));
return False;
}
convert_ntsid_to_DOMSID(sid, &ntsid);
return True;
}
BOOL opendirectory_find_usersid_from_record(
struct opendirectory_session *session,
CFDictionaryRef sam_record,
DOM_SID *sid)
{
void * mem_ctx = talloc_init("opendirectory_find_usersid_from_record");
DOM_SID samsid;
char * strval;
BOOL ret = False;
char * record_type;
record_type = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDSNAttrRecordType);
if (!record_type) {
goto done;
}
DEBUG(6, ("determining user SID for %s record\n", record_type));
if (strcmp(record_type, kDSStdRecordTypeUsers) != 0 &&
strcmp(record_type, kDSStdRecordTypeComputers) != 0) {
goto done;
}
if (memberd_record_uuid_to_sid(mem_ctx, sam_record, sid)) {
ret = True;
goto done;
}
if (memberd_record_ugid_to_sid(mem_ctx, sam_record,
kDS1AttrUniqueID, MBR_ID_TYPE_UID, sid)) {
ret = True;
goto done;
}
if (!session->local_path_cache) {
session->local_path_cache = opendirectory_local_paths(session);
}
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrSMBSID);
if (strval) {
string_to_sid(sid, strval);
ret = True;
goto done;
}
get_sid_for_samrecord(mem_ctx, session, sam_record, &samsid);
sid_copy(sid, &samsid);
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrSMBRID);
if (strval) {
uint32_t rid = (uint32_t)strtoul(strval, NULL, 10 );
sid_append_rid(sid, rid);
ret = True;
goto done;
}
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrUniqueID);
if (strval) {
int32_t uid = (int32_t)strtol(strval, NULL, 10 );
uint32_t rid;
if (opendirectory_match_record_attribute(sam_record,
kDSNAttrRecordName, lp_guestaccount())) {
rid = DOMAIN_USER_RID_GUEST;
} else {
rid = algorithmic_pdb_uid_to_user_rid(uid);
}
sid_append_rid(sid, rid);
ret = True;
goto done;
}
done:
TALLOC_FREE(mem_ctx);
return ret;
}
static BOOL find_groupsid_from_user(void *mem_ctx,
struct opendirectory_session *session,
CFDictionaryRef sam_record,
DOM_SID *group_sid,
unsigned fallback_rid)
{
CFDictionaryRef group_record;
char * strval;
DOM_SID samsid;
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrSMBPrimaryGroupSID);
if (strval) {
string_to_sid(group_sid, strval);
return True;
}
get_sid_for_samrecord(mem_ctx, session, sam_record, &samsid);
sid_copy(group_sid, &samsid);
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record,
kDS1AttrSMBGroupRID);
if (strval) {
uint32_t rid;
rid = (uint32_t)strtoul(strval, NULL, 10 );
sid_append_rid(group_sid, rid);
return True;
}
if (memberd_record_ugid_to_sid(mem_ctx, sam_record,
kDS1AttrPrimaryGroupID, MBR_ID_TYPE_GID, group_sid)) {
return True;
}
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrPrimaryGroupID);
if (!strval) {
return False;
}
group_record = sam_searchattr_first(session, kDSStdRecordTypeGroups,
kDS1AttrPrimaryGroupID, strval);
if (group_record) {
BOOL ret;
ret = opendirectory_find_groupsid_from_record(session,
group_record, group_sid);
CFRelease(group_record);
return ret;
}
sid_copy(group_sid, get_global_sam_sid());
sid_append_rid(group_sid, fallback_rid);
return True;
}
BOOL opendirectory_find_groupsid_from_record(
struct opendirectory_session *session,
CFDictionaryRef sam_record,
DOM_SID *sid)
{
void * mem_ctx = talloc_init("opendirectory_find_groupsid_from_record");
DOM_SID samsid;
char * strval;
BOOL ret = False;
char * record_type;
record_type = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDSNAttrRecordType);
if (!record_type) {
goto done;
}
DEBUG(6, ("determining group SID for %s record\n", record_type));
if (strcmp(record_type, kDSStdRecordTypeGroups) == 0 &&
memberd_record_uuid_to_sid(mem_ctx, sam_record, sid)) {
ret = True;
goto done;
}
if (!session->local_path_cache) {
session->local_path_cache = opendirectory_local_paths(session);
}
if (strcmp(record_type, kDSStdRecordTypeUsers) == 0) {
ret = find_groupsid_from_user(mem_ctx, session,
sam_record, sid,
DOMAIN_GROUP_RID_USERS);
goto done;
} else if (strcmp(record_type, kDSStdRecordTypeComputers) == 0) {
ret = find_groupsid_from_user(mem_ctx, session,
sam_record, sid,
DOMAIN_GROUP_RID_COMPUTERS);
goto done;
}
if (strcmp(record_type, kDSStdRecordTypeGroups) != 0) {
goto done;
}
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrSMBPrimaryGroupSID);
if (strval) {
string_to_sid(sid, strval);
ret = True;
goto done;
}
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrSMBSID);
if (strval) {
string_to_sid(sid, strval);
ret = True;
goto done;
}
get_sid_for_samrecord(mem_ctx, session, sam_record, &samsid);
sid_copy(sid, &samsid);
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrSMBGroupRID);
if (strval) {
uint32_t rid = (uint32_t)strtoul(strval, NULL, 10 );
sid_append_rid(sid, rid);
ret = True;
goto done;
}
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrSMBRID);
if (strval) {
uint32_t rid = (uint32_t)strtoul(strval, NULL, 10 );
sid_append_rid(sid, rid);
ret = True;
goto done;
}
if (memberd_record_ugid_to_sid(mem_ctx, sam_record,
kDS1AttrPrimaryGroupID, MBR_ID_TYPE_GID, sid)) {
ret = True;
goto done;
}
strval = opendirectory_get_record_attribute(mem_ctx,
sam_record, kDS1AttrPrimaryGroupID);
if (strval) {
int32_t gid = (int32_t)strtol(strval, NULL, 10 );
uint32_t rid;
if (opendirectory_match_record_attribute(sam_record,
kDSNAttrRecordName, lp_guestaccount())) {
rid = DOMAIN_GROUP_RID_GUESTS;
} else {
rid = algorithmic_pdb_gid_to_group_rid(gid);
}
sid_append_rid(sid, rid);
ret = True;
goto done;
}
if (strcmp(record_type, kDSStdRecordTypeComputers) == 0) {
sid_append_rid(sid, DOMAIN_GROUP_RID_COMPUTERS);
ret = True;
}
done:
TALLOC_FREE(mem_ctx);
return ret;
}
CFDictionaryRef opendirectory_sam_searchugid_first(
struct opendirectory_session *session,
const char *type,
const char *attr,
const id_t ugid)
{
char buf[64];
CFMutableArrayRef records = NULL;
tDirStatus status;
snprintf(buf, sizeof(buf), "%u", ugid);
status = opendirectory_sam_searchattr(session,
&records, type, attr, buf);
if (status == eDSNoErr && records == NULL) {
snprintf(buf, sizeof(buf), "%d", ugid);
status = opendirectory_sam_searchattr(session,
&records, type, attr, buf);
}
if (status == eDSNoErr && records) {
CFDictionaryRef first =
(CFDictionaryRef)CFArrayGetValueAtIndex(records, 0);
CFRetain(first);
CFRelease(records);
return first;
}
return NULL;
}
static CFDictionaryRef sam_searchuuid_first(
struct opendirectory_session *session,
const char *type,
const uuid_t uuid)
{
uuid_string_t str;
CFMutableArrayRef records = NULL;
tDirStatus status;
uuid_unparse_upper(uuid, str);
status = opendirectory_sam_searchattr(session,
&records, type, kDS1AttrGeneratedUID, str);
if (status == eDSNoErr && records) {
CFDictionaryRef first =
(CFDictionaryRef)CFArrayGetValueAtIndex(records, 0);
CFRetain(first);
CFRelease(records);
return first;
}
return NULL;
}
static CFDictionaryRef sam_searchname_first(
struct opendirectory_session *session,
const char *type,
const char *name)
{
CFMutableArrayRef records = NULL;
tDirStatus status;
status = opendirectory_sam_searchname(session,
&records, type, name);
if (status == eDSNoErr && records) {
CFDictionaryRef first =
(CFDictionaryRef)CFArrayGetValueAtIndex(records, 0);
CFRetain(first);
CFRelease(records);
return first;
}
return NULL;
}
static CFDictionaryRef sam_searchattr_first(
struct opendirectory_session *session,
const char *type,
const char *attr,
const char *value)
{
CFMutableArrayRef records = NULL;
tDirStatus status;
status = opendirectory_sam_searchattr(session,
&records, type, attr, value);
if (status == eDSNoErr && records) {
CFDictionaryRef first =
(CFDictionaryRef)CFArrayGetValueAtIndex(records, 0);
CFRetain(first);
CFRelease(records);
return first;
}
return NULL;
}
static CFDictionaryRef find_record_from_usersid_and_domsid(
struct opendirectory_session *session,
const char *domain_name __unused,
const DOM_SID *domain_sid,
const void *data)
{
CFDictionaryRef sam_record;
fstring rid_string;
fstring uid_string;
uint32_t rid;
const DOM_SID * sid = (const DOM_SID *)data;
if (!sid_peek_check_rid(domain_sid, sid, &rid)) {
return NULL;
}
if (rid == DOMAIN_USER_RID_GUEST) {
return sam_searchname_first(session, kDSStdRecordTypeUsers,
lp_guestaccount());
}
snprintf(rid_string, sizeof(rid_string) - 1, "%u", rid);
snprintf(uid_string, sizeof(uid_string) - 1, "%u",
algorithmic_pdb_user_rid_to_uid(rid));
sam_record = sam_searchattr_first(session, kDSStdRecordTypeUsers,
kDS1AttrSMBRID, rid_string);
if (sam_record) {
return sam_record;
}
sam_record = sam_searchattr_first(session, kDSStdRecordTypeComputers,
kDS1AttrSMBRID, rid_string);
if (sam_record) {
return sam_record;
}
if (!algorithmic_pdb_rid_is_user(rid)) {
return NULL;
}
sam_record = sam_searchattr_first(session, kDSStdRecordTypeUsers,
kDS1AttrUniqueID, uid_string);
if (sam_record) {
return sam_record;
}
sam_record = sam_searchattr_first(session, kDSStdRecordTypeComputers,
kDS1AttrUniqueID, uid_string);
if (sam_record) {
return sam_record;
}
return NULL;
}
static BOOL apple_wellknown_sid(const DOM_SID *sid, DOM_SID *apple_sid)
{
uint32_t rid;
DOM_SID apple_wellknown =
{ 1, 1, {0,0,0,0,0,5}, {21,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid)) {
return False;
}
return sid_compose(apple_sid, &apple_wellknown, rid);
}
static CFDictionaryRef memberd_find_record_from_sid(
struct opendirectory_session *session,
const int id_type,
const DOM_SID *sid)
{
id_t ugid;
int ugid_type;
uuid_t uuid;
int err;
const char * type =
(id_type == MBR_ID_TYPE_UID ? kDSStdRecordTypeUsers
: kDSStdRecordTypeGroups);
const char * ugid_attr =
(id_type == MBR_ID_TYPE_UID ? kDS1AttrUniqueID
: kDS1AttrPrimaryGroupID);
CFDictionaryRef sam_record = NULL;
SMB_ASSERT(id_type == MBR_ID_TYPE_UID || id_type == MBR_ID_TYPE_GID);
if (!memberd_sid_to_uuid(sid, uuid)) {
return NULL;
}
if (is_compatibility_guid(uuid, &ugid_type, &ugid) &&
ugid_type == id_type) {
DEBUG(6, ("searching compatibility ugid type=%d, ugid=%u\n",
id_type, ugid));
sam_record = opendirectory_sam_searchugid_first(session,
type, ugid_attr, ugid);
} else {
sam_record = sam_searchuuid_first(session, type, uuid);
}
if (sam_record) {
return sam_record;
}
err = mbr_uuid_to_id(uuid, &ugid, &ugid_type);
if (err) {
uuid_string_t str;
uuid_unparse_upper(uuid, str);
DEBUG(6, ("unable to map %s to an ID: %s\n",
str, strerror(err)));
return NULL;
}
if (ugid_type == id_type) {
sam_record = opendirectory_sam_searchugid_first(session,
type, ugid_attr, ugid);
}
return sam_record;
}
CFDictionaryRef opendirectory_find_record_from_usersid(
struct opendirectory_session *session,
const DOM_SID *sid)
{
CFDictionaryRef sam_record;
fstring sid_string;
DOM_SID domain_sid = {0};
DOM_SID apple_user_sid = {0};
DOM_SID sam_sid = {0};
sam_record = memberd_find_record_from_sid(session,
MBR_ID_TYPE_UID, sid);
if (sam_record) {
return sam_record;
}
secrets_fetch_domain_sid(lp_workgroup(), &domain_sid);
sam_sid = *get_global_sam_sid();
sid_to_string(sid_string, sid);
DEBUG(6, ("searching for user SID %s the hard way\n",
sid_string));
sam_record = sam_searchattr_first(session, kDSStdRecordTypeUsers,
kDS1AttrSMBSID, sid_string);
if (sam_record) {
return sam_record;
}
if (apple_wellknown_sid(sid, &apple_user_sid)) {
sid_to_string(sid_string, &apple_user_sid);
sam_record = sam_searchattr_first(session,
kDSStdRecordTypeUsers,
kDS1AttrSMBSID, sid_string);
if (sam_record) {
return sam_record;
}
}
sam_record = sam_searchattr_first(session, kDSStdRecordTypeComputers,
kDS1AttrSMBSID, sid_string);
if (sam_record) {
return sam_record;
}
sam_record = find_record_from_usersid_and_domsid(session,
NULL, &sam_sid, sid);
if (sam_record) {
return sam_record;
}
if (sid_compare_domain(&domain_sid, &sam_sid) != 0) {
sam_record = find_record_from_usersid_and_domsid(session,
NULL, &domain_sid, sid);
if (sam_record) {
return sam_record;
}
}
if (session->domain_sid_cache == NULL) {
void * mem_ctx = talloc_init(__FUNCTION__);
opendirectory_fill_domain_sid_cache(mem_ctx, session);
TALLOC_FREE(mem_ctx);
}
return opendirectory_search_domain_sid_cache(session, sid,
find_record_from_usersid_and_domsid);
}
static CFDictionaryRef find_record_from_groupsid_and_domsid(
struct opendirectory_session *session,
const char *domain_name __unused,
const DOM_SID *domain_sid,
const void *match_data)
{
CFDictionaryRef sam_record;
fstring rid_string;
fstring gid_string;
uint32_t rid;
const DOM_SID *group_sid = (const DOM_SID *)match_data;
if (!sid_peek_check_rid(domain_sid, group_sid, &rid)) {
return NULL;
}
DEBUG(8, ("searching domain %s for group record with SID %s\n",
sid_to_string(gid_string, domain_sid),
sid_to_string(rid_string, group_sid)));
snprintf(rid_string, sizeof(rid_string) - 1, "%u", rid);
sam_record = sam_searchattr_first(session, kDSStdRecordTypeGroups,
kDS1AttrSMBRID, rid_string);
if (sam_record) {
return sam_record;
}
sam_record = sam_searchattr_first(session, kDSStdRecordTypeComputers,
kDS1AttrSMBGroupRID, rid_string);
if (sam_record) {
return sam_record;
}
switch (rid) {
case DOMAIN_GROUP_RID_USERS:
case BUILTIN_ALIAS_RID_USERS:
return sam_searchname_first(session, kDSStdRecordTypeGroups,
"staff");
case DOMAIN_GROUP_RID_GUESTS:
case BUILTIN_ALIAS_RID_GUESTS:
case DOMAIN_GROUP_RID_COMPUTERS:
sam_record = sam_searchname_first(session,
kDSStdRecordTypeGroups, lp_guestaccount());
if (!sam_record) {
sam_record = sam_searchname_first(session,
kDSStdRecordTypeGroups, "nobody");
}
return sam_record;
case DOMAIN_GROUP_RID_ADMINS:
case DOMAIN_GROUP_RID_CONTROLLERS:
case DOMAIN_GROUP_RID_CERT_ADMINS:
case DOMAIN_GROUP_RID_SCHEMA_ADMINS:
case DOMAIN_GROUP_RID_ENTERPRISE_ADMINS:
case BUILTIN_ALIAS_RID_ADMINS:
case BUILTIN_ALIAS_RID_POWER_USERS:
return sam_searchname_first(session, kDSStdRecordTypeGroups,
"admin");
}
if (rid >= BASE_RID) {
id_t ugid = pdb_group_rid_to_gid(rid);
if (algorithmic_pdb_rid_is_user(rid)) {
return NULL;
}
sam_record = opendirectory_sam_searchugid_first(session,
kDSStdRecordTypeGroups,
kDS1AttrPrimaryGroupID,
ugid);
if (sam_record) {
return sam_record;
}
}
return NULL;
}
CFDictionaryRef opendirectory_find_record_from_groupsid(
struct opendirectory_session *session,
const DOM_SID *group_sid)
{
CFDictionaryRef sam_record;
fstring sid_string;
DOM_SID domain_sid = {0};
DOM_SID sam_sid = {0};
DOM_SID apple_group_sid = {0};
sam_record = memberd_find_record_from_sid(session,
MBR_ID_TYPE_GID, group_sid);
if (sam_record) {
return sam_record;
}
sid_to_string(sid_string, group_sid);
secrets_fetch_domain_sid(lp_workgroup(), &domain_sid);
sam_sid = *get_global_sam_sid();
DEBUG(6, ("searching for group SID %s the hard way\n",
sid_string));
sam_record = sam_searchattr_first(session, kDSStdRecordTypeGroups,
kDS1AttrSMBSID, sid_string);
if (sam_record) {
return sam_record;
}
if (apple_wellknown_sid(group_sid, &apple_group_sid)) {
sid_to_string(sid_string, &apple_group_sid);
sam_record = sam_searchattr_first(session,
kDSStdRecordTypeGroups,
kDS1AttrSMBSID, sid_string);
if (sam_record) {
return sam_record;
}
}
sam_record = find_record_from_groupsid_and_domsid(session,
NULL, &sam_sid, group_sid);
if (sam_record) {
return sam_record;
}
if (sid_compare(&domain_sid, &sam_sid) != 0) {
sam_record = find_record_from_groupsid_and_domsid(session,
NULL, &domain_sid, group_sid);
if (sam_record) {
return sam_record;
}
}
if (session->domain_sid_cache == NULL) {
void * mem_ctx = talloc_init(__FUNCTION__);
opendirectory_fill_domain_sid_cache(mem_ctx, session);
TALLOC_FREE(mem_ctx);
}
return opendirectory_search_domain_sid_cache(session, group_sid,
find_record_from_groupsid_and_domsid);
}