/* * lsar_lookup_sids3 * * This function is only valid if the remote RPC server is a domain * controller and requires the security extensions defined in MS-RPCE. * * Domain controllers will return RPC_NT_PROTSEQ_NOT_SUPPORTED here * because we don't support the RPC_C_AUTHN_NETLOGON security provider. * Non-domain controllers will return NT_STATUS_INVALID_SERVER_STATE. */ static uint32_t /*LINTED E_STATIC_UNUSED*/ lsar_lookup_sids3(mlsvc_handle_t *lsa_handle, lsa_sid_t *sid, smb_account_t *account) { struct lsar_lookup_sids3 arg; lsar_translated_name_ex_t *name_entry; struct mslsa_lup_sid_entry sid_entry; struct mslsa_domain_entry *domain_entry; uint32_t status = NT_STATUS_SUCCESS; char *name; int opnum = LSARPC_OPNUM_LookupSids3; bzero(&arg, sizeof (struct lsar_lookup_sids3)); sid_entry.psid = sid; arg.lup_sid_table.n_entry = 1; arg.lup_sid_table.entries = &sid_entry; arg.lookup_level = LSA_LOOKUP_WKSTA; arg.client_revision = LSA_CLIENT_REVISION_AD; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_INVALID_PARAMETER); } if (arg.status != NT_STATUS_SUCCESS) { ndr_rpc_status(lsa_handle, opnum, arg.status); ndr_rpc_release(lsa_handle); if (arg.status == RPC_NT_PROTSEQ_NOT_SUPPORTED || arg.status == NT_STATUS_INVALID_SERVER_STATE) return (NT_STATUS_INVALID_PARAMETER); return (NT_SC_VALUE(arg.status)); } if (arg.mapped_count == 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } name_entry = &arg.name_table.entries[0]; if (name_entry->domain_ix != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } name = (char *)name_entry->name.str; account->a_name = (name) ? strdup(name) : strdup(""); account->a_type = name_entry->sid_name_use; account->a_sid = smb_sid_dup((smb_sid_t *)sid); (void) smb_sid_getrid(account->a_sid, &account->a_rid); domain_entry = &arg.domain_table->entries[0]; if ((name = (char *)domain_entry->domain_name.str) != NULL) account->a_domain = strdup(name); account->a_domsid = smb_sid_dup((smb_sid_t *)domain_entry->domain_sid); ndr_rpc_release(lsa_handle); return (status); }
/* * lsar_lookup_sids1 */ static uint32_t lsar_lookup_sids1(mlsvc_handle_t *lsa_handle, lsa_sid_t *sid, smb_account_t *account) { struct mslsa_LookupSids arg; struct mslsa_lup_sid_entry sid_entry; struct mslsa_name_entry *name_entry; struct mslsa_domain_entry *domain_entry; uint32_t status = NT_STATUS_SUCCESS; char *name; int opnum = LSARPC_OPNUM_LookupSids; bzero(&arg, sizeof (struct mslsa_LookupSids)); (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); arg.lookup_level = LSA_LOOKUP_WKSTA; sid_entry.psid = sid; arg.lup_sid_table.n_entry = 1; arg.lup_sid_table.entries = &sid_entry; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_INVALID_PARAMETER); } if (arg.status != NT_STATUS_SUCCESS) { ndr_rpc_status(lsa_handle, opnum, arg.status); ndr_rpc_release(lsa_handle); return (NT_SC_VALUE(arg.status)); } if (arg.mapped_count == 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } name_entry = &arg.name_table.entries[0]; if (name_entry->domain_ix != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } name = (char *)name_entry->name.str; account->a_name = (name) ? strdup(name) : strdup(""); account->a_type = name_entry->sid_name_use; account->a_sid = smb_sid_dup((smb_sid_t *)sid); (void) smb_sid_getrid(account->a_sid, &account->a_rid); domain_entry = &arg.domain_table->entries[0]; if ((name = (char *)domain_entry->domain_name.str) != NULL) account->a_domain = strdup(name); account->a_domsid = smb_sid_dup((smb_sid_t *)domain_entry->domain_sid); ndr_rpc_release(lsa_handle); return (status); }
/* * lsar_lookup_names4 * * This function is only valid if the remote RPC server is a domain * controller and requires the security extensions defined in MS-RPCE. * * Domain controllers will return RPC_NT_PROTSEQ_NOT_SUPPORTED here * because we don't support the RPC_C_AUTHN_NETLOGON security provider. * Non-domain controllers will return NT_STATUS_INVALID_SERVER_STATE. */ static uint32_t /*LINTED E_STATIC_UNUSED*/ lsar_lookup_names4(mlsvc_handle_t *lsa_handle, lsa_names_t *names, smb_account_t *info) { struct lsar_LookupNames4 arg; lsar_translated_sid_ex2_t *sid_entry; struct mslsa_domain_entry *domain_entry; uint32_t status = NT_STATUS_SUCCESS; char *domname; int opnum = LSARPC_OPNUM_LookupNames4; bzero(&arg, sizeof (struct lsar_LookupNames4)); arg.lookup_level = LSA_LOOKUP_WKSTA; arg.client_revision = LSA_CLIENT_REVISION_AD; arg.name_table = (struct mslsa_lup_name_table *)names; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_INVALID_PARAMETER); } if (arg.status != NT_STATUS_SUCCESS) { ndr_rpc_status(lsa_handle, opnum, arg.status); ndr_rpc_release(lsa_handle); if (arg.status == RPC_NT_PROTSEQ_NOT_SUPPORTED || arg.status == NT_STATUS_INVALID_SERVER_STATE) return (NT_STATUS_INVALID_PARAMETER); return (NT_SC_VALUE(arg.status)); } if (arg.mapped_count == 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } sid_entry = &arg.translated_sids.sids[0]; if (sid_entry->domain_index != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } domain_entry = &arg.domain_table->entries[0]; info->a_type = sid_entry->sid_name_use; info->a_domsid = smb_sid_dup((smb_sid_t *)domain_entry->domain_sid); if ((domname = (char *)domain_entry->domain_name.str) != NULL) info->a_domain = strdup(domname); info->a_sid = smb_sid_dup((smb_sid_t *)sid_entry->sid); (void) smb_sid_getrid(info->a_sid, &info->a_rid); ndr_rpc_release(lsa_handle); return (status); }
/* * Lookup well known accounts table * * Return status: * * NT_STATUS_SUCCESS Account is translated successfully * NT_STATUS_NOT_FOUND This is not a well known account * NT_STATUS_NONE_MAPPED Account is found but domains don't match * NT_STATUS_NO_MEMORY Memory shortage * NT_STATUS_INTERNAL_ERROR Internal error/unexpected failure */ static uint32_t lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info) { smb_wka_t *wka; char *wkadom; bzero(info, sizeof (smb_account_t)); if ((wka = smb_wka_lookup_name(name)) == NULL) return (NT_STATUS_NOT_FOUND); if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL) return (NT_STATUS_INTERNAL_ERROR); if ((domain != NULL) && (smb_strcasecmp(domain, wkadom, 0) != 0)) return (NT_STATUS_NONE_MAPPED); info->a_name = strdup(name); info->a_sid = smb_sid_dup(wka->wka_binsid); info->a_domain = strdup(wkadom); info->a_domsid = smb_sid_split(wka->wka_binsid, &info->a_rid); info->a_type = wka->wka_type; if (!smb_account_validate(info)) { smb_account_free(info); return (NT_STATUS_NO_MEMORY); } return (NT_STATUS_SUCCESS); }
/* * Lookup well known accounts table for the given SID * * Return status: * * NT_STATUS_SUCCESS Account is translated successfully * NT_STATUS_NOT_FOUND This is not a well known account * NT_STATUS_NO_MEMORY Memory shortage * NT_STATUS_INTERNAL_ERROR Internal error/unexpected failure */ static uint32_t lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo) { smb_wka_t *wka; char *wkadom; bzero(ainfo, sizeof (smb_account_t)); if ((wka = smb_wka_lookup_sid(sid)) == NULL) return (NT_STATUS_NOT_FOUND); if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL) return (NT_STATUS_INTERNAL_ERROR); ainfo->a_name = strdup(wka->wka_name); ainfo->a_sid = smb_sid_dup(wka->wka_binsid); ainfo->a_domain = strdup(wkadom); ainfo->a_domsid = smb_sid_split(ainfo->a_sid, &ainfo->a_rid); ainfo->a_type = wka->wka_type; if (!smb_account_validate(ainfo)) { smb_account_free(ainfo); return (NT_STATUS_NO_MEMORY); } return (NT_STATUS_SUCCESS); }
/* * samr_lookup_domain * * Lookup up the domain SID for the specified domain name. The handle * should be one returned from samr_connect. The allocated memory for * the returned SID must be freed by caller. */ smb_sid_t * samr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name) { struct samr_LookupDomain arg; smb_sid_t *domsid = NULL; int opnum; size_t length; if (ndr_is_null_handle(samr_handle) || domain_name == NULL) return (NULL); opnum = SAMR_OPNUM_LookupDomain; bzero(&arg, sizeof (struct samr_LookupDomain)); (void) memcpy(&arg.handle, &samr_handle->handle, sizeof (samr_handle_t)); length = smb_wcequiv_strlen(domain_name); if (ndr_rpc_server_os(samr_handle) == NATIVE_OS_WIN2000) length += sizeof (smb_wchar_t); arg.domain_name.length = length; arg.domain_name.allosize = length; arg.domain_name.str = (unsigned char *)domain_name; if (ndr_rpc_call(samr_handle, opnum, &arg) == 0) domsid = smb_sid_dup((smb_sid_t *)arg.sid); ndr_rpc_release(samr_handle); return (domsid); }
/* * lsar_enum_accounts * * Enumerate the list of accounts (i.e. SIDs). Use the handle returned * from lsa_open_policy2. The enum_context is used to support multiple * calls to this enumeration function. It should be set to 0 on the * first call. It will be updated by the domain controller and should * simply be passed unchanged to subsequent calls until there are no * more accounts. A warning status of 0x1A indicates that no more data * is available. The list of accounts will be returned in accounts. * This list is dynamically allocated using malloc, it should be freed * by the caller when it is no longer required. */ int lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context, struct mslsa_EnumAccountBuf *accounts) { struct mslsa_EnumerateAccounts arg; struct mslsa_AccountInfo *info; int opnum; int rc; DWORD n_entries; DWORD i; int nbytes; if (lsa_handle == NULL || enum_context == NULL || accounts == NULL) return (-1); accounts->entries_read = 0; accounts->info = 0; opnum = LSARPC_OPNUM_EnumerateAccounts; bzero(&arg, sizeof (struct mslsa_EnumerateAccounts)); (void) memcpy(&arg.handle, lsa_handle, sizeof (mslsa_handle_t)); arg.enum_context = *enum_context; arg.max_length = MLSVC_MAX_RESPONSE_LEN; rc = ndr_rpc_call(lsa_handle, opnum, &arg); if (rc == 0) { if (arg.status != 0) { if (arg.status == NT_STATUS_NO_MORE_ENTRIES) { *enum_context = arg.enum_context; } else { ndr_rpc_status(lsa_handle, opnum, arg.status); rc = -1; } } else if (arg.enum_buf->entries_read != 0) { n_entries = arg.enum_buf->entries_read; nbytes = n_entries * sizeof (struct mslsa_AccountInfo); if ((info = malloc(nbytes)) == NULL) { ndr_rpc_release(lsa_handle); return (-1); } for (i = 0; i < n_entries; ++i) info[i].sid = (lsa_sid_t *)smb_sid_dup( (smb_sid_t *)arg.enum_buf->info[i].sid); accounts->entries_read = n_entries; accounts->info = info; *enum_context = arg.enum_context; } } ndr_rpc_release(lsa_handle); return (rc); }
/* * Converts groups information in the returned structure by domain controller * (info3) to an internal representation (gids) */ static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids) { smb_sid_t *domain_sid; smb_id_t *ids; int i, total_cnt; if ((i = info3->GroupCount) == 0) i++; i += info3->SidCount; total_cnt = gids->i_cnt + i; gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t)); if (gids->i_ids == NULL) return (NT_STATUS_NO_MEMORY); domain_sid = (smb_sid_t *)info3->LogonDomainId; ids = gids->i_ids + gids->i_cnt; for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) { ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid); if (ids->i_sid == NULL) return (NT_STATUS_NO_MEMORY); ids->i_attrs = info3->GroupIds[i].attributes; } if (info3->GroupCount == 0) { /* * if there's no global group should add the primary group. */ ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId); if (ids->i_sid == NULL) return (NT_STATUS_NO_MEMORY); ids->i_attrs = 0x7; gids->i_cnt++; ids++; } /* Add the extra SIDs */ for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) { ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid); if (ids->i_sid == NULL) return (NT_STATUS_NO_MEMORY); ids->i_attrs = info3->ExtraSids[i].attributes; } return (NT_STATUS_SUCCESS); }
/* * Lookup local SMB group account database (/var/smb/smbgroup.db) * The memory for the returned SID must be freed by the caller. */ static uint32_t smb_sam_lookup_group(char *name, smb_sid_t **sid) { smb_group_t grp; if (smb_lgrp_getbyname(name, &grp) != SMB_LGRP_SUCCESS) return (NT_STATUS_NO_SUCH_ALIAS); *sid = smb_sid_dup(grp.sg_id.gs_sid); smb_lgrp_free(&grp); return ((*sid == NULL) ? NT_STATUS_NO_MEMORY : NT_STATUS_SUCCESS); }
/* * lsar_lookup_names2 */ static uint32_t lsar_lookup_names2(mlsvc_handle_t *lsa_handle, lsa_names_t *names, smb_account_t *info) { struct lsar_LookupNames2 arg; struct lsar_rid_entry2 *rid_entry; struct mslsa_domain_entry *domain_entry; uint32_t status = NT_STATUS_SUCCESS; char *domname; int opnum = LSARPC_OPNUM_LookupNames2; bzero(&arg, sizeof (struct lsar_LookupNames2)); (void) memcpy(&arg.policy_handle, lsa_handle, sizeof (mslsa_handle_t)); arg.lookup_level = LSA_LOOKUP_WKSTA; arg.client_revision = LSA_CLIENT_REVISION_AD; arg.name_table = (struct mslsa_lup_name_table *)names; if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_INVALID_PARAMETER); } if (arg.status != NT_STATUS_SUCCESS) { ndr_rpc_status(lsa_handle, opnum, arg.status); ndr_rpc_release(lsa_handle); return (NT_SC_VALUE(arg.status)); } if (arg.mapped_count == 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } rid_entry = &arg.translated_sids.rids[0]; if (rid_entry->domain_index != 0) { ndr_rpc_release(lsa_handle); return (NT_STATUS_NONE_MAPPED); } domain_entry = &arg.domain_table->entries[0]; info->a_type = rid_entry->sid_name_use; info->a_domsid = smb_sid_dup((smb_sid_t *)domain_entry->domain_sid); if ((domname = (char *)domain_entry->domain_name.str) != NULL) info->a_domain = strdup(domname); info->a_rid = rid_entry->rid; info->a_sid = smb_sid_splice(info->a_domsid, info->a_rid); ndr_rpc_release(lsa_handle); return (status); }
/* * smb_acl_from_zfs * * Converts given ZFS ACL to a Windows ACL. * * A pointer to allocated memory for the Win ACL will be * returned upon successful conversion. */ smb_acl_t * smb_acl_from_zfs(acl_t *zacl) { ace_t *zace; int numaces; smb_acl_t *acl; smb_ace_t *ace; smb_idmap_batch_t sib; smb_idmap_t *sim; idmap_stat idm_stat; idm_stat = smb_idmap_batch_create(&sib, zacl->acl_cnt, SMB_IDMAP_ID2SID); if (idm_stat != IDMAP_SUCCESS) return (NULL); if (smb_fsacl_getsids(&sib, zacl) != IDMAP_SUCCESS) { smb_idmap_batch_destroy(&sib); return (NULL); } acl = smb_acl_alloc(ACL_REVISION, SMB_ACL_HDRSIZE, zacl->acl_cnt); sim = sib.sib_maps; for (numaces = 0, zace = zacl->acl_aclp; numaces < zacl->acl_cnt; zace++, numaces++, sim++) { ASSERT(sim->sim_sid); if (sim->sim_sid == NULL) { smb_acl_free(acl); acl = NULL; break; } ace = &acl->sl_aces[numaces]; ace->se_hdr.se_type = zace->a_type; ace->se_hdr.se_flags = smb_ace_flags_fromzfs(zace->a_flags); ace->se_mask = zace->a_access_mask; ace->se_sid = smb_sid_dup(sim->sim_sid); ace->se_hdr.se_bsize = smb_ace_len(ace); acl->sl_bsize += ace->se_hdr.se_bsize; } smb_idmap_batch_destroy(&sib); return (acl); }
smb_sid_t * smb_sid_fromstr(const char *sidstr) { smb_sid_t *sid; smb_sid_t *retsid; const char *p; int size; uint8_t i; unsigned long sua; if (sidstr == NULL) return (NULL); if (strncmp(sidstr, "S-1-", 4) != 0) return (NULL); size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); sid = kmem_zalloc(size, KM_SLEEP); sid->sid_revision = NT_SID_REVISION; sua = 0; (void) ddi_strtoul(&sidstr[4], 0, 10, &sua); sid->sid_authority[5] = (uint8_t)sua; for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { while (*p && *p == '-') ++p; if (*p < '0' || *p > '9') { kmem_free(sid, size); return (NULL); } sua = 0; (void) ddi_strtoul(p, 0, 10, &sua); sid->sid_subauth[i] = (uint32_t)sua; while (*p && *p != '-') ++p; } sid->sid_subauthcnt = i; retsid = smb_sid_dup(sid); kmem_free(sid, size); return (retsid); }
/* * smb_sid_split * * Take a full sid and split it into a domain sid and a relative id (rid). * The domain SID is allocated and a pointer to it will be returned. The * RID value is passed back in 'rid' arg if it's not NULL. The allocated * memory for the domain SID must be freed by caller. */ smb_sid_t * smb_sid_split(smb_sid_t *sid, uint32_t *rid) { smb_sid_t *domsid; if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) return (NULL); if ((domsid = smb_sid_dup(sid)) == NULL) return (NULL); --domsid->sid_subauthcnt; if (rid) *rid = domsid->sid_subauth[domsid->sid_subauthcnt]; return (domsid); }
/* * Looks up the given name in local account databases: * * SMB Local users are looked up in /var/smb/smbpasswd * SMB Local groups are looked up in /var/smb/smbgroup.db * * If the account is found, its information is populated * in the passed smb_account_t structure. Caller must free * allocated memories by calling smb_account_free() upon * successful return. * * The type of account is specified by 'type', which can be user, * alias (local group) or unknown. If the caller doesn't know * whether the name is a user or group name then SidTypeUnknown * should be passed. * * If a local user and group have the same name, the user will * always be picked. Note that this situation cannot happen on * Windows systems. * * If a SMB local user/group is found but it turns out that * it'll be mapped to a domain user/group the lookup is considered * failed and NT_STATUS_NONE_MAPPED is returned. * * Return status: * * NT_STATUS_NOT_FOUND This is not a local account * NT_STATUS_NONE_MAPPED It's a local account but cannot be * translated. * other error status codes. */ uint32_t smb_sam_lookup_name(char *domain, char *name, uint16_t type, smb_account_t *account) { smb_domain_t di; smb_sid_t *sid; uint32_t status; smb_lwka_t *lwka; bzero(account, sizeof (smb_account_t)); if (domain != NULL) { if (!smb_domain_lookup_name(domain, &di) || (di.di_type != SMB_DOMAIN_LOCAL)) return (NT_STATUS_NOT_FOUND); /* Only Netbios hostname is accepted */ if (smb_strcasecmp(domain, di.di_nbname, 0) != 0) return (NT_STATUS_NONE_MAPPED); } else { if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); } if (smb_strcasecmp(name, di.di_nbname, 0) == 0) { /* This is the local domain name */ account->a_type = SidTypeDomain; account->a_name = strdup(""); account->a_domain = strdup(di.di_nbname); account->a_sid = smb_sid_dup(di.di_binsid); account->a_domsid = smb_sid_dup(di.di_binsid); account->a_rid = (uint32_t)-1; if (!smb_account_validate(account)) { smb_account_free(account); return (NT_STATUS_NO_MEMORY); } return (NT_STATUS_SUCCESS); } if ((lwka = smb_lwka_lookup_name(name)) != NULL) { sid = smb_sid_splice(di.di_binsid, lwka->lwka_rid); type = lwka->lwka_type; } else { switch (type) { case SidTypeUser: status = smb_sam_lookup_user(name, &sid); if (status != NT_STATUS_SUCCESS) return (status); break; case SidTypeAlias: status = smb_sam_lookup_group(name, &sid); if (status != NT_STATUS_SUCCESS) return (status); break; case SidTypeUnknown: type = SidTypeUser; status = smb_sam_lookup_user(name, &sid); if (status == NT_STATUS_SUCCESS) break; if (status == NT_STATUS_NONE_MAPPED) return (status); type = SidTypeAlias; status = smb_sam_lookup_group(name, &sid); if (status != NT_STATUS_SUCCESS) return (status); break; default: return (NT_STATUS_INVALID_PARAMETER); } } account->a_name = strdup(name); account->a_sid = sid; account->a_domain = strdup(di.di_nbname); account->a_domsid = smb_sid_split(sid, &account->a_rid); account->a_type = type; if (!smb_account_validate(account)) { smb_account_free(account); return (NT_STATUS_NO_MEMORY); } return (NT_STATUS_SUCCESS); }
/* * Updates a list of groups in which the given user is a member * by adding any local (SAM) groups. * * We are a member of local groups where the local group * contains either the user's primary SID, or any of their * other SIDs such as from domain groups, SID history, etc. * We can have indirect membership via domain groups. */ uint32_t smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids) { smb_ids_t new_gids; smb_id_t *ids, *new_ids; smb_giter_t gi; smb_group_t lgrp; int i, gcnt, total_cnt; uint32_t ret; boolean_t member; /* * First pass: count groups to be added (gcnt) */ gcnt = 0; if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) return (NT_STATUS_INTERNAL_ERROR); while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) { member = B_FALSE; if (smb_lgrp_is_member(&lgrp, user_sid)) member = B_TRUE; else for (i = 0, ids = gids->i_ids; i < gids->i_cnt; i++, ids++) { if (smb_lgrp_is_member(&lgrp, ids->i_sid)) { member = B_TRUE; break; } } /* Careful: only count lgrp once */ if (member) gcnt++; smb_lgrp_free(&lgrp); } smb_lgrp_iterclose(&gi); if (gcnt == 0) return (NT_STATUS_SUCCESS); /* * Second pass: add to groups list. * Do not modify gcnt after here. */ if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) return (NT_STATUS_INTERNAL_ERROR); /* * Expand the list (copy to a new, larger one) * Note: were're copying pointers from the old * array to the new (larger) array, and then * adding new pointers after what we copied. */ ret = 0; new_gids.i_cnt = gids->i_cnt; total_cnt = gids->i_cnt + gcnt; new_gids.i_ids = malloc(total_cnt * sizeof (smb_id_t)); if (new_gids.i_ids == NULL) { ret = NT_STATUS_NO_MEMORY; goto out; } (void) memcpy(new_gids.i_ids, gids->i_ids, gids->i_cnt * sizeof (smb_id_t)); new_ids = new_gids.i_ids + gids->i_cnt; (void) memset(new_ids, 0, gcnt * sizeof (smb_id_t)); /* * Add group SIDs starting at the end of the * previous list. (new_ids) */ while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) { member = B_FALSE; if (smb_lgrp_is_member(&lgrp, user_sid)) member = B_TRUE; else for (i = 0, ids = gids->i_ids; i < gids->i_cnt; i++, ids++) { if (smb_lgrp_is_member(&lgrp, ids->i_sid)) { member = B_TRUE; break; } } if (member && (new_gids.i_cnt < (gids->i_cnt + gcnt))) { new_ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid); if (new_ids->i_sid == NULL) { smb_lgrp_free(&lgrp); ret = NT_STATUS_NO_MEMORY; goto out; } new_ids->i_attrs = lgrp.sg_attr; new_ids++; new_gids.i_cnt++; } smb_lgrp_free(&lgrp); } out: smb_lgrp_iterclose(&gi); if (ret != 0) { if (new_gids.i_ids != NULL) { /* * Free only the new sids we added. * The old ones were copied ptrs. */ ids = new_gids.i_ids + gids->i_cnt; for (i = 0; i < gcnt; i++, ids++) { smb_sid_free(ids->i_sid); } free(new_gids.i_ids); } return (ret); } /* * Success! Update passed gids and * free the old array. */ free(gids->i_ids); *gids = new_gids; return (NT_STATUS_SUCCESS); }
/* * Looks up the given SID in local account databases: * * SMB Local users are looked up in /var/smb/smbpasswd * SMB Local groups are looked up in /var/smb/smbgroup.db * * If the account is found, its information is populated * in the passed smb_account_t structure. Caller must free * allocated memories by calling smb_account_free() upon * successful return. * * Return status: * * NT_STATUS_NOT_FOUND This is not a local account * NT_STATUS_NONE_MAPPED It's a local account but cannot be * translated. * other error status codes. */ uint32_t smb_sam_lookup_sid(smb_sid_t *sid, smb_account_t *account) { char hostname[MAXHOSTNAMELEN]; smb_passwd_t smbpw; smb_group_t grp; smb_lwka_t *lwka; smb_domain_t di; uint32_t rid; uid_t id; int id_type; int rc; bzero(account, sizeof (smb_account_t)); if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); if (smb_sid_cmp(sid, di.di_binsid)) { /* This is the local domain SID */ account->a_type = SidTypeDomain; account->a_name = strdup(""); account->a_domain = strdup(di.di_nbname); account->a_sid = smb_sid_dup(sid); account->a_domsid = smb_sid_dup(sid); account->a_rid = (uint32_t)-1; if (!smb_account_validate(account)) { smb_account_free(account); return (NT_STATUS_NO_MEMORY); } return (NT_STATUS_SUCCESS); } if (!smb_sid_indomain(di.di_binsid, sid)) { /* This is not a local SID */ return (NT_STATUS_NOT_FOUND); } if ((lwka = smb_lwka_lookup_sid(sid)) != NULL) { account->a_type = lwka->lwka_type; account->a_name = strdup(lwka->lwka_name); } else { id_type = SMB_IDMAP_UNKNOWN; if (smb_idmap_getid(sid, &id, &id_type) != IDMAP_SUCCESS) return (NT_STATUS_NONE_MAPPED); switch (id_type) { case SMB_IDMAP_USER: account->a_type = SidTypeUser; if (smb_pwd_getpwuid(id, &smbpw) == NULL) return (NT_STATUS_NO_SUCH_USER); account->a_name = strdup(smbpw.pw_name); break; case SMB_IDMAP_GROUP: account->a_type = SidTypeAlias; (void) smb_sid_getrid(sid, &rid); rc = smb_lgrp_getbyrid(rid, SMB_DOMAIN_LOCAL, &grp); if (rc != SMB_LGRP_SUCCESS) return (NT_STATUS_NO_SUCH_ALIAS); account->a_name = strdup(grp.sg_name); smb_lgrp_free(&grp); break; default: return (NT_STATUS_NONE_MAPPED); } } if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) == 0) account->a_domain = strdup(hostname); account->a_sid = smb_sid_dup(sid); account->a_domsid = smb_sid_split(sid, &account->a_rid); if (!smb_account_validate(account)) { smb_account_free(account); return (NT_STATUS_NO_MEMORY); } return (NT_STATUS_SUCCESS); }