summaryrefslogtreecommitdiffstats
path: root/source4/libcli/util/clilsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli/util/clilsa.c')
-rw-r--r--source4/libcli/util/clilsa.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/source4/libcli/util/clilsa.c b/source4/libcli/util/clilsa.c
new file mode 100644
index 0000000..8476fd8
--- /dev/null
+++ b/source4/libcli/util/clilsa.c
@@ -0,0 +1,548 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ lsa calls for file sharing connections
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ when dealing with ACLs the file sharing client code needs to
+ sometimes make LSA RPC calls. This code provides an easy interface
+ for doing those calls.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/libcli.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/util/clilsa.h"
+#include "libcli/smb/smbXcli_base.h"
+
+struct smblsa_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct smbcli_tree *ipc_tree;
+ struct policy_handle handle;
+};
+
+/*
+ establish the lsa pipe connection
+*/
+static NTSTATUS smblsa_connect(struct smbcli_state *cli)
+{
+ struct smblsa_state *lsa;
+ struct dcerpc_pipe *lsa_pipe;
+ NTSTATUS status;
+ struct lsa_OpenPolicy r;
+ uint16_t system_name = '\\';
+ union smb_tcon tcon;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+
+ if (cli->lsa != NULL) {
+ return NT_STATUS_OK;
+ }
+
+ lsa = talloc(cli, struct smblsa_state);
+ if (lsa == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lsa->ipc_tree = smbcli_tree_init(cli->session, lsa, false);
+ if (lsa->ipc_tree == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* connect to IPC$ */
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE;
+ tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = "ipc$";
+ tcon.tconx.in.device = "IPC";
+ status = smb_raw_tcon(lsa->ipc_tree, lsa, &tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+ lsa->ipc_tree->tid = tcon.tconx.out.tid;
+
+ if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) {
+ smb1cli_session_protect_session_key(cli->session->smbXcli);
+ }
+
+ lsa_pipe = dcerpc_pipe_init(lsa, cli->transport->ev);
+ if (lsa_pipe == NULL) {
+ talloc_free(lsa);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* open the LSA pipe */
+ status = dcerpc_pipe_open_smb(lsa_pipe, lsa->ipc_tree, NDR_LSARPC_NAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ /* bind to the LSA pipe */
+ status = dcerpc_bind_auth_none(lsa_pipe, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+ lsa->binding_handle = lsa_pipe->binding_handle;
+
+ /* open a lsa policy handle */
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = &system_name;
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &lsa->handle;
+
+ status = dcerpc_lsa_OpenPolicy_r(lsa->binding_handle, lsa, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(lsa);
+ return r.out.result;
+ }
+
+ cli->lsa = lsa;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS smb2lsa_connect(struct smb2_tree *tree)
+{
+ struct smb2lsa_state *lsa = NULL;
+ struct dcerpc_pipe *lsa_pipe = NULL;
+ NTSTATUS status;
+ struct lsa_OpenPolicy2 r = {{0}, {0}};
+ const char *system_name = "\\";
+ struct lsa_ObjectAttribute attr = {0};
+ struct lsa_QosInfo qos = {0};
+
+ if (tree->lsa != NULL) {
+ return NT_STATUS_OK;
+ }
+
+ lsa = talloc(tree, struct smb2lsa_state);
+ if (lsa == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lsa_pipe = dcerpc_pipe_init(lsa, tree->session->transport->ev);
+ if (lsa_pipe == NULL) {
+ talloc_free(lsa);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* open the LSA pipe */
+ status = dcerpc_pipe_open_smb2(lsa_pipe, tree, NDR_LSARPC_NAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ /* bind to the LSA pipe */
+ status = dcerpc_bind_auth_none(lsa_pipe, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+ lsa->binding_handle = lsa_pipe->binding_handle;
+
+ /* open a lsa policy handle */
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = system_name;
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &lsa->handle;
+
+ status = dcerpc_lsa_OpenPolicy2_r(lsa->binding_handle, lsa, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(lsa);
+ return r.out.result;
+ }
+
+ tree->lsa = lsa;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the set of privileges for the given sid
+*/
+NTSTATUS smblsa_sid_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.out.rights = rights;
+
+ status = dcerpc_lsa_EnumAccountRights_r(cli->lsa->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r.out.result;
+}
+
+
+NTSTATUS smb2lsa_sid_privileges(struct smb2_tree *tree, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccountRights r = {{0}, {0}};
+
+ status = smb2lsa_connect(tree);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &tree->lsa->handle;
+ r.in.sid = sid;
+ r.out.rights = rights;
+
+ status = dcerpc_lsa_EnumAccountRights_r(tree->lsa->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r.out.result;
+}
+
+
+/*
+ check if a named sid has a particular named privilege
+*/
+NTSTATUS smblsa_sid_check_privilege(struct smbcli_state *cli,
+ const char *sid_str,
+ const char *privilege)
+{
+ struct lsa_RightSet rights;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = talloc_new(cli);
+ struct dom_sid *sid;
+ unsigned i;
+
+ sid = dom_sid_parse_talloc(mem_ctx, sid_str);
+ if (sid == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_SID;
+ }
+
+ status = smblsa_sid_privileges(cli, sid, mem_ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ for (i=0;i<rights.count;i++) {
+ if (strcmp(rights.names[i].string, privilege) == 0) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+}
+
+
+NTSTATUS smb2lsa_sid_check_privilege(struct smb2_tree *tree,
+ const char *sid_str,
+ const char *privilege)
+{
+ struct lsa_RightSet rights = {0};
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct dom_sid *sid = NULL;
+ unsigned i;
+
+ mem_ctx = talloc_new(tree);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sid = dom_sid_parse_talloc(mem_ctx, sid_str);
+ if (sid == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_SID;
+ }
+
+ status = smb2lsa_sid_privileges(tree, sid, mem_ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ for (i=0;i<rights.count;i++) {
+ if (strcmp(rights.names[i].string, privilege) == 0) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+}
+
+
+/*
+ lookup a SID, returning its name
+*/
+NTSTATUS smblsa_lookup_sid(struct smbcli_state *cli,
+ const char *sid_str,
+ TALLOC_CTX *mem_ctx,
+ const char **name)
+{
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ struct lsa_SidArray sids;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = 1;
+ NTSTATUS status;
+ struct dom_sid *sid;
+ TALLOC_CTX *mem_ctx2 = talloc_new(mem_ctx);
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sid = dom_sid_parse_talloc(mem_ctx2, sid_str);
+ if (sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ names.count = 0;
+ names.names = NULL;
+
+ sids.num_sids = 1;
+ sids.sids = talloc(mem_ctx2, struct lsa_SidPtr);
+ sids.sids[0].sid = sid;
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sids = &sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids_r(cli->lsa->binding_handle, mem_ctx2, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx2);
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(mem_ctx2);
+ return r.out.result;
+ }
+ if (names.count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (domains == NULL) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (domains->count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (names.names[0].sid_index != UINT32_MAX &&
+ names.names[0].sid_index >= domains->count)
+ {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ (*name) = talloc_asprintf(mem_ctx, "%s\\%s",
+ domains->domains[0].name.string,
+ names.names[0].name.string);
+
+ talloc_free(mem_ctx2);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lookup a name, returning its sid
+*/
+NTSTATUS smblsa_lookup_name(struct smbcli_state *cli,
+ const char *name,
+ TALLOC_CTX *mem_ctx,
+ const char **sid_str)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_String names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = 1;
+ NTSTATUS status;
+ struct dom_sid sid;
+ TALLOC_CTX *mem_ctx2 = talloc_new(mem_ctx);
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names.string = name;
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.num_names = 1;
+ r.in.names = &names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupNames_r(cli->lsa->binding_handle, mem_ctx2, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx2);
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(mem_ctx2);
+ return r.out.result;
+ }
+ if (sids.count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (domains->count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ sid_compose(&sid, domains->domains[0].sid, sids.sids[0].rid);
+
+ (*sid_str) = dom_sid_string(mem_ctx, &sid);
+
+ talloc_free(mem_ctx2);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add a set of privileges to the given sid
+*/
+NTSTATUS smblsa_sid_add_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_AddAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.in.rights = rights;
+
+ status = dcerpc_lsa_AddAccountRights_r(cli->lsa->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r.out.result;
+}
+
+/*
+ remove a set of privileges from the given sid
+*/
+NTSTATUS smblsa_sid_del_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_RemoveAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.in.remove_all = 0;
+ r.in.rights = rights;
+
+ status = dcerpc_lsa_RemoveAccountRights_r(cli->lsa->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r.out.result;
+}