2 Unix SMB/CIFS implementation.
4 endpoint server for the samr pipe
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Volker Lendecke 2004
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
9 Copyright (C) Matthias Dieter Wallnöfer 2009
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "librpc/gen_ndr/ndr_samr.h"
27 #include "rpc_server/dcerpc_server.h"
28 #include "rpc_server/common/common.h"
29 #include "rpc_server/samr/dcesrv_samr.h"
30 #include "system/time.h"
32 #include <ldb_errors.h>
33 #include "../libds/common/flags.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "dsdb/common/util.h"
36 #include "libcli/ldap/ldap_ndr.h"
37 #include "libcli/security/security.h"
38 #include "rpc_server/samr/proto.h"
39 #include "../lib/util/util_ldb.h"
40 #include "param/param.h"
41 #include "lib/util/tsort.h"
42 #include "libds/common/flag_mapping.h"
44 #define DCESRV_INTERFACE_SAMR_BIND(call, iface) \
45 dcesrv_interface_samr_bind(call, iface)
46 static NTSTATUS dcesrv_interface_samr_bind(struct dcesrv_call_state *dce_call,
47 const struct dcesrv_interface *iface)
49 struct dcesrv_connection_context *context = dce_call->context;
50 return dcesrv_interface_bind_reject_connect(context, iface);
53 /* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
55 #define QUERY_STRING(msg, field, attr) \
56 info->field.string = ldb_msg_find_attr_as_string(msg, attr, "");
57 #define QUERY_UINT(msg, field, attr) \
58 info->field = ldb_msg_find_attr_as_uint(msg, attr, 0);
59 #define QUERY_RID(msg, field, attr) \
60 info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
61 #define QUERY_UINT64(msg, field, attr) \
62 info->field = ldb_msg_find_attr_as_uint64(msg, attr, 0);
63 #define QUERY_APASSC(msg, field, attr) \
64 info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
65 a_state->domain_state->domain_dn, msg, attr);
66 #define QUERY_BPWDCT(msg, field, attr) \
67 info->field = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, \
68 a_state->domain_state->domain_dn, msg);
69 #define QUERY_LHOURS(msg, field, attr) \
70 info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
71 #define QUERY_AFLAGS(msg, field, attr) \
72 info->field = samdb_result_acct_flags(msg, attr);
75 /* these are used to make the Set[User|Group]Info code easier to follow */
77 #define SET_STRING(msg, field, attr) do { \
78 struct ldb_message_element *set_el; \
79 if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
80 if (r->in.info->field.string[0] == '\0') { \
81 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
82 return NT_STATUS_NO_MEMORY; \
85 if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
86 return NT_STATUS_NO_MEMORY; \
88 set_el = ldb_msg_find_element(msg, attr); \
89 set_el->flags = LDB_FLAG_MOD_REPLACE; \
92 #define SET_UINT(msg, field, attr) do { \
93 struct ldb_message_element *set_el; \
94 if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
95 return NT_STATUS_NO_MEMORY; \
97 set_el = ldb_msg_find_element(msg, attr); \
98 set_el->flags = LDB_FLAG_MOD_REPLACE; \
101 #define SET_INT64(msg, field, attr) do { \
102 struct ldb_message_element *set_el; \
103 if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
104 return NT_STATUS_NO_MEMORY; \
106 set_el = ldb_msg_find_element(msg, attr); \
107 set_el->flags = LDB_FLAG_MOD_REPLACE; \
110 #define SET_UINT64(msg, field, attr) do { \
111 struct ldb_message_element *set_el; \
112 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
113 return NT_STATUS_NO_MEMORY; \
115 set_el = ldb_msg_find_element(msg, attr); \
116 set_el->flags = LDB_FLAG_MOD_REPLACE; \
119 /* Set account flags, discarding flags that cannot be set with SAMR */
120 #define SET_AFLAGS(msg, field, attr) do { \
121 struct ldb_message_element *set_el; \
122 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
123 return NT_STATUS_NO_MEMORY; \
125 set_el = ldb_msg_find_element(msg, attr); \
126 set_el->flags = LDB_FLAG_MOD_REPLACE; \
129 #define SET_LHOURS(msg, field, attr) do { \
130 struct ldb_message_element *set_el; \
131 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
132 return NT_STATUS_NO_MEMORY; \
134 set_el = ldb_msg_find_element(msg, attr); \
135 set_el->flags = LDB_FLAG_MOD_REPLACE; \
138 #define SET_PARAMETERS(msg, field, attr) do { \
139 struct ldb_message_element *set_el; \
140 if (r->in.info->field.length != 0) { \
141 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
142 return NT_STATUS_NO_MEMORY; \
144 set_el = ldb_msg_find_element(msg, attr); \
145 set_el->flags = LDB_FLAG_MOD_REPLACE; \
152 static void clear_guid_cache(struct samr_guid_cache *cache)
156 TALLOC_FREE(cache->entries);
160 * initialize a GUID cache
162 static void initialize_guid_cache(struct samr_guid_cache *cache)
166 cache->entries = NULL;
169 static NTSTATUS load_guid_cache(
170 struct samr_guid_cache *cache,
171 struct samr_domain_state *d_state,
172 unsigned int ldb_cnt,
173 struct ldb_message **res)
175 NTSTATUS status = NT_STATUS_OK;
177 TALLOC_CTX *frame = talloc_stackframe();
179 clear_guid_cache(cache);
182 * Store the GUID's in the cache.
185 cache->size = ldb_cnt;
186 cache->entries = talloc_array(d_state, struct GUID, ldb_cnt);
187 if (cache->entries == NULL) {
188 clear_guid_cache(cache);
189 status = NT_STATUS_NO_MEMORY;
194 * Extract a list of the GUIDs for all the matching objects
195 * we cache just the GUIDS to reduce the memory overhead of
198 for (i = 0; i < ldb_cnt; i++) {
199 cache->entries[i] = samdb_result_guid(res[i], "objectGUID");
209 create a connection to the SAM database
211 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
212 struct samr_Connect *r)
214 struct auth_session_info *session_info =
215 dcesrv_call_session_info(dce_call);
216 struct samr_connect_state *c_state;
217 struct dcesrv_handle *handle;
219 ZERO_STRUCTP(r->out.connect_handle);
221 c_state = talloc(mem_ctx, struct samr_connect_state);
223 return NT_STATUS_NO_MEMORY;
226 /* make sure the sam database is accessible */
227 c_state->sam_ctx = samdb_connect(c_state,
229 dce_call->conn->dce_ctx->lp_ctx,
231 dce_call->conn->remote_address,
233 if (c_state->sam_ctx == NULL) {
234 talloc_free(c_state);
235 return NT_STATUS_INVALID_SYSTEM_SERVICE;
239 handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_CONNECT);
241 talloc_free(c_state);
242 return NT_STATUS_NO_MEMORY;
245 handle->data = talloc_steal(handle, c_state);
247 c_state->access_mask = r->in.access_mask;
248 *r->out.connect_handle = handle->wire_handle;
257 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
258 struct samr_Close *r)
260 struct dcesrv_handle *h;
262 *r->out.handle = *r->in.handle;
264 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
268 ZERO_STRUCTP(r->out.handle);
277 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
278 struct samr_SetSecurity *r)
280 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
287 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
288 struct samr_QuerySecurity *r)
290 struct dcesrv_handle *h;
291 struct sec_desc_buf *sd;
293 *r->out.sdbuf = NULL;
295 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
297 sd = talloc(mem_ctx, struct sec_desc_buf);
299 return NT_STATUS_NO_MEMORY;
302 sd->sd = samdb_default_security_descriptor(mem_ctx);
313 we refuse this operation completely. If a admin wants to shutdown samr
314 in Samba then they should use the samba admin tools to disable the samr pipe
316 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
317 struct samr_Shutdown *r)
319 return NT_STATUS_ACCESS_DENIED;
326 this maps from a domain name to a SID
328 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
329 struct samr_LookupDomain *r)
331 struct samr_connect_state *c_state;
332 struct dcesrv_handle *h;
334 const char * const dom_attrs[] = { "objectSid", NULL};
335 struct ldb_message **dom_msgs;
340 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
344 if (r->in.domain_name->string == NULL) {
345 return NT_STATUS_INVALID_PARAMETER;
348 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
349 ret = gendb_search(c_state->sam_ctx,
350 mem_ctx, NULL, &dom_msgs, dom_attrs,
351 "(objectClass=builtinDomain)");
352 } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
353 ret = gendb_search_dn(c_state->sam_ctx,
354 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
355 &dom_msgs, dom_attrs);
357 return NT_STATUS_NO_SUCH_DOMAIN;
360 return NT_STATUS_NO_SUCH_DOMAIN;
363 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
367 return NT_STATUS_NO_SUCH_DOMAIN;
379 list the domains in the SAM
381 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
382 struct samr_EnumDomains *r)
384 struct dcesrv_handle *h;
385 struct samr_SamArray *array;
388 *r->out.resume_handle = 0;
390 *r->out.num_entries = 0;
392 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
394 *r->out.resume_handle = 2;
396 start_i = *r->in.resume_handle;
399 /* search past end of list is not an error for this call */
403 array = talloc(mem_ctx, struct samr_SamArray);
405 return NT_STATUS_NO_MEMORY;
409 array->entries = NULL;
411 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
412 if (array->entries == NULL) {
413 return NT_STATUS_NO_MEMORY;
416 for (i=0;i<2-start_i;i++) {
417 array->entries[i].idx = start_i + i;
419 array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
421 array->entries[i].name.string = "BUILTIN";
426 *r->out.num_entries = i;
427 array->count = *r->out.num_entries;
436 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
437 struct samr_OpenDomain *r)
439 struct dcesrv_handle *h_conn, *h_domain;
440 struct samr_connect_state *c_state;
441 struct samr_domain_state *d_state;
442 const char * const dom_attrs[] = { "cn", NULL};
443 struct ldb_message **dom_msgs;
447 ZERO_STRUCTP(r->out.domain_handle);
449 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
451 c_state = h_conn->data;
453 if (r->in.sid == NULL) {
454 return NT_STATUS_INVALID_PARAMETER;
457 d_state = talloc(mem_ctx, struct samr_domain_state);
459 return NT_STATUS_NO_MEMORY;
462 d_state->domain_sid = talloc_steal(d_state, r->in.sid);
464 if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
465 d_state->builtin = true;
466 d_state->domain_name = "BUILTIN";
468 d_state->builtin = false;
469 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
472 ret = gendb_search(c_state->sam_ctx,
473 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
475 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
478 talloc_free(d_state);
479 return NT_STATUS_NO_SUCH_DOMAIN;
480 } else if (ret > 1) {
481 talloc_free(d_state);
482 return NT_STATUS_INTERNAL_DB_CORRUPTION;
483 } else if (ret == -1) {
484 talloc_free(d_state);
485 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
486 return NT_STATUS_INTERNAL_DB_CORRUPTION;
489 d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
490 d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
491 d_state->connect_state = talloc_reference(d_state, c_state);
492 d_state->sam_ctx = c_state->sam_ctx;
493 d_state->access_mask = r->in.access_mask;
495 d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
497 for (i = 0; i < SAMR_LAST_CACHE; i++) {
498 initialize_guid_cache(&d_state->guid_caches[i]);
501 h_domain = dcesrv_handle_create(dce_call, SAMR_HANDLE_DOMAIN);
503 talloc_free(d_state);
504 return NT_STATUS_NO_MEMORY;
507 h_domain->data = talloc_steal(h_domain, d_state);
509 *r->out.domain_handle = h_domain->wire_handle;
517 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
519 struct ldb_message **dom_msgs,
520 struct samr_DomInfo1 *info)
522 info->min_password_length =
523 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
524 info->password_history_length =
525 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
526 info->password_properties =
527 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
528 info->max_password_age =
529 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
530 info->min_password_age =
531 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
539 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
541 struct ldb_message **dom_msgs,
542 struct samr_DomGeneralInformation *info)
544 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
545 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
549 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
550 0x8000000000000000LL);
552 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
555 info->domain_name.string = state->domain_name;
557 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
559 switch (state->role) {
560 case ROLE_ACTIVE_DIRECTORY_DC:
561 /* This pulls the NetBIOS name from the
562 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
564 if (samdb_is_pdc(state->sam_ctx)) {
565 info->role = SAMR_ROLE_DOMAIN_PDC;
567 info->role = SAMR_ROLE_DOMAIN_BDC;
570 case ROLE_DOMAIN_PDC:
571 case ROLE_DOMAIN_BDC:
573 return NT_STATUS_INTERNAL_ERROR;
574 case ROLE_DOMAIN_MEMBER:
575 info->role = SAMR_ROLE_DOMAIN_MEMBER;
577 case ROLE_STANDALONE:
578 info->role = SAMR_ROLE_STANDALONE;
582 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx,
584 "(objectClass=user)");
585 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx,
587 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
588 GTYPE_SECURITY_UNIVERSAL_GROUP,
589 GTYPE_SECURITY_GLOBAL_GROUP);
590 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx,
592 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
593 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
594 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
602 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
604 struct ldb_message **dom_msgs,
605 struct samr_DomInfo3 *info)
607 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
608 0x8000000000000000LL);
616 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
618 struct ldb_message **dom_msgs,
619 struct samr_DomOEMInformation *info)
621 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
631 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
633 struct ldb_message **dom_msgs,
634 struct samr_DomInfo5 *info)
636 info->domain_name.string = state->domain_name;
644 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
646 struct ldb_message **dom_msgs,
647 struct samr_DomInfo6 *info)
649 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
650 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
660 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
662 struct ldb_message **dom_msgs,
663 struct samr_DomInfo7 *info)
666 switch (state->role) {
667 case ROLE_ACTIVE_DIRECTORY_DC:
668 /* This pulls the NetBIOS name from the
669 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
671 if (samdb_is_pdc(state->sam_ctx)) {
672 info->role = SAMR_ROLE_DOMAIN_PDC;
674 info->role = SAMR_ROLE_DOMAIN_BDC;
677 case ROLE_DOMAIN_PDC:
678 case ROLE_DOMAIN_BDC:
680 return NT_STATUS_INTERNAL_ERROR;
681 case ROLE_DOMAIN_MEMBER:
682 info->role = SAMR_ROLE_DOMAIN_MEMBER;
684 case ROLE_STANDALONE:
685 info->role = SAMR_ROLE_STANDALONE;
695 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
697 struct ldb_message **dom_msgs,
698 struct samr_DomInfo8 *info)
700 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
703 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
712 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
714 struct ldb_message **dom_msgs,
715 struct samr_DomInfo9 *info)
717 info->domain_server_state = DOMAIN_SERVER_ENABLED;
725 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
727 struct ldb_message **dom_msgs,
728 struct samr_DomGeneralInformation2 *info)
731 status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
732 if (!NT_STATUS_IS_OK(status)) {
736 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
738 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
740 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
748 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
750 struct ldb_message **dom_msgs,
751 struct samr_DomInfo12 *info)
753 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
755 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
757 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
765 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
767 struct ldb_message **dom_msgs,
768 struct samr_DomInfo13 *info)
770 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
773 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
776 info->modified_count_at_last_promotion = 0;
784 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
786 struct samr_QueryDomainInfo *r)
788 struct dcesrv_handle *h;
789 struct samr_domain_state *d_state;
790 union samr_DomainInfo *info;
792 struct ldb_message **dom_msgs;
793 const char * const *attrs = NULL;
797 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
801 switch (r->in.level) {
804 static const char * const attrs2[] = { "minPwdLength",
815 static const char * const attrs2[] = {"forceLogoff",
825 static const char * const attrs2[] = {"forceLogoff",
832 static const char * const attrs2[] = {"oEMInformation",
844 static const char * const attrs2[] = { "domainReplica",
856 static const char * const attrs2[] = { "modifiedCount",
869 static const char * const attrs2[] = { "oEMInformation",
873 "lockOutObservationWindow",
881 static const char * const attrs2[] = { "lockoutDuration",
882 "lockOutObservationWindow",
890 static const char * const attrs2[] = { "modifiedCount",
898 return NT_STATUS_INVALID_INFO_CLASS;
902 /* some levels don't need a search */
905 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
906 d_state->domain_dn, &dom_msgs, attrs);
908 return NT_STATUS_NO_SUCH_DOMAIN;
911 return NT_STATUS_INTERNAL_DB_CORRUPTION;
915 /* allocate the info structure */
916 info = talloc_zero(mem_ctx, union samr_DomainInfo);
918 return NT_STATUS_NO_MEMORY;
923 switch (r->in.level) {
925 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
928 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
931 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
934 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
937 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
940 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
943 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
946 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
949 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
952 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
955 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
958 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
961 return NT_STATUS_INVALID_INFO_CLASS;
969 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
970 struct samr_SetDomainInfo *r)
972 struct dcesrv_handle *h;
973 struct samr_domain_state *d_state;
974 struct ldb_message *msg;
976 struct ldb_context *sam_ctx;
978 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
981 sam_ctx = d_state->sam_ctx;
983 msg = ldb_msg_new(mem_ctx);
985 return NT_STATUS_NO_MEMORY;
988 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
990 return NT_STATUS_NO_MEMORY;
993 switch (r->in.level) {
995 SET_UINT (msg, info1.min_password_length, "minPwdLength");
996 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
997 SET_UINT (msg, info1.password_properties, "pwdProperties");
998 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
999 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
1002 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
1005 SET_STRING(msg, oem.oem_information, "oEMInformation");
1011 /* No op, we don't know where to set these */
1012 return NT_STATUS_OK;
1016 * It is not possible to set lockout_duration < lockout_window.
1017 * (The test is the other way around since the negative numbers
1021 * This check should be moved to the backend, i.e. to some
1022 * ldb module under dsdb/samdb/ldb_modules/ .
1024 * This constraint is documented here for the samr rpc service:
1025 * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
1026 * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
1028 * And here for the ldap backend:
1029 * MS-ADTS 3.1.1.5.3.2 Constraints
1030 * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
1032 if (r->in.info->info12.lockout_duration >
1033 r->in.info->info12.lockout_window)
1035 return NT_STATUS_INVALID_PARAMETER;
1037 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
1038 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
1039 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
1043 /* many info classes are not valid for SetDomainInfo */
1044 return NT_STATUS_INVALID_INFO_CLASS;
1047 /* modify the samdb record */
1048 ret = ldb_modify(sam_ctx, msg);
1049 if (ret != LDB_SUCCESS) {
1050 DEBUG(1,("Failed to modify record %s: %s\n",
1051 ldb_dn_get_linearized(d_state->domain_dn),
1052 ldb_errstring(sam_ctx)));
1053 return dsdb_ldb_err_to_ntstatus(ret);
1056 return NT_STATUS_OK;
1060 samr_CreateDomainGroup
1062 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1063 struct samr_CreateDomainGroup *r)
1066 struct samr_domain_state *d_state;
1067 struct samr_account_state *a_state;
1068 struct dcesrv_handle *h;
1069 const char *groupname;
1070 struct dom_sid *group_sid;
1071 struct ldb_dn *group_dn;
1072 struct dcesrv_handle *g_handle;
1074 ZERO_STRUCTP(r->out.group_handle);
1077 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1081 if (d_state->builtin) {
1082 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
1083 return NT_STATUS_ACCESS_DENIED;
1086 groupname = r->in.name->string;
1088 if (groupname == NULL) {
1089 return NT_STATUS_INVALID_PARAMETER;
1092 status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1093 if (!NT_STATUS_IS_OK(status)) {
1097 a_state = talloc(mem_ctx, struct samr_account_state);
1099 return NT_STATUS_NO_MEMORY;
1101 a_state->sam_ctx = d_state->sam_ctx;
1102 a_state->access_mask = r->in.access_mask;
1103 a_state->domain_state = talloc_reference(a_state, d_state);
1104 a_state->account_dn = talloc_steal(a_state, group_dn);
1106 a_state->account_name = talloc_steal(a_state, groupname);
1108 /* create the policy handle */
1109 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
1111 return NT_STATUS_NO_MEMORY;
1114 g_handle->data = talloc_steal(g_handle, a_state);
1116 *r->out.group_handle = g_handle->wire_handle;
1117 *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1119 return NT_STATUS_OK;
1124 comparison function for sorting SamEntry array
1126 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1128 return e1->idx - e2->idx;
1131 static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) {
1132 struct dom_sid *sid1 = NULL;
1133 struct dom_sid *sid2 = NULL;
1138 TALLOC_CTX *frame = talloc_stackframe();
1140 sid1 = samdb_result_dom_sid(frame, *m1, "objectSid");
1141 sid2 = samdb_result_dom_sid(frame, *m2, "objectSid");
1144 * If entries don't have a SID we want to sort them to the end of
1147 if (sid1 == NULL && sid2 == NULL) {
1150 } else if (sid2 == NULL) {
1153 } else if (sid1 == NULL) {
1159 * Get and compare the rids, if we fail to extract a rid treat it as a
1160 * missing SID and sort to the end of the list
1162 status = dom_sid_split_rid(NULL, sid1, NULL, &rid1);
1163 if (!NT_STATUS_IS_OK(status)) {
1168 status = dom_sid_split_rid(NULL, sid2, NULL, &rid2);
1169 if (!NT_STATUS_IS_OK(status)) {
1177 else if (rid1 > rid2) {
1189 samr_EnumDomainGroups
1191 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1192 struct samr_EnumDomainGroups *r)
1194 struct dcesrv_handle *h;
1195 struct samr_domain_state *d_state;
1196 struct ldb_message **res;
1200 uint32_t max_entries;
1201 uint32_t remaining_entries;
1202 uint32_t resume_handle;
1203 struct samr_SamEntry *entries;
1204 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1205 const char * const cache_attrs[] = { "objectSid", "objectGUID", NULL };
1206 struct samr_SamArray *sam;
1207 struct samr_guid_cache *cache = NULL;
1209 *r->out.resume_handle = 0;
1211 *r->out.num_entries = 0;
1213 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1216 cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_GROUPS_CACHE];
1219 * If the resume_handle is zero, query the database and cache the
1222 if (*r->in.resume_handle == 0) {
1225 clear_guid_cache(cache);
1227 * search for all domain groups in this domain.
1229 ldb_cnt = samdb_search_domain(
1235 d_state->domain_sid,
1236 "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1237 GTYPE_SECURITY_UNIVERSAL_GROUP,
1238 GTYPE_SECURITY_GLOBAL_GROUP);
1240 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1243 * Sort the results into RID order, while the spec states there
1244 * is no order, Windows appears to sort the results by RID and
1245 * so it is possible that there are clients that depend on
1248 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1251 * cache the sorted GUID's
1253 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1255 if (!NT_STATUS_IS_OK(status)) {
1263 * If the resume handle is out of range we return an empty response
1264 * and invalidate the cache.
1266 * From the specification:
1267 * Servers SHOULD validate that EnumerationContext is an expected
1268 * value for the server's implementation. Windows does NOT validate
1269 * the input, though the result of malformed information merely results
1270 * in inconsistent output to the client.
1272 if (*r->in.resume_handle >= cache->size) {
1273 clear_guid_cache(cache);
1274 sam = talloc(mem_ctx, struct samr_SamArray);
1276 return NT_STATUS_NO_MEMORY;
1278 sam->entries = NULL;
1282 *r->out.resume_handle = 0;
1283 return NT_STATUS_OK;
1288 * Calculate the number of entries to return limit by max_size.
1289 * Note that we use the w2k3 element size value of 54
1291 max_entries = 1 + (r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER);
1292 remaining_entries = cache->size - *r->in.resume_handle;
1293 results = MIN(remaining_entries, max_entries);
1296 * Process the list of result GUID's.
1297 * Read the details of each object and populate the Entries
1298 * for the current level.
1301 resume_handle = *r->in.resume_handle;
1302 entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1303 if (entries == NULL) {
1304 clear_guid_cache(cache);
1305 return NT_STATUS_NO_MEMORY;
1307 for (i = 0; i < results; i++) {
1308 struct dom_sid *objectsid;
1310 struct ldb_result *rec;
1311 const uint32_t idx = *r->in.resume_handle + i;
1314 const char *name = NULL;
1317 * Read an object from disk using the GUID as the key
1319 * If the object can not be read, or it does not have a SID
1322 * As a consequence of this, if all the remaining GUID's
1323 * have been deleted an empty result will be returned.
1324 * i.e. even if the previous call returned a non zero
1325 * resume_handle it is possible for no results to be returned.
1328 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1331 &cache->entries[idx],
1334 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1335 struct GUID_txt_buf guid_buf;
1337 "GUID [%s] not found\n",
1338 GUID_buf_string(&cache->entries[idx], &guid_buf));
1340 } else if (ret != LDB_SUCCESS) {
1341 clear_guid_cache(cache);
1342 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1345 objectsid = samdb_result_dom_sid(mem_ctx,
1348 if (objectsid == NULL) {
1349 struct GUID_txt_buf guid_buf;
1351 "objectSID for GUID [%s] not found\n",
1352 GUID_buf_string(&cache->entries[idx], &guid_buf));
1355 status = dom_sid_split_rid(NULL,
1359 if (!NT_STATUS_IS_OK(status)) {
1360 struct dom_sid_buf sid_buf;
1361 struct GUID_txt_buf guid_buf;
1363 "objectSID [%s] for GUID [%s] invalid\n",
1364 dom_sid_str_buf(objectsid, &sid_buf),
1365 GUID_buf_string(&cache->entries[idx], &guid_buf));
1369 entries[count].idx = rid;
1370 name = ldb_msg_find_attr_as_string(
1371 rec->msgs[0], "sAMAccountName", "");
1372 entries[count].name.string = talloc_strdup(entries, name);
1376 sam = talloc(mem_ctx, struct samr_SamArray);
1378 clear_guid_cache(cache);
1379 return NT_STATUS_NO_MEMORY;
1382 sam->entries = entries;
1386 *r->out.resume_handle = resume_handle;
1387 *r->out.num_entries = count;
1390 * Signal no more results by returning zero resume handle,
1391 * the cache is also cleared at this point
1393 if (*r->out.resume_handle >= cache->size) {
1394 *r->out.resume_handle = 0;
1395 clear_guid_cache(cache);
1396 return NT_STATUS_OK;
1399 * There are more results to be returned.
1401 return STATUS_MORE_ENTRIES;
1408 This call uses transactions to ensure we don't get a new conflicting
1409 user while we are processing this, and to ensure the user either
1410 completly exists, or does not.
1412 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1413 struct samr_CreateUser2 *r)
1416 struct samr_domain_state *d_state;
1417 struct samr_account_state *a_state;
1418 struct dcesrv_handle *h;
1420 struct dom_sid *sid;
1421 struct dcesrv_handle *u_handle;
1422 const char *account_name;
1424 ZERO_STRUCTP(r->out.user_handle);
1425 *r->out.access_granted = 0;
1428 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1432 if (d_state->builtin) {
1433 DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
1434 return NT_STATUS_ACCESS_DENIED;
1435 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1436 /* Domain trust accounts must be created by the LSA calls */
1437 return NT_STATUS_ACCESS_DENIED;
1439 account_name = r->in.account_name->string;
1441 if (account_name == NULL) {
1442 return NT_STATUS_INVALID_PARAMETER;
1445 status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1447 if (!NT_STATUS_IS_OK(status)) {
1450 a_state = talloc(mem_ctx, struct samr_account_state);
1452 return NT_STATUS_NO_MEMORY;
1454 a_state->sam_ctx = d_state->sam_ctx;
1455 a_state->access_mask = r->in.access_mask;
1456 a_state->domain_state = talloc_reference(a_state, d_state);
1457 a_state->account_dn = talloc_steal(a_state, dn);
1459 a_state->account_name = talloc_steal(a_state, account_name);
1460 if (!a_state->account_name) {
1461 return NT_STATUS_NO_MEMORY;
1464 /* create the policy handle */
1465 u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
1467 return NT_STATUS_NO_MEMORY;
1470 u_handle->data = talloc_steal(u_handle, a_state);
1472 *r->out.user_handle = u_handle->wire_handle;
1473 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1475 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1477 return NT_STATUS_OK;
1484 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1485 struct samr_CreateUser *r)
1487 struct samr_CreateUser2 r2;
1488 uint32_t access_granted = 0;
1491 /* a simple wrapper around samr_CreateUser2 works nicely */
1493 r2 = (struct samr_CreateUser2) {
1494 .in.domain_handle = r->in.domain_handle,
1495 .in.account_name = r->in.account_name,
1496 .in.acct_flags = ACB_NORMAL,
1497 .in.access_mask = r->in.access_mask,
1498 .out.user_handle = r->out.user_handle,
1499 .out.access_granted = &access_granted,
1500 .out.rid = r->out.rid
1503 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1507 samr_EnumDomainUsers
1509 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1510 struct samr_EnumDomainUsers *r)
1512 struct dcesrv_handle *h;
1513 struct samr_domain_state *d_state;
1514 struct ldb_message **res;
1518 uint32_t max_entries;
1519 uint32_t remaining_entries;
1520 uint32_t resume_handle;
1521 struct samr_SamEntry *entries;
1522 const char * const attrs[] = { "objectSid", "sAMAccountName",
1523 "userAccountControl", NULL };
1524 const char *const cache_attrs[] = {"objectSid", "objectGUID", NULL};
1525 struct samr_SamArray *sam;
1526 struct samr_guid_cache *cache = NULL;
1528 *r->out.resume_handle = 0;
1530 *r->out.num_entries = 0;
1532 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1535 cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_USERS_CACHE];
1538 * If the resume_handle is zero, query the database and cache the
1541 if (*r->in.resume_handle == 0) {
1544 clear_guid_cache(cache);
1546 * search for all domain users in this domain.
1548 ldb_cnt = samdb_search_domain(d_state->sam_ctx,
1553 d_state->domain_sid,
1554 "(objectClass=user)");
1556 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1559 * Sort the results into RID order, while the spec states there
1560 * is no order, Windows appears to sort the results by RID and
1561 * so it is possible that there are clients that depend on
1564 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1567 * cache the sorted GUID's
1569 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1571 if (!NT_STATUS_IS_OK(status)) {
1578 * If the resume handle is out of range we return an empty response
1579 * and invalidate the cache.
1581 * From the specification:
1582 * Servers SHOULD validate that EnumerationContext is an expected
1583 * value for the server's implementation. Windows does NOT validate
1584 * the input, though the result of malformed information merely results
1585 * in inconsistent output to the client.
1587 if (*r->in.resume_handle >= cache->size) {
1588 clear_guid_cache(cache);
1589 sam = talloc(mem_ctx, struct samr_SamArray);
1591 return NT_STATUS_NO_MEMORY;
1593 sam->entries = NULL;
1597 *r->out.resume_handle = 0;
1598 return NT_STATUS_OK;
1602 * Calculate the number of entries to return limit by max_size.
1603 * Note that we use the w2k3 element size value of 54
1605 max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER);
1606 remaining_entries = cache->size - *r->in.resume_handle;
1607 results = MIN(remaining_entries, max_entries);
1610 * Process the list of result GUID's.
1611 * Read the details of each object and populate the Entries
1612 * for the current level.
1615 resume_handle = *r->in.resume_handle;
1616 entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1617 if (entries == NULL) {
1618 clear_guid_cache(cache);
1619 return NT_STATUS_NO_MEMORY;
1621 for (i = 0; i < results; i++) {
1622 struct dom_sid *objectsid;
1624 struct ldb_result *rec;
1625 const uint32_t idx = *r->in.resume_handle + i;
1628 const char *name = NULL;
1632 * Read an object from disk using the GUID as the key
1634 * If the object can not be read, or it does not have a SID
1637 * As a consequence of this, if all the remaining GUID's
1638 * have been deleted an empty result will be returned.
1639 * i.e. even if the previous call returned a non zero
1640 * resume_handle it is possible for no results to be returned.
1643 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1646 &cache->entries[idx],
1649 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1650 struct GUID_txt_buf guid_buf;
1652 "GUID [%s] not found\n",
1653 GUID_buf_string(&cache->entries[idx], &guid_buf));
1655 } else if (ret != LDB_SUCCESS) {
1656 clear_guid_cache(cache);
1657 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1659 objectsid = samdb_result_dom_sid(mem_ctx,
1662 if (objectsid == NULL) {
1663 struct GUID_txt_buf guid_buf;
1665 "objectSID for GUID [%s] not found\n",
1666 GUID_buf_string(&cache->entries[idx], &guid_buf));
1669 if (r->in.acct_flags &&
1670 ((samdb_result_acct_flags(rec->msgs[0], NULL) &
1671 r->in.acct_flags) == 0)) {
1674 status = dom_sid_split_rid(NULL,
1678 if (!NT_STATUS_IS_OK(status)) {
1679 struct dom_sid_buf sid_buf;
1680 struct GUID_txt_buf guid_buf;
1682 "objectSID [%s] for GUID [%s] invalid\n",
1683 dom_sid_str_buf(objectsid, &sid_buf),
1684 GUID_buf_string(&cache->entries[idx], &guid_buf));
1688 entries[count].idx = rid;
1689 name = ldb_msg_find_attr_as_string(
1690 rec->msgs[0], "sAMAccountName", "");
1691 entries[count].name.string = talloc_strdup(entries, name);
1695 sam = talloc(mem_ctx, struct samr_SamArray);
1697 clear_guid_cache(cache);
1698 return NT_STATUS_NO_MEMORY;
1701 sam->entries = entries;
1705 *r->out.resume_handle = resume_handle;
1706 *r->out.num_entries = count;
1709 * Signal no more results by returning zero resume handle,
1710 * the cache is also cleared at this point
1712 if (*r->out.resume_handle >= cache->size) {
1713 *r->out.resume_handle = 0;
1714 clear_guid_cache(cache);
1715 return NT_STATUS_OK;
1718 * There are more results to be returned.
1720 return STATUS_MORE_ENTRIES;
1727 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1728 struct samr_CreateDomAlias *r)
1730 struct samr_domain_state *d_state;
1731 struct samr_account_state *a_state;
1732 struct dcesrv_handle *h;
1733 const char *alias_name;
1734 struct dom_sid *sid;
1735 struct dcesrv_handle *a_handle;
1739 ZERO_STRUCTP(r->out.alias_handle);
1742 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1746 if (d_state->builtin) {
1747 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1748 return NT_STATUS_ACCESS_DENIED;
1751 alias_name = r->in.alias_name->string;
1753 if (alias_name == NULL) {
1754 return NT_STATUS_INVALID_PARAMETER;
1757 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1758 if (!NT_STATUS_IS_OK(status)) {
1762 a_state = talloc(mem_ctx, struct samr_account_state);
1764 return NT_STATUS_NO_MEMORY;
1767 a_state->sam_ctx = d_state->sam_ctx;
1768 a_state->access_mask = r->in.access_mask;
1769 a_state->domain_state = talloc_reference(a_state, d_state);
1770 a_state->account_dn = talloc_steal(a_state, dn);
1772 a_state->account_name = talloc_steal(a_state, alias_name);
1774 /* create the policy handle */
1775 a_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
1776 if (a_handle == NULL)
1777 return NT_STATUS_NO_MEMORY;
1779 a_handle->data = talloc_steal(a_handle, a_state);
1781 *r->out.alias_handle = a_handle->wire_handle;
1783 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1785 return NT_STATUS_OK;
1790 samr_EnumDomainAliases
1792 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1793 struct samr_EnumDomainAliases *r)
1795 struct dcesrv_handle *h;
1796 struct samr_domain_state *d_state;
1797 struct ldb_message **res;
1799 uint32_t first, count;
1800 struct samr_SamEntry *entries;
1801 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1802 struct samr_SamArray *sam;
1804 *r->out.resume_handle = 0;
1806 *r->out.num_entries = 0;
1808 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1812 /* search for all domain aliases in this domain. This could possibly be
1813 cached and resumed based on resume_key */
1814 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1816 d_state->domain_sid,
1817 "(&(|(grouptype=%d)(grouptype=%d)))"
1818 "(objectclass=group))",
1819 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1820 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1822 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1825 /* convert to SamEntry format */
1826 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1828 return NT_STATUS_NO_MEMORY;
1833 for (i=0;i<ldb_cnt;i++) {
1834 struct dom_sid *alias_sid;
1836 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1839 if (alias_sid == NULL) {
1840 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1843 entries[count].idx =
1844 alias_sid->sub_auths[alias_sid->num_auths-1];
1845 entries[count].name.string =
1846 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1850 /* sort the results by rid */
1851 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1853 /* find the first entry to return */
1855 first<count && entries[first].idx <= *r->in.resume_handle;
1858 /* return the rest, limit by max_size. Note that we
1859 use the w2k3 element size value of 54 */
1860 *r->out.num_entries = count - first;
1861 *r->out.num_entries = MIN(*r->out.num_entries,
1862 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1864 sam = talloc(mem_ctx, struct samr_SamArray);
1866 return NT_STATUS_NO_MEMORY;
1869 sam->entries = entries+first;
1870 sam->count = *r->out.num_entries;
1874 if (first == count) {
1875 return NT_STATUS_OK;
1878 if (*r->out.num_entries < count - first) {
1879 *r->out.resume_handle =
1880 entries[first+*r->out.num_entries-1].idx;
1881 return STATUS_MORE_ENTRIES;
1884 return NT_STATUS_OK;
1889 samr_GetAliasMembership
1891 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1892 struct samr_GetAliasMembership *r)
1894 struct dcesrv_handle *h;
1895 struct samr_domain_state *d_state;
1897 const char * const attrs[] = { "objectSid", NULL };
1898 struct ldb_message **res;
1902 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1906 filter = talloc_asprintf(mem_ctx,
1907 "(&(|(grouptype=%d)(grouptype=%d))"
1908 "(objectclass=group)(|",
1909 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1910 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1911 if (filter == NULL) {
1912 return NT_STATUS_NO_MEMORY;
1915 for (i=0; i<r->in.sids->num_sids; i++) {
1916 struct dom_sid_buf buf;
1918 filter = talloc_asprintf_append(
1920 "(member=<SID=%s>)",
1921 dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
1923 if (filter == NULL) {
1924 return NT_STATUS_NO_MEMORY;
1928 /* Find out if we had at least one valid member SID passed - otherwise
1929 * just skip the search. */
1930 if (strstr(filter, "member") != NULL) {
1931 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1932 &res, attrs, d_state->domain_sid,
1935 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1939 r->out.rids->count = 0;
1940 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1941 if (r->out.rids->ids == NULL)
1942 return NT_STATUS_NO_MEMORY;
1944 for (i=0; i<count; i++) {
1945 struct dom_sid *alias_sid;
1947 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1948 if (alias_sid == NULL) {
1949 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1952 r->out.rids->ids[r->out.rids->count] =
1953 alias_sid->sub_auths[alias_sid->num_auths-1];
1954 r->out.rids->count += 1;
1957 return NT_STATUS_OK;
1964 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1965 struct samr_LookupNames *r)
1967 struct dcesrv_handle *h;
1968 struct samr_domain_state *d_state;
1969 uint32_t i, num_mapped;
1970 NTSTATUS status = NT_STATUS_OK;
1971 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1974 ZERO_STRUCTP(r->out.rids);
1975 ZERO_STRUCTP(r->out.types);
1977 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1981 if (r->in.num_names == 0) {
1982 return NT_STATUS_OK;
1985 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1986 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1987 if (!r->out.rids->ids || !r->out.types->ids) {
1988 return NT_STATUS_NO_MEMORY;
1990 r->out.rids->count = r->in.num_names;
1991 r->out.types->count = r->in.num_names;
1995 for (i=0;i<r->in.num_names;i++) {
1996 struct ldb_message **res;
1997 struct dom_sid *sid;
1998 uint32_t atype, rtype;
2000 r->out.rids->ids[i] = 0;
2001 r->out.types->ids[i] = SID_NAME_UNKNOWN;
2003 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
2004 "sAMAccountName=%s",
2005 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
2007 status = STATUS_SOME_UNMAPPED;
2011 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
2013 status = STATUS_SOME_UNMAPPED;
2017 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
2019 status = STATUS_SOME_UNMAPPED;
2023 rtype = ds_atype_map(atype);
2025 if (rtype == SID_NAME_UNKNOWN) {
2026 status = STATUS_SOME_UNMAPPED;
2030 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
2031 r->out.types->ids[i] = rtype;
2035 if (num_mapped == 0) {
2036 return NT_STATUS_NONE_MAPPED;
2045 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2046 struct samr_LookupRids *r)
2049 struct dcesrv_handle *h;
2050 struct samr_domain_state *d_state;
2052 struct lsa_String *lsa_names;
2053 enum lsa_SidType *ids;
2055 ZERO_STRUCTP(r->out.names);
2056 ZERO_STRUCTP(r->out.types);
2058 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2062 if (r->in.num_rids == 0)
2063 return NT_STATUS_OK;
2065 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
2066 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
2067 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
2069 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
2070 return NT_STATUS_NO_MEMORY;
2072 r->out.names->names = lsa_names;
2073 r->out.names->count = r->in.num_rids;
2075 r->out.types->ids = (uint32_t *) ids;
2076 r->out.types->count = r->in.num_rids;
2078 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
2079 r->in.num_rids, r->in.rids, names, ids);
2080 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
2082 for (i = 0; i < r->in.num_rids; i++) {
2083 lsa_names[i].string = names[i];
2093 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2094 struct samr_OpenGroup *r)
2096 struct samr_domain_state *d_state;
2097 struct samr_account_state *a_state;
2098 struct dcesrv_handle *h;
2099 const char *groupname;
2100 struct dom_sid *sid;
2101 struct ldb_message **msgs;
2102 struct dcesrv_handle *g_handle;
2103 const char * const attrs[2] = { "sAMAccountName", NULL };
2106 ZERO_STRUCTP(r->out.group_handle);
2108 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2112 /* form the group SID */
2113 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2115 return NT_STATUS_NO_MEMORY;
2118 /* search for the group record */
2119 if (d_state->builtin) {
2120 ret = gendb_search(d_state->sam_ctx,
2121 mem_ctx, d_state->domain_dn, &msgs, attrs,
2122 "(&(objectSid=%s)(objectClass=group)"
2124 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2125 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
2127 ret = gendb_search(d_state->sam_ctx,
2128 mem_ctx, d_state->domain_dn, &msgs, attrs,
2129 "(&(objectSid=%s)(objectClass=group)"
2130 "(|(groupType=%d)(groupType=%d)))",
2131 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2132 GTYPE_SECURITY_UNIVERSAL_GROUP,
2133 GTYPE_SECURITY_GLOBAL_GROUP);
2136 return NT_STATUS_NO_SUCH_GROUP;
2139 DEBUG(0,("Found %d records matching sid %s\n",
2140 ret, dom_sid_string(mem_ctx, sid)));
2141 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2144 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2145 if (groupname == NULL) {
2146 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2147 dom_sid_string(mem_ctx, sid)));
2148 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2151 a_state = talloc(mem_ctx, struct samr_account_state);
2153 return NT_STATUS_NO_MEMORY;
2155 a_state->sam_ctx = d_state->sam_ctx;
2156 a_state->access_mask = r->in.access_mask;
2157 a_state->domain_state = talloc_reference(a_state, d_state);
2158 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2159 a_state->account_sid = talloc_steal(a_state, sid);
2160 a_state->account_name = talloc_strdup(a_state, groupname);
2161 if (!a_state->account_name) {
2162 return NT_STATUS_NO_MEMORY;
2165 /* create the policy handle */
2166 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
2168 return NT_STATUS_NO_MEMORY;
2171 g_handle->data = talloc_steal(g_handle, a_state);
2173 *r->out.group_handle = g_handle->wire_handle;
2175 return NT_STATUS_OK;
2181 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2182 struct samr_QueryGroupInfo *r)
2184 struct dcesrv_handle *h;
2185 struct samr_account_state *a_state;
2186 struct ldb_message *msg, **res;
2187 const char * const attrs[4] = { "sAMAccountName", "description",
2188 "numMembers", NULL };
2190 union samr_GroupInfo *info;
2192 *r->out.info = NULL;
2194 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2198 /* pull all the group attributes */
2199 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2200 a_state->account_dn, &res, attrs);
2202 return NT_STATUS_NO_SUCH_GROUP;
2205 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2209 /* allocate the info structure */
2210 info = talloc_zero(mem_ctx, union samr_GroupInfo);
2212 return NT_STATUS_NO_MEMORY;
2215 /* Fill in the level */
2216 switch (r->in.level) {
2218 QUERY_STRING(msg, all.name, "sAMAccountName");
2219 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2220 QUERY_UINT (msg, all.num_members, "numMembers")
2221 QUERY_STRING(msg, all.description, "description");
2224 QUERY_STRING(msg, name, "sAMAccountName");
2226 case GROUPINFOATTRIBUTES:
2227 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2229 case GROUPINFODESCRIPTION:
2230 QUERY_STRING(msg, description, "description");
2233 QUERY_STRING(msg, all2.name, "sAMAccountName");
2234 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2235 QUERY_UINT (msg, all2.num_members, "numMembers")
2236 QUERY_STRING(msg, all2.description, "description");
2240 return NT_STATUS_INVALID_INFO_CLASS;
2243 *r->out.info = info;
2245 return NT_STATUS_OK;
2252 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2253 struct samr_SetGroupInfo *r)
2255 struct dcesrv_handle *h;
2256 struct samr_account_state *g_state;
2257 struct ldb_message *msg;
2260 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2264 msg = ldb_msg_new(mem_ctx);
2266 return NT_STATUS_NO_MEMORY;
2269 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2271 return NT_STATUS_NO_MEMORY;
2274 switch (r->in.level) {
2275 case GROUPINFODESCRIPTION:
2276 SET_STRING(msg, description, "description");
2279 /* On W2k3 this does not change the name, it changes the
2280 * sAMAccountName attribute */
2281 SET_STRING(msg, name, "sAMAccountName");
2283 case GROUPINFOATTRIBUTES:
2284 /* This does not do anything obviously visible in W2k3 LDAP */
2285 return NT_STATUS_OK;
2287 return NT_STATUS_INVALID_INFO_CLASS;
2290 /* modify the samdb record */
2291 ret = ldb_modify(g_state->sam_ctx, msg);
2292 if (ret != LDB_SUCCESS) {
2293 return dsdb_ldb_err_to_ntstatus(ret);
2296 return NT_STATUS_OK;
2303 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2304 struct samr_AddGroupMember *r)
2306 struct dcesrv_handle *h;
2307 struct samr_account_state *a_state;
2308 struct samr_domain_state *d_state;
2309 struct ldb_message *mod;
2310 struct dom_sid *membersid;
2311 const char *memberdn;
2312 struct ldb_result *res;
2313 const char * const attrs[] = { NULL };
2316 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2319 d_state = a_state->domain_state;
2321 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2322 if (membersid == NULL) {
2323 return NT_STATUS_NO_MEMORY;
2326 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2327 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2328 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2330 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2332 if (ret != LDB_SUCCESS) {
2333 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2336 if (res->count == 0) {
2337 return NT_STATUS_NO_SUCH_USER;
2340 if (res->count > 1) {
2341 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2344 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2346 if (memberdn == NULL)
2347 return NT_STATUS_NO_MEMORY;
2349 mod = ldb_msg_new(mem_ctx);
2351 return NT_STATUS_NO_MEMORY;
2354 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2356 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2358 if (ret != LDB_SUCCESS) {
2359 return dsdb_ldb_err_to_ntstatus(ret);
2362 ret = ldb_modify(a_state->sam_ctx, mod);
2365 return NT_STATUS_OK;
2366 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2367 return NT_STATUS_MEMBER_IN_GROUP;
2368 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2369 return NT_STATUS_ACCESS_DENIED;
2371 return dsdb_ldb_err_to_ntstatus(ret);
2377 samr_DeleteDomainGroup
2379 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2380 struct samr_DeleteDomainGroup *r)
2382 struct dcesrv_handle *h;
2383 struct samr_account_state *a_state;
2386 *r->out.group_handle = *r->in.group_handle;
2388 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2392 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2393 if (ret != LDB_SUCCESS) {
2394 return dsdb_ldb_err_to_ntstatus(ret);
2398 ZERO_STRUCTP(r->out.group_handle);
2400 return NT_STATUS_OK;
2405 samr_DeleteGroupMember
2407 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2408 struct samr_DeleteGroupMember *r)
2410 struct dcesrv_handle *h;
2411 struct samr_account_state *a_state;
2412 struct samr_domain_state *d_state;
2413 struct ldb_message *mod;
2414 struct dom_sid *membersid;
2415 const char *memberdn;
2416 struct ldb_result *res;
2417 const char * const attrs[] = { NULL };
2420 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2423 d_state = a_state->domain_state;
2425 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2426 if (membersid == NULL) {
2427 return NT_STATUS_NO_MEMORY;
2430 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2431 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2432 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2434 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2436 if (ret != LDB_SUCCESS) {
2437 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2440 if (res->count == 0) {
2441 return NT_STATUS_NO_SUCH_USER;
2444 if (res->count > 1) {
2445 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2448 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2450 if (memberdn == NULL)
2451 return NT_STATUS_NO_MEMORY;
2453 mod = ldb_msg_new(mem_ctx);
2455 return NT_STATUS_NO_MEMORY;
2458 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2460 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2462 if (ret != LDB_SUCCESS) {
2463 return NT_STATUS_NO_MEMORY;
2466 ret = ldb_modify(a_state->sam_ctx, mod);
2469 return NT_STATUS_OK;
2470 case LDB_ERR_UNWILLING_TO_PERFORM:
2471 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2472 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2473 return NT_STATUS_ACCESS_DENIED;
2475 return dsdb_ldb_err_to_ntstatus(ret);
2481 samr_QueryGroupMember
2483 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2484 struct samr_QueryGroupMember *r)
2486 struct dcesrv_handle *h;
2487 struct samr_account_state *a_state;
2488 struct samr_domain_state *d_state;
2489 struct samr_RidAttrArray *array;
2490 unsigned int i, num_members;
2491 struct dom_sid *members;
2494 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2497 d_state = a_state->domain_state;
2499 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2500 a_state->account_dn, &members,
2502 if (!NT_STATUS_IS_OK(status)) {
2506 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2507 if (array == NULL) {
2508 return NT_STATUS_NO_MEMORY;
2511 if (num_members == 0) {
2512 *r->out.rids = array;
2514 return NT_STATUS_OK;
2517 array->rids = talloc_array(array, uint32_t, num_members);
2518 if (array->rids == NULL) {
2519 return NT_STATUS_NO_MEMORY;
2522 array->attributes = talloc_array(array, uint32_t, num_members);
2523 if (array->attributes == NULL) {
2524 return NT_STATUS_NO_MEMORY;
2528 for (i=0; i<num_members; i++) {
2529 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2533 status = dom_sid_split_rid(NULL, &members[i], NULL,
2534 &array->rids[array->count]);
2535 if (!NT_STATUS_IS_OK(status)) {
2539 array->attributes[array->count] = SE_GROUP_MANDATORY |
2540 SE_GROUP_ENABLED_BY_DEFAULT |
2545 *r->out.rids = array;
2547 return NT_STATUS_OK;
2552 samr_SetMemberAttributesOfGroup
2554 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2555 struct samr_SetMemberAttributesOfGroup *r)
2557 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2564 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2565 struct samr_OpenAlias *r)
2567 struct samr_domain_state *d_state;
2568 struct samr_account_state *a_state;
2569 struct dcesrv_handle *h;
2570 const char *alias_name;
2571 struct dom_sid *sid;
2572 struct ldb_message **msgs;
2573 struct dcesrv_handle *g_handle;
2574 const char * const attrs[2] = { "sAMAccountName", NULL };
2577 ZERO_STRUCTP(r->out.alias_handle);
2579 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2583 /* form the alias SID */
2584 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2586 return NT_STATUS_NO_MEMORY;
2588 /* search for the group record */
2589 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2590 "(&(objectSid=%s)(objectclass=group)"
2591 "(|(grouptype=%d)(grouptype=%d)))",
2592 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2593 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2594 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2596 return NT_STATUS_NO_SUCH_ALIAS;
2599 DEBUG(0,("Found %d records matching sid %s\n",
2600 ret, dom_sid_string(mem_ctx, sid)));
2601 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2604 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2605 if (alias_name == NULL) {
2606 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2607 dom_sid_string(mem_ctx, sid)));
2608 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2611 a_state = talloc(mem_ctx, struct samr_account_state);
2613 return NT_STATUS_NO_MEMORY;
2615 a_state->sam_ctx = d_state->sam_ctx;
2616 a_state->access_mask = r->in.access_mask;
2617 a_state->domain_state = talloc_reference(a_state, d_state);
2618 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2619 a_state->account_sid = talloc_steal(a_state, sid);
2620 a_state->account_name = talloc_strdup(a_state, alias_name);
2621 if (!a_state->account_name) {
2622 return NT_STATUS_NO_MEMORY;
2625 /* create the policy handle */
2626 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
2628 return NT_STATUS_NO_MEMORY;
2631 g_handle->data = talloc_steal(g_handle, a_state);
2633 *r->out.alias_handle = g_handle->wire_handle;
2635 return NT_STATUS_OK;
2642 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2643 struct samr_QueryAliasInfo *r)
2645 struct dcesrv_handle *h;
2646 struct samr_account_state *a_state;
2647 struct ldb_message *msg, **res;
2648 const char * const attrs[4] = { "sAMAccountName", "description",
2649 "numMembers", NULL };
2651 union samr_AliasInfo *info;
2653 *r->out.info = NULL;
2655 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2659 /* pull all the alias attributes */
2660 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2661 a_state->account_dn, &res, attrs);
2663 return NT_STATUS_NO_SUCH_ALIAS;
2666 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2670 /* allocate the info structure */
2671 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2673 return NT_STATUS_NO_MEMORY;
2676 switch(r->in.level) {
2678 QUERY_STRING(msg, all.name, "sAMAccountName");
2679 QUERY_UINT (msg, all.num_members, "numMembers");
2680 QUERY_STRING(msg, all.description, "description");
2683 QUERY_STRING(msg, name, "sAMAccountName");
2685 case ALIASINFODESCRIPTION:
2686 QUERY_STRING(msg, description, "description");
2690 return NT_STATUS_INVALID_INFO_CLASS;
2693 *r->out.info = info;
2695 return NT_STATUS_OK;
2702 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2703 struct samr_SetAliasInfo *r)
2705 struct dcesrv_handle *h;
2706 struct samr_account_state *a_state;
2707 struct ldb_message *msg;
2710 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2714 msg = ldb_msg_new(mem_ctx);
2716 return NT_STATUS_NO_MEMORY;
2719 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2721 return NT_STATUS_NO_MEMORY;
2724 switch (r->in.level) {
2725 case ALIASINFODESCRIPTION:
2726 SET_STRING(msg, description, "description");
2729 /* On W2k3 this does not change the name, it changes the
2730 * sAMAccountName attribute */
2731 SET_STRING(msg, name, "sAMAccountName");
2734 return NT_STATUS_INVALID_INFO_CLASS;
2737 /* modify the samdb record */
2738 ret = ldb_modify(a_state->sam_ctx, msg);
2739 if (ret != LDB_SUCCESS) {
2740 return dsdb_ldb_err_to_ntstatus(ret);
2743 return NT_STATUS_OK;
2750 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2751 struct samr_DeleteDomAlias *r)
2753 struct dcesrv_handle *h;
2754 struct samr_account_state *a_state;
2757 *r->out.alias_handle = *r->in.alias_handle;
2759 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2763 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2764 if (ret != LDB_SUCCESS) {
2765 return dsdb_ldb_err_to_ntstatus(ret);
2769 ZERO_STRUCTP(r->out.alias_handle);
2771 return NT_STATUS_OK;
2778 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2779 struct samr_AddAliasMember *r)
2781 struct dcesrv_handle *h;
2782 struct samr_account_state *a_state;
2783 struct samr_domain_state *d_state;
2784 struct ldb_message *mod;
2785 struct ldb_message **msgs;
2786 const char * const attrs[] = { NULL };
2787 struct ldb_dn *memberdn = NULL;
2791 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2794 d_state = a_state->domain_state;
2796 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2797 &msgs, attrs, "(objectsid=%s)",
2798 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2801 memberdn = msgs[0]->dn;
2802 } else if (ret == 0) {
2803 status = samdb_create_foreign_security_principal(
2804 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2805 if (!NT_STATUS_IS_OK(status)) {
2809 DEBUG(0,("Found %d records matching sid %s\n",
2810 ret, dom_sid_string(mem_ctx, r->in.sid)));
2811 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2814 if (memberdn == NULL) {
2815 DEBUG(0, ("Could not find memberdn\n"));
2816 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2819 mod = ldb_msg_new(mem_ctx);
2821 return NT_STATUS_NO_MEMORY;
2824 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2826 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2827 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2828 if (ret != LDB_SUCCESS) {
2829 return dsdb_ldb_err_to_ntstatus(ret);
2832 ret = ldb_modify(a_state->sam_ctx, mod);
2835 return NT_STATUS_OK;
2836 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2837 return NT_STATUS_MEMBER_IN_GROUP;
2838 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2839 return NT_STATUS_ACCESS_DENIED;
2841 return dsdb_ldb_err_to_ntstatus(ret);
2847 samr_DeleteAliasMember
2849 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2850 struct samr_DeleteAliasMember *r)
2852 struct dcesrv_handle *h;
2853 struct samr_account_state *a_state;
2854 struct samr_domain_state *d_state;
2855 struct ldb_message *mod;
2856 const char *memberdn;
2859 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2862 d_state = a_state->domain_state;
2864 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2865 "distinguishedName", "(objectSid=%s)",
2866 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2867 if (memberdn == NULL) {
2868 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2871 mod = ldb_msg_new(mem_ctx);
2873 return NT_STATUS_NO_MEMORY;
2876 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2878 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2880 if (ret != LDB_SUCCESS) {
2881 return dsdb_ldb_err_to_ntstatus(ret);
2884 ret = ldb_modify(a_state->sam_ctx, mod);
2887 return NT_STATUS_OK;
2888 case LDB_ERR_UNWILLING_TO_PERFORM:
2889 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2890 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2891 return NT_STATUS_ACCESS_DENIED;
2893 return dsdb_ldb_err_to_ntstatus(ret);
2899 samr_GetMembersInAlias
2901 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2902 struct samr_GetMembersInAlias *r)
2904 struct dcesrv_handle *h;
2905 struct samr_account_state *a_state;
2906 struct samr_domain_state *d_state;
2907 struct lsa_SidPtr *array;
2908 unsigned int i, num_members;
2909 struct dom_sid *members;
2912 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2915 d_state = a_state->domain_state;
2917 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2918 a_state->account_dn, &members,
2920 if (!NT_STATUS_IS_OK(status)) {
2924 if (num_members == 0) {
2925 r->out.sids->sids = NULL;
2927 return NT_STATUS_OK;
2930 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
2931 if (array == NULL) {
2932 return NT_STATUS_NO_MEMORY;
2935 for (i=0; i<num_members; i++) {
2936 array[i].sid = &members[i];
2939 r->out.sids->num_sids = num_members;
2940 r->out.sids->sids = array;
2942 return NT_STATUS_OK;
2948 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2949 struct samr_OpenUser *r)
2951 struct samr_domain_state *d_state;
2952 struct samr_account_state *a_state;
2953 struct dcesrv_handle *h;
2954 const char *account_name;
2955 struct dom_sid *sid;
2956 struct ldb_message **msgs;
2957 struct dcesrv_handle *u_handle;
2958 const char * const attrs[2] = { "sAMAccountName", NULL };
2961 ZERO_STRUCTP(r->out.user_handle);
2963 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2967 /* form the users SID */
2968 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2970 return NT_STATUS_NO_MEMORY;
2973 /* search for the user record */
2974 ret = gendb_search(d_state->sam_ctx,
2975 mem_ctx, d_state->domain_dn, &msgs, attrs,
2976 "(&(objectSid=%s)(objectclass=user))",
2977 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2979 return NT_STATUS_NO_SUCH_USER;
2982 DEBUG(0,("Found %d records matching sid %s\n", ret,
2983 dom_sid_string(mem_ctx, sid)));
2984 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2987 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2988 if (account_name == NULL) {
2989 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2990 dom_sid_string(mem_ctx, sid)));
2991 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2994 a_state = talloc(mem_ctx, struct samr_account_state);
2996 return NT_STATUS_NO_MEMORY;
2998 a_state->sam_ctx = d_state->sam_ctx;
2999 a_state->access_mask = r->in.access_mask;
3000 a_state->domain_state = talloc_reference(a_state, d_state);
3001 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
3002 a_state->account_sid = talloc_steal(a_state, sid);
3003 a_state->account_name = talloc_strdup(a_state, account_name);
3004 if (!a_state->account_name) {
3005 return NT_STATUS_NO_MEMORY;
3008 /* create the policy handle */
3009 u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
3011 return NT_STATUS_NO_MEMORY;
3014 u_handle->data = talloc_steal(u_handle, a_state);
3016 *r->out.user_handle = u_handle->wire_handle;
3018 return NT_STATUS_OK;
3026 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3027 struct samr_DeleteUser *r)
3029 struct dcesrv_handle *h;
3030 struct samr_account_state *a_state;
3033 *r->out.user_handle = *r->in.user_handle;
3035 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3039 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
3040 if (ret != LDB_SUCCESS) {
3041 DEBUG(1, ("Failed to delete user: %s: %s\n",
3042 ldb_dn_get_linearized(a_state->account_dn),
3043 ldb_errstring(a_state->sam_ctx)));
3044 return dsdb_ldb_err_to_ntstatus(ret);
3048 ZERO_STRUCTP(r->out.user_handle);
3050 return NT_STATUS_OK;
3057 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3058 struct samr_QueryUserInfo *r)
3060 struct dcesrv_handle *h;
3061 struct samr_account_state *a_state;
3062 struct ldb_message *msg, **res;
3064 struct ldb_context *sam_ctx;
3066 const char * const *attrs = NULL;
3067 union samr_UserInfo *info;
3071 *r->out.info = NULL;
3073 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3076 sam_ctx = a_state->sam_ctx;
3078 /* fill in the reply */
3079 switch (r->in.level) {
3082 static const char * const attrs2[] = {"sAMAccountName",
3093 static const char * const attrs2[] = {"comment",
3102 static const char * const attrs2[] = {"sAMAccountName",
3114 "msDS-UserPasswordExpiryTimeComputed",
3119 "userAccountControl",
3120 "msDS-User-Account-Control-Computed",
3127 static const char * const attrs2[] = {"logonHours",
3134 static const char * const attrs2[] = {"sAMAccountName",
3151 "msDS-ResultantPSO",
3152 "msDS-UserPasswordExpiryTimeComputed",
3154 "userAccountControl",
3155 "msDS-User-Account-Control-Computed",
3162 static const char * const attrs2[] = {"sAMAccountName",
3170 static const char * const attrs2[] = {"sAMAccountName",
3177 static const char * const attrs2[] = {"displayName",
3184 static const char * const attrs2[] = {"primaryGroupID",
3191 static const char * const attrs2[] = {"homeDirectory",
3199 static const char * const attrs2[] = {"scriptPath",
3206 static const char * const attrs2[] = {"profilePath",
3213 static const char * const attrs2[] = {"description",
3220 static const char * const attrs2[] = {"userWorkstations",
3227 static const char * const attrs2[] = {"userAccountControl",
3228 "msDS-User-Account-Control-Computed",
3230 "msDS-UserPasswordExpiryTimeComputed",
3237 static const char * const attrs2[] = {"accountExpires",
3244 return NT_STATUS_NOT_SUPPORTED;
3248 static const char * const attrs2[] = {"userParameters",
3255 static const char * const attrs2[] = {"lastLogon",
3258 "msDS-ResultantPSO",
3259 "msDS-UserPasswordExpiryTimeComputed",
3273 "userAccountControl",
3274 "msDS-User-Account-Control-Computed",
3290 return NT_STATUS_NOT_SUPPORTED;
3294 return NT_STATUS_INVALID_INFO_CLASS;
3298 /* pull all the user attributes */
3299 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3300 a_state->account_dn, &res, attrs);
3302 return NT_STATUS_NO_SUCH_USER;
3305 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3309 /* allocate the info structure */
3310 info = talloc_zero(mem_ctx, union samr_UserInfo);
3312 return NT_STATUS_NO_MEMORY;
3315 /* fill in the reply */
3316 switch (r->in.level) {
3318 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
3319 QUERY_STRING(msg, info1.full_name, "displayName");
3320 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3321 QUERY_STRING(msg, info1.description, "description");
3322 QUERY_STRING(msg, info1.comment, "comment");
3326 QUERY_STRING(msg, info2.comment, "comment");
3327 QUERY_UINT (msg, info2.country_code, "countryCode");
3328 QUERY_UINT (msg, info2.code_page, "codePage");
3332 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
3333 QUERY_STRING(msg, info3.full_name, "displayName");
3334 QUERY_RID (msg, info3.rid, "objectSid");
3335 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3336 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
3337 QUERY_STRING(msg, info3.home_drive, "homeDrive");
3338 QUERY_STRING(msg, info3.logon_script, "scriptPath");
3339 QUERY_STRING(msg, info3.profile_path, "profilePath");
3340 QUERY_STRING(msg, info3.workstations, "userWorkstations");
3341 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
3342 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
3343 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
3344 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3345 QUERY_UINT64(msg, info3.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3346 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3347 /* level 3 gives the raw badPwdCount value */
3348 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3349 QUERY_UINT (msg, info3.logon_count, "logonCount");
3350 QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
3354 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3358 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
3359 QUERY_STRING(msg, info5.full_name, "displayName");
3360 QUERY_RID (msg, info5.rid, "objectSid");
3361 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3362 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
3363 QUERY_STRING(msg, info5.home_drive, "homeDrive");
3364 QUERY_STRING(msg, info5.logon_script, "scriptPath");
3365 QUERY_STRING(msg, info5.profile_path, "profilePath");
3366 QUERY_STRING(msg, info5.description, "description");
3367 QUERY_STRING(msg, info5.workstations, "userWorkstations");
3368 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
3369 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
3370 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3371 QUERY_BPWDCT(msg, info5.bad_password_count, "badPwdCount");
3372 QUERY_UINT (msg, info5.logon_count, "logonCount");
3373 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
3374 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
3375 QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
3379 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3380 QUERY_STRING(msg, info6.full_name, "displayName");
3384 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3388 QUERY_STRING(msg, info8.full_name, "displayName");
3392 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3396 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3397 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3401 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3405 QUERY_STRING(msg, info12.profile_path, "profilePath");
3409 QUERY_STRING(msg, info13.description, "description");
3413 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3417 QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3421 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3425 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
3426 if (!NT_STATUS_IS_OK(status)) {
3433 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3434 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3435 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3436 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3437 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3438 QUERY_UINT64(msg, info21.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3439 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3440 QUERY_STRING(msg, info21.full_name, "displayName");
3441 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3442 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3443 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3444 QUERY_STRING(msg, info21.profile_path, "profilePath");
3445 QUERY_STRING(msg, info21.description, "description");
3446 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3447 QUERY_STRING(msg, info21.comment, "comment");
3448 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
3449 if (!NT_STATUS_IS_OK(status)) {
3454 QUERY_RID (msg, info21.rid, "objectSid");
3455 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3456 QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3457 info->info21.fields_present = 0x08FFFFFF;
3458 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3459 QUERY_BPWDCT(msg, info21.bad_password_count, "badPwdCount");
3460 QUERY_UINT (msg, info21.logon_count, "logonCount");
3461 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3462 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3464 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3466 QUERY_UINT (msg, info21.country_code, "countryCode");
3467 QUERY_UINT (msg, info21.code_page, "codePage");
3473 return NT_STATUS_INVALID_INFO_CLASS;
3476 *r->out.info = info;
3478 return NT_STATUS_OK;
3485 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3486 struct samr_SetUserInfo *r)
3488 struct dcesrv_handle *h;
3489 struct samr_account_state *a_state;
3490 struct ldb_message *msg;
3492 NTSTATUS status = NT_STATUS_OK;
3493 struct ldb_context *sam_ctx;
3495 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3498 sam_ctx = a_state->sam_ctx;
3500 msg = ldb_msg_new(mem_ctx);
3502 return NT_STATUS_NO_MEMORY;
3505 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3507 return NT_STATUS_NO_MEMORY;
3510 switch (r->in.level) {
3512 SET_STRING(msg, info2.comment, "comment");
3513 SET_UINT (msg, info2.country_code, "countryCode");
3514 SET_UINT (msg, info2.code_page, "codePage");
3518 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3522 SET_STRING(msg, info6.account_name, "samAccountName");
3523 SET_STRING(msg, info6.full_name, "displayName");
3527 SET_STRING(msg, info7.account_name, "samAccountName");
3531 SET_STRING(msg, info8.full_name, "displayName");
3535 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3539 SET_STRING(msg, info10.home_directory, "homeDirectory");
3540 SET_STRING(msg, info10.home_drive, "homeDrive");
3544 SET_STRING(msg, info11.logon_script, "scriptPath");
3548 SET_STRING(msg, info12.profile_path, "profilePath");
3552 SET_STRING(msg, info13.description, "description");
3556 SET_STRING(msg, info14.workstations, "userWorkstations");
3560 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3564 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3568 status = samr_set_password_buffers(dce_call,
3570 a_state->account_dn,
3571 a_state->domain_state->domain_dn,
3573 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3574 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3575 if (!NT_STATUS_IS_OK(status)) {
3579 if (r->in.info->info18.password_expired > 0) {
3580 struct ldb_message_element *set_el;
3581 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3582 return NT_STATUS_NO_MEMORY;
3584 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3585 set_el->flags = LDB_FLAG_MOD_REPLACE;
3590 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3594 if (r->in.info->info21.fields_present == 0)
3595 return NT_STATUS_INVALID_PARAMETER;
3597 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3598 IFSET(SAMR_FIELD_LAST_LOGON)
3599 SET_UINT64(msg, info21.last_logon, "lastLogon");
3600 IFSET(SAMR_FIELD_LAST_LOGOFF)
3601 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3602 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3603 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3604 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3605 SET_STRING(msg, info21.account_name, "samAccountName");
3606 IFSET(SAMR_FIELD_FULL_NAME)
3607 SET_STRING(msg, info21.full_name, "displayName");
3608 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3609 SET_STRING(msg, info21.home_directory, "homeDirectory");
3610 IFSET(SAMR_FIELD_HOME_DRIVE)
3611 SET_STRING(msg, info21.home_drive, "homeDrive");
3612 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3613 SET_STRING(msg, info21.logon_script, "scriptPath");
3614 IFSET(SAMR_FIELD_PROFILE_PATH)
3615 SET_STRING(msg, info21.profile_path, "profilePath");
3616 IFSET(SAMR_FIELD_DESCRIPTION)
3617 SET_STRING(msg, info21.description, "description");
3618 IFSET(SAMR_FIELD_WORKSTATIONS)
3619 SET_STRING(msg, info21.workstations, "userWorkstations");
3620 IFSET(SAMR_FIELD_COMMENT)
3621 SET_STRING(msg, info21.comment, "comment");
3622 IFSET(SAMR_FIELD_PARAMETERS)
3623 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3624 IFSET(SAMR_FIELD_PRIMARY_GID)
3625 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3626 IFSET(SAMR_FIELD_ACCT_FLAGS)
3627 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3628 IFSET(SAMR_FIELD_LOGON_HOURS)
3629 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3630 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3631 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3632 IFSET(SAMR_FIELD_NUM_LOGONS)
3633 SET_UINT (msg, info21.logon_count, "logonCount");
3634 IFSET(SAMR_FIELD_COUNTRY_CODE)
3635 SET_UINT (msg, info21.country_code, "countryCode");
3636 IFSET(SAMR_FIELD_CODE_PAGE)
3637 SET_UINT (msg, info21.code_page, "codePage");
3639 /* password change fields */
3640 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3641 return NT_STATUS_ACCESS_DENIED;
3643 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3644 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3645 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3647 if (r->in.info->info21.lm_password_set) {
3648 if ((r->in.info->info21.lm_owf_password.length != 16)
3649 || (r->in.info->info21.lm_owf_password.size != 16)) {
3650 return NT_STATUS_INVALID_PARAMETER;
3653 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3655 if (r->in.info->info21.nt_password_set) {
3656 if ((r->in.info->info21.nt_owf_password.length != 16)
3657 || (r->in.info->info21.nt_owf_password.size != 16)) {
3658 return NT_STATUS_INVALID_PARAMETER;
3661 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3663 status = samr_set_password_buffers(dce_call,
3665 a_state->account_dn,
3666 a_state->domain_state->domain_dn,
3670 if (!NT_STATUS_IS_OK(status)) {
3676 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3677 const char *t = "0";
3678 struct ldb_message_element *set_el;
3679 if (r->in.info->info21.password_expired
3680 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3683 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3684 return NT_STATUS_NO_MEMORY;
3686 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3687 set_el->flags = LDB_FLAG_MOD_REPLACE;
3693 if (r->in.info->info23.info.fields_present == 0)
3694 return NT_STATUS_INVALID_PARAMETER;
3696 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3697 IFSET(SAMR_FIELD_LAST_LOGON)
3698 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3699 IFSET(SAMR_FIELD_LAST_LOGOFF)
3700 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3701 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3702 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3703 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3704 SET_STRING(msg, info23.info.account_name, "samAccountName");
3705 IFSET(SAMR_FIELD_FULL_NAME)
3706 SET_STRING(msg, info23.info.full_name, "displayName");
3707 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3708 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3709 IFSET(SAMR_FIELD_HOME_DRIVE)
3710 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3711 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3712 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3713 IFSET(SAMR_FIELD_PROFILE_PATH)
3714 SET_STRING(msg, info23.info.profile_path, "profilePath");
3715 IFSET(SAMR_FIELD_DESCRIPTION)
3716 SET_STRING(msg, info23.info.description, "description");
3717 IFSET(SAMR_FIELD_WORKSTATIONS)
3718 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3719 IFSET(SAMR_FIELD_COMMENT)
3720 SET_STRING(msg, info23.info.comment, "comment");
3721 IFSET(SAMR_FIELD_PARAMETERS)
3722 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3723 IFSET(SAMR_FIELD_PRIMARY_GID)
3724 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3725 IFSET(SAMR_FIELD_ACCT_FLAGS)
3726 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3727 IFSET(SAMR_FIELD_LOGON_HOURS)
3728 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3729 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3730 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3731 IFSET(SAMR_FIELD_NUM_LOGONS)
3732 SET_UINT (msg, info23.info.logon_count, "logonCount");
3734 IFSET(SAMR_FIELD_COUNTRY_CODE)
3735 SET_UINT (msg, info23.info.country_code, "countryCode");
3736 IFSET(SAMR_FIELD_CODE_PAGE)
3737 SET_UINT (msg, info23.info.code_page, "codePage");
3739 /* password change fields */
3740 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3741 return NT_STATUS_ACCESS_DENIED;
3743 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3744 status = samr_set_password(dce_call,
3746 a_state->account_dn,
3747 a_state->domain_state->domain_dn,
3749 &r->in.info->info23.password);
3750 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3751 status = samr_set_password(dce_call,
3753 a_state->account_dn,
3754 a_state->domain_state->domain_dn,
3756 &r->in.info->info23.password);
3758 if (!NT_STATUS_IS_OK(status)) {
3762 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3763 const char *t = "0";
3764 struct ldb_message_element *set_el;
3765 if (r->in.info->info23.info.password_expired
3766 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3769 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3770 return NT_STATUS_NO_MEMORY;
3772 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3773 set_el->flags = LDB_FLAG_MOD_REPLACE;
3778 /* the set password levels are handled separately */
3780 status = samr_set_password(dce_call,
3782 a_state->account_dn,
3783 a_state->domain_state->domain_dn,
3785 &r->in.info->info24.password);
3786 if (!NT_STATUS_IS_OK(status)) {
3790 if (r->in.info->info24.password_expired > 0) {
3791 struct ldb_message_element *set_el;
3792 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3793 return NT_STATUS_NO_MEMORY;
3795 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3796 set_el->flags = LDB_FLAG_MOD_REPLACE;
3801 if (r->in.info->info25.info.fields_present == 0)
3802 return NT_STATUS_INVALID_PARAMETER;
3804 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3805 IFSET(SAMR_FIELD_LAST_LOGON)
3806 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3807 IFSET(SAMR_FIELD_LAST_LOGOFF)
3808 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3809 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3810 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3811 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3812 SET_STRING(msg, info25.info.account_name, "samAccountName");
3813 IFSET(SAMR_FIELD_FULL_NAME)
3814 SET_STRING(msg, info25.info.full_name, "displayName");
3815 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3816 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3817 IFSET(SAMR_FIELD_HOME_DRIVE)
3818 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3819 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3820 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3821 IFSET(SAMR_FIELD_PROFILE_PATH)
3822 SET_STRING(msg, info25.info.profile_path, "profilePath");
3823 IFSET(SAMR_FIELD_DESCRIPTION)
3824 SET_STRING(msg, info25.info.description, "description");
3825 IFSET(SAMR_FIELD_WORKSTATIONS)
3826 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3827 IFSET(SAMR_FIELD_COMMENT)
3828 SET_STRING(msg, info25.info.comment, "comment");
3829 IFSET(SAMR_FIELD_PARAMETERS)
3830 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3831 IFSET(SAMR_FIELD_PRIMARY_GID)
3832 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3833 IFSET(SAMR_FIELD_ACCT_FLAGS)
3834 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3835 IFSET(SAMR_FIELD_LOGON_HOURS)
3836 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3837 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3838 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
3839 IFSET(SAMR_FIELD_NUM_LOGONS)
3840 SET_UINT (msg, info25.info.logon_count, "logonCount");
3841 IFSET(SAMR_FIELD_COUNTRY_CODE)
3842 SET_UINT (msg, info25.info.country_code, "countryCode");
3843 IFSET(SAMR_FIELD_CODE_PAGE)
3844 SET_UINT (msg, info25.info.code_page, "codePage");
3846 /* password change fields */
3847 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3848 return NT_STATUS_ACCESS_DENIED;
3850 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3851 status = samr_set_password_ex(dce_call,
3853 a_state->account_dn,
3854 a_state->domain_state->domain_dn,
3856 &r->in.info->info25.password);
3857 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3858 status = samr_set_password_ex(dce_call,
3860 a_state->account_dn,
3861 a_state->domain_state->domain_dn,
3863 &r->in.info->info25.password);
3865 if (!NT_STATUS_IS_OK(status)) {
3869 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3870 const char *t = "0";
3871 struct ldb_message_element *set_el;
3872 if (r->in.info->info25.info.password_expired
3873 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3876 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3877 return NT_STATUS_NO_MEMORY;
3879 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3880 set_el->flags = LDB_FLAG_MOD_REPLACE;
3885 /* the set password levels are handled separately */
3887 status = samr_set_password_ex(dce_call,
3889 a_state->account_dn,
3890 a_state->domain_state->domain_dn,
3892 &r->in.info->info26.password);
3893 if (!NT_STATUS_IS_OK(status)) {
3897 if (r->in.info->info26.password_expired > 0) {
3898 const char *t = "0";
3899 struct ldb_message_element *set_el;
3900 if (r->in.info->info26.password_expired
3901 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3904 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3905 return NT_STATUS_NO_MEMORY;
3907 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3908 set_el->flags = LDB_FLAG_MOD_REPLACE;
3913 /* many info classes are not valid for SetUserInfo */
3914 return NT_STATUS_INVALID_INFO_CLASS;
3917 if (!NT_STATUS_IS_OK(status)) {
3921 /* modify the samdb record */
3922 if (msg->num_elements > 0) {
3923 ret = ldb_modify(a_state->sam_ctx, msg);
3924 if (ret != LDB_SUCCESS) {
3925 DEBUG(1,("Failed to modify record %s: %s\n",
3926 ldb_dn_get_linearized(a_state->account_dn),
3927 ldb_errstring(a_state->sam_ctx)));
3929 return dsdb_ldb_err_to_ntstatus(ret);
3933 return NT_STATUS_OK;
3938 samr_GetGroupsForUser
3940 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3941 struct samr_GetGroupsForUser *r)
3943 struct dcesrv_handle *h;
3944 struct samr_account_state *a_state;
3945 struct samr_domain_state *d_state;
3946 struct ldb_result *res, *res_memberof;
3947 const char * const attrs[] = { "primaryGroupID",
3950 const char * const group_attrs[] = { "objectSid",
3953 struct samr_RidWithAttributeArray *array;
3954 struct ldb_message_element *memberof_el;
3955 int i, ret, count = 0;
3956 uint32_t primary_group_id;
3959 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3962 d_state = a_state->domain_state;
3964 ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
3966 a_state->account_dn,
3967 attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
3969 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3970 return NT_STATUS_NO_SUCH_USER;
3971 } else if (ret != LDB_SUCCESS) {
3972 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3973 } else if (res->count != 1) {
3974 return NT_STATUS_NO_SUCH_USER;
3977 primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
3980 filter = talloc_asprintf(mem_ctx,
3981 "(&(|(grouptype=%d)(grouptype=%d))"
3982 "(objectclass=group)(|",
3983 GTYPE_SECURITY_UNIVERSAL_GROUP,
3984 GTYPE_SECURITY_GLOBAL_GROUP);
3985 if (filter == NULL) {
3986 return NT_STATUS_NO_MEMORY;
3989 memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
3990 if (memberof_el != NULL) {
3991 for (i = 0; i < memberof_el->num_values; i++) {
3992 const struct ldb_val *memberof_sid_binary;
3993 char *memberof_sid_escaped;
3994 struct ldb_dn *memberof_dn
3995 = ldb_dn_from_ldb_val(mem_ctx,
3997 &memberof_el->values[i]);
3998 if (memberof_dn == NULL) {
3999 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4003 = ldb_dn_get_extended_component(memberof_dn,
4005 if (memberof_sid_binary == NULL) {
4006 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4009 memberof_sid_escaped = ldb_binary_encode(mem_ctx,
4010 *memberof_sid_binary);
4011 if (memberof_sid_escaped == NULL) {
4012 return NT_STATUS_NO_MEMORY;
4014 filter = talloc_asprintf_append(filter, "(objectSID=%s)",
4015 memberof_sid_escaped);
4016 if (filter == NULL) {
4017 return NT_STATUS_NO_MEMORY;
4021 ret = dsdb_search(a_state->sam_ctx, mem_ctx,
4028 if (ret != LDB_SUCCESS) {
4029 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4031 count = res_memberof->count;
4034 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
4036 return NT_STATUS_NO_MEMORY;
4041 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
4043 if (array->rids == NULL)
4044 return NT_STATUS_NO_MEMORY;
4046 /* Adds the primary group */
4048 array->rids[0].rid = primary_group_id;
4049 array->rids[0].attributes = SE_GROUP_MANDATORY
4050 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
4053 /* Adds the additional groups */
4054 for (i = 0; i < count; i++) {
4055 struct dom_sid *group_sid;
4057 group_sid = samdb_result_dom_sid(mem_ctx,
4058 res_memberof->msgs[i],
4060 if (group_sid == NULL) {
4061 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4064 array->rids[i + 1].rid =
4065 group_sid->sub_auths[group_sid->num_auths-1];
4066 array->rids[i + 1].attributes = SE_GROUP_MANDATORY
4067 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
4071 *r->out.rids = array;
4073 return NT_STATUS_OK;
4077 * samr_QueryDisplayInfo
4079 * A cache of the GUID's matching the last query is maintained
4080 * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
4081 * n the dcesrv_handle.
4083 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4084 struct samr_QueryDisplayInfo *r)
4086 struct dcesrv_handle *h;
4087 struct samr_domain_state *d_state;
4088 struct ldb_result *res;
4090 uint32_t results = 0;
4092 const char *const cache_attrs[] = {"objectGUID", NULL};
4093 const char *const attrs[] = {
4094 "objectSID", "sAMAccountName", "displayName", "description", NULL};
4095 struct samr_DispEntryFull *entriesFull = NULL;
4096 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
4097 struct samr_DispEntryAscii *entriesAscii = NULL;
4098 struct samr_DispEntryGeneral *entriesGeneral = NULL;
4102 struct samr_guid_cache *cache = NULL;
4104 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4108 cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
4110 * Can the cached results be used?
4111 * The cache is discarded if the start index is zero, or the requested
4112 * level is different from that in the cache.
4114 if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
4116 * The cached results can not be used, so will need to query
4121 * Get the search filter for the current level
4123 switch (r->in.level) {
4126 filter = talloc_asprintf(mem_ctx,
4127 "(&(objectclass=user)"
4128 "(sAMAccountType=%d))",
4129 ATYPE_NORMAL_ACCOUNT);
4132 filter = talloc_asprintf(mem_ctx,
4133 "(&(objectclass=user)"
4134 "(sAMAccountType=%d))",
4135 ATYPE_WORKSTATION_TRUST);
4140 talloc_asprintf(mem_ctx,
4141 "(&(|(groupType=%d)(groupType=%d))"
4142 "(objectClass=group))",
4143 GTYPE_SECURITY_UNIVERSAL_GROUP,
4144 GTYPE_SECURITY_GLOBAL_GROUP);
4147 return NT_STATUS_INVALID_INFO_CLASS;
4149 clear_guid_cache(cache);
4152 * search for all requested objects in all domains.
4154 ret = dsdb_search(d_state->sam_ctx,
4157 ldb_get_default_basedn(d_state->sam_ctx),
4163 if (ret != LDB_SUCCESS) {
4164 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4166 if ((res->count == 0) || (r->in.max_entries == 0)) {
4167 return NT_STATUS_OK;
4170 status = load_guid_cache(cache, d_state, res->count, res->msgs);
4172 if (!NT_STATUS_IS_OK(status)) {
4175 cache->handle = r->in.level;
4177 *r->out.total_size = cache->size;
4180 * if there are no entries or the requested start index is greater
4181 * than the number of entries, we return an empty response.
4183 if (r->in.start_idx >= cache->size) {
4184 *r->out.returned_size = 0;
4185 switch(r->in.level) {
4187 r->out.info->info1.count = *r->out.returned_size;
4188 r->out.info->info1.entries = NULL;
4191 r->out.info->info2.count = *r->out.returned_size;
4192 r->out.info->info2.entries = NULL;
4195 r->out.info->info3.count = *r->out.returned_size;
4196 r->out.info->info3.entries = NULL;
4199 r->out.info->info4.count = *r->out.returned_size;
4200 r->out.info->info4.entries = NULL;
4203 r->out.info->info5.count = *r->out.returned_size;
4204 r->out.info->info5.entries = NULL;
4207 return NT_STATUS_OK;
4211 * Allocate an array of the appropriate result structures for the
4212 * current query level.
4214 * r->in.start_idx is always < cache->size due to the check above
4216 results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
4217 switch (r->in.level) {
4219 entriesGeneral = talloc_array(
4220 mem_ctx, struct samr_DispEntryGeneral, results);
4224 talloc_array(mem_ctx, struct samr_DispEntryFull, results);
4227 entriesFullGroup = talloc_array(
4228 mem_ctx, struct samr_DispEntryFullGroup, results);
4233 talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
4237 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
4238 (entriesAscii == NULL) && (entriesFullGroup == NULL))
4239 return NT_STATUS_NO_MEMORY;
4242 * Process the list of result GUID's.
4243 * Read the details of each object and populate the result structure
4244 * for the current level.
4247 for (i = 0; i < results; i++) {
4248 struct dom_sid *objectsid;
4249 struct ldb_result *rec;
4250 const uint32_t idx = r->in.start_idx + i;
4254 * Read an object from disk using the GUID as the key
4256 * If the object can not be read, or it does not have a SID
4257 * it is ignored. In this case the number of entries returned
4258 * will be less than the requested size, there will also be
4259 * a gap in the idx numbers in the returned elements e.g. if
4260 * there are 3 GUIDs a, b, c in the cache and b is deleted from
4261 * disk then details for a, and c will be returned with
4262 * idx values of 1 and 3 respectively.
4265 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
4268 &cache->entries[idx],
4271 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4272 struct GUID_txt_buf guid_buf;
4274 GUID_buf_string(&cache->entries[idx],
4276 DBG_WARNING("GUID [%s] not found\n", guid_str);
4278 } else if (ret != LDB_SUCCESS) {
4279 clear_guid_cache(cache);
4280 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4282 objectsid = samdb_result_dom_sid(mem_ctx,
4285 if (objectsid == NULL) {
4286 struct GUID_txt_buf guid_buf;
4288 "objectSID for GUID [%s] not found\n",
4289 GUID_buf_string(&cache->entries[idx], &guid_buf));
4292 status = dom_sid_split_rid(NULL,
4296 if (!NT_STATUS_IS_OK(status)) {
4297 struct dom_sid_buf sid_buf;
4298 struct GUID_txt_buf guid_buf;
4300 "objectSID [%s] for GUID [%s] invalid\n",
4301 dom_sid_str_buf(objectsid, &sid_buf),
4302 GUID_buf_string(&cache->entries[idx], &guid_buf));
4307 * Populate the result structure for the current object
4309 switch(r->in.level) {
4312 entriesGeneral[count].idx = idx + 1;
4313 entriesGeneral[count].rid = rid;
4315 entriesGeneral[count].acct_flags =
4316 samdb_result_acct_flags(rec->msgs[0], NULL);
4317 entriesGeneral[count].account_name.string =
4318 ldb_msg_find_attr_as_string(
4319 rec->msgs[0], "sAMAccountName", "");
4320 entriesGeneral[count].full_name.string =
4321 ldb_msg_find_attr_as_string(
4322 rec->msgs[0], "displayName", "");
4323 entriesGeneral[count].description.string =
4324 ldb_msg_find_attr_as_string(
4325 rec->msgs[0], "description", "");
4328 entriesFull[count].idx = idx + 1;
4329 entriesFull[count].rid = rid;
4332 * No idea why we need to or in ACB_NORMAL here,
4333 * but this is what Win2k3 seems to do...
4335 entriesFull[count].acct_flags =
4336 samdb_result_acct_flags(rec->msgs[0], NULL) |
4338 entriesFull[count].account_name.string =
4339 ldb_msg_find_attr_as_string(
4340 rec->msgs[0], "sAMAccountName", "");
4341 entriesFull[count].description.string =
4342 ldb_msg_find_attr_as_string(
4343 rec->msgs[0], "description", "");
4346 entriesFullGroup[count].idx = idx + 1;
4347 entriesFullGroup[count].rid = rid;
4350 * We get a "7" here for groups
4352 entriesFullGroup[count].acct_flags =
4353 SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
4355 entriesFullGroup[count].account_name.string =
4356 ldb_msg_find_attr_as_string(
4357 rec->msgs[0], "sAMAccountName", "");
4358 entriesFullGroup[count].description.string =
4359 ldb_msg_find_attr_as_string(
4360 rec->msgs[0], "description", "");
4364 entriesAscii[count].idx = idx + 1;
4365 entriesAscii[count].account_name.string =
4366 ldb_msg_find_attr_as_string(
4367 rec->msgs[0], "sAMAccountName", "");
4374 * Build the response based on the request level.
4376 *r->out.returned_size = count;
4377 switch(r->in.level) {
4379 r->out.info->info1.count = count;
4380 r->out.info->info1.entries = entriesGeneral;
4383 r->out.info->info2.count = count;
4384 r->out.info->info2.entries = entriesFull;
4387 r->out.info->info3.count = count;
4388 r->out.info->info3.entries = entriesFullGroup;
4391 r->out.info->info4.count = count;
4392 r->out.info->info4.entries = entriesAscii;
4395 r->out.info->info5.count = count;
4396 r->out.info->info5.entries = entriesAscii;
4400 return ((r->in.start_idx + results) < cache->size)
4401 ? STATUS_MORE_ENTRIES
4407 samr_GetDisplayEnumerationIndex
4409 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4410 struct samr_GetDisplayEnumerationIndex *r)
4412 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4417 samr_TestPrivateFunctionsDomain
4419 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4420 struct samr_TestPrivateFunctionsDomain *r)
4422 return NT_STATUS_NOT_IMPLEMENTED;
4427 samr_TestPrivateFunctionsUser
4429 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4430 struct samr_TestPrivateFunctionsUser *r)
4432 return NT_STATUS_NOT_IMPLEMENTED;
4439 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4440 struct samr_GetUserPwInfo *r)
4442 struct dcesrv_handle *h;
4443 struct samr_account_state *a_state;
4445 ZERO_STRUCTP(r->out.info);
4447 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4451 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
4452 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
4454 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
4455 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
4457 return NT_STATUS_OK;
4462 samr_RemoveMemberFromForeignDomain
4464 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
4465 TALLOC_CTX *mem_ctx,
4466 struct samr_RemoveMemberFromForeignDomain *r)
4468 struct dcesrv_handle *h;
4469 struct samr_domain_state *d_state;
4470 const char *memberdn;
4471 struct ldb_message **res;
4472 const char *no_attrs[] = { NULL };
4475 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4479 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
4480 "distinguishedName", "(objectSid=%s)",
4481 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
4483 if (memberdn == NULL) {
4484 return NT_STATUS_OK;
4487 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4488 d_state->domain_dn, &res, no_attrs,
4489 d_state->domain_sid,
4490 "(&(member=%s)(objectClass=group)"
4491 "(|(groupType=%d)(groupType=%d)))",
4493 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4494 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4497 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4499 for (i=0; i<count; i++) {
4500 struct ldb_message *mod;
4503 mod = ldb_msg_new(mem_ctx);
4505 return NT_STATUS_NO_MEMORY;
4508 mod->dn = res[i]->dn;
4510 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4511 "member", memberdn) != LDB_SUCCESS)
4512 return NT_STATUS_NO_MEMORY;
4514 ret = ldb_modify(d_state->sam_ctx, mod);
4516 if (ret != LDB_SUCCESS) {
4517 return dsdb_ldb_err_to_ntstatus(ret);
4521 return NT_STATUS_OK;
4526 samr_QueryDomainInfo2
4528 just an alias for samr_QueryDomainInfo
4530 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4531 struct samr_QueryDomainInfo2 *r)
4533 struct samr_QueryDomainInfo r1;
4536 r1 = (struct samr_QueryDomainInfo) {
4537 .in.domain_handle = r->in.domain_handle,
4538 .in.level = r->in.level,
4539 .out.info = r->out.info,
4542 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4551 just an alias for samr_QueryUserInfo
4553 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4554 struct samr_QueryUserInfo2 *r)
4556 struct samr_QueryUserInfo r1;
4559 r1 = (struct samr_QueryUserInfo) {
4560 .in.user_handle = r->in.user_handle,
4561 .in.level = r->in.level,
4562 .out.info = r->out.info
4565 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4572 samr_QueryDisplayInfo2
4574 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4575 struct samr_QueryDisplayInfo2 *r)
4577 struct samr_QueryDisplayInfo q;
4580 q = (struct samr_QueryDisplayInfo) {
4581 .in.domain_handle = r->in.domain_handle,
4582 .in.level = r->in.level,
4583 .in.start_idx = r->in.start_idx,
4584 .in.max_entries = r->in.max_entries,
4585 .in.buf_size = r->in.buf_size,
4586 .out.total_size = r->out.total_size,
4587 .out.returned_size = r->out.returned_size,
4588 .out.info = r->out.info,
4591 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4598 samr_GetDisplayEnumerationIndex2
4600 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4601 struct samr_GetDisplayEnumerationIndex2 *r)
4603 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4608 samr_QueryDisplayInfo3
4610 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4611 struct samr_QueryDisplayInfo3 *r)
4613 struct samr_QueryDisplayInfo q;
4616 q = (struct samr_QueryDisplayInfo) {
4617 .in.domain_handle = r->in.domain_handle,
4618 .in.level = r->in.level,
4619 .in.start_idx = r->in.start_idx,
4620 .in.max_entries = r->in.max_entries,
4621 .in.buf_size = r->in.buf_size,
4622 .out.total_size = r->out.total_size,
4623 .out.returned_size = r->out.returned_size,
4624 .out.info = r->out.info,
4627 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4634 samr_AddMultipleMembersToAlias
4636 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4637 struct samr_AddMultipleMembersToAlias *r)
4639 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4644 samr_RemoveMultipleMembersFromAlias
4646 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4647 struct samr_RemoveMultipleMembersFromAlias *r)
4649 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4656 this fetches the default password properties for a domain
4658 note that w2k3 completely ignores the domain name in this call, and
4659 always returns the information for the servers primary domain
4661 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4662 struct samr_GetDomPwInfo *r)
4664 struct auth_session_info *session_info =
4665 dcesrv_call_session_info(dce_call);
4666 struct ldb_message **msgs;
4668 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4669 struct ldb_context *sam_ctx;
4671 ZERO_STRUCTP(r->out.info);
4673 sam_ctx = samdb_connect(mem_ctx,
4674 dce_call->event_ctx,
4675 dce_call->conn->dce_ctx->lp_ctx,
4677 dce_call->conn->remote_address,
4679 if (sam_ctx == NULL) {
4680 return NT_STATUS_INVALID_SYSTEM_SERVICE;
4683 /* The domain name in this call is ignored */
4684 ret = gendb_search_dn(sam_ctx,
4685 mem_ctx, NULL, &msgs, attrs);
4687 talloc_free(sam_ctx);
4689 return NT_STATUS_NO_SUCH_DOMAIN;
4693 talloc_free(sam_ctx);
4695 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4698 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
4700 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
4701 "pwdProperties", 1);
4704 talloc_unlink(mem_ctx, sam_ctx);
4706 return NT_STATUS_OK;
4713 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4714 struct samr_Connect2 *r)
4716 struct samr_Connect c;
4718 c = (struct samr_Connect) {
4719 .in.system_name = NULL,
4720 .in.access_mask = r->in.access_mask,
4721 .out.connect_handle = r->out.connect_handle,
4724 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4731 just an alias for samr_SetUserInfo
4733 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4734 struct samr_SetUserInfo2 *r)
4736 struct samr_SetUserInfo r2;
4738 r2 = (struct samr_SetUserInfo) {
4739 .in.user_handle = r->in.user_handle,
4740 .in.level = r->in.level,
4741 .in.info = r->in.info,
4744 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4749 samr_SetBootKeyInformation
4751 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4752 struct samr_SetBootKeyInformation *r)
4754 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4759 samr_GetBootKeyInformation
4761 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4762 struct samr_GetBootKeyInformation *r)
4764 /* Windows Server 2008 returns this */
4765 return NT_STATUS_NOT_SUPPORTED;
4772 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4773 struct samr_Connect3 *r)
4775 struct samr_Connect c;
4777 c = (struct samr_Connect) {
4778 .in.system_name = NULL,
4779 .in.access_mask = r->in.access_mask,
4780 .out.connect_handle = r->out.connect_handle,
4783 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4790 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4791 struct samr_Connect4 *r)
4793 struct samr_Connect c;
4795 c = (struct samr_Connect) {
4796 .in.system_name = NULL,
4797 .in.access_mask = r->in.access_mask,
4798 .out.connect_handle = r->out.connect_handle,
4801 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4808 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4809 struct samr_Connect5 *r)
4811 struct samr_Connect c;
4814 c = (struct samr_Connect) {
4815 .in.system_name = NULL,
4816 .in.access_mask = r->in.access_mask,
4817 .out.connect_handle = r->out.connect_handle,
4820 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4822 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4823 r->out.info_out->info1.unknown2 = 0;
4824 *r->out.level_out = r->in.level_in;
4833 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4834 struct samr_RidToSid *r)
4836 struct samr_domain_state *d_state;
4837 struct dcesrv_handle *h;
4839 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4843 /* form the users SID */
4844 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4846 return NT_STATUS_NO_MEMORY;
4849 return NT_STATUS_OK;
4854 samr_SetDsrmPassword
4856 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4857 struct samr_SetDsrmPassword *r)
4859 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4864 samr_ValidatePassword
4866 For now the call checks the password complexity (if active) and the minimum
4867 password length on level 2 and 3. Level 1 is ignored for now.
4869 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
4870 TALLOC_CTX *mem_ctx,
4871 struct samr_ValidatePassword *r)
4873 struct samr_GetDomPwInfo r2;
4874 struct samr_PwInfo pwInfo;
4876 enum samr_ValidationStatus res;
4878 enum dcerpc_transport_t transport =
4879 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
4880 enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
4882 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
4883 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4886 dcesrv_call_auth_info(dce_call, NULL, &auth_level);
4887 if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
4888 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4891 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
4893 r2 = (struct samr_GetDomPwInfo) {
4894 .in.domain_name = NULL,
4895 .out.info = &pwInfo,
4898 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
4899 if (!NT_STATUS_IS_OK(status)) {
4903 switch (r->in.level) {
4904 case NetValidateAuthentication:
4905 /* we don't support this yet */
4906 return NT_STATUS_NOT_SUPPORTED;
4908 case NetValidatePasswordChange:
4909 password = data_blob_const(r->in.req->req2.password.string,
4910 r->in.req->req2.password.length);
4911 res = samdb_check_password(mem_ctx,
4912 dce_call->conn->dce_ctx->lp_ctx,
4914 pwInfo.password_properties,
4915 pwInfo.min_password_length);
4916 (*r->out.rep)->ctr2.status = res;
4918 case NetValidatePasswordReset:
4919 password = data_blob_const(r->in.req->req3.password.string,
4920 r->in.req->req3.password.length);
4921 res = samdb_check_password(mem_ctx,
4922 dce_call->conn->dce_ctx->lp_ctx,
4924 pwInfo.password_properties,
4925 pwInfo.min_password_length);
4926 (*r->out.rep)->ctr3.status = res;
4929 return NT_STATUS_INVALID_INFO_CLASS;
4933 return NT_STATUS_OK;
4937 /* include the generated boilerplate */
4938 #include "librpc/gen_ndr/ndr_samr_s.c"