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"
31 #include "lib/ldb/include/ldb.h"
32 #include "lib/ldb/include/ldb_errors.h"
33 #include "../libds/common/flags.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "libcli/ldap/ldap_ndr.h"
36 #include "libcli/security/security.h"
37 #include "rpc_server/samr/proto.h"
38 #include "../lib/util/util_ldb.h"
39 #include "param/param.h"
41 /* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
43 #define QUERY_STRING(msg, field, attr) \
44 info->field.string = samdb_result_string(msg, attr, "");
45 #define QUERY_UINT(msg, field, attr) \
46 info->field = samdb_result_uint(msg, attr, 0);
47 #define QUERY_RID(msg, field, attr) \
48 info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
49 #define QUERY_UINT64(msg, field, attr) \
50 info->field = samdb_result_uint64(msg, attr, 0);
51 #define QUERY_APASSC(msg, field, attr) \
52 info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
53 a_state->domain_state->domain_dn, msg, attr);
54 #define QUERY_FPASSC(msg, field, attr) \
55 info->field = samdb_result_force_password_change(sam_ctx, mem_ctx, \
56 a_state->domain_state->domain_dn, msg);
57 #define QUERY_LHOURS(msg, field, attr) \
58 info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
59 #define QUERY_AFLAGS(msg, field, attr) \
60 info->field = samdb_result_acct_flags(sam_ctx, mem_ctx, msg, a_state->domain_state->domain_dn);
61 #define QUERY_PARAMETERS(msg, field, attr) \
62 info->field = samdb_result_parameters(mem_ctx, msg, attr);
65 /* these are used to make the Set[User|Group]Info code easier to follow */
67 #define SET_STRING(msg, field, attr) do { \
68 struct ldb_message_element *set_el; \
69 if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
70 if (r->in.info->field.string[0] == '\0') { \
71 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL)) { \
72 return NT_STATUS_NO_MEMORY; \
75 if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != 0) { \
76 return NT_STATUS_NO_MEMORY; \
78 set_el = ldb_msg_find_element(msg, attr); \
79 set_el->flags = LDB_FLAG_MOD_REPLACE; \
82 #define SET_UINT(msg, field, attr) do { \
83 struct ldb_message_element *set_el; \
84 if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
85 return NT_STATUS_NO_MEMORY; \
87 set_el = ldb_msg_find_element(msg, attr); \
88 set_el->flags = LDB_FLAG_MOD_REPLACE; \
91 #define SET_INT64(msg, field, attr) do { \
92 struct ldb_message_element *set_el; \
93 if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
94 return NT_STATUS_NO_MEMORY; \
96 set_el = ldb_msg_find_element(msg, attr); \
97 set_el->flags = LDB_FLAG_MOD_REPLACE; \
100 #define SET_UINT64(msg, field, attr) do { \
101 struct ldb_message_element *set_el; \
102 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
103 return NT_STATUS_NO_MEMORY; \
105 set_el = ldb_msg_find_element(msg, attr); \
106 set_el->flags = LDB_FLAG_MOD_REPLACE; \
109 #define CHECK_FOR_MULTIPLES(value, flag, poss_flags) \
111 if ((value & flag) && ((value & flag) != (value & (poss_flags)))) { \
112 return NT_STATUS_INVALID_PARAMETER; \
116 /* Set account flags, discarding flags that cannot be set with SAMR */
117 #define SET_AFLAGS(msg, field, attr) do { \
118 struct ldb_message_element *set_el; \
119 if ((r->in.info->field & (ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST)) == 0) { \
120 return NT_STATUS_INVALID_PARAMETER; \
122 CHECK_FOR_MULTIPLES(r->in.info->field, ACB_NORMAL, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
123 CHECK_FOR_MULTIPLES(r->in.info->field, ACB_DOMTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
124 CHECK_FOR_MULTIPLES(r->in.info->field, ACB_WSTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
125 CHECK_FOR_MULTIPLES(r->in.info->field, ACB_SVRTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
126 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, (r->in.info->field & ~(ACB_AUTOLOCK|ACB_PW_EXPIRED))) != 0) { \
127 return NT_STATUS_NO_MEMORY; \
129 set_el = ldb_msg_find_element(msg, attr); \
130 set_el->flags = LDB_FLAG_MOD_REPLACE; \
133 #define SET_LHOURS(msg, field, attr) do { \
134 struct ldb_message_element *set_el; \
135 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != 0) { \
136 return NT_STATUS_NO_MEMORY; \
138 set_el = ldb_msg_find_element(msg, attr); \
139 set_el->flags = LDB_FLAG_MOD_REPLACE; \
142 #define SET_PARAMETERS(msg, field, attr) do { \
143 struct ldb_message_element *set_el; \
144 if (r->in.info->field.length != 0) { \
145 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != 0) { \
146 return NT_STATUS_NO_MEMORY; \
148 set_el = ldb_msg_find_element(msg, attr); \
149 set_el->flags = LDB_FLAG_MOD_REPLACE; \
158 create a connection to the SAM database
160 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
161 struct samr_Connect *r)
163 struct samr_connect_state *c_state;
164 struct dcesrv_handle *handle;
166 ZERO_STRUCTP(r->out.connect_handle);
168 c_state = talloc(mem_ctx, struct samr_connect_state);
170 return NT_STATUS_NO_MEMORY;
173 /* make sure the sam database is accessible */
174 c_state->sam_ctx = samdb_connect(c_state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
175 if (c_state->sam_ctx == NULL) {
176 talloc_free(c_state);
177 return NT_STATUS_INVALID_SYSTEM_SERVICE;
181 handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
183 talloc_free(c_state);
184 return NT_STATUS_NO_MEMORY;
187 handle->data = talloc_steal(handle, c_state);
189 c_state->access_mask = r->in.access_mask;
190 *r->out.connect_handle = handle->wire_handle;
199 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
200 struct samr_Close *r)
202 struct dcesrv_handle *h;
204 *r->out.handle = *r->in.handle;
206 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
210 ZERO_STRUCTP(r->out.handle);
219 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
220 struct samr_SetSecurity *r)
222 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
229 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
230 struct samr_QuerySecurity *r)
232 struct dcesrv_handle *h;
233 struct sec_desc_buf *sd;
235 *r->out.sdbuf = NULL;
237 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
239 sd = talloc(mem_ctx, struct sec_desc_buf);
241 return NT_STATUS_NO_MEMORY;
244 sd->sd = samdb_default_security_descriptor(mem_ctx);
255 we refuse this operation completely. If a admin wants to shutdown samr
256 in Samba then they should use the samba admin tools to disable the samr pipe
258 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
259 struct samr_Shutdown *r)
261 return NT_STATUS_ACCESS_DENIED;
268 this maps from a domain name to a SID
270 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
271 struct samr_LookupDomain *r)
273 struct samr_connect_state *c_state;
274 struct dcesrv_handle *h;
276 const char * const dom_attrs[] = { "objectSid", NULL};
277 struct ldb_message **dom_msgs;
282 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
286 if (r->in.domain_name->string == NULL) {
287 return NT_STATUS_INVALID_PARAMETER;
290 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
291 ret = gendb_search(c_state->sam_ctx,
292 mem_ctx, NULL, &dom_msgs, dom_attrs,
293 "(objectClass=builtinDomain)");
294 } else if (strcasecmp_m(r->in.domain_name->string, lp_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
295 ret = gendb_search_dn(c_state->sam_ctx,
296 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
297 &dom_msgs, dom_attrs);
299 return NT_STATUS_NO_SUCH_DOMAIN;
302 return NT_STATUS_NO_SUCH_DOMAIN;
305 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
309 return NT_STATUS_NO_SUCH_DOMAIN;
321 list the domains in the SAM
323 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
324 struct samr_EnumDomains *r)
326 struct samr_connect_state *c_state;
327 struct dcesrv_handle *h;
328 struct samr_SamArray *array;
331 *r->out.resume_handle = 0;
333 *r->out.num_entries = 0;
335 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
339 *r->out.resume_handle = 2;
341 start_i = *r->in.resume_handle;
344 /* search past end of list is not an error for this call */
348 array = talloc(mem_ctx, struct samr_SamArray);
350 return NT_STATUS_NO_MEMORY;
354 array->entries = NULL;
356 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
357 if (array->entries == NULL) {
358 return NT_STATUS_NO_MEMORY;
361 for (i=0;i<2-start_i;i++) {
362 array->entries[i].idx = start_i + i;
364 array->entries[i].name.string = lp_sam_name(dce_call->conn->dce_ctx->lp_ctx);
366 array->entries[i].name.string = "BUILTIN";
371 *r->out.num_entries = i;
372 array->count = *r->out.num_entries;
381 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
382 struct samr_OpenDomain *r)
384 struct dcesrv_handle *h_conn, *h_domain;
385 struct samr_connect_state *c_state;
386 struct samr_domain_state *d_state;
387 const char * const dom_attrs[] = { "cn", NULL};
388 struct ldb_message **dom_msgs;
391 ZERO_STRUCTP(r->out.domain_handle);
393 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
395 c_state = h_conn->data;
397 if (r->in.sid == NULL) {
398 return NT_STATUS_INVALID_PARAMETER;
401 d_state = talloc(mem_ctx, struct samr_domain_state);
403 return NT_STATUS_NO_MEMORY;
406 d_state->domain_sid = talloc_steal(d_state, r->in.sid);
408 if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
409 d_state->builtin = true;
410 d_state->domain_name = "BUILTIN";
412 d_state->builtin = false;
413 d_state->domain_name = lp_sam_name(dce_call->conn->dce_ctx->lp_ctx);
416 ret = gendb_search(c_state->sam_ctx,
417 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
419 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
422 talloc_free(d_state);
423 return NT_STATUS_NO_SUCH_DOMAIN;
424 } else if (ret > 1) {
425 talloc_free(d_state);
426 return NT_STATUS_INTERNAL_DB_CORRUPTION;
427 } else if (ret == -1) {
428 talloc_free(d_state);
429 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
430 return NT_STATUS_INTERNAL_DB_CORRUPTION;
433 d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
434 d_state->role = lp_server_role(dce_call->conn->dce_ctx->lp_ctx);
435 d_state->connect_state = talloc_reference(d_state, c_state);
436 d_state->sam_ctx = c_state->sam_ctx;
437 d_state->access_mask = r->in.access_mask;
439 d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
441 h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
443 talloc_free(d_state);
444 return NT_STATUS_NO_MEMORY;
447 h_domain->data = talloc_steal(h_domain, d_state);
449 *r->out.domain_handle = h_domain->wire_handle;
457 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
459 struct ldb_message **dom_msgs,
460 struct samr_DomInfo1 *info)
462 info->min_password_length =
463 samdb_result_uint(dom_msgs[0], "minPwdLength", 0);
464 info->password_history_length =
465 samdb_result_uint(dom_msgs[0], "pwdHistoryLength", 0);
466 info->password_properties =
467 samdb_result_uint(dom_msgs[0], "pwdProperties", 0);
468 info->max_password_age =
469 samdb_result_int64(dom_msgs[0], "maxPwdAge", 0);
470 info->min_password_age =
471 samdb_result_int64(dom_msgs[0], "minPwdAge", 0);
479 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
481 struct ldb_message **dom_msgs,
482 struct samr_DomGeneralInformation *info)
484 /* This pulls the NetBIOS name from the
485 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
487 info->primary.string = samdb_result_fsmo_name(state->sam_ctx, mem_ctx, dom_msgs[0], "fSMORoleOwner");
489 if (!info->primary.string) {
490 info->primary.string = lp_netbios_name(state->lp_ctx);
493 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
494 0x8000000000000000LL);
496 info->oem_information.string = samdb_result_string(dom_msgs[0], "oEMInformation", NULL);
497 info->domain_name.string = state->domain_name;
499 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
501 switch (state->role) {
502 case ROLE_DOMAIN_CONTROLLER:
503 /* This pulls the NetBIOS name from the
504 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
506 if (samdb_is_pdc(state->sam_ctx)) {
507 info->role = SAMR_ROLE_DOMAIN_PDC;
509 info->role = SAMR_ROLE_DOMAIN_BDC;
512 case ROLE_DOMAIN_MEMBER:
513 info->role = SAMR_ROLE_DOMAIN_MEMBER;
515 case ROLE_STANDALONE:
516 info->role = SAMR_ROLE_STANDALONE;
520 /* No users in BUILTIN, and the LOCAL group types are only in builtin, and the global group type is never in BUILTIN */
521 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
522 "(objectClass=user)");
523 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
524 "(&(objectClass=group)(sAMAccountType=%u))",
526 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
527 "(&(objectClass=group)(sAMAccountType=%u))",
536 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
538 struct ldb_message **dom_msgs,
539 struct samr_DomInfo3 *info)
541 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
542 0x8000000000000000LL);
550 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
552 struct ldb_message **dom_msgs,
553 struct samr_DomOEMInformation *info)
555 info->oem_information.string = samdb_result_string(dom_msgs[0], "oEMInformation", NULL);
563 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
565 struct ldb_message **dom_msgs,
566 struct samr_DomInfo5 *info)
568 info->domain_name.string = state->domain_name;
576 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
578 struct ldb_message **dom_msgs,
579 struct samr_DomInfo6 *info)
581 /* This pulls the NetBIOS name from the
582 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
584 info->primary.string = samdb_result_fsmo_name(state->sam_ctx, mem_ctx,
585 dom_msgs[0], "fSMORoleOwner");
587 if (!info->primary.string) {
588 info->primary.string = lp_netbios_name(state->lp_ctx);
597 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
599 struct ldb_message **dom_msgs,
600 struct samr_DomInfo7 *info)
603 switch (state->role) {
604 case ROLE_DOMAIN_CONTROLLER:
605 /* This pulls the NetBIOS name from the
606 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
608 if (samdb_is_pdc(state->sam_ctx)) {
609 info->role = SAMR_ROLE_DOMAIN_PDC;
611 info->role = SAMR_ROLE_DOMAIN_BDC;
614 case ROLE_DOMAIN_MEMBER:
615 info->role = SAMR_ROLE_DOMAIN_MEMBER;
617 case ROLE_STANDALONE:
618 info->role = SAMR_ROLE_STANDALONE;
628 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
630 struct ldb_message **dom_msgs,
631 struct samr_DomInfo8 *info)
633 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
636 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
645 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
647 struct ldb_message **dom_msgs,
648 struct samr_DomInfo9 *info)
650 info->domain_server_state = DOMAIN_SERVER_ENABLED;
658 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
660 struct ldb_message **dom_msgs,
661 struct samr_DomGeneralInformation2 *info)
664 status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
665 if (!NT_STATUS_IS_OK(status)) {
669 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
671 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
673 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
681 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
683 struct ldb_message **dom_msgs,
684 struct samr_DomInfo12 *info)
686 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
688 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
690 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
698 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
700 struct ldb_message **dom_msgs,
701 struct samr_DomInfo13 *info)
703 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
706 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
709 info->modified_count_at_last_promotion = 0;
717 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
718 struct samr_QueryDomainInfo *r)
720 struct dcesrv_handle *h;
721 struct samr_domain_state *d_state;
722 union samr_DomainInfo *info;
724 struct ldb_message **dom_msgs;
725 const char * const *attrs = NULL;
729 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
733 info = talloc(mem_ctx, union samr_DomainInfo);
735 return NT_STATUS_NO_MEMORY;
738 switch (r->in.level) {
741 static const char * const attrs2[] = { "minPwdLength",
752 static const char * const attrs2[] = {"forceLogoff",
762 static const char * const attrs2[] = {"forceLogoff",
769 static const char * const attrs2[] = {"oEMInformation",
781 static const char * const attrs2[] = {"fSMORoleOwner",
793 static const char * const attrs2[] = { "modifiedCount",
804 static const char * const attrs2[] = { "oEMInformation",
808 "lockOutObservationWindow",
816 static const char * const attrs2[] = { "lockoutDuration",
817 "lockOutObservationWindow",
825 static const char * const attrs2[] = { "modifiedCount",
833 /* some levels don't need a search */
836 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
837 d_state->domain_dn, &dom_msgs, attrs);
839 return NT_STATUS_INTERNAL_DB_CORRUPTION;
847 switch (r->in.level) {
849 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
852 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
855 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
858 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
861 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
864 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
867 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
870 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
873 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
876 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
879 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
882 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
886 return NT_STATUS_INVALID_INFO_CLASS;
893 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
894 struct samr_SetDomainInfo *r)
896 struct dcesrv_handle *h;
897 struct samr_domain_state *d_state;
898 struct ldb_message *msg;
900 struct ldb_context *sam_ctx;
902 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
905 sam_ctx = d_state->sam_ctx;
907 msg = ldb_msg_new(mem_ctx);
909 return NT_STATUS_NO_MEMORY;
912 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
914 return NT_STATUS_NO_MEMORY;
917 switch (r->in.level) {
919 SET_UINT (msg, info1.min_password_length, "minPwdLength");
920 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
921 SET_UINT (msg, info1.password_properties, "pwdProperties");
922 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
923 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
926 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
929 SET_STRING(msg, oem.oem_information, "oEMInformation");
935 /* No op, we don't know where to set these */
940 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
941 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
942 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
946 /* many info classes are not valid for SetDomainInfo */
947 return NT_STATUS_INVALID_INFO_CLASS;
950 /* modify the samdb record */
951 ret = ldb_modify(sam_ctx, msg);
952 if (ret != LDB_SUCCESS) {
953 DEBUG(1,("Failed to modify record %s: %s\n",
954 ldb_dn_get_linearized(d_state->domain_dn),
955 ldb_errstring(sam_ctx)));
957 /* we really need samdb.c to return NTSTATUS */
958 return NT_STATUS_UNSUCCESSFUL;
965 samr_CreateDomainGroup
967 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
968 struct samr_CreateDomainGroup *r)
970 struct samr_domain_state *d_state;
971 struct samr_account_state *a_state;
972 struct dcesrv_handle *h;
974 struct ldb_message *msg;
976 const char *groupname;
977 struct dcesrv_handle *g_handle;
980 ZERO_STRUCTP(r->out.group_handle);
983 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
987 if (d_state->builtin) {
988 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
989 return NT_STATUS_ACCESS_DENIED;
992 groupname = r->in.name->string;
994 if (groupname == NULL) {
995 return NT_STATUS_INVALID_PARAMETER;
998 /* check if the group already exists */
999 name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1001 "(&(sAMAccountName=%s)(objectclass=group))",
1002 ldb_binary_encode_string(mem_ctx, groupname));
1004 return NT_STATUS_GROUP_EXISTS;
1007 msg = ldb_msg_new(mem_ctx);
1009 return NT_STATUS_NO_MEMORY;
1012 /* add core elements to the ldb_message for the user */
1013 msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
1014 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Users", groupname);
1016 return NT_STATUS_NO_MEMORY;
1018 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", groupname);
1019 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
1021 /* create the group */
1022 ret = ldb_add(d_state->sam_ctx, msg);
1026 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1027 DEBUG(0,("Failed to create group record %s: %s\n",
1028 ldb_dn_get_linearized(msg->dn),
1029 ldb_errstring(d_state->sam_ctx)));
1030 return NT_STATUS_GROUP_EXISTS;
1031 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
1032 DEBUG(0,("Failed to create group record %s: %s\n",
1033 ldb_dn_get_linearized(msg->dn),
1034 ldb_errstring(d_state->sam_ctx)));
1035 return NT_STATUS_ACCESS_DENIED;
1037 DEBUG(0,("Failed to create group record %s: %s\n",
1038 ldb_dn_get_linearized(msg->dn),
1039 ldb_errstring(d_state->sam_ctx)));
1040 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1043 a_state = talloc(mem_ctx, struct samr_account_state);
1045 return NT_STATUS_NO_MEMORY;
1047 a_state->sam_ctx = d_state->sam_ctx;
1048 a_state->access_mask = r->in.access_mask;
1049 a_state->domain_state = talloc_reference(a_state, d_state);
1050 a_state->account_dn = talloc_steal(a_state, msg->dn);
1052 /* retrieve the sid for the group just created */
1053 sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
1054 msg->dn, "objectSid", NULL);
1056 return NT_STATUS_UNSUCCESSFUL;
1059 a_state->account_name = talloc_strdup(a_state, groupname);
1060 if (!a_state->account_name) {
1061 return NT_STATUS_NO_MEMORY;
1064 /* create the policy handle */
1065 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1067 return NT_STATUS_NO_MEMORY;
1070 g_handle->data = talloc_steal(g_handle, a_state);
1072 *r->out.group_handle = g_handle->wire_handle;
1073 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1075 return NT_STATUS_OK;
1080 comparison function for sorting SamEntry array
1082 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1084 return e1->idx - e2->idx;
1088 samr_EnumDomainGroups
1090 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1091 struct samr_EnumDomainGroups *r)
1093 struct dcesrv_handle *h;
1094 struct samr_domain_state *d_state;
1095 struct ldb_message **res;
1096 int ldb_cnt, count, i, first;
1097 struct samr_SamEntry *entries;
1098 const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
1099 struct samr_SamArray *sam;
1101 *r->out.resume_handle = 0;
1103 *r->out.num_entries = 0;
1105 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1109 /* search for all domain groups in this domain. This could possibly be
1110 cached and resumed based on resume_key */
1111 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1112 d_state->domain_dn, &res, attrs,
1113 d_state->domain_sid,
1114 "(&(grouptype=%d)(objectclass=group))",
1115 GTYPE_SECURITY_GLOBAL_GROUP);
1116 if (ldb_cnt == -1) {
1117 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1120 /* convert to SamEntry format */
1121 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1123 return NT_STATUS_NO_MEMORY;
1128 for (i=0;i<ldb_cnt;i++) {
1129 struct dom_sid *group_sid;
1131 group_sid = samdb_result_dom_sid(mem_ctx, res[i],
1133 if (group_sid == NULL)
1136 entries[count].idx =
1137 group_sid->sub_auths[group_sid->num_auths-1];
1138 entries[count].name.string =
1139 samdb_result_string(res[i], "sAMAccountName", "");
1143 /* sort the results by rid */
1144 qsort(entries, count, sizeof(struct samr_SamEntry),
1145 (comparison_fn_t)compare_SamEntry);
1147 /* find the first entry to return */
1149 first<count && entries[first].idx <= *r->in.resume_handle;
1152 /* return the rest, limit by max_size. Note that we
1153 use the w2k3 element size value of 54 */
1154 *r->out.num_entries = count - first;
1155 *r->out.num_entries = MIN(*r->out.num_entries,
1156 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1158 sam = talloc(mem_ctx, struct samr_SamArray);
1160 return NT_STATUS_NO_MEMORY;
1163 sam->entries = entries+first;
1164 sam->count = *r->out.num_entries;
1168 if (*r->out.num_entries < count - first) {
1169 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1170 return STATUS_MORE_ENTRIES;
1173 return NT_STATUS_OK;
1180 This call uses transactions to ensure we don't get a new conflicting
1181 user while we are processing this, and to ensure the user either
1182 completly exists, or does not.
1184 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1185 struct samr_CreateUser2 *r)
1187 struct samr_domain_state *d_state;
1188 struct samr_account_state *a_state;
1189 struct dcesrv_handle *h;
1191 struct ldb_message *msg;
1192 struct dom_sid *sid;
1193 const char *account_name;
1194 struct dcesrv_handle *u_handle;
1196 const char *container, *obj_class=NULL;
1200 const char *attrs[] = {
1202 "userAccountControl",
1206 uint32_t user_account_control;
1208 struct ldb_message **msgs;
1210 ZERO_STRUCTP(r->out.user_handle);
1211 *r->out.access_granted = 0;
1214 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1218 if (d_state->builtin) {
1219 DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
1220 return NT_STATUS_ACCESS_DENIED;
1221 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1222 /* Domain trust accounts must be created by the LSA calls */
1223 return NT_STATUS_ACCESS_DENIED;
1225 account_name = r->in.account_name->string;
1227 if (account_name == NULL) {
1228 return NT_STATUS_INVALID_PARAMETER;
1232 * Start a transaction, so we can query and do a subsequent atomic
1236 ret = ldb_transaction_start(d_state->sam_ctx);
1237 if (ret != LDB_SUCCESS) {
1238 DEBUG(0,("Failed to start a transaction for user creation: %s\n",
1239 ldb_errstring(d_state->sam_ctx)));
1240 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1243 /* check if the user already exists */
1244 name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1246 "(&(sAMAccountName=%s)(objectclass=user))",
1247 ldb_binary_encode_string(mem_ctx, account_name));
1249 ldb_transaction_cancel(d_state->sam_ctx);
1250 return NT_STATUS_USER_EXISTS;
1253 msg = ldb_msg_new(mem_ctx);
1255 ldb_transaction_cancel(d_state->sam_ctx);
1256 return NT_STATUS_NO_MEMORY;
1259 cn_name = talloc_strdup(mem_ctx, account_name);
1261 ldb_transaction_cancel(d_state->sam_ctx);
1262 return NT_STATUS_NO_MEMORY;
1265 cn_name_len = strlen(cn_name);
1267 /* This must be one of these values *only* */
1268 if (r->in.acct_flags == ACB_NORMAL) {
1269 container = "CN=Users";
1272 } else if (r->in.acct_flags == ACB_WSTRUST) {
1273 if (cn_name[cn_name_len - 1] != '$') {
1274 ldb_transaction_cancel(d_state->sam_ctx);
1275 return NT_STATUS_FOOBAR;
1277 cn_name[cn_name_len - 1] = '\0';
1278 container = "CN=Computers";
1279 obj_class = "computer";
1280 samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg,
1281 "primaryGroupID", DOMAIN_RID_DOMAIN_MEMBERS);
1283 } else if (r->in.acct_flags == ACB_SVRTRUST) {
1284 if (cn_name[cn_name_len - 1] != '$') {
1285 ldb_transaction_cancel(d_state->sam_ctx);
1286 return NT_STATUS_FOOBAR;
1288 cn_name[cn_name_len - 1] = '\0';
1289 container = "OU=Domain Controllers";
1290 obj_class = "computer";
1291 samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg,
1292 "primaryGroupID", DOMAIN_RID_DCS);
1294 ldb_transaction_cancel(d_state->sam_ctx);
1295 return NT_STATUS_INVALID_PARAMETER;
1298 /* add core elements to the ldb_message for the user */
1299 msg->dn = ldb_dn_copy(msg, d_state->domain_dn);
1300 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s,%s", cn_name, container)) {
1301 ldb_transaction_cancel(d_state->sam_ctx);
1302 return NT_STATUS_FOOBAR;
1305 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName",
1307 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass",
1310 /* create the user */
1311 ret = ldb_add(d_state->sam_ctx, msg);
1315 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1316 ldb_transaction_cancel(d_state->sam_ctx);
1317 DEBUG(0,("Failed to create user record %s: %s\n",
1318 ldb_dn_get_linearized(msg->dn),
1319 ldb_errstring(d_state->sam_ctx)));
1320 return NT_STATUS_USER_EXISTS;
1321 case LDB_ERR_UNWILLING_TO_PERFORM:
1322 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
1323 ldb_transaction_cancel(d_state->sam_ctx);
1324 DEBUG(0,("Failed to create user record %s: %s\n",
1325 ldb_dn_get_linearized(msg->dn),
1326 ldb_errstring(d_state->sam_ctx)));
1327 return NT_STATUS_ACCESS_DENIED;
1329 ldb_transaction_cancel(d_state->sam_ctx);
1330 DEBUG(0,("Failed to create user record %s: %s\n",
1331 ldb_dn_get_linearized(msg->dn),
1332 ldb_errstring(d_state->sam_ctx)));
1333 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1336 a_state = talloc(mem_ctx, struct samr_account_state);
1338 ldb_transaction_cancel(d_state->sam_ctx);
1339 return NT_STATUS_NO_MEMORY;
1341 a_state->sam_ctx = d_state->sam_ctx;
1342 a_state->access_mask = r->in.access_mask;
1343 a_state->domain_state = talloc_reference(a_state, d_state);
1344 a_state->account_dn = talloc_steal(a_state, msg->dn);
1346 /* retrieve the sid and account control bits for the user just created */
1347 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
1348 msg->dn, &msgs, attrs);
1351 ldb_transaction_cancel(d_state->sam_ctx);
1352 DEBUG(0,("Apparently we failed to create an account record, as %s now doesn't exist\n",
1353 ldb_dn_get_linearized(msg->dn)));
1354 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1356 sid = samdb_result_dom_sid(mem_ctx, msgs[0], "objectSid");
1358 ldb_transaction_cancel(d_state->sam_ctx);
1359 DEBUG(0,("Apparently we failed to get the objectSid of the just created account record %s\n",
1360 ldb_dn_get_linearized(msg->dn)));
1361 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1364 /* Change the account control to be the correct account type.
1365 * The default is for a workstation account */
1366 user_account_control = samdb_result_uint(msgs[0], "userAccountControl", 0);
1367 user_account_control = (user_account_control &
1368 ~(UF_NORMAL_ACCOUNT |
1369 UF_INTERDOMAIN_TRUST_ACCOUNT |
1370 UF_WORKSTATION_TRUST_ACCOUNT |
1371 UF_SERVER_TRUST_ACCOUNT));
1372 user_account_control |= ds_acb2uf(r->in.acct_flags);
1375 msg = ldb_msg_new(mem_ctx);
1377 ldb_transaction_cancel(d_state->sam_ctx);
1378 return NT_STATUS_NO_MEMORY;
1381 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
1383 if (samdb_msg_add_uint(a_state->sam_ctx, mem_ctx, msg,
1384 "userAccountControl",
1385 user_account_control) != 0) {
1386 ldb_transaction_cancel(d_state->sam_ctx);
1387 return NT_STATUS_NO_MEMORY;
1390 /* modify the samdb record */
1391 ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
1392 if (ret != LDB_SUCCESS) {
1393 DEBUG(0,("Failed to modify account record %s to set userAccountControl: %s\n",
1394 ldb_dn_get_linearized(msg->dn),
1395 ldb_errstring(d_state->sam_ctx)));
1396 ldb_transaction_cancel(d_state->sam_ctx);
1398 /* we really need samdb.c to return NTSTATUS */
1399 return NT_STATUS_UNSUCCESSFUL;
1402 ret = ldb_transaction_commit(d_state->sam_ctx);
1403 if (ret != LDB_SUCCESS) {
1404 DEBUG(0,("Failed to commit transaction to add and modify account record %s: %s\n",
1405 ldb_dn_get_linearized(msg->dn),
1406 ldb_errstring(d_state->sam_ctx)));
1407 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1410 a_state->account_name = talloc_steal(a_state, account_name);
1411 if (!a_state->account_name) {
1412 return NT_STATUS_NO_MEMORY;
1415 /* create the policy handle */
1416 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
1418 return NT_STATUS_NO_MEMORY;
1421 u_handle->data = talloc_steal(u_handle, a_state);
1423 *r->out.user_handle = u_handle->wire_handle;
1424 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1426 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1428 return NT_STATUS_OK;
1435 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1436 struct samr_CreateUser *r)
1438 struct samr_CreateUser2 r2;
1439 uint32_t access_granted = 0;
1442 /* a simple wrapper around samr_CreateUser2 works nicely */
1443 r2.in.domain_handle = r->in.domain_handle;
1444 r2.in.account_name = r->in.account_name;
1445 r2.in.acct_flags = ACB_NORMAL;
1446 r2.in.access_mask = r->in.access_mask;
1447 r2.out.user_handle = r->out.user_handle;
1448 r2.out.access_granted = &access_granted;
1449 r2.out.rid = r->out.rid;
1451 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1455 samr_EnumDomainUsers
1457 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1458 struct samr_EnumDomainUsers *r)
1460 struct dcesrv_handle *h;
1461 struct samr_domain_state *d_state;
1462 struct ldb_result *res;
1463 int ret, num_filtered_entries, i, first;
1464 struct samr_SamEntry *entries;
1465 const char * const attrs[] = { "objectSid", "sAMAccountName",
1466 "userAccountControl", NULL };
1467 struct samr_SamArray *sam;
1469 *r->out.resume_handle = 0;
1471 *r->out.num_entries = 0;
1473 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1477 /* don't have to worry about users in the builtin domain, as there are none */
1478 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res, d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs, "objectClass=user");
1480 if (ret != LDB_SUCCESS) {
1481 DEBUG(3, ("Failed to search for Domain Users in %s: %s\n",
1482 ldb_dn_get_linearized(d_state->domain_dn), ldb_errstring(d_state->sam_ctx)));
1483 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1486 /* convert to SamEntry format */
1487 entries = talloc_array(mem_ctx, struct samr_SamEntry, res->count);
1489 return NT_STATUS_NO_MEMORY;
1491 num_filtered_entries = 0;
1492 for (i=0;i<res->count;i++) {
1493 /* Check if a mask has been requested */
1494 if (r->in.acct_flags
1495 && ((samdb_result_acct_flags(d_state->sam_ctx, mem_ctx, res->msgs[i],
1496 d_state->domain_dn) & r->in.acct_flags) == 0)) {
1499 entries[num_filtered_entries].idx = samdb_result_rid_from_sid(mem_ctx, res->msgs[i], "objectSid", 0);
1500 entries[num_filtered_entries].name.string = samdb_result_string(res->msgs[i], "sAMAccountName", "");
1501 num_filtered_entries++;
1504 /* sort the results by rid */
1505 qsort(entries, num_filtered_entries, sizeof(struct samr_SamEntry),
1506 (comparison_fn_t)compare_SamEntry);
1508 /* find the first entry to return */
1510 first<num_filtered_entries && entries[first].idx <= *r->in.resume_handle;
1513 /* return the rest, limit by max_size. Note that we
1514 use the w2k3 element size value of 54 */
1515 *r->out.num_entries = num_filtered_entries - first;
1516 *r->out.num_entries = MIN(*r->out.num_entries,
1517 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1519 sam = talloc(mem_ctx, struct samr_SamArray);
1521 return NT_STATUS_NO_MEMORY;
1524 sam->entries = entries+first;
1525 sam->count = *r->out.num_entries;
1529 if (first == num_filtered_entries) {
1530 return NT_STATUS_OK;
1533 if (*r->out.num_entries < num_filtered_entries - first) {
1534 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1535 return STATUS_MORE_ENTRIES;
1538 return NT_STATUS_OK;
1545 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1546 struct samr_CreateDomAlias *r)
1548 struct samr_domain_state *d_state;
1549 struct samr_account_state *a_state;
1550 struct dcesrv_handle *h;
1551 const char *alias_name, *name;
1552 struct ldb_message *msg;
1553 struct dom_sid *sid;
1554 struct dcesrv_handle *a_handle;
1557 ZERO_STRUCTP(r->out.alias_handle);
1560 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1564 if (d_state->builtin) {
1565 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1566 return NT_STATUS_ACCESS_DENIED;
1569 alias_name = r->in.alias_name->string;
1571 if (alias_name == NULL) {
1572 return NT_STATUS_INVALID_PARAMETER;
1575 /* Check if alias already exists */
1576 name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1578 "(sAMAccountName=%s)(objectclass=group))",
1579 ldb_binary_encode_string(mem_ctx, alias_name));
1582 return NT_STATUS_ALIAS_EXISTS;
1585 msg = ldb_msg_new(mem_ctx);
1587 return NT_STATUS_NO_MEMORY;
1590 /* add core elements to the ldb_message for the alias */
1591 msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
1592 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Users", alias_name);
1594 return NT_STATUS_NO_MEMORY;
1597 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", alias_name);
1598 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
1599 samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "groupType", GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1601 /* create the alias */
1602 ret = ldb_add(d_state->sam_ctx, msg);
1606 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1607 return NT_STATUS_ALIAS_EXISTS;
1608 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
1609 return NT_STATUS_ACCESS_DENIED;
1611 DEBUG(0,("Failed to create alias record %s: %s\n",
1612 ldb_dn_get_linearized(msg->dn),
1613 ldb_errstring(d_state->sam_ctx)));
1614 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1617 a_state = talloc(mem_ctx, struct samr_account_state);
1619 return NT_STATUS_NO_MEMORY;
1622 a_state->sam_ctx = d_state->sam_ctx;
1623 a_state->access_mask = r->in.access_mask;
1624 a_state->domain_state = talloc_reference(a_state, d_state);
1625 a_state->account_dn = talloc_steal(a_state, msg->dn);
1627 /* retrieve the sid for the alias just created */
1628 sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
1629 msg->dn, "objectSid", NULL);
1631 a_state->account_name = talloc_strdup(a_state, alias_name);
1632 if (!a_state->account_name) {
1633 return NT_STATUS_NO_MEMORY;
1636 /* create the policy handle */
1637 a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1638 if (a_handle == NULL)
1639 return NT_STATUS_NO_MEMORY;
1641 a_handle->data = talloc_steal(a_handle, a_state);
1643 *r->out.alias_handle = a_handle->wire_handle;
1645 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1647 return NT_STATUS_OK;
1652 samr_EnumDomainAliases
1654 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1655 struct samr_EnumDomainAliases *r)
1657 struct dcesrv_handle *h;
1658 struct samr_domain_state *d_state;
1659 struct ldb_message **res;
1660 int ldb_cnt, count, i, first;
1661 struct samr_SamEntry *entries;
1662 const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
1663 struct samr_SamArray *sam;
1665 *r->out.resume_handle = 0;
1667 *r->out.num_entries = 0;
1669 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1673 /* search for all domain groups in this domain. This could possibly be
1674 cached and resumed based on resume_key */
1675 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1678 d_state->domain_sid,
1679 "(&(|(grouptype=%d)(grouptype=%d)))"
1680 "(objectclass=group))",
1681 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1682 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1683 if (ldb_cnt == -1) {
1684 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1687 return NT_STATUS_OK;
1690 /* convert to SamEntry format */
1691 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1693 return NT_STATUS_NO_MEMORY;
1698 for (i=0;i<ldb_cnt;i++) {
1699 struct dom_sid *alias_sid;
1701 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1704 if (alias_sid == NULL)
1707 entries[count].idx =
1708 alias_sid->sub_auths[alias_sid->num_auths-1];
1709 entries[count].name.string =
1710 samdb_result_string(res[i], "sAMAccountName", "");
1714 /* sort the results by rid */
1715 qsort(entries, count, sizeof(struct samr_SamEntry),
1716 (comparison_fn_t)compare_SamEntry);
1718 /* find the first entry to return */
1720 first<count && entries[first].idx <= *r->in.resume_handle;
1723 if (first == count) {
1724 return NT_STATUS_OK;
1727 *r->out.num_entries = count - first;
1728 *r->out.num_entries = MIN(*r->out.num_entries, 1000);
1730 sam = talloc(mem_ctx, struct samr_SamArray);
1732 return NT_STATUS_NO_MEMORY;
1735 sam->entries = entries+first;
1736 sam->count = *r->out.num_entries;
1740 if (*r->out.num_entries < count - first) {
1741 *r->out.resume_handle =
1742 entries[first+*r->out.num_entries-1].idx;
1743 return STATUS_MORE_ENTRIES;
1746 return NT_STATUS_OK;
1751 samr_GetAliasMembership
1753 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1754 struct samr_GetAliasMembership *r)
1756 struct dcesrv_handle *h;
1757 struct samr_domain_state *d_state;
1758 struct ldb_message **res;
1761 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1765 if (r->in.sids->num_sids > 0) {
1767 const char * const attrs[2] = { "objectSid", NULL };
1769 filter = talloc_asprintf(mem_ctx,
1770 "(&(|(grouptype=%d)(grouptype=%d))"
1771 "(objectclass=group)(|",
1772 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1773 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1775 return NT_STATUS_NO_MEMORY;
1777 for (i=0; i<r->in.sids->num_sids; i++) {
1778 const char *memberdn;
1781 samdb_search_string(d_state->sam_ctx,
1783 "distinguishedName",
1785 ldap_encode_ndr_dom_sid(mem_ctx,
1786 r->in.sids->sids[i].sid));
1788 if (memberdn == NULL)
1791 filter = talloc_asprintf(mem_ctx, "%s(member=%s)",
1794 return NT_STATUS_NO_MEMORY;
1797 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1798 d_state->domain_dn, &res, attrs,
1799 d_state->domain_sid, "%s))", filter);
1801 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1804 r->out.rids->count = 0;
1805 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1806 if (r->out.rids->ids == NULL)
1807 return NT_STATUS_NO_MEMORY;
1809 for (i=0; i<count; i++) {
1810 struct dom_sid *alias_sid;
1812 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1814 if (alias_sid == NULL) {
1815 DEBUG(0, ("Could not find objectSid\n"));
1819 r->out.rids->ids[r->out.rids->count] =
1820 alias_sid->sub_auths[alias_sid->num_auths-1];
1821 r->out.rids->count += 1;
1824 return NT_STATUS_OK;
1831 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1832 struct samr_LookupNames *r)
1834 struct dcesrv_handle *h;
1835 struct samr_domain_state *d_state;
1837 NTSTATUS status = NT_STATUS_OK;
1838 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1841 ZERO_STRUCTP(r->out.rids);
1842 ZERO_STRUCTP(r->out.types);
1844 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1848 if (r->in.num_names == 0) {
1849 return NT_STATUS_OK;
1852 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1853 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1854 if (!r->out.rids->ids || !r->out.types->ids) {
1855 return NT_STATUS_NO_MEMORY;
1857 r->out.rids->count = r->in.num_names;
1858 r->out.types->count = r->in.num_names;
1862 for (i=0;i<r->in.num_names;i++) {
1863 struct ldb_message **res;
1864 struct dom_sid *sid;
1865 uint32_t atype, rtype;
1867 r->out.rids->ids[i] = 0;
1868 r->out.types->ids[i] = SID_NAME_UNKNOWN;
1870 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1871 "sAMAccountName=%s",
1872 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
1874 status = STATUS_SOME_UNMAPPED;
1878 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
1880 status = STATUS_SOME_UNMAPPED;
1884 atype = samdb_result_uint(res[0], "sAMAccountType", 0);
1886 status = STATUS_SOME_UNMAPPED;
1890 rtype = ds_atype_map(atype);
1892 if (rtype == SID_NAME_UNKNOWN) {
1893 status = STATUS_SOME_UNMAPPED;
1897 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
1898 r->out.types->ids[i] = rtype;
1902 if (num_mapped == 0) {
1903 return NT_STATUS_NONE_MAPPED;
1912 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1913 struct samr_LookupRids *r)
1915 struct dcesrv_handle *h;
1916 struct samr_domain_state *d_state;
1918 NTSTATUS status = NT_STATUS_OK;
1919 struct lsa_String *names;
1922 ZERO_STRUCTP(r->out.names);
1923 ZERO_STRUCTP(r->out.types);
1925 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1929 if (r->in.num_rids == 0)
1930 return NT_STATUS_OK;
1932 names = talloc_array(mem_ctx, struct lsa_String, r->in.num_rids);
1933 ids = talloc_array(mem_ctx, uint32_t, r->in.num_rids);
1935 if ((names == NULL) || (ids == NULL))
1936 return NT_STATUS_NO_MEMORY;
1940 for (i=0; i<r->in.num_rids; i++) {
1941 struct ldb_message **res;
1943 const char * const attrs[] = { "sAMAccountType",
1944 "sAMAccountName", NULL };
1946 struct dom_sid *sid;
1948 ids[i] = SID_NAME_UNKNOWN;
1950 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid,
1953 names[i].string = NULL;
1954 status = STATUS_SOME_UNMAPPED;
1958 count = gendb_search(d_state->sam_ctx, mem_ctx,
1959 d_state->domain_dn, &res, attrs,
1961 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1963 names[i].string = NULL;
1964 status = STATUS_SOME_UNMAPPED;
1968 names[i].string = samdb_result_string(res[0], "sAMAccountName",
1971 atype = samdb_result_uint(res[0], "sAMAccountType", 0);
1973 status = STATUS_SOME_UNMAPPED;
1977 ids[i] = ds_atype_map(atype);
1979 if (ids[i] == SID_NAME_UNKNOWN) {
1980 status = STATUS_SOME_UNMAPPED;
1985 r->out.names->names = names;
1986 r->out.names->count = r->in.num_rids;
1988 r->out.types->ids = ids;
1989 r->out.types->count = r->in.num_rids;
1998 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1999 struct samr_OpenGroup *r)
2001 struct samr_domain_state *d_state;
2002 struct samr_account_state *a_state;
2003 struct dcesrv_handle *h;
2004 const char *groupname;
2005 struct dom_sid *sid;
2006 struct ldb_message **msgs;
2007 struct dcesrv_handle *g_handle;
2008 const char * const attrs[2] = { "sAMAccountName", NULL };
2011 ZERO_STRUCTP(r->out.group_handle);
2013 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2017 /* form the group SID */
2018 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2020 return NT_STATUS_NO_MEMORY;
2023 /* search for the group record */
2024 ret = gendb_search(d_state->sam_ctx,
2025 mem_ctx, d_state->domain_dn, &msgs, attrs,
2026 "(&(objectSid=%s)(objectclass=group)"
2028 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2029 GTYPE_SECURITY_GLOBAL_GROUP);
2031 return NT_STATUS_NO_SUCH_GROUP;
2034 DEBUG(0,("Found %d records matching sid %s\n",
2035 ret, dom_sid_string(mem_ctx, sid)));
2036 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2039 groupname = samdb_result_string(msgs[0], "sAMAccountName", NULL);
2040 if (groupname == NULL) {
2041 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2042 dom_sid_string(mem_ctx, sid)));
2043 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2046 a_state = talloc(mem_ctx, struct samr_account_state);
2048 return NT_STATUS_NO_MEMORY;
2050 a_state->sam_ctx = d_state->sam_ctx;
2051 a_state->access_mask = r->in.access_mask;
2052 a_state->domain_state = talloc_reference(a_state, d_state);
2053 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2054 a_state->account_sid = talloc_steal(a_state, sid);
2055 a_state->account_name = talloc_strdup(a_state, groupname);
2056 if (!a_state->account_name) {
2057 return NT_STATUS_NO_MEMORY;
2060 /* create the policy handle */
2061 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
2063 return NT_STATUS_NO_MEMORY;
2066 g_handle->data = talloc_steal(g_handle, a_state);
2068 *r->out.group_handle = g_handle->wire_handle;
2070 return NT_STATUS_OK;
2076 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2077 struct samr_QueryGroupInfo *r)
2079 struct dcesrv_handle *h;
2080 struct samr_account_state *a_state;
2081 struct ldb_message *msg;
2082 struct ldb_result *res;
2083 const char * const attrs[4] = { "sAMAccountName", "description",
2084 "numMembers", NULL };
2086 union samr_GroupInfo *info;
2088 *r->out.info = NULL;
2090 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2094 ret = ldb_search(a_state->sam_ctx, mem_ctx, &res, a_state->account_dn,
2095 LDB_SCOPE_SUBTREE, attrs, "objectClass=*");
2097 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
2098 return NT_STATUS_NO_SUCH_GROUP;
2099 } else if (ret != LDB_SUCCESS) {
2100 DEBUG(2, ("Error reading group info: %s\n", ldb_errstring(a_state->sam_ctx)));
2101 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2104 if (res->count != 1) {
2105 DEBUG(2, ("Error finding group info, got %d entries\n", res->count));
2107 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2111 /* allocate the info structure */
2112 info = talloc_zero(mem_ctx, union samr_GroupInfo);
2114 return NT_STATUS_NO_MEMORY;
2117 /* Fill in the level */
2118 switch (r->in.level) {
2120 QUERY_STRING(msg, all.name, "sAMAccountName");
2121 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2122 QUERY_UINT (msg, all.num_members, "numMembers")
2123 QUERY_STRING(msg, all.description, "description");
2126 QUERY_STRING(msg, name, "sAMAccountName");
2128 case GROUPINFOATTRIBUTES:
2129 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2131 case GROUPINFODESCRIPTION:
2132 QUERY_STRING(msg, description, "description");
2135 QUERY_STRING(msg, all2.name, "sAMAccountName");
2136 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2137 QUERY_UINT (msg, all2.num_members, "numMembers")
2138 QUERY_STRING(msg, all2.description, "description");
2142 return NT_STATUS_INVALID_INFO_CLASS;
2145 *r->out.info = info;
2147 return NT_STATUS_OK;
2154 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2155 struct samr_SetGroupInfo *r)
2157 struct dcesrv_handle *h;
2158 struct samr_account_state *g_state;
2159 struct ldb_message *msg;
2160 struct ldb_context *sam_ctx;
2163 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2166 sam_ctx = g_state->sam_ctx;
2168 msg = ldb_msg_new(mem_ctx);
2170 return NT_STATUS_NO_MEMORY;
2173 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2175 return NT_STATUS_NO_MEMORY;
2178 switch (r->in.level) {
2179 case GROUPINFODESCRIPTION:
2180 SET_STRING(msg, description, "description");
2183 /* On W2k3 this does not change the name, it changes the
2184 * sAMAccountName attribute */
2185 SET_STRING(msg, name, "sAMAccountName");
2187 case GROUPINFOATTRIBUTES:
2188 /* This does not do anything obviously visible in W2k3 LDAP */
2189 return NT_STATUS_OK;
2191 return NT_STATUS_INVALID_INFO_CLASS;
2194 /* modify the samdb record */
2195 ret = ldb_modify(g_state->sam_ctx, msg);
2196 if (ret != LDB_SUCCESS) {
2197 /* we really need samdb.c to return NTSTATUS */
2198 return NT_STATUS_UNSUCCESSFUL;
2201 return NT_STATUS_OK;
2208 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2209 struct samr_AddGroupMember *r)
2211 struct dcesrv_handle *h;
2212 struct samr_account_state *a_state;
2213 struct samr_domain_state *d_state;
2214 struct ldb_message *mod;
2215 struct dom_sid *membersid;
2216 const char *memberdn;
2217 struct ldb_result *res;
2218 const char * const attrs[] = { NULL };
2221 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2224 d_state = a_state->domain_state;
2226 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2227 if (membersid == NULL) {
2228 return NT_STATUS_NO_MEMORY;
2231 /* In native mode, AD can also nest domain groups. Not sure yet
2232 * whether this is also available via RPC. */
2233 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2234 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2235 "(&(objectSid=%s)(objectclass=user))",
2236 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2238 if (ret != LDB_SUCCESS) {
2239 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2242 if (res->count == 0) {
2243 return NT_STATUS_NO_SUCH_USER;
2246 if (res->count > 1) {
2247 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2250 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2252 if (memberdn == NULL)
2253 return NT_STATUS_NO_MEMORY;
2255 mod = ldb_msg_new(mem_ctx);
2257 return NT_STATUS_NO_MEMORY;
2260 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2262 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2264 if (ret != LDB_SUCCESS) {
2265 return NT_STATUS_UNSUCCESSFUL;
2268 ret = ldb_modify(a_state->sam_ctx, mod);
2271 return NT_STATUS_OK;
2272 case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
2273 return NT_STATUS_MEMBER_IN_GROUP;
2274 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2275 return NT_STATUS_ACCESS_DENIED;
2277 return NT_STATUS_UNSUCCESSFUL;
2283 samr_DeleteDomainGroup
2285 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2286 struct samr_DeleteDomainGroup *r)
2288 struct dcesrv_handle *h;
2289 struct samr_account_state *a_state;
2292 *r->out.group_handle = *r->in.group_handle;
2294 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2298 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2299 if (ret != LDB_SUCCESS) {
2300 return NT_STATUS_UNSUCCESSFUL;
2304 ZERO_STRUCTP(r->out.group_handle);
2306 return NT_STATUS_OK;
2311 samr_DeleteGroupMember
2313 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2314 struct samr_DeleteGroupMember *r)
2316 struct dcesrv_handle *h;
2317 struct samr_account_state *a_state;
2318 struct samr_domain_state *d_state;
2319 struct ldb_message *mod;
2320 struct dom_sid *membersid;
2321 const char *memberdn;
2322 struct ldb_result *res;
2323 const char * const attrs[] = { NULL };
2326 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2329 d_state = a_state->domain_state;
2331 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2332 if (membersid == NULL)
2333 return NT_STATUS_NO_MEMORY;
2335 /* In native mode, AD can also nest domain groups. Not sure yet
2336 * whether this is also available via RPC. */
2337 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2338 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2339 "(&(objectSid=%s)(objectclass=user))",
2340 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2342 if (ret != LDB_SUCCESS) {
2343 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2346 if (res->count == 0) {
2347 return NT_STATUS_NO_SUCH_USER;
2350 if (res->count > 1) {
2351 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2354 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2356 if (memberdn == NULL)
2357 return NT_STATUS_NO_MEMORY;
2359 mod = ldb_msg_new(mem_ctx);
2361 return NT_STATUS_NO_MEMORY;
2364 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2366 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2368 if (ret != LDB_SUCCESS) {
2369 return NT_STATUS_NO_MEMORY;
2372 ret = ldb_modify(a_state->sam_ctx, mod);
2375 return NT_STATUS_OK;
2376 case LDB_ERR_NO_SUCH_ATTRIBUTE:
2377 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2378 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2379 return NT_STATUS_ACCESS_DENIED;
2381 return NT_STATUS_UNSUCCESSFUL;
2387 samr_QueryGroupMember
2389 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2390 struct samr_QueryGroupMember *r)
2392 struct dcesrv_handle *h;
2393 struct samr_account_state *a_state;
2394 struct ldb_message **res;
2395 struct ldb_message_element *el;
2396 struct samr_RidTypeArray *array;
2397 const char * const attrs[2] = { "member", NULL };
2400 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2404 /* pull the member attribute */
2405 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2406 a_state->account_dn, &res, attrs);
2409 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2412 array = talloc(mem_ctx, struct samr_RidTypeArray);
2415 return NT_STATUS_NO_MEMORY;
2417 ZERO_STRUCTP(array);
2419 el = ldb_msg_find_element(res[0], "member");
2424 array->count = el->num_values;
2426 array->rids = talloc_array(mem_ctx, uint32_t,
2428 if (array->rids == NULL)
2429 return NT_STATUS_NO_MEMORY;
2431 array->types = talloc_array(mem_ctx, uint32_t,
2433 if (array->types == NULL)
2434 return NT_STATUS_NO_MEMORY;
2436 for (i=0; i<el->num_values; i++) {
2437 struct ldb_message **res2;
2438 const char * const attrs2[2] = { "objectSid", NULL };
2439 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2440 ldb_dn_from_ldb_val(mem_ctx, a_state->sam_ctx, &el->values[i]),
2443 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2446 samdb_result_rid_from_sid(mem_ctx, res2[0],
2449 if (array->rids[i] == 0)
2450 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2452 array->types[i] = 7; /* RID type of some kind, not sure what the value means. */
2456 *r->out.rids = array;
2458 return NT_STATUS_OK;
2463 samr_SetMemberAttributesOfGroup
2465 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2466 struct samr_SetMemberAttributesOfGroup *r)
2468 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2475 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2476 struct samr_OpenAlias *r)
2478 struct samr_domain_state *d_state;
2479 struct samr_account_state *a_state;
2480 struct dcesrv_handle *h;
2481 const char *alias_name;
2482 struct dom_sid *sid;
2483 struct ldb_message **msgs;
2484 struct dcesrv_handle *g_handle;
2485 const char * const attrs[2] = { "sAMAccountName", NULL };
2488 ZERO_STRUCTP(r->out.alias_handle);
2490 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2494 /* form the alias SID */
2495 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2497 return NT_STATUS_NO_MEMORY;
2499 /* search for the group record */
2500 ret = gendb_search(d_state->sam_ctx,
2501 mem_ctx, d_state->domain_dn, &msgs, attrs,
2502 "(&(objectSid=%s)(objectclass=group)"
2503 "(|(grouptype=%d)(grouptype=%d)))",
2504 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2505 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2506 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2508 return NT_STATUS_NO_SUCH_ALIAS;
2511 DEBUG(0,("Found %d records matching sid %s\n",
2512 ret, dom_sid_string(mem_ctx, sid)));
2513 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2516 alias_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
2517 if (alias_name == NULL) {
2518 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2519 dom_sid_string(mem_ctx, sid)));
2520 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2523 a_state = talloc(mem_ctx, struct samr_account_state);
2525 return NT_STATUS_NO_MEMORY;
2527 a_state->sam_ctx = d_state->sam_ctx;
2528 a_state->access_mask = r->in.access_mask;
2529 a_state->domain_state = talloc_reference(a_state, d_state);
2530 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2531 a_state->account_sid = talloc_steal(a_state, sid);
2532 a_state->account_name = talloc_strdup(a_state, alias_name);
2533 if (!a_state->account_name) {
2534 return NT_STATUS_NO_MEMORY;
2537 /* create the policy handle */
2538 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2540 return NT_STATUS_NO_MEMORY;
2543 g_handle->data = talloc_steal(g_handle, a_state);
2545 *r->out.alias_handle = g_handle->wire_handle;
2547 return NT_STATUS_OK;
2554 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2555 struct samr_QueryAliasInfo *r)
2557 struct dcesrv_handle *h;
2558 struct samr_account_state *a_state;
2559 struct ldb_message *msg, **res;
2560 const char * const attrs[4] = { "sAMAccountName", "description",
2561 "numMembers", NULL };
2563 union samr_AliasInfo *info;
2565 *r->out.info = NULL;
2567 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2571 /* pull all the alias attributes */
2572 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2573 a_state->account_dn ,&res, attrs);
2575 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2579 /* allocate the info structure */
2580 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2582 return NT_STATUS_NO_MEMORY;
2585 switch(r->in.level) {
2587 QUERY_STRING(msg, all.name, "sAMAccountName");
2588 QUERY_UINT (msg, all.num_members, "numMembers");
2589 QUERY_STRING(msg, all.description, "description");
2592 QUERY_STRING(msg, name, "sAMAccountName");
2594 case ALIASINFODESCRIPTION:
2595 QUERY_STRING(msg, description, "description");
2599 return NT_STATUS_INVALID_INFO_CLASS;
2602 *r->out.info = info;
2604 return NT_STATUS_OK;
2611 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2612 struct samr_SetAliasInfo *r)
2614 struct dcesrv_handle *h;
2615 struct samr_account_state *a_state;
2616 struct ldb_message *msg;
2617 struct ldb_context *sam_ctx;
2620 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2623 sam_ctx = a_state->sam_ctx;
2625 msg = ldb_msg_new(mem_ctx);
2627 return NT_STATUS_NO_MEMORY;
2630 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2632 return NT_STATUS_NO_MEMORY;
2635 switch (r->in.level) {
2636 case ALIASINFODESCRIPTION:
2637 SET_STRING(msg, description, "description");
2640 /* On W2k3 this does not change the name, it changes the
2641 * sAMAccountName attribute */
2642 SET_STRING(msg, name, "sAMAccountName");
2645 return NT_STATUS_INVALID_INFO_CLASS;
2648 /* modify the samdb record */
2649 ret = ldb_modify(a_state->sam_ctx, msg);
2650 if (ret != LDB_SUCCESS) {
2651 /* we really need samdb.c to return NTSTATUS */
2652 return NT_STATUS_UNSUCCESSFUL;
2655 return NT_STATUS_OK;
2662 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2663 struct samr_DeleteDomAlias *r)
2665 struct dcesrv_handle *h;
2666 struct samr_account_state *a_state;
2669 *r->out.alias_handle = *r->in.alias_handle;
2671 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2675 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2677 return NT_STATUS_UNSUCCESSFUL;
2681 ZERO_STRUCTP(r->out.alias_handle);
2683 return NT_STATUS_OK;
2690 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2691 struct samr_AddAliasMember *r)
2693 struct dcesrv_handle *h;
2694 struct samr_account_state *a_state;
2695 struct samr_domain_state *d_state;
2696 struct ldb_message *mod;
2697 struct ldb_message **msgs;
2698 const char * const attrs[] = { NULL };
2699 struct ldb_dn *memberdn = NULL;
2703 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2706 d_state = a_state->domain_state;
2708 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2709 &msgs, attrs, "(objectsid=%s)",
2710 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2713 memberdn = msgs[0]->dn;
2714 } else if (ret > 1) {
2715 DEBUG(0,("Found %d records matching sid %s\n",
2716 ret, dom_sid_string(mem_ctx, r->in.sid)));
2717 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2718 } else if (ret == 0) {
2719 status = samdb_create_foreign_security_principal(
2720 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2721 if (!NT_STATUS_IS_OK(status)) {
2725 DEBUG(0, ("samdb_search returned %d: %s\n", ret, ldb_errstring(d_state->sam_ctx)));
2728 if (memberdn == NULL) {
2729 DEBUG(0, ("Could not find memberdn\n"));
2730 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2733 mod = ldb_msg_new(mem_ctx);
2735 return NT_STATUS_NO_MEMORY;
2738 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2740 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2741 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2742 if (ret != LDB_SUCCESS) {
2743 return NT_STATUS_UNSUCCESSFUL;
2746 if (ldb_modify(a_state->sam_ctx, mod) != LDB_SUCCESS) {
2747 return NT_STATUS_UNSUCCESSFUL;
2750 return NT_STATUS_OK;
2755 samr_DeleteAliasMember
2757 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2758 struct samr_DeleteAliasMember *r)
2760 struct dcesrv_handle *h;
2761 struct samr_account_state *a_state;
2762 struct samr_domain_state *d_state;
2763 struct ldb_message *mod;
2764 const char *memberdn;
2767 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2770 d_state = a_state->domain_state;
2772 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2773 "distinguishedName", "(objectSid=%s)",
2774 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2776 if (memberdn == NULL)
2777 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2779 mod = ldb_msg_new(mem_ctx);
2781 return NT_STATUS_NO_MEMORY;
2784 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2786 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2788 if (ret != LDB_SUCCESS)
2789 return NT_STATUS_UNSUCCESSFUL;
2791 if (ldb_modify(a_state->sam_ctx, mod) != LDB_SUCCESS)
2792 return NT_STATUS_UNSUCCESSFUL;
2794 return NT_STATUS_OK;
2799 samr_GetMembersInAlias
2801 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2802 struct samr_GetMembersInAlias *r)
2804 struct dcesrv_handle *h;
2805 struct samr_account_state *a_state;
2806 struct samr_domain_state *d_state;
2807 struct ldb_message **msgs;
2808 struct lsa_SidPtr *sids;
2809 struct ldb_message_element *el;
2810 const char * const attrs[2] = { "member", NULL};
2813 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2816 d_state = a_state->domain_state;
2818 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
2819 a_state->account_dn, &msgs, attrs);
2822 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2823 } else if (ret == 0) {
2824 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2825 } else if (ret != 1) {
2826 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2829 r->out.sids->num_sids = 0;
2830 r->out.sids->sids = NULL;
2832 el = ldb_msg_find_element(msgs[0], "member");
2837 sids = talloc_array(mem_ctx, struct lsa_SidPtr,
2841 return NT_STATUS_NO_MEMORY;
2843 for (i=0; i<el->num_values; i++) {
2844 struct ldb_message **msgs2;
2845 const char * const attrs2[2] = { "objectSid", NULL };
2846 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2847 ldb_dn_from_ldb_val(mem_ctx, a_state->sam_ctx, &el->values[i]),
2850 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2852 sids[i].sid = samdb_result_dom_sid(mem_ctx, msgs2[0],
2855 if (sids[i].sid == NULL)
2856 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2858 r->out.sids->num_sids = el->num_values;
2859 r->out.sids->sids = sids;
2862 return NT_STATUS_OK;
2868 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2869 struct samr_OpenUser *r)
2871 struct samr_domain_state *d_state;
2872 struct samr_account_state *a_state;
2873 struct dcesrv_handle *h;
2874 const char *account_name;
2875 struct dom_sid *sid;
2876 struct ldb_message **msgs;
2877 struct dcesrv_handle *u_handle;
2878 const char * const attrs[2] = { "sAMAccountName", NULL };
2881 ZERO_STRUCTP(r->out.user_handle);
2883 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2887 /* form the users SID */
2888 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2890 return NT_STATUS_NO_MEMORY;
2893 /* search for the user record */
2894 ret = gendb_search(d_state->sam_ctx,
2895 mem_ctx, d_state->domain_dn, &msgs, attrs,
2896 "(&(objectSid=%s)(objectclass=user))",
2897 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2899 return NT_STATUS_NO_SUCH_USER;
2902 DEBUG(0,("Found %d records matching sid %s\n", ret,
2903 dom_sid_string(mem_ctx, sid)));
2904 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2907 account_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
2908 if (account_name == NULL) {
2909 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2910 dom_sid_string(mem_ctx, sid)));
2911 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2914 a_state = talloc(mem_ctx, struct samr_account_state);
2916 return NT_STATUS_NO_MEMORY;
2918 a_state->sam_ctx = d_state->sam_ctx;
2919 a_state->access_mask = r->in.access_mask;
2920 a_state->domain_state = talloc_reference(a_state, d_state);
2921 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2922 a_state->account_sid = talloc_steal(a_state, sid);
2923 a_state->account_name = talloc_strdup(a_state, account_name);
2924 if (!a_state->account_name) {
2925 return NT_STATUS_NO_MEMORY;
2928 /* create the policy handle */
2929 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
2931 return NT_STATUS_NO_MEMORY;
2934 u_handle->data = talloc_steal(u_handle, a_state);
2936 *r->out.user_handle = u_handle->wire_handle;
2938 return NT_STATUS_OK;
2946 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2947 struct samr_DeleteUser *r)
2949 struct dcesrv_handle *h;
2950 struct samr_account_state *a_state;
2953 *r->out.user_handle = *r->in.user_handle;
2955 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2959 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2960 if (ret != LDB_SUCCESS) {
2961 DEBUG(1, ("Failed to delete user: %s: %s\n",
2962 ldb_dn_get_linearized(a_state->account_dn),
2963 ldb_errstring(a_state->sam_ctx)));
2964 return NT_STATUS_UNSUCCESSFUL;
2968 ZERO_STRUCTP(r->out.user_handle);
2970 return NT_STATUS_OK;
2977 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2978 struct samr_QueryUserInfo *r)
2980 struct dcesrv_handle *h;
2981 struct samr_account_state *a_state;
2982 struct ldb_message *msg, **res;
2984 struct ldb_context *sam_ctx;
2986 const char * const *attrs = NULL;
2987 union samr_UserInfo *info;
2989 *r->out.info = NULL;
2991 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2994 sam_ctx = a_state->sam_ctx;
2996 /* fill in the reply */
2997 switch (r->in.level) {
3000 static const char * const attrs2[] = {"sAMAccountName",
3011 static const char * const attrs2[] = {"comment",
3020 static const char * const attrs2[] = {"sAMAccountName",
3035 "userAccountControl",
3042 static const char * const attrs2[] = {"logonHours",
3049 static const char * const attrs2[] = {"sAMAccountName",
3066 "userAccountControl",
3073 static const char * const attrs2[] = {"sAMAccountName",
3081 static const char * const attrs2[] = {"sAMAccountName",
3088 static const char * const attrs2[] = {"displayName",
3095 static const char * const attrs2[] = {"primaryGroupID",
3102 static const char * const attrs2[] = {"homeDirectory",
3110 static const char * const attrs2[] = {"scriptPath",
3117 static const char * const attrs2[] = {"profilePath",
3124 static const char * const attrs2[] = {"description",
3131 static const char * const attrs2[] = {"userWorkstations",
3138 static const char * const attrs2[] = {"userAccountControl",
3146 static const char * const attrs2[] = {"accountExpires",
3153 static const char * const attrs2[] = {"userParameters",
3160 static const char * const attrs2[] = {"lastLogon",
3176 "userAccountControl",
3188 /* pull all the user attributes */
3189 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3190 a_state->account_dn ,&res, attrs);
3192 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3196 /* allocate the info structure */
3197 info = talloc_zero(mem_ctx, union samr_UserInfo);
3199 return NT_STATUS_NO_MEMORY;
3202 /* fill in the reply */
3203 switch (r->in.level) {
3205 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
3206 QUERY_STRING(msg, info1.full_name, "displayName");
3207 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3208 QUERY_STRING(msg, info1.description, "description");
3209 QUERY_STRING(msg, info1.comment, "comment");
3213 QUERY_STRING(msg, info2.comment, "comment");
3214 QUERY_UINT (msg, info2.country_code, "countryCode");
3215 QUERY_UINT (msg, info2.code_page, "codePage");
3219 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
3220 QUERY_STRING(msg, info3.full_name, "displayName");
3221 QUERY_RID (msg, info3.rid, "objectSid");
3222 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3223 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
3224 QUERY_STRING(msg, info3.home_drive, "homeDrive");
3225 QUERY_STRING(msg, info3.logon_script, "scriptPath");
3226 QUERY_STRING(msg, info3.profile_path, "profilePath");
3227 QUERY_STRING(msg, info3.workstations, "userWorkstations");
3228 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
3229 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
3230 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
3231 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3232 QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
3233 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3234 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3235 QUERY_UINT (msg, info3.logon_count, "logonCount");
3236 QUERY_AFLAGS(msg, info3.acct_flags, "userAccountControl");
3240 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3244 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
3245 QUERY_STRING(msg, info5.full_name, "displayName");
3246 QUERY_RID (msg, info5.rid, "objectSid");
3247 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3248 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
3249 QUERY_STRING(msg, info5.home_drive, "homeDrive");
3250 QUERY_STRING(msg, info5.logon_script, "scriptPath");
3251 QUERY_STRING(msg, info5.profile_path, "profilePath");
3252 QUERY_STRING(msg, info5.description, "description");
3253 QUERY_STRING(msg, info5.workstations, "userWorkstations");
3254 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
3255 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
3256 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3257 QUERY_UINT (msg, info5.bad_password_count, "badPwdCount");
3258 QUERY_UINT (msg, info5.logon_count, "logonCount");
3259 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
3260 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
3261 QUERY_AFLAGS(msg, info5.acct_flags, "userAccountControl");
3265 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3266 QUERY_STRING(msg, info6.full_name, "displayName");
3270 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3274 QUERY_STRING(msg, info8.full_name, "displayName");
3278 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3282 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3283 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3287 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3291 QUERY_STRING(msg, info12.profile_path, "profilePath");
3295 QUERY_STRING(msg, info13.description, "description");
3299 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3303 QUERY_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3307 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3311 QUERY_PARAMETERS(msg, info20.parameters, "userParameters");
3315 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3316 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3317 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3318 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3319 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3320 QUERY_FPASSC(msg, info21.force_password_change,"pwdLastSet");
3321 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3322 QUERY_STRING(msg, info21.full_name, "displayName");
3323 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3324 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3325 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3326 QUERY_STRING(msg, info21.profile_path, "profilePath");
3327 QUERY_STRING(msg, info21.description, "description");
3328 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3329 QUERY_STRING(msg, info21.comment, "comment");
3330 QUERY_PARAMETERS(msg, info21.parameters, "userParameters");
3331 QUERY_RID (msg, info21.rid, "objectSid");
3332 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3333 QUERY_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3334 info->info21.fields_present = 0x00FFFFFF;
3335 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3336 QUERY_UINT (msg, info21.bad_password_count, "badPwdCount");
3337 QUERY_UINT (msg, info21.logon_count, "logonCount");
3338 QUERY_UINT (msg, info21.country_code, "countryCode");
3339 QUERY_UINT (msg, info21.code_page, "codePage");
3345 return NT_STATUS_INVALID_INFO_CLASS;
3348 *r->out.info = info;
3350 return NT_STATUS_OK;
3357 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3358 struct samr_SetUserInfo *r)
3360 struct dcesrv_handle *h;
3361 struct samr_account_state *a_state;
3362 struct ldb_message *msg;
3364 NTSTATUS status = NT_STATUS_OK;
3365 struct ldb_context *sam_ctx;
3367 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3370 sam_ctx = a_state->sam_ctx;
3372 msg = ldb_msg_new(mem_ctx);
3374 return NT_STATUS_NO_MEMORY;
3377 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3379 return NT_STATUS_NO_MEMORY;
3382 switch (r->in.level) {
3384 SET_STRING(msg, info2.comment, "comment");
3385 SET_UINT (msg, info2.country_code, "countryCode");
3386 SET_UINT (msg, info2.code_page, "codePage");
3390 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3394 SET_STRING(msg, info6.account_name, "samAccountName");
3395 SET_STRING(msg, info6.full_name, "displayName");
3399 SET_STRING(msg, info7.account_name, "samAccountName");
3403 SET_STRING(msg, info8.full_name, "displayName");
3407 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3411 SET_STRING(msg, info10.home_directory, "homeDirectory");
3412 SET_STRING(msg, info10.home_drive, "homeDrive");
3416 SET_STRING(msg, info11.logon_script, "scriptPath");
3420 SET_STRING(msg, info12.profile_path, "profilePath");
3424 SET_STRING(msg, info13.description, "description");
3428 SET_STRING(msg, info14.workstations, "userWorkstations");
3432 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3436 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3440 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3444 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3445 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3446 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3447 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3448 SET_STRING(msg, info21.account_name, "samAccountName");
3449 IFSET(SAMR_FIELD_FULL_NAME)
3450 SET_STRING(msg, info21.full_name, "displayName");
3451 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3452 SET_STRING(msg, info21.home_directory, "homeDirectory");
3453 IFSET(SAMR_FIELD_HOME_DRIVE)
3454 SET_STRING(msg, info21.home_drive, "homeDrive");
3455 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3456 SET_STRING(msg, info21.logon_script, "scriptPath");
3457 IFSET(SAMR_FIELD_PROFILE_PATH)
3458 SET_STRING(msg, info21.profile_path, "profilePath");
3459 IFSET(SAMR_FIELD_DESCRIPTION)
3460 SET_STRING(msg, info21.description, "description");
3461 IFSET(SAMR_FIELD_WORKSTATIONS)
3462 SET_STRING(msg, info21.workstations, "userWorkstations");
3463 IFSET(SAMR_FIELD_COMMENT)
3464 SET_STRING(msg, info21.comment, "comment");
3465 IFSET(SAMR_FIELD_PARAMETERS)
3466 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3467 IFSET(SAMR_FIELD_PRIMARY_GID)
3468 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3469 IFSET(SAMR_FIELD_ACCT_FLAGS)
3470 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3471 IFSET(SAMR_FIELD_LOGON_HOURS)
3472 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3473 IFSET(SAMR_FIELD_COUNTRY_CODE)
3474 SET_UINT (msg, info21.country_code, "countryCode");
3475 IFSET(SAMR_FIELD_CODE_PAGE)
3476 SET_UINT (msg, info21.code_page, "codePage");
3481 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3482 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3483 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3484 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3485 SET_STRING(msg, info23.info.account_name, "samAccountName");
3486 IFSET(SAMR_FIELD_FULL_NAME)
3487 SET_STRING(msg, info23.info.full_name, "displayName");
3488 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3489 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3490 IFSET(SAMR_FIELD_HOME_DRIVE)
3491 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3492 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3493 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3494 IFSET(SAMR_FIELD_PROFILE_PATH)
3495 SET_STRING(msg, info23.info.profile_path, "profilePath");
3496 IFSET(SAMR_FIELD_DESCRIPTION)
3497 SET_STRING(msg, info23.info.description, "description");
3498 IFSET(SAMR_FIELD_WORKSTATIONS)
3499 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3500 IFSET(SAMR_FIELD_COMMENT)
3501 SET_STRING(msg, info23.info.comment, "comment");
3502 IFSET(SAMR_FIELD_PARAMETERS)
3503 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3504 IFSET(SAMR_FIELD_PRIMARY_GID)
3505 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3506 IFSET(SAMR_FIELD_ACCT_FLAGS)
3507 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3508 IFSET(SAMR_FIELD_LOGON_HOURS)
3509 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3510 IFSET(SAMR_FIELD_COUNTRY_CODE)
3511 SET_UINT (msg, info23.info.country_code, "countryCode");
3512 IFSET(SAMR_FIELD_CODE_PAGE)
3513 SET_UINT (msg, info23.info.code_page, "codePage");
3515 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3516 status = samr_set_password(dce_call,
3518 a_state->account_dn,
3519 a_state->domain_state->domain_dn,
3521 &r->in.info->info23.password);
3522 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3523 status = samr_set_password(dce_call,
3525 a_state->account_dn,
3526 a_state->domain_state->domain_dn,
3528 &r->in.info->info23.password);
3533 /* the set password levels are handled separately */
3535 status = samr_set_password(dce_call,
3537 a_state->account_dn,
3538 a_state->domain_state->domain_dn,
3540 &r->in.info->info24.password);
3544 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3545 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3546 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3547 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3548 SET_STRING(msg, info25.info.account_name, "samAccountName");
3549 IFSET(SAMR_FIELD_FULL_NAME)
3550 SET_STRING(msg, info25.info.full_name, "displayName");
3551 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3552 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3553 IFSET(SAMR_FIELD_HOME_DRIVE)
3554 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3555 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3556 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3557 IFSET(SAMR_FIELD_PROFILE_PATH)
3558 SET_STRING(msg, info25.info.profile_path, "profilePath");
3559 IFSET(SAMR_FIELD_DESCRIPTION)
3560 SET_STRING(msg, info25.info.description, "description");
3561 IFSET(SAMR_FIELD_WORKSTATIONS)
3562 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3563 IFSET(SAMR_FIELD_COMMENT)
3564 SET_STRING(msg, info25.info.comment, "comment");
3565 IFSET(SAMR_FIELD_PARAMETERS)
3566 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3567 IFSET(SAMR_FIELD_PRIMARY_GID)
3568 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3569 IFSET(SAMR_FIELD_ACCT_FLAGS)
3570 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3571 IFSET(SAMR_FIELD_LOGON_HOURS)
3572 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3573 IFSET(SAMR_FIELD_COUNTRY_CODE)
3574 SET_UINT (msg, info25.info.country_code, "countryCode");
3575 IFSET(SAMR_FIELD_CODE_PAGE)
3576 SET_UINT (msg, info25.info.code_page, "codePage");
3578 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3579 status = samr_set_password_ex(dce_call,
3581 a_state->account_dn,
3582 a_state->domain_state->domain_dn,
3584 &r->in.info->info25.password);
3585 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3586 status = samr_set_password_ex(dce_call,
3588 a_state->account_dn,
3589 a_state->domain_state->domain_dn,
3591 &r->in.info->info25.password);
3596 /* the set password levels are handled separately */
3598 status = samr_set_password_ex(dce_call,
3600 a_state->account_dn,
3601 a_state->domain_state->domain_dn,
3603 &r->in.info->info26.password);
3608 /* many info classes are not valid for SetUserInfo */
3609 return NT_STATUS_INVALID_INFO_CLASS;
3612 if (!NT_STATUS_IS_OK(status)) {
3616 /* modify the samdb record */
3617 if (msg->num_elements > 0) {
3618 ret = ldb_modify(a_state->sam_ctx, msg);
3619 if (ret != LDB_SUCCESS) {
3620 DEBUG(1,("Failed to modify record %s: %s\n",
3621 ldb_dn_get_linearized(a_state->account_dn),
3622 ldb_errstring(a_state->sam_ctx)));
3624 /* we really need samdb.c to return NTSTATUS */
3625 return NT_STATUS_UNSUCCESSFUL;
3629 return NT_STATUS_OK;
3634 samr_GetGroupsForUser
3636 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3637 struct samr_GetGroupsForUser *r)
3639 struct dcesrv_handle *h;
3640 struct samr_account_state *a_state;
3641 struct samr_domain_state *d_state;
3642 struct ldb_message **res;
3643 const char * const attrs[2] = { "objectSid", NULL };
3644 struct samr_RidWithAttributeArray *array;
3647 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3650 d_state = a_state->domain_state;
3652 count = samdb_search_domain(a_state->sam_ctx, mem_ctx,
3653 d_state->domain_dn, &res,
3654 attrs, d_state->domain_sid,
3655 "(&(member=%s)(grouptype=%d)(objectclass=group))",
3656 ldb_dn_get_linearized(a_state->account_dn),
3657 GTYPE_SECURITY_GLOBAL_GROUP);
3659 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3661 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
3663 return NT_STATUS_NO_MEMORY;
3668 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
3670 if (array->rids == NULL)
3671 return NT_STATUS_NO_MEMORY;
3673 /* Adds the primary group */
3674 array->rids[0].rid = samdb_search_uint(a_state->sam_ctx, mem_ctx,
3675 ~0, a_state->account_dn,
3676 "primaryGroupID", NULL);
3677 array->rids[0].attributes = SE_GROUP_MANDATORY
3678 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3681 /* Adds the additional groups */
3682 for (i = 0; i < count; i++) {
3683 struct dom_sid *group_sid;
3685 group_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
3686 if (group_sid == NULL) {
3687 DEBUG(0, ("Couldn't find objectSid attrib\n"));
3691 array->rids[i + 1].rid =
3692 group_sid->sub_auths[group_sid->num_auths-1];
3693 array->rids[i + 1].attributes = SE_GROUP_MANDATORY
3694 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3698 *r->out.rids = array;
3700 return NT_STATUS_OK;
3705 samr_QueryDisplayInfo
3707 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3708 struct samr_QueryDisplayInfo *r)
3710 struct dcesrv_handle *h;
3711 struct samr_domain_state *d_state;
3712 struct ldb_message **res;
3713 int ldb_cnt, count, i;
3714 const char * const attrs[] = { "objectSid", "sAMAccountName",
3715 "displayName", "description", "userAccountControl",
3716 "pwdLastSet", NULL };
3717 struct samr_DispEntryFull *entriesFull = NULL;
3718 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
3719 struct samr_DispEntryAscii *entriesAscii = NULL;
3720 struct samr_DispEntryGeneral *entriesGeneral = NULL;
3723 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3727 switch (r->in.level) {
3730 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3731 "(sAMAccountType=%u))",
3732 ATYPE_NORMAL_ACCOUNT);
3735 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3736 "(sAMAccountType=%u))",
3737 ATYPE_WORKSTATION_TRUST);
3741 filter = talloc_asprintf(mem_ctx, "(&(grouptype=%d)"
3742 "(objectclass=group))",
3743 GTYPE_SECURITY_GLOBAL_GROUP);
3746 return NT_STATUS_INVALID_INFO_CLASS;
3749 /* search for all requested objects in this domain. This could
3750 possibly be cached and resumed based on resume_key */
3751 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
3752 d_state->domain_dn, &res, attrs,
3753 d_state->domain_sid, "%s", filter);
3754 if (ldb_cnt == -1) {
3755 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3757 if (ldb_cnt == 0 || r->in.max_entries == 0) {
3758 return NT_STATUS_OK;
3761 switch (r->in.level) {
3763 entriesGeneral = talloc_array(mem_ctx,
3764 struct samr_DispEntryGeneral,
3768 entriesFull = talloc_array(mem_ctx,
3769 struct samr_DispEntryFull,
3773 entriesFullGroup = talloc_array(mem_ctx,
3774 struct samr_DispEntryFullGroup,
3779 entriesAscii = talloc_array(mem_ctx,
3780 struct samr_DispEntryAscii,
3785 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
3786 (entriesAscii == NULL) && (entriesFullGroup == NULL))
3787 return NT_STATUS_NO_MEMORY;
3791 for (i=0; i<ldb_cnt; i++) {
3792 struct dom_sid *objectsid;
3794 objectsid = samdb_result_dom_sid(mem_ctx, res[i],
3796 if (objectsid == NULL)
3799 switch(r->in.level) {
3801 entriesGeneral[count].idx = count + 1;
3802 entriesGeneral[count].rid =
3803 objectsid->sub_auths[objectsid->num_auths-1];
3804 entriesGeneral[count].acct_flags =
3805 samdb_result_acct_flags(d_state->sam_ctx, mem_ctx,
3807 d_state->domain_dn);
3808 entriesGeneral[count].account_name.string =
3809 samdb_result_string(res[i],
3810 "sAMAccountName", "");
3811 entriesGeneral[count].full_name.string =
3812 samdb_result_string(res[i], "displayName", "");
3813 entriesGeneral[count].description.string =
3814 samdb_result_string(res[i], "description", "");
3817 entriesFull[count].idx = count + 1;
3818 entriesFull[count].rid =
3819 objectsid->sub_auths[objectsid->num_auths-1];
3821 /* No idea why we need to or in ACB_NORMAL here, but this is what Win2k3 seems to do... */
3822 entriesFull[count].acct_flags =
3823 samdb_result_acct_flags(d_state->sam_ctx, mem_ctx,
3825 d_state->domain_dn) | ACB_NORMAL;
3826 entriesFull[count].account_name.string =
3827 samdb_result_string(res[i], "sAMAccountName",
3829 entriesFull[count].description.string =
3830 samdb_result_string(res[i], "description", "");
3833 entriesFullGroup[count].idx = count + 1;
3834 entriesFullGroup[count].rid =
3835 objectsid->sub_auths[objectsid->num_auths-1];
3836 /* We get a "7" here for groups */
3837 entriesFullGroup[count].acct_flags
3838 = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3839 entriesFullGroup[count].account_name.string =
3840 samdb_result_string(res[i], "sAMAccountName",
3842 entriesFullGroup[count].description.string =
3843 samdb_result_string(res[i], "description", "");
3847 entriesAscii[count].idx = count + 1;
3848 entriesAscii[count].account_name.string =
3849 samdb_result_string(res[i], "sAMAccountName",
3857 *r->out.total_size = count;
3859 if (r->in.start_idx >= count) {
3860 *r->out.returned_size = 0;
3861 switch(r->in.level) {
3863 r->out.info->info1.count = *r->out.returned_size;
3864 r->out.info->info1.entries = NULL;
3867 r->out.info->info2.count = *r->out.returned_size;
3868 r->out.info->info2.entries = NULL;
3871 r->out.info->info3.count = *r->out.returned_size;
3872 r->out.info->info3.entries = NULL;
3875 r->out.info->info4.count = *r->out.returned_size;
3876 r->out.info->info4.entries = NULL;
3879 r->out.info->info5.count = *r->out.returned_size;
3880 r->out.info->info5.entries = NULL;
3884 *r->out.returned_size = MIN(count - r->in.start_idx,
3886 switch(r->in.level) {
3888 r->out.info->info1.count = *r->out.returned_size;
3889 r->out.info->info1.entries =
3890 &(entriesGeneral[r->in.start_idx]);
3893 r->out.info->info2.count = *r->out.returned_size;
3894 r->out.info->info2.entries =
3895 &(entriesFull[r->in.start_idx]);
3898 r->out.info->info3.count = *r->out.returned_size;
3899 r->out.info->info3.entries =
3900 &(entriesFullGroup[r->in.start_idx]);
3903 r->out.info->info4.count = *r->out.returned_size;
3904 r->out.info->info4.entries =
3905 &(entriesAscii[r->in.start_idx]);
3908 r->out.info->info5.count = *r->out.returned_size;
3909 r->out.info->info5.entries =
3910 &(entriesAscii[r->in.start_idx]);
3915 return (*r->out.returned_size < (count - r->in.start_idx)) ?
3916 STATUS_MORE_ENTRIES : NT_STATUS_OK;
3921 samr_GetDisplayEnumerationIndex
3923 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3924 struct samr_GetDisplayEnumerationIndex *r)
3926 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3931 samr_TestPrivateFunctionsDomain
3933 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3934 struct samr_TestPrivateFunctionsDomain *r)
3936 return NT_STATUS_NOT_IMPLEMENTED;
3941 samr_TestPrivateFunctionsUser
3943 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3944 struct samr_TestPrivateFunctionsUser *r)
3946 return NT_STATUS_NOT_IMPLEMENTED;
3953 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3954 struct samr_GetUserPwInfo *r)
3956 struct dcesrv_handle *h;
3957 struct samr_account_state *a_state;
3959 ZERO_STRUCTP(r->out.info);
3961 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3965 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
3966 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
3968 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
3969 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
3971 return NT_STATUS_OK;
3976 samr_RemoveMemberFromForeignDomain
3978 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3979 struct samr_RemoveMemberFromForeignDomain *r)
3981 struct dcesrv_handle *h;
3982 struct samr_domain_state *d_state;
3983 const char *memberdn;
3984 struct ldb_message **res;
3985 const char * const attrs[3] = { "distinguishedName", "objectSid", NULL };
3988 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3992 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3993 "distinguishedName", "(objectSid=%s)",
3994 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3996 if (memberdn == NULL) {
3997 return NT_STATUS_OK;
4000 /* TODO: Does this call only remove alias members, or does it do this
4001 * for domain groups as well? */
4003 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4004 d_state->domain_dn, &res, attrs,
4005 d_state->domain_sid,
4006 "(&(member=%s)(objectClass=group)"
4007 "(|(groupType=%d)(groupType=%d)))",
4009 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4010 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4013 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4015 for (i=0; i<count; i++) {
4016 struct ldb_message *mod;
4018 mod = ldb_msg_new(mem_ctx);
4020 return NT_STATUS_NO_MEMORY;
4023 mod->dn = samdb_result_dn(d_state->sam_ctx, mod, res[i], "distinguishedName", NULL);
4024 if (mod->dn == NULL) {
4029 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4030 "member", memberdn) != 0)
4031 return NT_STATUS_NO_MEMORY;
4033 if (ldb_modify(d_state->sam_ctx, mod) != 0)
4034 return NT_STATUS_UNSUCCESSFUL;
4039 return NT_STATUS_OK;
4044 samr_QueryDomainInfo2
4046 just an alias for samr_QueryDomainInfo
4048 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4049 struct samr_QueryDomainInfo2 *r)
4051 struct samr_QueryDomainInfo r1;
4054 ZERO_STRUCT(r1.out);
4055 r1.in.domain_handle = r->in.domain_handle;
4056 r1.in.level = r->in.level;
4057 r1.out.info = r->out.info;
4059 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4068 just an alias for samr_QueryUserInfo
4070 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4071 struct samr_QueryUserInfo2 *r)
4073 struct samr_QueryUserInfo r1;
4076 r1.in.user_handle = r->in.user_handle;
4077 r1.in.level = r->in.level;
4078 r1.out.info = r->out.info;
4080 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4087 samr_QueryDisplayInfo2
4089 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4090 struct samr_QueryDisplayInfo2 *r)
4092 struct samr_QueryDisplayInfo q;
4095 q.in.domain_handle = r->in.domain_handle;
4096 q.in.level = r->in.level;
4097 q.in.start_idx = r->in.start_idx;
4098 q.in.max_entries = r->in.max_entries;
4099 q.in.buf_size = r->in.buf_size;
4100 q.out.total_size = r->out.total_size;
4101 q.out.returned_size = r->out.returned_size;
4102 q.out.info = r->out.info;
4104 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4111 samr_GetDisplayEnumerationIndex2
4113 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4114 struct samr_GetDisplayEnumerationIndex2 *r)
4116 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4121 samr_QueryDisplayInfo3
4123 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4124 struct samr_QueryDisplayInfo3 *r)
4126 struct samr_QueryDisplayInfo q;
4129 q.in.domain_handle = r->in.domain_handle;
4130 q.in.level = r->in.level;
4131 q.in.start_idx = r->in.start_idx;
4132 q.in.max_entries = r->in.max_entries;
4133 q.in.buf_size = r->in.buf_size;
4134 q.out.total_size = r->out.total_size;
4135 q.out.returned_size = r->out.returned_size;
4136 q.out.info = r->out.info;
4138 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4145 samr_AddMultipleMembersToAlias
4147 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4148 struct samr_AddMultipleMembersToAlias *r)
4150 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4155 samr_RemoveMultipleMembersFromAlias
4157 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4158 struct samr_RemoveMultipleMembersFromAlias *r)
4160 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4167 this fetches the default password properties for a domain
4169 note that w2k3 completely ignores the domain name in this call, and
4170 always returns the information for the servers primary domain
4172 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4173 struct samr_GetDomPwInfo *r)
4175 struct ldb_message **msgs;
4177 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4178 struct ldb_context *sam_ctx;
4180 ZERO_STRUCTP(r->out.info);
4182 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
4183 dce_call->conn->dce_ctx->lp_ctx,
4184 dce_call->conn->auth_state.session_info);
4185 if (sam_ctx == NULL) {
4186 return NT_STATUS_INVALID_SYSTEM_SERVICE;
4189 /* The domain name in this call is ignored */
4190 ret = gendb_search_dn(sam_ctx,
4191 mem_ctx, NULL, &msgs, attrs);
4193 talloc_free(sam_ctx);
4195 return NT_STATUS_NO_SUCH_DOMAIN;
4199 talloc_free(sam_ctx);
4201 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4204 r->out.info->min_password_length = samdb_result_uint(msgs[0],
4206 r->out.info->password_properties = samdb_result_uint(msgs[0],
4207 "pwdProperties", 1);
4210 talloc_unlink(mem_ctx, sam_ctx);
4212 return NT_STATUS_OK;
4219 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4220 struct samr_Connect2 *r)
4222 struct samr_Connect c;
4224 c.in.system_name = NULL;
4225 c.in.access_mask = r->in.access_mask;
4226 c.out.connect_handle = r->out.connect_handle;
4228 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4235 just an alias for samr_SetUserInfo
4237 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4238 struct samr_SetUserInfo2 *r)
4240 struct samr_SetUserInfo r2;
4242 r2.in.user_handle = r->in.user_handle;
4243 r2.in.level = r->in.level;
4244 r2.in.info = r->in.info;
4246 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4251 samr_SetBootKeyInformation
4253 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4254 struct samr_SetBootKeyInformation *r)
4256 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4261 samr_GetBootKeyInformation
4263 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4264 struct samr_GetBootKeyInformation *r)
4266 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4273 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4274 struct samr_Connect3 *r)
4276 struct samr_Connect c;
4278 c.in.system_name = NULL;
4279 c.in.access_mask = r->in.access_mask;
4280 c.out.connect_handle = r->out.connect_handle;
4282 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4289 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4290 struct samr_Connect4 *r)
4292 struct samr_Connect c;
4294 c.in.system_name = NULL;
4295 c.in.access_mask = r->in.access_mask;
4296 c.out.connect_handle = r->out.connect_handle;
4298 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4305 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4306 struct samr_Connect5 *r)
4308 struct samr_Connect c;
4311 c.in.system_name = NULL;
4312 c.in.access_mask = r->in.access_mask;
4313 c.out.connect_handle = r->out.connect_handle;
4315 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4317 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4318 r->out.info_out->info1.unknown2 = 0;
4319 *r->out.level_out = r->in.level_in;
4328 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4329 struct samr_RidToSid *r)
4331 struct samr_domain_state *d_state;
4332 struct dcesrv_handle *h;
4334 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4338 /* form the users SID */
4339 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4341 return NT_STATUS_NO_MEMORY;
4344 return NT_STATUS_OK;
4349 samr_SetDsrmPassword
4351 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4352 struct samr_SetDsrmPassword *r)
4354 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4359 samr_ValidatePassword
4361 For now the call checks the password complexity (if active) and the minimum
4362 password length on level 2 and 3. Level 1 is ignored for now.
4364 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
4365 TALLOC_CTX *mem_ctx,
4366 struct samr_ValidatePassword *r)
4368 struct samr_GetDomPwInfo r2;
4369 struct samr_PwInfo pwInfo;
4371 enum samr_ValidationStatus res;
4374 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
4376 r2.in.domain_name = NULL;
4377 r2.out.info = &pwInfo;
4378 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
4379 if (!NT_STATUS_IS_OK(status)) {
4383 switch (r->in.level) {
4384 case NetValidateAuthentication:
4385 /* we don't support this yet */
4386 return NT_STATUS_NOT_SUPPORTED;
4388 case NetValidatePasswordChange:
4389 password = data_blob_const(r->in.req->req2.password.string,
4390 r->in.req->req2.password.length);
4391 res = samdb_check_password(mem_ctx,
4392 dce_call->conn->dce_ctx->lp_ctx,
4394 pwInfo.password_properties,
4395 pwInfo.min_password_length);
4396 (*r->out.rep)->ctr2.status = res;
4398 case NetValidatePasswordReset:
4399 password = data_blob_const(r->in.req->req3.password.string,
4400 r->in.req->req3.password.length);
4401 res = samdb_check_password(mem_ctx,
4402 dce_call->conn->dce_ctx->lp_ctx,
4404 pwInfo.password_properties,
4405 pwInfo.min_password_length);
4406 (*r->out.rep)->ctr3.status = res;
4410 return NT_STATUS_OK;
4414 /* include the generated boilerplate */
4415 #include "librpc/gen_ndr/ndr_samr_s.c"