/* * 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * Security Accounts Manager RPC (SAMR) server-side interface. * * The SAM is a hierarchical database: * - If you want to talk to the SAM you need a SAM handle. * - If you want to work with a domain, use the SAM handle. * to obtain a domain handle. * - Use domain handles to obtain user handles etc. */ #include #include #include #include #include #include #include #include #include #include #include #include /* * The keys associated with the various handles dispensed by the SAMR * server. These keys can be used to validate client activity. * These values are never passed over the wire so security shouldn't * be an issue. */ typedef enum { SAMR_KEY_NULL = 0, SAMR_KEY_CONNECT, SAMR_KEY_DOMAIN, SAMR_KEY_USER, SAMR_KEY_GROUP, SAMR_KEY_ALIAS } samr_key_t; typedef struct samr_keydata { samr_key_t kd_key; smb_domain_type_t kd_type; DWORD kd_rid; } samr_keydata_t; /* * DomainDisplayUser All user objects (or those derived from user) with * userAccountControl containing the UF_NORMAL_ACCOUNT bit. * * DomainDisplayMachine All user objects (or those derived from user) with * userAccountControl containing the * UF_WORKSTATION_TRUST_ACCOUNT or UF_SERVER_TRUST_ACCOUNT * bit. * * DomainDisplayGroup All group objects (or those derived from group) with * groupType equal to GROUP_TYPE_SECURITY_UNIVERSAL or * GROUP_TYPE_SECURITY_ACCOUNT. * * DomainDisplayOemUser Same as DomainDisplayUser with OEM strings * * DomainDisplayOemGroup Same as DomainDisplayGroup with OEM strings */ typedef enum { DomainDisplayUser = 1, DomainDisplayMachine, DomainDispalyGroup, DomainDisplayOemUser, DomainDisplayOemGroup } samr_displvl_t; #define SAMR_VALID_DISPLEVEL(lvl) \ (((lvl) >= DomainDisplayUser) && ((lvl) <= DomainDisplayOemGroup)) #define SAMR_SUPPORTED_DISPLEVEL(lvl) (lvl == DomainDisplayUser) static ndr_hdid_t *samr_hdalloc(ndr_xa_t *, samr_key_t, smb_domain_type_t, DWORD); static void samr_hdfree(ndr_xa_t *, ndr_hdid_t *); static ndr_handle_t *samr_hdlookup(ndr_xa_t *, ndr_hdid_t *, samr_key_t); static ndr_handle_t *samr_hdlookup_any(ndr_xa_t *, ndr_hdid_t *); static int samr_call_stub(ndr_xa_t *mxa); static DWORD samr_s_enum_local_domains(struct samr_EnumLocalDomain *, ndr_xa_t *); static ndr_stub_table_t samr_stub_table[]; static ndr_service_t samr_service = { "SAMR", /* name */ "Security Accounts Manager", /* desc */ "\\samr", /* endpoint */ PIPE_LSASS, /* sec_addr_port */ "12345778-1234-abcd-ef00-0123456789ac", 1, /* abstract */ NDR_TRANSFER_SYNTAX_UUID, 2, /* transfer */ 0, /* no bind_instance_size */ NULL, /* no bind_req() */ NULL, /* no unbind_and_close() */ samr_call_stub, /* call_stub() */ &TYPEINFO(samr_interface), /* interface ti */ samr_stub_table /* stub_table */ }; /* * samr_initialize * * This function registers the SAM RPC interface with the RPC runtime * library. It must be called in order to use either the client side * or the server side functions. */ void samr_initialize(void) { (void) ndr_svc_register(&samr_service); } /* * Custom call_stub to set the stream string policy. */ static int samr_call_stub(ndr_xa_t *mxa) { NDS_SETF(&mxa->send_nds, NDS_F_NOTERM); NDS_SETF(&mxa->recv_nds, NDS_F_NOTERM); return (ndr_generic_call_stub(mxa)); } /* * Handle allocation wrapper to setup the local context. */ static ndr_hdid_t * samr_hdalloc(ndr_xa_t *mxa, samr_key_t key, smb_domain_type_t domain_type, DWORD rid) { ndr_handle_t *hd; ndr_hdid_t *id; samr_keydata_t *data; if ((data = malloc(sizeof (samr_keydata_t))) == NULL) return (NULL); data->kd_key = key; data->kd_type = domain_type; data->kd_rid = rid; if ((id = ndr_hdalloc(mxa, data)) == NULL) { free(data); return (NULL); } if ((hd = ndr_hdlookup(mxa, id)) != NULL) hd->nh_data_free = free; return (id); } /* * Handle deallocation wrapper to free the local context. */ static void samr_hdfree(ndr_xa_t *mxa, ndr_hdid_t *id) { ndr_handle_t *hd; if ((hd = ndr_hdlookup(mxa, id)) != NULL) { free(hd->nh_data); hd->nh_data = NULL; ndr_hdfree(mxa, id); } } /* * Handle lookup wrapper to validate the local context. */ static ndr_handle_t * samr_hdlookup(ndr_xa_t *mxa, ndr_hdid_t *id, samr_key_t key) { ndr_handle_t *hd; samr_keydata_t *data; if ((hd = ndr_hdlookup(mxa, id)) == NULL) return (NULL); if ((data = (samr_keydata_t *)hd->nh_data) == NULL) return (NULL); if (data->kd_key != key) return (NULL); return (hd); } /* * Handle lookup wrapper to validate the local context, * but don't limit to one type. */ static ndr_handle_t * samr_hdlookup_any(ndr_xa_t *mxa, ndr_hdid_t *id) { ndr_handle_t *hd; if ((hd = ndr_hdlookup(mxa, id)) == NULL) return (NULL); if (hd->nh_data == NULL) return (NULL); return (hd); } /* * samr_s_Connect * * This is a request to connect to the local SAM database. We don't * support any form of update request and our database doesn't * contain any private information, so there is little point in * doing any access access checking here. * * Return a handle for use with subsequent SAM requests. */ static int samr_s_Connect(void *arg, ndr_xa_t *mxa) { struct samr_Connect *param = arg; ndr_hdid_t *id; id = samr_hdalloc(mxa, SAMR_KEY_CONNECT, SMB_DOMAIN_NULL, 0); if (id) { bcopy(id, ¶m->handle, sizeof (samr_handle_t)); param->status = 0; } else { bzero(¶m->handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); } return (NDR_DRC_OK); } /* * samr_s_CloseHandle * * Close the SAM interface specified by the handle. * Free the handle and zero out the result handle for the client. */ static int samr_s_CloseHandle(void *arg, ndr_xa_t *mxa) { struct samr_CloseHandle *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; samr_hdfree(mxa, id); bzero(¶m->result_handle, sizeof (samr_handle_t)); param->status = 0; return (NDR_DRC_OK); } /* * samr_s_QuerySecObject */ static int samr_s_QuerySecObject(void *arg, ndr_xa_t *mxa) { struct samr_QuerySecObject *param = arg; ndr_hdid_t *id; uint32_t status; struct samr_sec_desc *sd; id = (ndr_hdid_t *)¶m->obj_handle; if (samr_hdlookup_any(mxa, id) == NULL) { status = NT_STATUS_INVALID_HANDLE; goto QuerySecObjectError; } param->sd = NDR_MALLOC(mxa, sizeof (samr_sd_t)); if (param->sd == NULL) { status = NT_STATUS_NO_MEMORY; goto QuerySecObjectError; } param->sd->length = sizeof (struct samr_sec_desc); sd = NDR_MALLOC(mxa, param->sd->length); if (sd == NULL) { status = NT_STATUS_NO_MEMORY; goto QuerySecObjectError; } bzero(sd, param->sd->length); sd->Revision = 1; sd->Control = SE_SELF_RELATIVE; param->sd->data = (void *)sd; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); QuerySecObjectError: bzero(param, sizeof (struct samr_QuerySecObject)); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } /* * samr_s_LookupDomain * * This is a request to map a domain name to a domain SID. We can map * the primary domain name, our local domain name (hostname) and the * builtin domain names to the appropriate SID. Anything else will be * rejected. */ static int samr_s_LookupDomain(void *arg, ndr_xa_t *mxa) { struct samr_LookupDomain *param = arg; char *domain_name; smb_domain_t di; if ((domain_name = (char *)param->domain_name.str) == NULL) { bzero(param, sizeof (struct samr_LookupDomain)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_PARAMETER); return (NDR_DRC_OK); } if (!smb_domain_lookup_name(domain_name, &di)) { bzero(param, sizeof (struct samr_LookupDomain)); param->status = NT_SC_ERROR(NT_STATUS_NO_SUCH_DOMAIN); return (NDR_DRC_OK); } param->sid = (struct samr_sid *)NDR_SIDDUP(mxa, di.di_binsid); if (param->sid == NULL) { bzero(param, sizeof (struct samr_LookupDomain)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); return (NDR_DRC_OK); } param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } /* * samr_s_EnumLocalDomains * * This is a request for the local domains supported by this server. * All we do here is validate the handle and set the status. The real * work is done in samr_s_enum_local_domains. */ static int samr_s_EnumLocalDomains(void *arg, ndr_xa_t *mxa) { struct samr_EnumLocalDomain *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; DWORD status; if (samr_hdlookup(mxa, id, SAMR_KEY_CONNECT) == NULL) status = NT_STATUS_ACCESS_DENIED; else status = samr_s_enum_local_domains(param, mxa); if (status == NT_STATUS_SUCCESS) { param->enum_context = param->info->entries_read; param->total_entries = param->info->entries_read; param->status = NT_STATUS_SUCCESS; } else { bzero(param, sizeof (struct samr_EnumLocalDomain)); param->status = NT_SC_ERROR(status); } return (NDR_DRC_OK); } /* * samr_s_enum_local_domains * * This function should only be called via samr_s_EnumLocalDomains to * ensure that the appropriate validation is performed. We will answer * queries about two domains: the local domain, synonymous with the * local hostname, and the BUILTIN domain. So we return these two * strings. * * Returns NT status values. */ static DWORD samr_s_enum_local_domains(struct samr_EnumLocalDomain *param, ndr_xa_t *mxa) { struct samr_LocalDomainInfo *info; struct samr_LocalDomainEntry *entry; char *hostname; hostname = NDR_MALLOC(mxa, NETBIOS_NAME_SZ); if (hostname == NULL) return (NT_STATUS_NO_MEMORY); if (smb_getnetbiosname(hostname, NETBIOS_NAME_SZ) != 0) return (NT_STATUS_NO_MEMORY); entry = NDR_NEWN(mxa, struct samr_LocalDomainEntry, 2); if (entry == NULL) return (NT_STATUS_NO_MEMORY); bzero(entry, (sizeof (struct samr_LocalDomainEntry) * 2)); (void) NDR_MSTRING(mxa, hostname, (ndr_mstring_t *)&entry[0].name); (void) NDR_MSTRING(mxa, "Builtin", (ndr_mstring_t *)&entry[1].name); info = NDR_NEW(mxa, struct samr_LocalDomainInfo); if (info == NULL) return (NT_STATUS_NO_MEMORY); info->entries_read = 2; info->entry = entry; param->info = info; return (NT_STATUS_SUCCESS); } /* * samr_s_OpenDomain * * This is a request to open a domain within the local SAM database. * The caller must supply a valid connect handle. * We return a handle to be used to access objects within this domain. */ static int samr_s_OpenDomain(void *arg, ndr_xa_t *mxa) { struct samr_OpenDomain *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; smb_domain_t domain; if (samr_hdlookup(mxa, id, SAMR_KEY_CONNECT) == NULL) { bzero(¶m->domain_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } if (!smb_domain_lookup_sid((smb_sid_t *)param->sid, &domain)) { bzero(¶m->domain_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO); return (NDR_DRC_OK); } if ((domain.di_type != SMB_DOMAIN_BUILTIN) && (domain.di_type != SMB_DOMAIN_LOCAL)) { bzero(¶m->domain_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_CANT_ACCESS_DOMAIN_INFO); return (NDR_DRC_OK); } id = samr_hdalloc(mxa, SAMR_KEY_DOMAIN, domain.di_type, 0); if (id) { bcopy(id, ¶m->domain_handle, sizeof (samr_handle_t)); param->status = 0; } else { bzero(¶m->domain_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); } return (NDR_DRC_OK); } /* * samr_s_QueryDomainInfo * * The caller should pass a domain handle. * * Windows 95 Server Manager sends requests for levels 6 and 7 when * the services menu item is selected. Level 2 is basically for getting * number of users, groups, and aliases in a domain. * We have no information on what the various information levels mean. */ static int samr_s_QueryDomainInfo(void *arg, ndr_xa_t *mxa) { struct samr_QueryDomainInfo *param = arg; struct samr_QueryDomainInfoRes *info; ndr_hdid_t *id = (ndr_hdid_t *)¶m->domain_handle; ndr_handle_t *hd; samr_keydata_t *data; char *domain; char hostname[NETBIOS_NAME_SZ]; int alias_cnt, user_cnt; int rc = 0; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN)) == NULL) { bzero(param, sizeof (struct samr_QueryDomainInfo)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } info = NDR_NEW(mxa, struct samr_QueryDomainInfoRes); if (info == NULL) { bzero(param, sizeof (struct samr_QueryDomainInfo)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); return (NDR_DRC_OK); } info->switch_value = param->info_level; param->info = info; data = (samr_keydata_t *)hd->nh_data; switch (data->kd_type) { case SMB_DOMAIN_BUILTIN: domain = "BUILTIN"; user_cnt = 0; alias_cnt = smb_sam_grp_cnt(data->kd_type); break; case SMB_DOMAIN_LOCAL: rc = smb_getnetbiosname(hostname, sizeof (hostname)); if (rc == 0) { domain = hostname; user_cnt = smb_sam_usr_cnt(); alias_cnt = smb_sam_grp_cnt(data->kd_type); } break; default: bzero(param, sizeof (struct samr_QueryDomainInfo)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } if (rc != 0) { bzero(param, sizeof (struct samr_QueryDomainInfo)); param->status = NT_SC_ERROR(NT_STATUS_INTERNAL_ERROR); return (NDR_DRC_OK); } switch (param->info_level) { case SAMR_QUERY_DOMAIN_INFO_6: info->ru.info6.unknown1 = 0x00000000; info->ru.info6.unknown2 = 0x00147FB0; info->ru.info6.unknown3 = 0x00000000; info->ru.info6.unknown4 = 0x00000000; info->ru.info6.unknown5 = 0x00000000; param->status = NT_STATUS_SUCCESS; break; case SAMR_QUERY_DOMAIN_INFO_7: info->ru.info7.unknown1 = 0x00000003; param->status = NT_STATUS_SUCCESS; break; case SAMR_QUERY_DOMAIN_INFO_2: info->ru.info2.unknown1 = 0x00000000; info->ru.info2.unknown2 = 0x80000000; (void) NDR_MSTRING(mxa, "", (ndr_mstring_t *)&(info->ru.info2.s1)); (void) NDR_MSTRING(mxa, domain, (ndr_mstring_t *)&(info->ru.info2.domain)); (void) NDR_MSTRING(mxa, "", (ndr_mstring_t *)&(info->ru.info2.s2)); info->ru.info2.sequence_num = 0x0000002B; info->ru.info2.unknown3 = 0x00000000; info->ru.info2.unknown4 = 0x00000001; info->ru.info2.unknown5 = 0x00000003; info->ru.info2.unknown6 = 0x00000001; info->ru.info2.num_users = user_cnt; info->ru.info2.num_groups = 0; info->ru.info2.num_aliases = alias_cnt; param->status = NT_STATUS_SUCCESS; break; default: bzero(param, sizeof (struct samr_QueryDomainInfo)); return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID); }; return (NDR_DRC_OK); } /* * QueryInfoDomain2: Identical to QueryDomainInfo. */ static int samr_s_QueryInfoDomain2(void *arg, ndr_xa_t *mxa) { return (samr_s_QueryDomainInfo(arg, mxa)); } /* * Looks up the given name in the specified domain which could * be either the built-in or local domain. * * CAVEAT: this function should be able to handle a list of * names but currently it can only handle one name at a time. */ static int samr_s_LookupNames(void *arg, ndr_xa_t *mxa) { struct samr_LookupNames *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; ndr_handle_t *hd; samr_keydata_t *data; smb_account_t account; smb_wka_t *wka; uint32_t status = NT_STATUS_SUCCESS; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN)) == NULL) status = NT_STATUS_INVALID_HANDLE; if (param->n_entry != 1) status = NT_STATUS_ACCESS_DENIED; if (param->name.str == NULL) { /* * Windows NT returns NT_STATUS_NONE_MAPPED. * Windows 2000 returns STATUS_INVALID_ACCOUNT_NAME. */ status = NT_STATUS_NONE_MAPPED; } if (status != NT_STATUS_SUCCESS) { bzero(param, sizeof (struct samr_LookupNames)); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } param->rids.rid = NDR_NEW(mxa, DWORD); param->rid_types.rid_type = NDR_NEW(mxa, DWORD); data = (samr_keydata_t *)hd->nh_data; switch (data->kd_type) { case SMB_DOMAIN_BUILTIN: wka = smb_wka_lookup_builtin((char *)param->name.str); if (wka != NULL) { param->rids.n_entry = 1; (void) smb_sid_getrid(wka->wka_binsid, ¶m->rids.rid[0]); param->rid_types.n_entry = 1; param->rid_types.rid_type[0] = wka->wka_type; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } break; case SMB_DOMAIN_LOCAL: status = smb_sam_lookup_name(NULL, (char *)param->name.str, SidTypeUnknown, &account); if (status == NT_STATUS_SUCCESS) { param->rids.n_entry = 1; param->rids.rid[0] = account.a_rid; param->rid_types.n_entry = 1; param->rid_types.rid_type[0] = account.a_type; param->status = NT_STATUS_SUCCESS; smb_account_free(&account); return (NDR_DRC_OK); } break; default: bzero(param, sizeof (struct samr_LookupNames)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } param->rids.n_entry = 0; param->rid_types.n_entry = 0; param->status = NT_SC_ERROR(NT_STATUS_NONE_MAPPED); return (NDR_DRC_OK); } /* * samr_s_OpenUser * * This is a request to open a user within a specified domain in the * local SAM database. The caller must supply a valid domain handle, * obtained via a successful domain open request. The user is * specified by the rid in the request. */ static int samr_s_OpenUser(void *arg, ndr_xa_t *mxa) { struct samr_OpenUser *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; ndr_handle_t *hd; samr_keydata_t *data; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN)) == NULL) { bzero(¶m->user_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } data = (samr_keydata_t *)hd->nh_data; id = samr_hdalloc(mxa, SAMR_KEY_USER, data->kd_type, param->rid); if (id == NULL) { bzero(¶m->user_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); } else { bcopy(id, ¶m->user_handle, sizeof (samr_handle_t)); param->status = NT_STATUS_SUCCESS; } return (NDR_DRC_OK); } /* * samr_s_DeleteUser * * Request to delete a user within a specified domain in the local * SAM database. The caller should supply a valid user handle. */ /*ARGSUSED*/ static int samr_s_DeleteUser(void *arg, ndr_xa_t *mxa) { struct samr_DeleteUser *param = arg; bzero(param, sizeof (struct samr_DeleteUser)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } /* * samr_s_QueryUserInfo * * Returns: * NT_STATUS_SUCCESS * NT_STATUS_ACCESS_DENIED * NT_STATUS_INVALID_INFO_CLASS */ /*ARGSUSED*/ static int samr_s_QueryUserInfo(void *arg, ndr_xa_t *mxa) { struct samr_QueryUserInfo *param = arg; struct samr_QueryUserInfo21 *all_info; ndr_hdid_t *id; ndr_handle_t *hd; samr_keydata_t *data; smb_domain_t di; smb_account_t account; smb_sid_t *sid; uint32_t status; id = (ndr_hdid_t *)¶m->user_handle; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_USER)) == NULL) { status = NT_STATUS_INVALID_HANDLE; goto QueryUserInfoError; } data = (samr_keydata_t *)hd->nh_data; if (param->switch_value != SAMR_QUERY_USER_ALL_INFO) { status = NT_STATUS_ACCESS_DENIED; goto QueryUserInfoError; } if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) { status = NT_STATUS_ACCESS_DENIED; goto QueryUserInfoError; } if ((sid = smb_sid_splice(di.di_binsid, data->kd_rid)) == NULL) { status = NT_STATUS_ACCESS_DENIED; goto QueryUserInfoError; } if (smb_sam_lookup_sid(sid, &account) != NT_STATUS_SUCCESS) { status = NT_STATUS_ACCESS_DENIED; goto QueryUserInfoError; } all_info = ¶m->ru.info21; bzero(all_info, sizeof (struct samr_QueryUserInfo21)); all_info->WhichFields = SAMR_USER_ALL_USERNAME | SAMR_USER_ALL_USERID | SAMR_USER_ALL_FULLNAME | SAMR_USER_ALL_USERACCOUNTCONTROL | SAMR_USER_ALL_ADMINCOMMENT; (void) NDR_MSTRING(mxa, account.a_name, (ndr_mstring_t *)&all_info->UserName); (void) NDR_MSTRING(mxa, account.a_name, (ndr_mstring_t *)&all_info->FullName); (void) NDR_MSTRING(mxa, "", (ndr_mstring_t *)&all_info->AdminComment); all_info->UserId = data->kd_rid; all_info->UserAccountControl = SAMR_AF_NORMAL_ACCOUNT | SAMR_AF_DONT_EXPIRE_PASSWD; if ((account.a_flags & SMB_PWF_DISABLE) != 0) all_info->UserAccountControl |= SAMR_AF_ACCOUNTDISABLE; param->address = 1; param->switch_index = SAMR_QUERY_USER_ALL_INFO; param->status = NT_STATUS_SUCCESS; smb_account_free(&account); smb_sid_free(sid); return (NDR_DRC_OK); QueryUserInfoError: smb_sid_free(sid); bzero(param, sizeof (struct samr_QueryUserInfo)); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } /* * samr_s_QueryUserGroups * * Request the list of groups of which a user is a member. * The user is identified from the handle, which contains an * rid in the discriminator field. Note that this is a local user. */ static int samr_s_QueryUserGroups(void *arg, ndr_xa_t *mxa) { struct samr_QueryUserGroups *param = arg; struct samr_UserGroupInfo *info; struct samr_UserGroups *group; ndr_hdid_t *id = (ndr_hdid_t *)¶m->user_handle; ndr_handle_t *hd; samr_keydata_t *data; smb_sid_t *user_sid = NULL; smb_group_t grp; smb_giter_t gi; smb_domain_t di; uint32_t status; int size; int ngrp_max; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_USER)) == NULL) { status = NT_STATUS_ACCESS_DENIED; goto query_error; } data = (samr_keydata_t *)hd->nh_data; switch (data->kd_type) { case SMB_DOMAIN_BUILTIN: case SMB_DOMAIN_LOCAL: if (!smb_domain_lookup_type(data->kd_type, &di)) { status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; goto query_error; } break; default: status = NT_STATUS_INVALID_HANDLE; goto query_error; } user_sid = smb_sid_splice(di.di_binsid, data->kd_rid); if (user_sid == NULL) { status = NT_STATUS_NO_MEMORY; goto query_error; } info = NDR_NEW(mxa, struct samr_UserGroupInfo); if (info == NULL) { status = NT_STATUS_NO_MEMORY; goto query_error; } bzero(info, sizeof (struct samr_UserGroupInfo)); size = 32 * 1024; info->groups = NDR_MALLOC(mxa, size); if (info->groups == NULL) { status = NT_STATUS_NO_MEMORY; goto query_error; } ngrp_max = size / sizeof (struct samr_UserGroups); if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) { status = NT_STATUS_INTERNAL_ERROR; goto query_error; } info->n_entry = 0; group = info->groups; while ((info->n_entry < ngrp_max) && (smb_lgrp_iterate(&gi, &grp) == SMB_LGRP_SUCCESS)) { if (smb_lgrp_is_member(&grp, user_sid)) { group->rid = grp.sg_rid; group->attr = grp.sg_attr; group++; info->n_entry++; } smb_lgrp_free(&grp); } smb_lgrp_iterclose(&gi); free(user_sid); param->info = info; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); query_error: free(user_sid); bzero(param, sizeof (struct samr_QueryUserGroups)); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } /* * samr_s_OpenGroup * * This is a request to open a group within the specified domain in the * local SAM database. The caller must supply a valid domain handle, * obtained via a successful domain open request. The group is * specified by the rid in the request. If this is a local RID it * should already be encoded with type information. * * We return a handle to be used to access information about this group. */ static int samr_s_OpenGroup(void *arg, ndr_xa_t *mxa) { struct samr_OpenGroup *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; ndr_handle_t *hd; samr_keydata_t *data; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN)) == NULL) { bzero(¶m->group_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } data = (samr_keydata_t *)hd->nh_data; id = samr_hdalloc(mxa, SAMR_KEY_GROUP, data->kd_type, param->rid); if (id) { bcopy(id, ¶m->group_handle, sizeof (samr_handle_t)); param->status = 0; } else { bzero(¶m->group_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); } return (NDR_DRC_OK); } /* * samr_s_AddAliasMember * * Add a member to a local SAM group. * The caller must supply a valid group handle. * The member is specified by the sid in the request. */ static int samr_s_AddAliasMember(void *arg, ndr_xa_t *mxa) { struct samr_AddAliasMember *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->alias_handle; ndr_handle_t *hd; samr_keydata_t *data; smb_group_t grp; uint32_t rc; uint32_t status = NT_STATUS_SUCCESS; if (param->sid == NULL) { bzero(param, sizeof (struct samr_AddAliasMember)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_PARAMETER); return (NDR_DRC_OK); } if (!ndr_is_admin(mxa)) { bzero(param, sizeof (struct samr_AddAliasMember)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_ALIAS)) == NULL) { bzero(param, sizeof (struct samr_AddAliasMember)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } data = (samr_keydata_t *)hd->nh_data; rc = smb_lgrp_getbyrid(data->kd_rid, data->kd_type, &grp); if (rc != SMB_LGRP_SUCCESS) { bzero(param, sizeof (struct samr_AddAliasMember)); status = smb_lgrp_err_to_ntstatus(rc); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } rc = smb_lgrp_add_member(grp.sg_name, (smb_sid_t *)param->sid, SidTypeUser); if (rc != SMB_LGRP_SUCCESS) { bzero(param, sizeof (struct samr_AddAliasMember)); status = smb_lgrp_err_to_ntstatus(rc); param->status = NT_SC_ERROR(status); } smb_lgrp_free(&grp); param->status = status; return (NDR_DRC_OK); } /* * samr_s_DeleteAliasMember * * Delete a member from a local SAM group. * The caller must supply a valid group handle. * The member is specified by the sid in the request. */ static int samr_s_DeleteAliasMember(void *arg, ndr_xa_t *mxa) { struct samr_DeleteAliasMember *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->alias_handle; ndr_handle_t *hd; samr_keydata_t *data; smb_group_t grp; uint32_t rc; uint32_t status = NT_STATUS_SUCCESS; if (param->sid == NULL) { bzero(param, sizeof (struct samr_DeleteAliasMember)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_PARAMETER); return (NDR_DRC_OK); } if (!ndr_is_admin(mxa)) { bzero(param, sizeof (struct samr_DeleteAliasMember)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_ALIAS)) == NULL) { bzero(param, sizeof (struct samr_DeleteAliasMember)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } data = (samr_keydata_t *)hd->nh_data; rc = smb_lgrp_getbyrid(data->kd_rid, data->kd_type, &grp); if (rc != SMB_LGRP_SUCCESS) { bzero(param, sizeof (struct samr_DeleteAliasMember)); status = smb_lgrp_err_to_ntstatus(rc); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } rc = smb_lgrp_del_member(grp.sg_name, (smb_sid_t *)param->sid, SidTypeUser); if (rc != SMB_LGRP_SUCCESS) { bzero(param, sizeof (struct samr_DeleteAliasMember)); status = smb_lgrp_err_to_ntstatus(rc); param->status = NT_SC_ERROR(status); } smb_lgrp_free(&grp); param->status = status; return (NDR_DRC_OK); } /* * samr_s_ListAliasMembers * * List members from a local SAM group. * The caller must supply a valid group handle. * A list of user SIDs in the specified group is returned to the caller. */ static int samr_s_ListAliasMembers(void *arg, ndr_xa_t *mxa) { struct samr_ListAliasMembers *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->alias_handle; ndr_handle_t *hd; samr_keydata_t *data; smb_group_t grp; smb_gsid_t *members; struct samr_SidInfo info; struct samr_SidList *user; uint32_t num = 0, size; int i; uint32_t rc; uint32_t status = NT_STATUS_SUCCESS; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_ALIAS)) == NULL) { bzero(param, sizeof (struct samr_ListAliasMembers)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } bzero(&info, sizeof (struct samr_SidInfo)); data = (samr_keydata_t *)hd->nh_data; rc = smb_lgrp_getbyrid(data->kd_rid, data->kd_type, &grp); if (rc != SMB_LGRP_SUCCESS) { bzero(param, sizeof (struct samr_ListAliasMembers)); status = smb_lgrp_err_to_ntstatus(rc); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } num = grp.sg_nmembers; members = grp.sg_members; size = num * sizeof (struct samr_SidList); info.sidlist = NDR_MALLOC(mxa, size); if (info.sidlist == NULL) { bzero(param, sizeof (struct samr_ListAliasMembers)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); smb_lgrp_free(&grp); return (NDR_DRC_OK); } info.n_entry = num; user = info.sidlist; for (i = 0; i < num; i++) { user->sid = (struct samr_sid *)NDR_SIDDUP(mxa, members[i].gs_sid); if (user->sid == NULL) { bzero(param, sizeof (struct samr_ListAliasMembers)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); smb_lgrp_free(&grp); return (NDR_DRC_OK); } user++; } smb_lgrp_free(&grp); param->info = info; param->status = status; return (NDR_DRC_OK); } /* * samr_s_Connect2 * * This is a request to connect to the local SAM database. * We don't support any form of update request and our database doesn't * contain any private information, so there is little point in doing * any access access checking here. * * Return a handle for use with subsequent SAM requests. */ static int samr_s_Connect2(void *arg, ndr_xa_t *mxa) { struct samr_Connect2 *param = arg; ndr_hdid_t *id; id = samr_hdalloc(mxa, SAMR_KEY_CONNECT, SMB_DOMAIN_NULL, 0); if (id) { bcopy(id, ¶m->handle, sizeof (samr_handle_t)); param->status = 0; } else { bzero(¶m->handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); } return (NDR_DRC_OK); } /* * samr_s_GetUserPwInfo * * Request for a user's password policy information. */ /*ARGSUSED*/ static int samr_s_GetUserPwInfo(void *arg, ndr_xa_t *mxa) { static samr_password_info_t pwinfo; struct samr_GetUserPwInfo *param = arg; param->pwinfo = &pwinfo; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } /* * samr_s_CreateUser */ /*ARGSUSED*/ static int samr_s_CreateUser(void *arg, ndr_xa_t *mxa) { struct samr_CreateUser *param = arg; bzero(¶m->user_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } /* * samr_s_ChangePasswordUser2 */ /*ARGSUSED*/ static int samr_s_ChangePasswordUser2(void *arg, ndr_xa_t *mxa) { struct samr_ChangePasswordUser2 *param = arg; bzero(param, sizeof (*param)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } /* * samr_s_GetDomainPwInfo * * Request for the domain password policy information. */ /*ARGSUSED*/ static int samr_s_GetDomainPwInfo(void *arg, ndr_xa_t *mxa) { static samr_password_info_t pwinfo; struct samr_GetDomainPwInfo *param = arg; param->pwinfo = &pwinfo; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } /* * samr_s_SetUserInfo */ /*ARGSUSED*/ static int samr_s_SetUserInfo(void *arg, ndr_xa_t *mxa) { struct samr_SetUserInfo *param = arg; bzero(param, sizeof (struct samr_SetUserInfo)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } /* * samr_s_QueryDispInfo * * This function currently return local users' information only. * This RPC is called repeatedly until all the users info are * retrieved. * * The total count and the returned count are returned as total size * and returned size. The client doesn't seem to care. */ static int samr_s_QueryDispInfo(void *arg, ndr_xa_t *mxa) { struct samr_QueryDispInfo *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->domain_handle; ndr_handle_t *hd; samr_keydata_t *data; DWORD status = NT_STATUS_SUCCESS; struct user_acct_info *user; smb_pwditer_t pwi; smb_luser_t *uinfo; int num_users; int start_idx; int max_retcnt, retcnt; int skip; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN)) == NULL) { status = NT_STATUS_INVALID_HANDLE; goto error; } if (!SAMR_VALID_DISPLEVEL(param->level)) { status = NT_STATUS_INVALID_INFO_CLASS; goto error; } if (!SAMR_SUPPORTED_DISPLEVEL(param->level)) { status = NT_STATUS_NOT_IMPLEMENTED; goto error; } data = (samr_keydata_t *)hd->nh_data; switch (data->kd_type) { case SMB_DOMAIN_BUILTIN: goto no_info; case SMB_DOMAIN_LOCAL: num_users = smb_sam_usr_cnt(); start_idx = param->start_idx; if ((num_users == 0) || (start_idx >= num_users)) goto no_info; max_retcnt = num_users - start_idx; if (max_retcnt > param->max_entries) max_retcnt = param->max_entries; param->users.acct = NDR_MALLOC(mxa, max_retcnt * sizeof (struct user_acct_info)); user = param->users.acct; if (user == NULL) { status = NT_STATUS_NO_MEMORY; goto error; } bzero(user, max_retcnt * sizeof (struct user_acct_info)); if (smb_pwd_iteropen(&pwi) != SMB_PWE_SUCCESS) goto no_info; skip = retcnt = 0; while ((uinfo = smb_pwd_iterate(&pwi)) != NULL) { if (skip++ < start_idx) continue; if (retcnt++ >= max_retcnt) break; assert(uinfo->su_name != NULL); user->index = start_idx + retcnt; user->rid = uinfo->su_rid; user->ctrl = ACF_NORMUSER | ACF_PWDNOEXP; if (uinfo->su_ctrl & SMB_PWF_DISABLE) user->ctrl |= ACF_DISABLED; if (NDR_MSTRING(mxa, uinfo->su_name, (ndr_mstring_t *)&user->name) == -1) { smb_pwd_iterclose(&pwi); status = NT_STATUS_NO_MEMORY; goto error; } (void) NDR_MSTRING(mxa, uinfo->su_fullname, (ndr_mstring_t *)&user->fullname); (void) NDR_MSTRING(mxa, uinfo->su_desc, (ndr_mstring_t *)&user->desc); user++; } smb_pwd_iterclose(&pwi); if (retcnt >= max_retcnt) { retcnt = max_retcnt; param->status = status; } else { param->status = NT_STATUS_MORE_ENTRIES; } param->users.total_size = num_users; param->users.returned_size = retcnt; param->users.switch_value = param->level; param->users.count = retcnt; break; default: status = NT_STATUS_INVALID_HANDLE; goto error; } return (NDR_DRC_OK); no_info: param->users.total_size = 0; param->users.returned_size = 0; param->users.switch_value = param->level; param->users.count = 0; param->users.acct = NULL; param->status = status; return (NDR_DRC_OK); error: bzero(param, sizeof (struct samr_QueryDispInfo)); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } /* * samr_s_EnumDomainGroups * * * This function is supposed to return local group information. * As we don't support local users, this function dosen't send * back any information. * * Added template that returns information for a domain group as None. * All information is hard-coded from packet captures. */ static int samr_s_EnumDomainGroups(void *arg, ndr_xa_t *mxa) { struct samr_EnumDomainGroups *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->domain_handle; DWORD status = NT_STATUS_SUCCESS; if (samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN) == NULL) status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); param->total_size = 0; param->returned_size = 0; param->switch_value = 3; param->count = 0; param->groups = 0; param->status = status; return (NDR_DRC_OK); #ifdef SAMR_SUPPORT_GROUPS if ((desc->discrim != SAMR_LOCAL_DOMAIN) || (param->start_idx != 0)) { param->total_size = 0; param->returned_size = 0; param->switch_value = 3; param->count = 0; param->groups = 0; } else { param->total_size = 64; param->returned_size = 64; param->switch_value = 3; param->count = 1; param->groups = (struct group_disp_info *)NDR_MALLOC( mxa, sizeof (struct group_disp_info)); param->groups->count = 1; param->groups->acct[0].index = 1; param->groups->acct[0].rid = 513; param->groups->acct[0].ctrl = 0x7; (void) NDR_MSTRING(mxa, "None", (ndr_mstring_t *)¶m->groups->acct[0].name); (void) NDR_MSTRING(mxa, "Ordinary users", (ndr_mstring_t *)¶m->groups->acct[0].desc); } param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); #endif } /* * samr_s_OpenAlias * * Lookup for requested alias, if it exists return a handle * for that alias. The alias domain sid should match with * the passed domain handle. */ static int samr_s_OpenAlias(void *arg, ndr_xa_t *mxa) { struct samr_OpenAlias *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->domain_handle; ndr_handle_t *hd; samr_keydata_t *data; smb_domain_type_t gd_type; smb_sid_t *sid; smb_wka_t *wka; char sidstr[SMB_SID_STRSZ]; uint32_t rid; uint32_t status; int rc; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN)) == NULL) { status = NT_STATUS_INVALID_HANDLE; goto open_alias_err; } if ((param->access_mask & SAMR_ALIAS_ACCESS_ALL_ACCESS) == 0) { status = NT_STATUS_ACCESS_DENIED; goto open_alias_err; } data = (samr_keydata_t *)hd->nh_data; gd_type = (smb_domain_type_t)data->kd_type; rid = param->rid; switch (gd_type) { case SMB_DOMAIN_BUILTIN: (void) snprintf(sidstr, SMB_SID_STRSZ, "%s-%d", NT_BUILTIN_DOMAIN_SIDSTR, rid); if ((sid = smb_sid_fromstr(sidstr)) == NULL) { status = NT_STATUS_NO_SUCH_ALIAS; goto open_alias_err; } wka = smb_wka_lookup_sid(sid); smb_sid_free(sid); if (wka == NULL) { status = NT_STATUS_NO_SUCH_ALIAS; goto open_alias_err; } break; case SMB_DOMAIN_LOCAL: rc = smb_lgrp_getbyrid(rid, gd_type, NULL); if (rc != SMB_LGRP_SUCCESS) { status = NT_STATUS_NO_SUCH_ALIAS; goto open_alias_err; } break; default: status = NT_STATUS_NO_SUCH_ALIAS; goto open_alias_err; } id = samr_hdalloc(mxa, SAMR_KEY_ALIAS, data->kd_type, param->rid); if (id) { bcopy(id, ¶m->alias_handle, sizeof (samr_handle_t)); param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } status = NT_STATUS_NO_MEMORY; open_alias_err: bzero(¶m->alias_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } /* * samr_s_CreateDomainAlias * * Create a local group in the security accounts manager (SAM) database. * A local SAM group can only be added if a Solaris group already exists * with the same name. On success, a valid group handle is returned. * * The caller must have administrator rights to execute this function. */ static int samr_s_CreateDomainAlias(void *arg, ndr_xa_t *mxa) { struct samr_CreateDomainAlias *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->alias_handle; uint32_t status = NT_STATUS_SUCCESS; smb_group_t grp; uint32_t rc; char *gname; if (samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN) != NULL) { bzero(param, sizeof (struct samr_CreateDomainAlias)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } gname = (char *)param->alias_name.str; if (gname == NULL) { bzero(¶m->alias_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_PARAMETER); return (NDR_DRC_OK); } if ((!ndr_is_admin(mxa)) || ((param->access_mask & SAMR_ALIAS_ACCESS_WRITE_ACCOUNT) == 0)) { bzero(¶m->alias_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } rc = smb_lgrp_add(gname, ""); if (rc != SMB_LGRP_SUCCESS) { bzero(¶m->alias_handle, sizeof (samr_handle_t)); status = smb_lgrp_err_to_ntstatus(rc); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } rc = smb_lgrp_getbyname((char *)gname, &grp); if (rc != SMB_LGRP_SUCCESS) { bzero(¶m->alias_handle, sizeof (samr_handle_t)); status = smb_lgrp_err_to_ntstatus(rc); param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } id = samr_hdalloc(mxa, SAMR_KEY_ALIAS, SMB_DOMAIN_LOCAL, grp.sg_rid); smb_lgrp_free(&grp); if (id) { bcopy(id, ¶m->alias_handle, sizeof (samr_handle_t)); param->status = status; } else { bzero(¶m->alias_handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); } return (NDR_DRC_OK); } /* * samr_s_SetAliasInfo * * Similar to NetLocalGroupSetInfo. */ static int samr_s_SetAliasInfo(void *arg, ndr_xa_t *mxa) { struct samr_SetAliasInfo *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->alias_handle; DWORD status = NT_STATUS_SUCCESS; if (samr_hdlookup(mxa, id, SAMR_KEY_ALIAS) == NULL) status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); param->status = status; return (NDR_DRC_OK); } /* * samr_s_QueryAliasInfo * * Retrieves information about the specified local group account * by given handle. */ static int samr_s_QueryAliasInfo(void *arg, ndr_xa_t *mxa) { struct samr_QueryAliasInfo *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->alias_handle; ndr_handle_t *hd; samr_keydata_t *data; smb_group_t grp; smb_domain_type_t gd_type; smb_sid_t *sid; smb_wka_t *wka; char sidstr[SMB_SID_STRSZ]; char *name; char *desc; uint32_t rid; uint32_t status; int rc; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_ALIAS)) == NULL) { status = NT_STATUS_INVALID_HANDLE; goto query_alias_err; } data = (samr_keydata_t *)hd->nh_data; gd_type = (smb_domain_type_t)data->kd_type; rid = data->kd_rid; switch (gd_type) { case SMB_DOMAIN_BUILTIN: (void) snprintf(sidstr, SMB_SID_STRSZ, "%s-%d", NT_BUILTIN_DOMAIN_SIDSTR, rid); if ((sid = smb_sid_fromstr(sidstr)) == NULL) { status = NT_STATUS_NO_SUCH_ALIAS; goto query_alias_err; } wka = smb_wka_lookup_sid(sid); smb_sid_free(sid); if (wka == NULL) { status = NT_STATUS_NO_SUCH_ALIAS; goto query_alias_err; } name = wka->wka_name; desc = (wka->wka_desc != NULL) ? wka->wka_desc : ""; break; case SMB_DOMAIN_LOCAL: rc = smb_lgrp_getbyrid(rid, gd_type, &grp); if (rc != SMB_LGRP_SUCCESS) { status = NT_STATUS_NO_SUCH_ALIAS; goto query_alias_err; } name = grp.sg_name; desc = grp.sg_cmnt; break; default: status = NT_STATUS_NO_SUCH_ALIAS; goto query_alias_err; } switch (param->level) { case SAMR_QUERY_ALIAS_INFO_GENERAL: param->ru.info1.level = param->level; (void) NDR_MSTRING(mxa, name, (ndr_mstring_t *)¶m->ru.info1.name); (void) NDR_MSTRING(mxa, desc, (ndr_mstring_t *)¶m->ru.info1.desc); param->ru.info1.member_count = 1; break; case SAMR_QUERY_ALIAS_INFO_NAME: param->ru.info2.level = param->level; (void) NDR_MSTRING(mxa, name, (ndr_mstring_t *)¶m->ru.info2.name); break; case SAMR_QUERY_ALIAS_INFO_COMMENT: param->ru.info3.level = param->level; (void) NDR_MSTRING(mxa, desc, (ndr_mstring_t *)¶m->ru.info3.desc); break; default: if (gd_type == SMB_DOMAIN_LOCAL) smb_lgrp_free(&grp); status = NT_STATUS_INVALID_INFO_CLASS; goto query_alias_err; }; if (gd_type == SMB_DOMAIN_LOCAL) smb_lgrp_free(&grp); param->address = (DWORD)(uintptr_t)¶m->ru; param->status = 0; return (NDR_DRC_OK); query_alias_err: param->status = NT_SC_ERROR(status); return (NDR_DRC_OK); } /* * samr_s_DeleteDomainAlias * * Deletes a local group in the security database, which is the * security accounts manager (SAM). A valid group handle is returned * to the caller upon success. * * The caller must have administrator rights to execute this function. */ static int samr_s_DeleteDomainAlias(void *arg, ndr_xa_t *mxa) { struct samr_DeleteDomainAlias *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->alias_handle; ndr_handle_t *hd; smb_group_t grp; samr_keydata_t *data; smb_domain_type_t gd_type; uint32_t rid; uint32_t rc; uint32_t status = NT_STATUS_SUCCESS; if (!ndr_is_admin(mxa)) { bzero(param, sizeof (struct samr_DeleteDomainAlias)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_ALIAS)) == NULL) { bzero(param, sizeof (struct samr_DeleteDomainAlias)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } data = (samr_keydata_t *)hd->nh_data; gd_type = (smb_domain_type_t)data->kd_type; rid = data->kd_rid; switch (gd_type) { case SMB_DOMAIN_BUILTIN: bzero(param, sizeof (struct samr_DeleteDomainAlias)); status = NT_SC_ERROR(NT_STATUS_NOT_SUPPORTED); break; case SMB_DOMAIN_LOCAL: rc = smb_lgrp_getbyrid(rid, gd_type, &grp); if (rc != SMB_LGRP_SUCCESS) { bzero(param, sizeof (struct samr_DeleteDomainAlias)); status = smb_lgrp_err_to_ntstatus(rc); status = NT_SC_ERROR(status); break; } rc = smb_lgrp_delete(grp.sg_name); if (rc != SMB_LGRP_SUCCESS) { bzero(param, sizeof (struct samr_DeleteDomainAlias)); status = smb_lgrp_err_to_ntstatus(rc); status = NT_SC_ERROR(status); } smb_lgrp_free(&grp); break; default: bzero(param, sizeof (struct samr_DeleteDomainAlias)); status = NT_SC_ERROR(NT_STATUS_NO_SUCH_ALIAS); } param->status = status; return (NDR_DRC_OK); } /* * samr_s_EnumDomainAliases * * This function sends back a list which contains all local groups' name. */ static int samr_s_EnumDomainAliases(void *arg, ndr_xa_t *mxa) { struct samr_EnumDomainAliases *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->domain_handle; ndr_handle_t *hd; samr_keydata_t *data; smb_group_t grp; smb_giter_t gi; int cnt, skip, i; struct name_rid *info; if ((hd = samr_hdlookup(mxa, id, SAMR_KEY_DOMAIN)) == NULL) { bzero(param, sizeof (struct samr_EnumDomainAliases)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } data = (samr_keydata_t *)hd->nh_data; cnt = smb_sam_grp_cnt(data->kd_type); if (cnt <= param->resume_handle) { param->aliases = (struct aliases_info *)NDR_MALLOC(mxa, sizeof (struct aliases_info)); if (param->aliases == NULL) { bzero(param, sizeof (struct samr_EnumDomainAliases)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); return (NDR_DRC_OK); } bzero(param->aliases, sizeof (struct aliases_info)); param->out_resume = 0; param->entries = 0; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } cnt -= param->resume_handle; param->aliases = (struct aliases_info *)NDR_MALLOC(mxa, sizeof (struct aliases_info) + (cnt-1) * sizeof (struct name_rid)); if (param->aliases == NULL) { bzero(param, sizeof (struct samr_EnumDomainAliases)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); return (NDR_DRC_OK); } if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) { bzero(param, sizeof (struct samr_EnumDomainAliases)); param->status = NT_SC_ERROR(NT_STATUS_INTERNAL_ERROR); return (NDR_DRC_OK); } skip = i = 0; info = param->aliases->info; while (smb_lgrp_iterate(&gi, &grp) == SMB_LGRP_SUCCESS) { if ((skip++ >= param->resume_handle) && (grp.sg_domain == data->kd_type) && (i++ < cnt)) { info->rid = grp.sg_rid; (void) NDR_MSTRING(mxa, grp.sg_name, (ndr_mstring_t *)&info->name); info++; } smb_lgrp_free(&grp); } smb_lgrp_iterclose(&gi); param->aliases->count = i; param->aliases->address = i; param->out_resume = i; param->entries = i; param->status = 0; return (NDR_DRC_OK); } /* * samr_s_Connect4 */ static int samr_s_Connect4(void *arg, ndr_xa_t *mxa) { struct samr_Connect4 *param = arg; ndr_hdid_t *id; id = samr_hdalloc(mxa, SAMR_KEY_CONNECT, SMB_DOMAIN_NULL, 0); if (id) { bcopy(id, ¶m->handle, sizeof (samr_handle_t)); param->status = 0; } else { bzero(¶m->handle, sizeof (samr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); } return (NDR_DRC_OK); } /* * samr_s_Connect5 * * This is the connect5 form of the connect request used by Windows XP. * Returns an RPC fault for now. */ /*ARGSUSED*/ static int samr_s_Connect5(void *arg, ndr_xa_t *mxa) { struct samr_Connect5 *param = arg; bzero(param, sizeof (struct samr_Connect5)); return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID); } static ndr_stub_table_t samr_stub_table[] = { { samr_s_Connect, SAMR_OPNUM_Connect }, { samr_s_CloseHandle, SAMR_OPNUM_CloseHandle }, { samr_s_QuerySecObject, SAMR_OPNUM_QuerySecObject }, { samr_s_LookupDomain, SAMR_OPNUM_LookupDomain }, { samr_s_EnumLocalDomains, SAMR_OPNUM_EnumLocalDomains }, { samr_s_OpenDomain, SAMR_OPNUM_OpenDomain }, { samr_s_QueryDomainInfo, SAMR_OPNUM_QueryDomainInfo }, { samr_s_QueryInfoDomain2, SAMR_OPNUM_QueryInfoDomain2 }, { samr_s_LookupNames, SAMR_OPNUM_LookupNames }, { samr_s_OpenUser, SAMR_OPNUM_OpenUser }, { samr_s_DeleteUser, SAMR_OPNUM_DeleteUser }, { samr_s_QueryUserInfo, SAMR_OPNUM_QueryUserInfo }, { samr_s_QueryUserGroups, SAMR_OPNUM_QueryUserGroups }, { samr_s_OpenGroup, SAMR_OPNUM_OpenGroup }, { samr_s_Connect2, SAMR_OPNUM_Connect2 }, { samr_s_GetUserPwInfo, SAMR_OPNUM_GetUserPwInfo }, { samr_s_CreateUser, SAMR_OPNUM_CreateUser }, { samr_s_ChangePasswordUser2, SAMR_OPNUM_ChangePasswordUser2 }, { samr_s_GetDomainPwInfo, SAMR_OPNUM_GetDomainPwInfo }, { samr_s_SetUserInfo, SAMR_OPNUM_SetUserInfo }, { samr_s_Connect4, SAMR_OPNUM_Connect4 }, { samr_s_Connect5, SAMR_OPNUM_Connect5 }, { samr_s_QueryDispInfo, SAMR_OPNUM_QueryDispInfo }, { samr_s_OpenAlias, SAMR_OPNUM_OpenAlias }, { samr_s_CreateDomainAlias, SAMR_OPNUM_CreateDomainAlias }, { samr_s_SetAliasInfo, SAMR_OPNUM_SetAliasInfo }, { samr_s_QueryAliasInfo, SAMR_OPNUM_QueryAliasInfo }, { samr_s_DeleteDomainAlias, SAMR_OPNUM_DeleteDomainAlias }, { samr_s_EnumDomainAliases, SAMR_OPNUM_EnumDomainAliases }, { samr_s_EnumDomainGroups, SAMR_OPNUM_EnumDomainGroups }, { samr_s_AddAliasMember, SAMR_OPNUM_AddAliasMember }, { samr_s_DeleteAliasMember, SAMR_OPNUM_DeleteAliasMember }, { samr_s_ListAliasMembers, SAMR_OPNUM_ListAliasMembers }, {0} }; /* * There is a bug in the way that midl and the marshalling code handles * unions so we need to fix some of the data offsets at runtime. The * following macros and the fixup functions handle the corrections. */ DECL_FIXUP_STRUCT(samr_QueryAliasInfo_ru); DECL_FIXUP_STRUCT(samr_QueryAliasInfoRes); DECL_FIXUP_STRUCT(samr_QueryAliasInfo); DECL_FIXUP_STRUCT(QueryUserInfo_result_u); DECL_FIXUP_STRUCT(QueryUserInfo_result); DECL_FIXUP_STRUCT(samr_QueryUserInfo); void fixup_samr_QueryAliasInfo(struct samr_QueryAliasInfo *val) { unsigned short size1 = 0; unsigned short size2 = 0; unsigned short size3 = 0; switch (val->level) { case SAMR_QUERY_ALIAS_INFO_GENERAL: size1 = sizeof (struct samr_QueryAliasInfoGeneral); break; case SAMR_QUERY_ALIAS_INFO_NAME: size1 = sizeof (struct samr_QueryAliasInfoName); break; case SAMR_QUERY_ALIAS_INFO_COMMENT: size1 = sizeof (struct samr_QueryAliasInfoComment); break; default: return; }; size2 = size1 + (2 * sizeof (DWORD)); size3 = size2 + sizeof (ndr_request_hdr_t) + sizeof (DWORD); FIXUP_PDU_SIZE(samr_QueryAliasInfo_ru, size1); FIXUP_PDU_SIZE(samr_QueryAliasInfoRes, size2); FIXUP_PDU_SIZE(samr_QueryAliasInfo, size3); } void fixup_samr_QueryUserInfo(struct samr_QueryUserInfo *val) { unsigned short size1 = 0; unsigned short size2 = 0; unsigned short size3 = 0; switch (val->switch_index) { CASE_INFO_ENT(samr_QueryUserInfo, 1); CASE_INFO_ENT(samr_QueryUserInfo, 6); CASE_INFO_ENT(samr_QueryUserInfo, 7); CASE_INFO_ENT(samr_QueryUserInfo, 8); CASE_INFO_ENT(samr_QueryUserInfo, 9); CASE_INFO_ENT(samr_QueryUserInfo, 16); CASE_INFO_ENT(samr_QueryUserInfo, 21); default: return; }; size2 = size1 + (2 * sizeof (DWORD)); size3 = size2 + sizeof (ndr_request_hdr_t) + sizeof (DWORD); FIXUP_PDU_SIZE(QueryUserInfo_result_u, size1); FIXUP_PDU_SIZE(QueryUserInfo_result, size2); FIXUP_PDU_SIZE(samr_QueryUserInfo, size3); } /* * As long as there is only one entry in the union, there is no need * to patch anything. */ /*ARGSUSED*/ void fixup_samr_QueryGroupInfo(struct samr_QueryGroupInfo *val) { }