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
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "librpc/gen_ndr/ndr_samr.h"
26 #include "rpc_server/dcerpc_server.h"
27 #include "rpc_server/common/common.h"
28 #include "rpc_server/samr/dcesrv_samr.h"
29 #include "system/time.h"
30 #include "lib/ldb/include/ldb.h"
31 #include "lib/ldb/include/ldb_errors.h"
32 #include "dsdb/common/flags.h"
33 #include "dsdb/samdb/samdb.h"
34 #include "libcli/ldap/ldap_ndr.h"
35 #include "libcli/security/security.h"
36 #include "rpc_server/samr/proto.h"
37 #include "util/util_ldb.h"
38 #include "param/param.h"
40 /* these query macros make samr_Query[User|Group]Info a bit easier to read */
42 #define QUERY_STRING(msg, field, attr) \
43 r->out.info->field.string = samdb_result_string(msg, attr, "");
44 #define QUERY_UINT(msg, field, attr) \
45 r->out.info->field = samdb_result_uint(msg, attr, 0);
46 #define QUERY_RID(msg, field, attr) \
47 r->out.info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
48 #define QUERY_UINT64(msg, field, attr) \
49 r->out.info->field = samdb_result_uint64(msg, attr, 0);
50 #define QUERY_APASSC(msg, field, attr) \
51 r->out.info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
52 a_state->domain_state->domain_dn, msg, attr);
53 #define QUERY_FPASSC(msg, field, attr) \
54 r->out.info->field = samdb_result_force_password_change(sam_ctx, mem_ctx, \
55 a_state->domain_state->domain_dn, msg);
56 #define QUERY_LHOURS(msg, field, attr) \
57 r->out.info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
58 #define QUERY_AFLAGS(msg, field, attr) \
59 r->out.info->field = samdb_result_acct_flags(msg, attr);
62 /* these are used to make the Set[User|Group]Info code easier to follow */
64 #define SET_STRING(msg, field, attr) do { \
65 struct ldb_message_element *set_el; \
66 if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
67 if (r->in.info->field.string[0] == '\0') { \
68 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL)) { \
69 return NT_STATUS_NO_MEMORY; \
72 if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != 0) { \
73 return NT_STATUS_NO_MEMORY; \
75 set_el = ldb_msg_find_element(msg, attr); \
76 set_el->flags = LDB_FLAG_MOD_REPLACE; \
79 #define SET_UINT(msg, field, attr) do { \
80 struct ldb_message_element *set_el; \
81 if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
82 return NT_STATUS_NO_MEMORY; \
84 set_el = ldb_msg_find_element(msg, attr); \
85 set_el->flags = LDB_FLAG_MOD_REPLACE; \
88 #define SET_INT64(msg, field, attr) do { \
89 struct ldb_message_element *set_el; \
90 if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
91 return NT_STATUS_NO_MEMORY; \
93 set_el = ldb_msg_find_element(msg, attr); \
94 set_el->flags = LDB_FLAG_MOD_REPLACE; \
97 #define SET_UINT64(msg, field, attr) do { \
98 struct ldb_message_element *set_el; \
99 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
100 return NT_STATUS_NO_MEMORY; \
102 set_el = ldb_msg_find_element(msg, attr); \
103 set_el->flags = LDB_FLAG_MOD_REPLACE; \
106 #define SET_AFLAGS(msg, field, attr) do { \
107 struct ldb_message_element *set_el; \
108 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
109 return NT_STATUS_NO_MEMORY; \
111 set_el = ldb_msg_find_element(msg, attr); \
112 set_el->flags = LDB_FLAG_MOD_REPLACE; \
115 #define SET_LHOURS(msg, field, attr) do { \
116 struct ldb_message_element *set_el; \
117 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != 0) { \
118 return NT_STATUS_NO_MEMORY; \
120 set_el = ldb_msg_find_element(msg, attr); \
121 set_el->flags = LDB_FLAG_MOD_REPLACE; \
128 create a connection to the SAM database
130 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
131 struct samr_Connect *r)
133 struct samr_connect_state *c_state;
134 struct dcesrv_handle *handle;
136 ZERO_STRUCTP(r->out.connect_handle);
138 c_state = talloc(dce_call->conn, struct samr_connect_state);
140 return NT_STATUS_NO_MEMORY;
143 /* make sure the sam database is accessible */
144 c_state->sam_ctx = samdb_connect(c_state, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
145 if (c_state->sam_ctx == NULL) {
146 talloc_free(c_state);
147 return NT_STATUS_INVALID_SYSTEM_SERVICE;
151 handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
153 talloc_free(c_state);
154 return NT_STATUS_NO_MEMORY;
157 handle->data = talloc_steal(handle, c_state);
159 c_state->access_mask = r->in.access_mask;
160 *r->out.connect_handle = handle->wire_handle;
169 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
170 struct samr_Close *r)
172 struct dcesrv_handle *h;
174 *r->out.handle = *r->in.handle;
176 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
180 ZERO_STRUCTP(r->out.handle);
189 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
190 struct samr_SetSecurity *r)
192 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
199 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
200 struct samr_QuerySecurity *r)
202 struct dcesrv_handle *h;
203 struct sec_desc_buf *sd;
207 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
209 sd = talloc(mem_ctx, struct sec_desc_buf);
211 return NT_STATUS_NO_MEMORY;
214 sd->sd = samdb_default_security_descriptor(mem_ctx);
225 we refuse this operation completely. If a admin wants to shutdown samr
226 in Samba then they should use the samba admin tools to disable the samr pipe
228 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
229 struct samr_Shutdown *r)
231 return NT_STATUS_ACCESS_DENIED;
238 this maps from a domain name to a SID
240 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
241 struct samr_LookupDomain *r)
243 struct samr_connect_state *c_state;
244 struct dcesrv_handle *h;
246 const char * const dom_attrs[] = { "objectSid", NULL};
247 const char * const ref_attrs[] = { "ncName", NULL};
248 struct ldb_message **dom_msgs;
249 struct ldb_message **ref_msgs;
251 struct ldb_dn *partitions_basedn;
255 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
259 if (r->in.domain_name->string == NULL) {
260 return NT_STATUS_INVALID_PARAMETER;
263 partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
265 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
266 ret = gendb_search(c_state->sam_ctx,
267 mem_ctx, NULL, &dom_msgs, dom_attrs,
268 "(objectClass=builtinDomain)");
270 ret = gendb_search(c_state->sam_ctx,
271 mem_ctx, partitions_basedn, &ref_msgs, ref_attrs,
272 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
273 ldb_binary_encode_string(mem_ctx, r->in.domain_name->string));
275 return NT_STATUS_NO_SUCH_DOMAIN;
278 ret = gendb_search_dn(c_state->sam_ctx, mem_ctx,
279 samdb_result_dn(c_state->sam_ctx, mem_ctx,
280 ref_msgs[0], "ncName", NULL),
281 &dom_msgs, dom_attrs);
285 return NT_STATUS_NO_SUCH_DOMAIN;
288 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
292 return NT_STATUS_NO_SUCH_DOMAIN;
304 list the domains in the SAM
306 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
307 struct samr_EnumDomains *r)
309 struct samr_connect_state *c_state;
310 struct dcesrv_handle *h;
311 struct samr_SamArray *array;
312 int count, i, start_i;
313 const char * const dom_attrs[] = { "cn", NULL};
314 const char * const ref_attrs[] = { "nETBIOSName", NULL};
315 struct ldb_message **dom_msgs;
316 struct ldb_message **ref_msgs;
317 struct ldb_dn *partitions_basedn;
319 *r->out.resume_handle = 0;
321 r->out.num_entries = 0;
323 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
327 partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
329 count = gendb_search(c_state->sam_ctx,
330 mem_ctx, NULL, &dom_msgs, dom_attrs,
331 "(objectClass=domain)");
333 DEBUG(0,("samdb: no domains found in EnumDomains\n"));
334 return NT_STATUS_INTERNAL_DB_CORRUPTION;
337 *r->out.resume_handle = count;
339 start_i = *r->in.resume_handle;
341 if (start_i >= count) {
342 /* search past end of list is not an error for this call */
346 array = talloc(mem_ctx, struct samr_SamArray);
348 return NT_STATUS_NO_MEMORY;
352 array->entries = NULL;
354 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, count - start_i);
355 if (array->entries == NULL) {
356 return NT_STATUS_NO_MEMORY;
359 for (i=0;i<count-start_i;i++) {
361 array->entries[i].idx = start_i + i;
362 /* try and find the domain */
363 ret = gendb_search(c_state->sam_ctx, mem_ctx, partitions_basedn,
364 &ref_msgs, ref_attrs,
365 "(&(objectClass=crossRef)(ncName=%s))",
366 ldb_dn_get_linearized(dom_msgs[i]->dn));
368 array->entries[i].name.string = samdb_result_string(ref_msgs[0], "nETBIOSName", NULL);
370 array->entries[i].name.string = samdb_result_string(dom_msgs[i], "cn", NULL);
375 r->out.num_entries = i;
376 array->count = r->out.num_entries;
385 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
386 struct samr_OpenDomain *r)
388 struct dcesrv_handle *h_conn, *h_domain;
389 const char *domain_name;
390 struct samr_connect_state *c_state;
391 struct samr_domain_state *d_state;
392 const char * const dom_attrs[] = { "cn", NULL};
393 const char * const ref_attrs[] = { "nETBIOSName", NULL};
394 struct ldb_message **dom_msgs;
395 struct ldb_message **ref_msgs;
397 struct ldb_dn *partitions_basedn;
399 ZERO_STRUCTP(r->out.domain_handle);
401 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
403 c_state = h_conn->data;
405 if (r->in.sid == NULL) {
406 return NT_STATUS_INVALID_PARAMETER;
409 partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
411 ret = gendb_search(c_state->sam_ctx,
412 mem_ctx, NULL, &dom_msgs, dom_attrs,
413 "(&(objectSid=%s)(&(|(objectclass=domain)(objectClass=builtinDomain))))",
414 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
416 return NT_STATUS_NO_SUCH_DOMAIN;
417 } else if (ret > 1) {
418 return NT_STATUS_INTERNAL_DB_CORRUPTION;
419 } else if (ret == -1) {
420 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
421 return NT_STATUS_INTERNAL_DB_CORRUPTION;
423 ret = gendb_search(c_state->sam_ctx,
424 mem_ctx, partitions_basedn, &ref_msgs, ref_attrs,
425 "(&(&(nETBIOSName=*)(objectclass=crossRef))(ncName=%s))",
426 ldb_dn_get_linearized(dom_msgs[0]->dn));
428 domain_name = ldb_msg_find_attr_as_string(dom_msgs[0], "cn", NULL);
429 if (domain_name == NULL) {
430 return NT_STATUS_NO_SUCH_DOMAIN;
432 } else if (ret == 1) {
434 domain_name = ldb_msg_find_attr_as_string(ref_msgs[0], "nETBIOSName", NULL);
435 if (domain_name == NULL) {
436 return NT_STATUS_NO_SUCH_DOMAIN;
439 return NT_STATUS_NO_SUCH_DOMAIN;
443 d_state = talloc(c_state, struct samr_domain_state);
445 return NT_STATUS_NO_MEMORY;
448 d_state->role = lp_server_role(dce_call->conn->dce_ctx->lp_ctx);
449 d_state->connect_state = talloc_reference(d_state, c_state);
450 d_state->sam_ctx = c_state->sam_ctx;
451 d_state->domain_sid = dom_sid_dup(d_state, r->in.sid);
452 d_state->domain_name = talloc_strdup(d_state, domain_name);
453 d_state->domain_dn = ldb_dn_copy(d_state, dom_msgs[0]->dn);
454 if (!d_state->domain_sid || !d_state->domain_name || !d_state->domain_dn) {
455 talloc_free(d_state);
456 return NT_STATUS_NO_MEMORY;
458 d_state->access_mask = r->in.access_mask;
460 h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
462 talloc_free(d_state);
463 return NT_STATUS_NO_MEMORY;
466 h_domain->data = talloc_steal(h_domain, d_state);
468 *r->out.domain_handle = h_domain->wire_handle;
476 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
478 struct ldb_message **dom_msgs,
479 struct samr_DomInfo1 *info)
481 info->min_password_length =
482 samdb_result_uint(dom_msgs[0], "minPwdLength", 0);
483 info->password_history_length =
484 samdb_result_uint(dom_msgs[0], "pwdHistoryLength", 0);
485 info->password_properties =
486 samdb_result_uint(dom_msgs[0], "pwdProperties", 0);
487 info->max_password_age =
488 samdb_result_int64(dom_msgs[0], "maxPwdAge", 0);
489 info->min_password_age =
490 samdb_result_int64(dom_msgs[0], "minPwdAge", 0);
498 static NTSTATUS dcesrv_samr_info_DomInfo2(struct samr_domain_state *state,
500 struct ldb_message **dom_msgs,
501 struct samr_DomInfo2 *info)
503 /* This pulls the NetBIOS name from the
504 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
506 info->primary.string = samdb_result_fsmo_name(state->sam_ctx, mem_ctx, dom_msgs[0], "fSMORoleOwner");
508 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
509 0x8000000000000000LL);
511 info->comment.string = samdb_result_string(dom_msgs[0], "comment", NULL);
512 info->domain_name.string = state->domain_name;
514 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
516 switch (state->role) {
517 case ROLE_DOMAIN_CONTROLLER:
518 /* This pulls the NetBIOS name from the
519 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
521 if (samdb_is_pdc(state->sam_ctx)) {
522 info->role = SAMR_ROLE_DOMAIN_PDC;
524 info->role = SAMR_ROLE_DOMAIN_BDC;
527 case ROLE_DOMAIN_MEMBER:
528 info->role = SAMR_ROLE_DOMAIN_MEMBER;
530 case ROLE_STANDALONE:
531 info->role = SAMR_ROLE_STANDALONE;
535 /* No users in BUILTIN, and the LOCAL group types are only in builtin, and the global group type is never in BUILTIN */
536 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
537 "(objectClass=user)");
538 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
539 "(&(objectClass=group)(sAMAccountType=%u))",
541 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
542 "(&(objectClass=group)(sAMAccountType=%u))",
551 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
553 struct ldb_message **dom_msgs,
554 struct samr_DomInfo3 *info)
556 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
557 0x8000000000000000LL);
565 static NTSTATUS dcesrv_samr_info_DomInfo4(struct samr_domain_state *state,
567 struct ldb_message **dom_msgs,
568 struct samr_DomInfo4 *info)
570 info->comment.string = samdb_result_string(dom_msgs[0], "comment", NULL);
578 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
580 struct ldb_message **dom_msgs,
581 struct samr_DomInfo5 *info)
583 info->domain_name.string = state->domain_name;
591 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
593 struct ldb_message **dom_msgs,
594 struct samr_DomInfo6 *info)
596 /* This pulls the NetBIOS name from the
597 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
599 info->primary.string = samdb_result_fsmo_name(state->sam_ctx, mem_ctx,
600 dom_msgs[0], "fSMORoleOwner");
608 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
610 struct ldb_message **dom_msgs,
611 struct samr_DomInfo7 *info)
614 switch (state->role) {
615 case ROLE_DOMAIN_CONTROLLER:
616 /* This pulls the NetBIOS name from the
617 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
619 if (samdb_is_pdc(state->sam_ctx)) {
620 info->role = SAMR_ROLE_DOMAIN_PDC;
622 info->role = SAMR_ROLE_DOMAIN_BDC;
625 case ROLE_DOMAIN_MEMBER:
626 info->role = SAMR_ROLE_DOMAIN_MEMBER;
628 case ROLE_STANDALONE:
629 info->role = SAMR_ROLE_STANDALONE;
639 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
641 struct ldb_message **dom_msgs,
642 struct samr_DomInfo8 *info)
644 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
647 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
656 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
658 struct ldb_message **dom_msgs,
659 struct samr_DomInfo9 *info)
669 static NTSTATUS dcesrv_samr_info_DomInfo11(struct samr_domain_state *state,
671 struct ldb_message **dom_msgs,
672 struct samr_DomInfo11 *info)
675 status = dcesrv_samr_info_DomInfo2(state, mem_ctx, dom_msgs, &info->info2);
676 if (!NT_STATUS_IS_OK(status)) {
680 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
682 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
684 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
692 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
694 struct ldb_message **dom_msgs,
695 struct samr_DomInfo12 *info)
697 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
699 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
701 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
709 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
711 struct ldb_message **dom_msgs,
712 struct samr_DomInfo13 *info)
714 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
717 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
729 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
730 struct samr_QueryDomainInfo *r)
732 struct dcesrv_handle *h;
733 struct samr_domain_state *d_state;
735 struct ldb_message **dom_msgs;
736 const char * const *attrs = NULL;
740 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
744 r->out.info = talloc(mem_ctx, union samr_DomainInfo);
746 return NT_STATUS_NO_MEMORY;
749 switch (r->in.level) {
752 static const char * const attrs2[] = { "minPwdLength", "pwdHistoryLength",
753 "pwdProperties", "maxPwdAge",
760 static const char * const attrs2[] = {"forceLogoff",
770 static const char * const attrs2[] = {"forceLogoff",
777 static const char * const attrs2[] = {"comment",
789 static const char * const attrs2[] = {"fSMORoleOwner",
801 static const char * const attrs2[] = { "modifiedCount",
812 static const char * const attrs2[] = { "comment", "forceLogoff",
815 "lockOutObservationWindow",
823 static const char * const attrs2[] = { "lockoutDuration",
824 "lockOutObservationWindow",
832 static const char * const attrs2[] = { "modifiedCount",
840 /* some levels don't need a search */
843 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
844 d_state->domain_dn, &dom_msgs, attrs);
846 return NT_STATUS_INTERNAL_DB_CORRUPTION;
850 ZERO_STRUCTP(r->out.info);
852 switch (r->in.level) {
854 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
855 &r->out.info->info1);
857 return dcesrv_samr_info_DomInfo2(d_state, mem_ctx, dom_msgs,
858 &r->out.info->info2);
860 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
861 &r->out.info->info3);
863 return dcesrv_samr_info_DomInfo4(d_state, mem_ctx, dom_msgs,
864 &r->out.info->info4);
866 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
867 &r->out.info->info5);
869 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
870 &r->out.info->info6);
872 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
873 &r->out.info->info7);
875 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
876 &r->out.info->info8);
878 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
879 &r->out.info->info9);
881 return dcesrv_samr_info_DomInfo11(d_state, mem_ctx, dom_msgs,
882 &r->out.info->info11);
884 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
885 &r->out.info->info12);
887 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
888 &r->out.info->info13);
891 return NT_STATUS_INVALID_INFO_CLASS;
898 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
899 struct samr_SetDomainInfo *r)
901 struct dcesrv_handle *h;
902 struct samr_domain_state *d_state;
903 struct ldb_message *msg;
905 struct ldb_context *sam_ctx;
907 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
910 sam_ctx = d_state->sam_ctx;
912 msg = ldb_msg_new(mem_ctx);
914 return NT_STATUS_NO_MEMORY;
917 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
919 return NT_STATUS_NO_MEMORY;
922 switch (r->in.level) {
924 SET_UINT (msg, info1.min_password_length, "minPwdLength");
925 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
926 SET_UINT (msg, info1.password_properties, "pwdProperties");
927 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
928 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
931 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
934 SET_STRING(msg, info4.comment, "comment");
940 /* No op, we don't know where to set these */
945 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
946 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
947 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
951 /* many info classes are not valid for SetDomainInfo */
952 return NT_STATUS_INVALID_INFO_CLASS;
955 /* modify the samdb record */
956 ret = ldb_modify(sam_ctx, msg);
958 DEBUG(1,("Failed to modify record %s: %s\n",
959 ldb_dn_get_linearized(d_state->domain_dn),
960 ldb_errstring(sam_ctx)));
962 /* we really need samdb.c to return NTSTATUS */
963 return NT_STATUS_UNSUCCESSFUL;
970 samr_CreateDomainGroup
972 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
973 struct samr_CreateDomainGroup *r)
975 struct samr_domain_state *d_state;
976 struct samr_account_state *a_state;
977 struct dcesrv_handle *h;
979 struct ldb_message *msg;
981 const char *groupname;
982 struct dcesrv_handle *g_handle;
985 ZERO_STRUCTP(r->out.group_handle);
988 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
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(d_state, 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 };
1100 *r->out.resume_handle = 0;
1102 r->out.num_entries = 0;
1104 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1108 /* search for all domain groups in this domain. This could possibly be
1109 cached and resumed based on resume_key */
1110 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1111 d_state->domain_dn, &res, attrs,
1112 d_state->domain_sid,
1113 "(&(grouptype=%d)(objectclass=group))",
1114 GTYPE_SECURITY_GLOBAL_GROUP);
1115 if (ldb_cnt == -1) {
1116 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1118 if (ldb_cnt == 0 || r->in.max_size == 0) {
1119 return NT_STATUS_OK;
1122 /* convert to SamEntry format */
1123 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1125 return NT_STATUS_NO_MEMORY;
1130 for (i=0;i<ldb_cnt;i++) {
1131 struct dom_sid *group_sid;
1133 group_sid = samdb_result_dom_sid(mem_ctx, res[i],
1135 if (group_sid == NULL)
1138 entries[count].idx =
1139 group_sid->sub_auths[group_sid->num_auths-1];
1140 entries[count].name.string =
1141 samdb_result_string(res[i], "sAMAccountName", "");
1145 /* sort the results by rid */
1146 qsort(entries, count, sizeof(struct samr_SamEntry),
1147 (comparison_fn_t)compare_SamEntry);
1149 /* find the first entry to return */
1151 first<count && entries[first].idx <= *r->in.resume_handle;
1154 if (first == count) {
1155 return NT_STATUS_OK;
1158 /* return the rest, limit by max_size. Note that we
1159 use the w2k3 element size value of 54 */
1160 r->out.num_entries = count - first;
1161 r->out.num_entries = MIN(r->out.num_entries,
1162 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1164 r->out.sam = talloc(mem_ctx, struct samr_SamArray);
1166 return NT_STATUS_NO_MEMORY;
1169 r->out.sam->entries = entries+first;
1170 r->out.sam->count = r->out.num_entries;
1172 if (r->out.num_entries < count - first) {
1173 *r->out.resume_handle = entries[first+r->out.num_entries-1].idx;
1174 return STATUS_MORE_ENTRIES;
1177 return NT_STATUS_OK;
1184 This call uses transactions to ensure we don't get a new conflicting
1185 user while we are processing this, and to ensure the user either
1186 completly exists, or does not.
1188 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1189 struct samr_CreateUser2 *r)
1191 struct samr_domain_state *d_state;
1192 struct samr_account_state *a_state;
1193 struct dcesrv_handle *h;
1195 struct ldb_message *msg;
1196 struct dom_sid *sid;
1197 const char *account_name;
1198 struct dcesrv_handle *u_handle;
1200 const char *container, *obj_class=NULL;
1204 const char *attrs[] = {
1206 "userAccountControl",
1210 uint32_t user_account_control;
1212 struct ldb_message **msgs;
1214 ZERO_STRUCTP(r->out.user_handle);
1215 *r->out.access_granted = 0;
1218 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1222 account_name = r->in.account_name->string;
1224 if (account_name == NULL) {
1225 return NT_STATUS_INVALID_PARAMETER;
1228 ret = ldb_transaction_start(d_state->sam_ctx);
1230 DEBUG(0,("Failed to start a transaction for user creation: %s\n",
1231 ldb_errstring(d_state->sam_ctx)));
1232 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1235 /* check if the user already exists */
1236 name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1238 "(&(sAMAccountName=%s)(objectclass=user))",
1239 ldb_binary_encode_string(mem_ctx, account_name));
1241 ldb_transaction_cancel(d_state->sam_ctx);
1242 return NT_STATUS_USER_EXISTS;
1245 msg = ldb_msg_new(mem_ctx);
1247 ldb_transaction_cancel(d_state->sam_ctx);
1248 return NT_STATUS_NO_MEMORY;
1251 cn_name = talloc_strdup(mem_ctx, account_name);
1253 ldb_transaction_cancel(d_state->sam_ctx);
1254 return NT_STATUS_NO_MEMORY;
1257 cn_name_len = strlen(cn_name);
1259 /* This must be one of these values *only* */
1260 if (r->in.acct_flags == ACB_NORMAL) {
1261 container = "CN=Users";
1264 } else if (r->in.acct_flags == ACB_WSTRUST) {
1265 if (cn_name[cn_name_len - 1] != '$') {
1266 return NT_STATUS_FOOBAR;
1268 cn_name[cn_name_len - 1] = '\0';
1269 container = "CN=Computers";
1270 obj_class = "computer";
1271 samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "primaryGroupID", DOMAIN_RID_DOMAIN_MEMBERS);
1273 } else if (r->in.acct_flags == ACB_SVRTRUST) {
1274 if (cn_name[cn_name_len - 1] != '$') {
1275 return NT_STATUS_FOOBAR;
1277 cn_name[cn_name_len - 1] = '\0';
1278 container = "OU=Domain Controllers";
1279 obj_class = "computer";
1280 samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "primaryGroupID", DOMAIN_RID_DCS);
1282 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1283 container = "CN=Users";
1287 ldb_transaction_cancel(d_state->sam_ctx);
1288 return NT_STATUS_INVALID_PARAMETER;
1291 /* add core elements to the ldb_message for the user */
1292 msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
1293 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s,%s", cn_name, container)) {
1294 ldb_transaction_cancel(d_state->sam_ctx);
1295 return NT_STATUS_FOOBAR;
1298 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", account_name);
1299 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", obj_class);
1301 /* Start a transaction, so we can query and do a subsequent atomic modify */
1303 /* create the user */
1304 ret = ldb_add(d_state->sam_ctx, msg);
1308 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1309 ldb_transaction_cancel(d_state->sam_ctx);
1310 DEBUG(0,("Failed to create user record %s: %s\n",
1311 ldb_dn_get_linearized(msg->dn),
1312 ldb_errstring(d_state->sam_ctx)));
1313 return NT_STATUS_USER_EXISTS;
1314 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
1315 ldb_transaction_cancel(d_state->sam_ctx);
1316 DEBUG(0,("Failed to create user record %s: %s\n",
1317 ldb_dn_get_linearized(msg->dn),
1318 ldb_errstring(d_state->sam_ctx)));
1319 return NT_STATUS_ACCESS_DENIED;
1321 ldb_transaction_cancel(d_state->sam_ctx);
1322 DEBUG(0,("Failed to create user record %s: %s\n",
1323 ldb_dn_get_linearized(msg->dn),
1324 ldb_errstring(d_state->sam_ctx)));
1325 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1328 a_state = talloc(d_state, struct samr_account_state);
1330 ldb_transaction_cancel(d_state->sam_ctx);
1331 return NT_STATUS_NO_MEMORY;
1333 a_state->sam_ctx = d_state->sam_ctx;
1334 a_state->access_mask = r->in.access_mask;
1335 a_state->domain_state = talloc_reference(a_state, d_state);
1336 a_state->account_dn = talloc_steal(a_state, msg->dn);
1338 /* retrieve the sid and account control bits for the user just created */
1339 ret = gendb_search_dn(d_state->sam_ctx, a_state,
1340 msg->dn, &msgs, attrs);
1343 ldb_transaction_cancel(d_state->sam_ctx);
1344 DEBUG(0,("Apparently we failed to create an account record, as %s now doesn't exist\n",
1345 ldb_dn_get_linearized(msg->dn)));
1346 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1348 sid = samdb_result_dom_sid(mem_ctx, msgs[0], "objectSid");
1350 ldb_transaction_cancel(d_state->sam_ctx);
1351 DEBUG(0,("Apparently we failed to get the objectSid of the just created account record %s\n",
1352 ldb_dn_get_linearized(msg->dn)));
1353 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1356 /* Change the account control to be the correct account type.
1357 * The default is for a workstation account */
1358 user_account_control = samdb_result_uint(msgs[0], "userAccountControl", 0);
1359 user_account_control = (user_account_control &
1360 ~(UF_NORMAL_ACCOUNT |
1361 UF_INTERDOMAIN_TRUST_ACCOUNT |
1362 UF_WORKSTATION_TRUST_ACCOUNT |
1363 UF_SERVER_TRUST_ACCOUNT));
1364 user_account_control |= samdb_acb2uf(r->in.acct_flags);
1367 msg = ldb_msg_new(mem_ctx);
1369 ldb_transaction_cancel(d_state->sam_ctx);
1370 return NT_STATUS_NO_MEMORY;
1373 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
1375 if (samdb_msg_add_uint(a_state->sam_ctx, mem_ctx, msg,
1376 "userAccountControl",
1377 user_account_control) != 0) {
1378 ldb_transaction_cancel(d_state->sam_ctx);
1379 return NT_STATUS_NO_MEMORY;
1382 /* modify the samdb record */
1383 ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
1385 DEBUG(0,("Failed to modify account record %s to set userAccountControl: %s\n",
1386 ldb_dn_get_linearized(msg->dn),
1387 ldb_errstring(d_state->sam_ctx)));
1388 ldb_transaction_cancel(d_state->sam_ctx);
1390 /* we really need samdb.c to return NTSTATUS */
1391 return NT_STATUS_UNSUCCESSFUL;
1394 ret = ldb_transaction_commit(d_state->sam_ctx);
1396 DEBUG(0,("Failed to commit transaction to add and modify account record %s: %s\n",
1397 ldb_dn_get_linearized(msg->dn),
1398 ldb_errstring(d_state->sam_ctx)));
1399 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1402 a_state->account_name = talloc_steal(a_state, account_name);
1403 if (!a_state->account_name) {
1404 return NT_STATUS_NO_MEMORY;
1407 /* create the policy handle */
1408 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
1410 return NT_STATUS_NO_MEMORY;
1413 u_handle->data = talloc_steal(u_handle, a_state);
1415 *r->out.user_handle = u_handle->wire_handle;
1416 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1418 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1420 return NT_STATUS_OK;
1427 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1428 struct samr_CreateUser *r)
1430 struct samr_CreateUser2 r2;
1431 uint32_t access_granted = 0;
1434 /* a simple wrapper around samr_CreateUser2 works nicely */
1435 r2.in.domain_handle = r->in.domain_handle;
1436 r2.in.account_name = r->in.account_name;
1437 r2.in.acct_flags = ACB_NORMAL;
1438 r2.in.access_mask = r->in.access_mask;
1439 r2.out.user_handle = r->out.user_handle;
1440 r2.out.access_granted = &access_granted;
1441 r2.out.rid = r->out.rid;
1443 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1447 samr_EnumDomainUsers
1449 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1450 struct samr_EnumDomainUsers *r)
1452 struct dcesrv_handle *h;
1453 struct samr_domain_state *d_state;
1454 struct ldb_message **res;
1455 int count, num_filtered_entries, i, first;
1456 struct samr_SamEntry *entries;
1457 const char * const attrs[] = { "objectSid", "sAMAccountName", "userAccountControl", NULL };
1459 *r->out.resume_handle = 0;
1461 r->out.num_entries = 0;
1463 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1467 /* search for all users in this domain. This could possibly be cached and
1468 resumed based on resume_key */
1469 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1470 "objectclass=user");
1472 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1474 if (count == 0 || r->in.max_size == 0) {
1475 return NT_STATUS_OK;
1478 /* convert to SamEntry format */
1479 entries = talloc_array(mem_ctx, struct samr_SamEntry, count);
1481 return NT_STATUS_NO_MEMORY;
1483 num_filtered_entries = 0;
1484 for (i=0;i<count;i++) {
1485 /* Check if a mask has been requested */
1486 if (r->in.acct_flags
1487 && ((samdb_result_acct_flags(res[i],
1488 "userAccountControl") & r->in.acct_flags) == 0)) {
1491 entries[num_filtered_entries].idx = samdb_result_rid_from_sid(mem_ctx, res[i], "objectSid", 0);
1492 entries[num_filtered_entries].name.string = samdb_result_string(res[i], "sAMAccountName", "");
1493 num_filtered_entries++;
1496 /* sort the results by rid */
1497 qsort(entries, num_filtered_entries, sizeof(struct samr_SamEntry),
1498 (comparison_fn_t)compare_SamEntry);
1500 /* find the first entry to return */
1502 first<num_filtered_entries && entries[first].idx <= *r->in.resume_handle;
1505 /* return the rest, limit by max_size. Note that we
1506 use the w2k3 element size value of 54 */
1507 r->out.num_entries = num_filtered_entries - first;
1508 r->out.num_entries = MIN(r->out.num_entries,
1509 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1511 r->out.sam = talloc(mem_ctx, struct samr_SamArray);
1513 return NT_STATUS_NO_MEMORY;
1516 r->out.sam->entries = entries+first;
1517 r->out.sam->count = r->out.num_entries;
1519 if (first == num_filtered_entries) {
1520 return NT_STATUS_OK;
1523 if (r->out.num_entries < num_filtered_entries - first) {
1524 *r->out.resume_handle = entries[first+r->out.num_entries-1].idx;
1525 return STATUS_MORE_ENTRIES;
1528 return NT_STATUS_OK;
1535 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1536 struct samr_CreateDomAlias *r)
1538 struct samr_domain_state *d_state;
1539 struct samr_account_state *a_state;
1540 struct dcesrv_handle *h;
1541 const char *alias_name, *name;
1542 struct ldb_message *msg;
1543 struct dom_sid *sid;
1544 struct dcesrv_handle *a_handle;
1547 ZERO_STRUCTP(r->out.alias_handle);
1550 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1554 alias_name = r->in.alias_name->string;
1556 if (alias_name == NULL) {
1557 return NT_STATUS_INVALID_PARAMETER;
1560 /* Check if alias already exists */
1561 name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1563 "(sAMAccountName=%s)(objectclass=group))",
1564 ldb_binary_encode_string(mem_ctx, alias_name));
1567 return NT_STATUS_ALIAS_EXISTS;
1570 msg = ldb_msg_new(mem_ctx);
1572 return NT_STATUS_NO_MEMORY;
1575 /* add core elements to the ldb_message for the alias */
1576 msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
1577 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Users", alias_name);
1579 return NT_STATUS_NO_MEMORY;
1582 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", alias_name);
1583 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
1584 samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "groupType", GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1586 /* create the alias */
1587 ret = ldb_add(d_state->sam_ctx, msg);
1591 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1592 return NT_STATUS_ALIAS_EXISTS;
1593 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
1594 return NT_STATUS_ACCESS_DENIED;
1596 DEBUG(0,("Failed to create alias record %s: %s\n",
1597 ldb_dn_get_linearized(msg->dn),
1598 ldb_errstring(d_state->sam_ctx)));
1599 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1602 a_state = talloc(d_state, struct samr_account_state);
1604 return NT_STATUS_NO_MEMORY;
1607 a_state->sam_ctx = d_state->sam_ctx;
1608 a_state->access_mask = r->in.access_mask;
1609 a_state->domain_state = talloc_reference(a_state, d_state);
1610 a_state->account_dn = talloc_steal(a_state, msg->dn);
1612 /* retrieve the sid for the alias just created */
1613 sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
1614 msg->dn, "objectSid", NULL);
1616 a_state->account_name = talloc_strdup(a_state, alias_name);
1617 if (!a_state->account_name) {
1618 return NT_STATUS_NO_MEMORY;
1621 /* create the policy handle */
1622 a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1623 if (a_handle == NULL)
1624 return NT_STATUS_NO_MEMORY;
1626 a_handle->data = talloc_steal(a_handle, a_state);
1628 *r->out.alias_handle = a_handle->wire_handle;
1630 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1632 return NT_STATUS_OK;
1637 samr_EnumDomainAliases
1639 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1640 struct samr_EnumDomainAliases *r)
1642 struct dcesrv_handle *h;
1643 struct samr_domain_state *d_state;
1644 struct ldb_message **res;
1645 int ldb_cnt, count, i, first;
1646 struct samr_SamEntry *entries;
1647 const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
1649 *r->out.resume_handle = 0;
1651 r->out.num_entries = 0;
1653 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1657 /* search for all domain groups in this domain. This could possibly be
1658 cached and resumed based on resume_key */
1659 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1662 d_state->domain_sid,
1663 "(&(|(grouptype=%d)(grouptype=%d)))"
1664 "(objectclass=group))",
1665 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1666 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1667 if (ldb_cnt == -1) {
1668 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1671 return NT_STATUS_OK;
1674 /* convert to SamEntry format */
1675 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1677 return NT_STATUS_NO_MEMORY;
1682 for (i=0;i<ldb_cnt;i++) {
1683 struct dom_sid *alias_sid;
1685 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1688 if (alias_sid == NULL)
1691 entries[count].idx =
1692 alias_sid->sub_auths[alias_sid->num_auths-1];
1693 entries[count].name.string =
1694 samdb_result_string(res[i], "sAMAccountName", "");
1698 /* sort the results by rid */
1699 qsort(entries, count, sizeof(struct samr_SamEntry),
1700 (comparison_fn_t)compare_SamEntry);
1702 /* find the first entry to return */
1704 first<count && entries[first].idx <= *r->in.resume_handle;
1707 if (first == count) {
1708 return NT_STATUS_OK;
1711 r->out.num_entries = count - first;
1712 r->out.num_entries = MIN(r->out.num_entries, 1000);
1714 r->out.sam = talloc(mem_ctx, struct samr_SamArray);
1716 return NT_STATUS_NO_MEMORY;
1719 r->out.sam->entries = entries+first;
1720 r->out.sam->count = r->out.num_entries;
1722 if (r->out.num_entries < count - first) {
1723 *r->out.resume_handle =
1724 entries[first+r->out.num_entries-1].idx;
1725 return STATUS_MORE_ENTRIES;
1728 return NT_STATUS_OK;
1733 samr_GetAliasMembership
1735 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1736 struct samr_GetAliasMembership *r)
1738 struct dcesrv_handle *h;
1739 struct samr_domain_state *d_state;
1740 struct ldb_message **res;
1743 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1747 if (r->in.sids->num_sids > 0) {
1749 const char * const attrs[2] = { "objectSid", NULL };
1751 filter = talloc_asprintf(mem_ctx,
1752 "(&(|(grouptype=%d)(grouptype=%d))"
1753 "(objectclass=group)(|",
1754 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1755 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1757 return NT_STATUS_NO_MEMORY;
1759 for (i=0; i<r->in.sids->num_sids; i++) {
1760 const char *memberdn;
1763 samdb_search_string(d_state->sam_ctx,
1764 mem_ctx, NULL, "distinguishedName",
1766 ldap_encode_ndr_dom_sid(mem_ctx,
1767 r->in.sids->sids[i].sid));
1769 if (memberdn == NULL)
1772 filter = talloc_asprintf(mem_ctx, "%s(member=%s)",
1775 return NT_STATUS_NO_MEMORY;
1778 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1779 d_state->domain_dn, &res, attrs,
1780 d_state->domain_sid, "%s))", filter);
1782 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1785 r->out.rids->count = 0;
1786 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1787 if (r->out.rids->ids == NULL)
1788 return NT_STATUS_NO_MEMORY;
1790 for (i=0; i<count; i++) {
1791 struct dom_sid *alias_sid;
1793 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1795 if (alias_sid == NULL) {
1796 DEBUG(0, ("Could not find objectSid\n"));
1800 r->out.rids->ids[r->out.rids->count] =
1801 alias_sid->sub_auths[alias_sid->num_auths-1];
1802 r->out.rids->count += 1;
1805 return NT_STATUS_OK;
1812 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1813 struct samr_LookupNames *r)
1815 struct dcesrv_handle *h;
1816 struct samr_domain_state *d_state;
1818 NTSTATUS status = NT_STATUS_OK;
1819 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1822 ZERO_STRUCT(r->out.rids);
1823 ZERO_STRUCT(r->out.types);
1825 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1829 if (r->in.num_names == 0) {
1830 return NT_STATUS_OK;
1833 r->out.rids.ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1834 r->out.types.ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1835 if (!r->out.rids.ids || !r->out.types.ids) {
1836 return NT_STATUS_NO_MEMORY;
1838 r->out.rids.count = r->in.num_names;
1839 r->out.types.count = r->in.num_names;
1843 for (i=0;i<r->in.num_names;i++) {
1844 struct ldb_message **res;
1845 struct dom_sid *sid;
1846 uint32_t atype, rtype;
1848 r->out.rids.ids[i] = 0;
1849 r->out.types.ids[i] = SID_NAME_UNKNOWN;
1851 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1852 "sAMAccountName=%s",
1853 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
1855 status = STATUS_SOME_UNMAPPED;
1859 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
1861 status = STATUS_SOME_UNMAPPED;
1865 atype = samdb_result_uint(res[0], "sAMAccountType", 0);
1867 status = STATUS_SOME_UNMAPPED;
1871 rtype = samdb_atype_map(atype);
1873 if (rtype == SID_NAME_UNKNOWN) {
1874 status = STATUS_SOME_UNMAPPED;
1878 r->out.rids.ids[i] = sid->sub_auths[sid->num_auths-1];
1879 r->out.types.ids[i] = rtype;
1883 if (num_mapped == 0) {
1884 return NT_STATUS_NONE_MAPPED;
1893 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1894 struct samr_LookupRids *r)
1896 struct dcesrv_handle *h;
1897 struct samr_domain_state *d_state;
1899 NTSTATUS status = NT_STATUS_OK;
1900 struct lsa_String *names;
1903 ZERO_STRUCT(r->out.names);
1904 ZERO_STRUCT(r->out.types);
1906 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1910 if (r->in.num_rids == 0)
1911 return NT_STATUS_OK;
1913 names = talloc_array(mem_ctx, struct lsa_String, r->in.num_rids);
1914 ids = talloc_array(mem_ctx, uint32_t, r->in.num_rids);
1916 if ((names == NULL) || (ids == NULL))
1917 return NT_STATUS_NO_MEMORY;
1921 for (i=0; i<r->in.num_rids; i++) {
1922 struct ldb_message **res;
1924 const char * const attrs[] = { "sAMAccountType",
1925 "sAMAccountName", NULL };
1927 struct dom_sid *sid;
1929 ids[i] = SID_NAME_UNKNOWN;
1931 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rids[i]);
1933 names[i].string = NULL;
1934 status = STATUS_SOME_UNMAPPED;
1938 count = gendb_search(d_state->sam_ctx, mem_ctx,
1939 d_state->domain_dn, &res, attrs,
1941 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1943 names[i].string = NULL;
1944 status = STATUS_SOME_UNMAPPED;
1948 names[i].string = samdb_result_string(res[0], "sAMAccountName",
1951 atype = samdb_result_uint(res[0], "sAMAccountType", 0);
1953 status = STATUS_SOME_UNMAPPED;
1957 ids[i] = samdb_atype_map(atype);
1959 if (ids[i] == SID_NAME_UNKNOWN) {
1960 status = STATUS_SOME_UNMAPPED;
1965 r->out.names.names = names;
1966 r->out.names.count = r->in.num_rids;
1968 r->out.types.ids = ids;
1969 r->out.types.count = r->in.num_rids;
1978 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1979 struct samr_OpenGroup *r)
1981 struct samr_domain_state *d_state;
1982 struct samr_account_state *a_state;
1983 struct dcesrv_handle *h;
1984 const char *groupname;
1985 struct dom_sid *sid;
1986 struct ldb_message **msgs;
1987 struct dcesrv_handle *g_handle;
1988 const char * const attrs[2] = { "sAMAccountName", NULL };
1991 ZERO_STRUCTP(r->out.group_handle);
1993 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1997 /* form the group SID */
1998 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2000 return NT_STATUS_NO_MEMORY;
2003 /* search for the group record */
2004 ret = gendb_search(d_state->sam_ctx,
2005 mem_ctx, d_state->domain_dn, &msgs, attrs,
2006 "(&(objectSid=%s)(objectclass=group)"
2008 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2009 GTYPE_SECURITY_GLOBAL_GROUP);
2011 return NT_STATUS_NO_SUCH_GROUP;
2014 DEBUG(0,("Found %d records matching sid %s\n",
2015 ret, dom_sid_string(mem_ctx, sid)));
2016 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2019 groupname = samdb_result_string(msgs[0], "sAMAccountName", NULL);
2020 if (groupname == NULL) {
2021 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2022 dom_sid_string(mem_ctx, sid)));
2023 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2026 a_state = talloc(d_state, struct samr_account_state);
2028 return NT_STATUS_NO_MEMORY;
2030 a_state->sam_ctx = d_state->sam_ctx;
2031 a_state->access_mask = r->in.access_mask;
2032 a_state->domain_state = talloc_reference(a_state, d_state);
2033 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2034 a_state->account_sid = talloc_steal(a_state, sid);
2035 a_state->account_name = talloc_strdup(a_state, groupname);
2036 if (!a_state->account_name) {
2037 return NT_STATUS_NO_MEMORY;
2040 /* create the policy handle */
2041 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
2043 return NT_STATUS_NO_MEMORY;
2046 g_handle->data = talloc_steal(g_handle, a_state);
2048 *r->out.group_handle = g_handle->wire_handle;
2050 return NT_STATUS_OK;
2056 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2057 struct samr_QueryGroupInfo *r)
2059 struct dcesrv_handle *h;
2060 struct samr_account_state *a_state;
2061 struct ldb_message *msg, **res;
2062 const char * const attrs[4] = { "sAMAccountName", "description",
2063 "numMembers", NULL };
2068 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2072 /* pull all the group attributes */
2073 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2074 a_state->account_dn, &res, attrs);
2076 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2080 /* allocate the info structure */
2081 r->out.info = talloc(mem_ctx, union samr_GroupInfo);
2082 if (r->out.info == NULL) {
2083 return NT_STATUS_NO_MEMORY;
2085 ZERO_STRUCTP(r->out.info);
2087 /* Fill in the level */
2088 switch (r->in.level) {
2090 QUERY_STRING(msg, all.name, "sAMAccountName");
2091 r->out.info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2092 QUERY_UINT (msg, all.num_members, "numMembers")
2093 QUERY_STRING(msg, all.description, "description");
2096 QUERY_STRING(msg, name, "sAMAccountName");
2098 case GROUPINFOATTRIBUTES:
2099 r->out.info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2101 case GROUPINFODESCRIPTION:
2102 QUERY_STRING(msg, description, "description");
2105 QUERY_STRING(msg, all2.name, "sAMAccountName");
2106 r->out.info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2107 QUERY_UINT (msg, all2.num_members, "numMembers")
2108 QUERY_STRING(msg, all2.description, "description");
2112 return NT_STATUS_INVALID_INFO_CLASS;
2115 return NT_STATUS_OK;
2122 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2123 struct samr_SetGroupInfo *r)
2125 struct dcesrv_handle *h;
2126 struct samr_account_state *g_state;
2127 struct ldb_message *msg;
2128 struct ldb_context *sam_ctx;
2131 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2134 sam_ctx = g_state->sam_ctx;
2136 msg = ldb_msg_new(mem_ctx);
2138 return NT_STATUS_NO_MEMORY;
2141 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2143 return NT_STATUS_NO_MEMORY;
2146 switch (r->in.level) {
2147 case GROUPINFODESCRIPTION:
2148 SET_STRING(msg, description, "description");
2151 /* On W2k3 this does not change the name, it changes the
2152 * sAMAccountName attribute */
2153 SET_STRING(msg, name, "sAMAccountName");
2155 case GROUPINFOATTRIBUTES:
2156 /* This does not do anything obviously visible in W2k3 LDAP */
2157 return NT_STATUS_OK;
2159 return NT_STATUS_INVALID_INFO_CLASS;
2162 /* modify the samdb record */
2163 ret = ldb_modify(g_state->sam_ctx, msg);
2165 /* we really need samdb.c to return NTSTATUS */
2166 return NT_STATUS_UNSUCCESSFUL;
2169 return NT_STATUS_OK;
2176 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2177 struct samr_AddGroupMember *r)
2179 struct dcesrv_handle *h;
2180 struct samr_account_state *a_state;
2181 struct samr_domain_state *d_state;
2182 struct ldb_message *mod;
2183 struct dom_sid *membersid;
2184 const char *memberdn;
2185 struct ldb_result *res;
2186 const char * const attrs[] = { NULL };
2189 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2192 d_state = a_state->domain_state;
2194 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2195 if (membersid == NULL)
2196 return NT_STATUS_NO_MEMORY;
2198 /* In native mode, AD can also nest domain groups. Not sure yet
2199 * whether this is also available via RPC. */
2200 ret = ldb_search_exp_fmt(d_state->sam_ctx, mem_ctx, &res,
2201 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2202 "(&(objectSid=%s)(objectclass=user))",
2203 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2206 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2209 if (res->count == 0) {
2210 return NT_STATUS_NO_SUCH_USER;
2213 if (res->count > 1) {
2214 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2217 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2219 if (memberdn == NULL)
2220 return NT_STATUS_NO_MEMORY;
2222 mod = ldb_msg_new(mem_ctx);
2224 return NT_STATUS_NO_MEMORY;
2227 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2229 if (samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2231 return NT_STATUS_UNSUCCESSFUL;
2233 ret = ldb_modify(a_state->sam_ctx, mod);
2236 return NT_STATUS_OK;
2237 case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
2238 return NT_STATUS_MEMBER_IN_GROUP;
2239 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2240 return NT_STATUS_ACCESS_DENIED;
2242 return NT_STATUS_UNSUCCESSFUL;
2249 samr_DeleteDomainGroup
2251 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2252 struct samr_DeleteDomainGroup *r)
2254 struct dcesrv_handle *h;
2255 struct samr_account_state *a_state;
2258 *r->out.group_handle = *r->in.group_handle;
2260 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2264 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2266 return NT_STATUS_UNSUCCESSFUL;
2269 ZERO_STRUCTP(r->out.group_handle);
2271 return NT_STATUS_OK;
2276 samr_DeleteGroupMember
2278 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2279 struct samr_DeleteGroupMember *r)
2281 struct dcesrv_handle *h;
2282 struct samr_account_state *a_state;
2283 struct samr_domain_state *d_state;
2284 struct ldb_message *mod;
2285 struct dom_sid *membersid;
2286 const char *memberdn;
2287 struct ldb_result *res;
2288 const char * const attrs[] = { NULL };
2291 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2294 d_state = a_state->domain_state;
2296 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2297 if (membersid == NULL)
2298 return NT_STATUS_NO_MEMORY;
2300 /* In native mode, AD can also nest domain groups. Not sure yet
2301 * whether this is also available via RPC. */
2302 ret = ldb_search_exp_fmt(d_state->sam_ctx, mem_ctx, &res,
2303 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2304 "(&(objectSid=%s)(objectclass=user))",
2305 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2308 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2311 if (res->count == 0) {
2312 return NT_STATUS_NO_SUCH_USER;
2315 if (res->count > 1) {
2316 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2319 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2321 if (memberdn == NULL)
2322 return NT_STATUS_NO_MEMORY;
2324 mod = ldb_msg_new(mem_ctx);
2326 return NT_STATUS_NO_MEMORY;
2329 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2331 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2333 return NT_STATUS_NO_MEMORY;
2336 ret = ldb_modify(a_state->sam_ctx, mod);
2339 return NT_STATUS_OK;
2340 case LDB_ERR_NO_SUCH_ATTRIBUTE:
2341 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2342 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2343 return NT_STATUS_ACCESS_DENIED;
2345 return NT_STATUS_UNSUCCESSFUL;
2352 samr_QueryGroupMember
2354 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2355 struct samr_QueryGroupMember *r)
2357 struct dcesrv_handle *h;
2358 struct samr_account_state *a_state;
2359 struct ldb_message **res;
2360 struct ldb_message_element *el;
2361 struct samr_RidTypeArray *array;
2362 const char * const attrs[2] = { "member", NULL };
2365 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2369 /* pull the member attribute */
2370 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2371 a_state->account_dn, &res, attrs);
2374 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2377 array = talloc(mem_ctx, struct samr_RidTypeArray);
2380 return NT_STATUS_NO_MEMORY;
2382 ZERO_STRUCTP(array);
2384 el = ldb_msg_find_element(res[0], "member");
2389 array->count = el->num_values;
2391 array->rids = talloc_array(mem_ctx, uint32_t,
2393 if (array->rids == NULL)
2394 return NT_STATUS_NO_MEMORY;
2396 array->types = talloc_array(mem_ctx, uint32_t,
2398 if (array->types == NULL)
2399 return NT_STATUS_NO_MEMORY;
2401 for (i=0; i<el->num_values; i++) {
2402 struct ldb_message **res2;
2403 const char * const attrs2[2] = { "objectSid", NULL };
2404 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2405 ldb_dn_new(mem_ctx, a_state->sam_ctx, (const char *)el->values[i].data),
2408 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2411 samdb_result_rid_from_sid(mem_ctx, res2[0],
2414 if (array->rids[i] == 0)
2415 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2417 array->types[i] = 7; /* RID type of some kind, not sure what the value means. */
2421 r->out.rids = array;
2423 return NT_STATUS_OK;
2428 samr_SetMemberAttributesOfGroup
2430 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2431 struct samr_SetMemberAttributesOfGroup *r)
2433 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2440 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2441 struct samr_OpenAlias *r)
2443 struct samr_domain_state *d_state;
2444 struct samr_account_state *a_state;
2445 struct dcesrv_handle *h;
2446 const char *alias_name;
2447 struct dom_sid *sid;
2448 struct ldb_message **msgs;
2449 struct dcesrv_handle *g_handle;
2450 const char * const attrs[2] = { "sAMAccountName", NULL };
2453 ZERO_STRUCTP(r->out.alias_handle);
2455 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2459 /* form the alias SID */
2460 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2462 return NT_STATUS_NO_MEMORY;
2464 /* search for the group record */
2465 ret = gendb_search(d_state->sam_ctx,
2466 mem_ctx, d_state->domain_dn, &msgs, attrs,
2467 "(&(objectSid=%s)(objectclass=group)"
2468 "(|(grouptype=%d)(grouptype=%d)))",
2469 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2470 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2471 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2473 return NT_STATUS_NO_SUCH_ALIAS;
2476 DEBUG(0,("Found %d records matching sid %s\n",
2477 ret, dom_sid_string(mem_ctx, sid)));
2478 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2481 alias_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
2482 if (alias_name == NULL) {
2483 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2484 dom_sid_string(mem_ctx, sid)));
2485 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2488 a_state = talloc(d_state, struct samr_account_state);
2490 return NT_STATUS_NO_MEMORY;
2492 a_state->sam_ctx = d_state->sam_ctx;
2493 a_state->access_mask = r->in.access_mask;
2494 a_state->domain_state = talloc_reference(a_state, d_state);
2495 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2496 a_state->account_sid = talloc_steal(a_state, sid);
2497 a_state->account_name = talloc_strdup(a_state, alias_name);
2498 if (!a_state->account_name) {
2499 return NT_STATUS_NO_MEMORY;
2502 /* create the policy handle */
2503 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2505 return NT_STATUS_NO_MEMORY;
2508 g_handle->data = talloc_steal(g_handle, a_state);
2510 *r->out.alias_handle = g_handle->wire_handle;
2512 return NT_STATUS_OK;
2519 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2520 struct samr_QueryAliasInfo *r)
2522 struct dcesrv_handle *h;
2523 struct samr_account_state *a_state;
2524 struct ldb_message *msg, **res;
2525 const char * const attrs[4] = { "sAMAccountName", "description",
2526 "numMembers", NULL };
2531 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2535 /* pull all the alias attributes */
2536 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2537 a_state->account_dn ,&res, attrs);
2539 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2543 /* allocate the info structure */
2544 r->out.info = talloc(mem_ctx, union samr_AliasInfo);
2545 if (r->out.info == NULL) {
2546 return NT_STATUS_NO_MEMORY;
2548 ZERO_STRUCTP(r->out.info);
2550 switch(r->in.level) {
2552 QUERY_STRING(msg, all.name, "sAMAccountName");
2553 QUERY_UINT (msg, all.num_members, "numMembers");
2554 QUERY_STRING(msg, all.description, "description");
2557 QUERY_STRING(msg, name, "sAMAccountName");
2559 case ALIASINFODESCRIPTION:
2560 QUERY_STRING(msg, description, "description");
2564 return NT_STATUS_INVALID_INFO_CLASS;
2567 return NT_STATUS_OK;
2574 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2575 struct samr_SetAliasInfo *r)
2577 struct dcesrv_handle *h;
2578 struct samr_account_state *a_state;
2579 struct ldb_message *msg;
2580 struct ldb_context *sam_ctx;
2583 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2586 sam_ctx = a_state->sam_ctx;
2588 msg = ldb_msg_new(mem_ctx);
2590 return NT_STATUS_NO_MEMORY;
2593 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2595 return NT_STATUS_NO_MEMORY;
2598 switch (r->in.level) {
2599 case ALIASINFODESCRIPTION:
2600 SET_STRING(msg, description, "description");
2603 /* On W2k3 this does not change the name, it changes the
2604 * sAMAccountName attribute */
2605 SET_STRING(msg, name, "sAMAccountName");
2608 return NT_STATUS_INVALID_INFO_CLASS;
2611 /* modify the samdb record */
2612 ret = ldb_modify(a_state->sam_ctx, msg);
2614 /* we really need samdb.c to return NTSTATUS */
2615 return NT_STATUS_UNSUCCESSFUL;
2618 return NT_STATUS_OK;
2625 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2626 struct samr_DeleteDomAlias *r)
2628 struct dcesrv_handle *h;
2629 struct samr_account_state *a_state;
2632 *r->out.alias_handle = *r->in.alias_handle;
2634 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2638 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2640 return NT_STATUS_UNSUCCESSFUL;
2643 ZERO_STRUCTP(r->out.alias_handle);
2645 return NT_STATUS_OK;
2652 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2653 struct samr_AddAliasMember *r)
2655 struct dcesrv_handle *h;
2656 struct samr_account_state *a_state;
2657 struct samr_domain_state *d_state;
2658 struct ldb_message *mod;
2659 struct ldb_message **msgs;
2660 const char * const attrs[] = { NULL };
2661 struct ldb_dn *memberdn = NULL;
2665 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2668 d_state = a_state->domain_state;
2670 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2671 &msgs, attrs, "(objectsid=%s)",
2672 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2675 memberdn = msgs[0]->dn;
2676 } else if (ret > 1) {
2677 DEBUG(0,("Found %d records matching sid %s\n",
2678 ret, dom_sid_string(mem_ctx, r->in.sid)));
2679 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2680 } else if (ret == 0) {
2681 status = samdb_create_foreign_security_principal(d_state->sam_ctx, mem_ctx,
2682 r->in.sid, &memberdn);
2683 if (!NT_STATUS_IS_OK(status)) {
2687 DEBUG(0, ("samdb_search returned %d: %s\n", ret, ldb_errstring(d_state->sam_ctx)));
2690 if (memberdn == NULL) {
2691 DEBUG(0, ("Could not find memberdn\n"));
2692 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2695 mod = ldb_msg_new(mem_ctx);
2697 return NT_STATUS_NO_MEMORY;
2700 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2702 if (samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2703 ldb_dn_alloc_linearized(mem_ctx, memberdn)) != 0)
2704 return NT_STATUS_UNSUCCESSFUL;
2706 if (ldb_modify(a_state->sam_ctx, mod) != 0)
2707 return NT_STATUS_UNSUCCESSFUL;
2709 return NT_STATUS_OK;
2714 samr_DeleteAliasMember
2716 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2717 struct samr_DeleteAliasMember *r)
2719 struct dcesrv_handle *h;
2720 struct samr_account_state *a_state;
2721 struct samr_domain_state *d_state;
2722 struct ldb_message *mod;
2723 const char *memberdn;
2725 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2728 d_state = a_state->domain_state;
2730 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2731 "distinguishedName", "(objectSid=%s)",
2732 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2734 if (memberdn == NULL)
2735 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2737 mod = ldb_msg_new(mem_ctx);
2739 return NT_STATUS_NO_MEMORY;
2742 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2744 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2746 return NT_STATUS_UNSUCCESSFUL;
2748 if (ldb_modify(a_state->sam_ctx, mod) != 0)
2749 return NT_STATUS_UNSUCCESSFUL;
2751 return NT_STATUS_OK;
2756 samr_GetMembersInAlias
2758 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2759 struct samr_GetMembersInAlias *r)
2761 struct dcesrv_handle *h;
2762 struct samr_account_state *a_state;
2763 struct samr_domain_state *d_state;
2764 struct ldb_message **msgs;
2765 struct lsa_SidPtr *sids;
2766 struct ldb_message_element *el;
2767 const char * const attrs[2] = { "member", NULL};
2770 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2773 d_state = a_state->domain_state;
2775 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
2776 a_state->account_dn, &msgs, attrs);
2779 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2781 r->out.sids->num_sids = 0;
2782 r->out.sids->sids = NULL;
2784 el = ldb_msg_find_element(msgs[0], "member");
2789 sids = talloc_array(mem_ctx, struct lsa_SidPtr,
2793 return NT_STATUS_NO_MEMORY;
2795 for (i=0; i<el->num_values; i++) {
2796 struct ldb_message **msgs2;
2797 const char * const attrs2[2] = { "objectSid", NULL };
2798 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2799 ldb_dn_new(mem_ctx, a_state->sam_ctx, (const char *)el->values[i].data),
2802 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2804 sids[i].sid = samdb_result_dom_sid(mem_ctx, msgs2[0],
2807 if (sids[i].sid == NULL)
2808 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2810 r->out.sids->num_sids = el->num_values;
2811 r->out.sids->sids = sids;
2814 return NT_STATUS_OK;
2820 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2821 struct samr_OpenUser *r)
2823 struct samr_domain_state *d_state;
2824 struct samr_account_state *a_state;
2825 struct dcesrv_handle *h;
2826 const char *account_name;
2827 struct dom_sid *sid;
2828 struct ldb_message **msgs;
2829 struct dcesrv_handle *u_handle;
2830 const char * const attrs[2] = { "sAMAccountName", NULL };
2833 ZERO_STRUCTP(r->out.user_handle);
2835 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2839 /* form the users SID */
2840 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2842 return NT_STATUS_NO_MEMORY;
2845 /* search for the user record */
2846 ret = gendb_search(d_state->sam_ctx,
2847 mem_ctx, d_state->domain_dn, &msgs, attrs,
2848 "(&(objectSid=%s)(objectclass=user))",
2849 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2851 return NT_STATUS_NO_SUCH_USER;
2854 DEBUG(0,("Found %d records matching sid %s\n", ret,
2855 dom_sid_string(mem_ctx, sid)));
2856 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2859 account_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
2860 if (account_name == NULL) {
2861 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2862 dom_sid_string(mem_ctx, sid)));
2863 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2866 a_state = talloc(mem_ctx, struct samr_account_state);
2868 return NT_STATUS_NO_MEMORY;
2870 a_state->sam_ctx = d_state->sam_ctx;
2871 a_state->access_mask = r->in.access_mask;
2872 a_state->domain_state = talloc_reference(a_state, d_state);
2873 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2874 a_state->account_sid = talloc_steal(a_state, sid);
2875 a_state->account_name = talloc_strdup(a_state, account_name);
2876 if (!a_state->account_name) {
2877 return NT_STATUS_NO_MEMORY;
2880 /* create the policy handle */
2881 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
2883 return NT_STATUS_NO_MEMORY;
2886 u_handle->data = talloc_steal(u_handle, a_state);
2888 *r->out.user_handle = u_handle->wire_handle;
2890 return NT_STATUS_OK;
2898 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2899 struct samr_DeleteUser *r)
2901 struct dcesrv_handle *h;
2902 struct samr_account_state *a_state;
2905 *r->out.user_handle = *r->in.user_handle;
2907 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2911 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2913 DEBUG(1, ("Failed to delete user: %s: %s\n",
2914 ldb_dn_get_linearized(a_state->account_dn),
2915 ldb_errstring(a_state->sam_ctx)));
2916 return NT_STATUS_UNSUCCESSFUL;
2919 ZERO_STRUCTP(r->out.user_handle);
2921 return NT_STATUS_OK;
2928 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2929 struct samr_QueryUserInfo *r)
2931 struct dcesrv_handle *h;
2932 struct samr_account_state *a_state;
2933 struct ldb_message *msg, **res;
2935 struct ldb_context *sam_ctx;
2937 const char * const *attrs = NULL;
2941 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2944 sam_ctx = a_state->sam_ctx;
2946 /* fill in the reply */
2947 switch (r->in.level) {
2950 static const char * const attrs2[] = {"sAMAccountName", "displayName",
2951 "primaryGroupID", "description",
2958 static const char * const attrs2[] = {"comment", "countryCode", "codePage", NULL};
2964 static const char * const attrs2[] = {"sAMAccountName",
2979 "userAccountControl", NULL};
2985 static const char * const attrs2[] = {"logonHours", NULL};
2991 static const char * const attrs2[] = {"sAMAccountName",
3008 "userAccountControl",
3015 static const char * const attrs2[] = {"sAMAccountName", "displayName", NULL};
3021 static const char * const attrs2[] = {"sAMAccountName", NULL};
3027 static const char * const attrs2[] = {"displayName", NULL};
3033 static const char * const attrs2[] = {"primaryGroupID", NULL};
3039 static const char * const attrs2[] = {"homeDirectory", "homeDrive", NULL};
3045 static const char * const attrs2[] = {"scriptPath", NULL};
3051 static const char * const attrs2[] = {"profilePath", NULL};
3057 static const char * const attrs2[] = {"description", NULL};
3063 static const char * const attrs2[] = {"userWorkstations", NULL};
3069 static const char * const attrs2[] = {"userAccountControl", NULL};
3075 static const char * const attrs2[] = {"accountExpires", NULL};
3081 static const char * const attrs2[] = {"userParameters", NULL};
3087 static const char * const attrs2[] = {"lastLogon",
3103 "userAccountControl",
3115 /* pull all the user attributes */
3116 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3117 a_state->account_dn ,&res, attrs);
3119 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3123 /* allocate the info structure */
3124 r->out.info = talloc(mem_ctx, union samr_UserInfo);
3125 if (r->out.info == NULL) {
3126 return NT_STATUS_NO_MEMORY;
3128 ZERO_STRUCTP(r->out.info);
3130 /* fill in the reply */
3131 switch (r->in.level) {
3133 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
3134 QUERY_STRING(msg, info1.full_name, "displayName");
3135 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3136 QUERY_STRING(msg, info1.description, "description");
3137 QUERY_STRING(msg, info1.comment, "comment");
3141 QUERY_STRING(msg, info2.comment, "comment");
3142 QUERY_UINT (msg, info2.country_code, "countryCode");
3143 QUERY_UINT (msg, info2.code_page, "codePage");
3147 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
3148 QUERY_STRING(msg, info3.full_name, "displayName");
3149 QUERY_RID (msg, info3.rid, "objectSid");
3150 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3151 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
3152 QUERY_STRING(msg, info3.home_drive, "homeDrive");
3153 QUERY_STRING(msg, info3.logon_script, "scriptPath");
3154 QUERY_STRING(msg, info3.profile_path, "profilePath");
3155 QUERY_STRING(msg, info3.workstations, "userWorkstations");
3156 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
3157 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
3158 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
3159 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3160 QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
3161 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3162 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3163 QUERY_UINT (msg, info3.logon_count, "logonCount");
3164 QUERY_AFLAGS(msg, info3.acct_flags, "userAccountControl");
3168 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3172 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
3173 QUERY_STRING(msg, info5.full_name, "displayName");
3174 QUERY_RID (msg, info5.rid, "objectSid");
3175 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3176 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
3177 QUERY_STRING(msg, info5.home_drive, "homeDrive");
3178 QUERY_STRING(msg, info5.logon_script, "scriptPath");
3179 QUERY_STRING(msg, info5.profile_path, "profilePath");
3180 QUERY_STRING(msg, info5.description, "description");
3181 QUERY_STRING(msg, info5.workstations, "userWorkstations");
3182 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
3183 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
3184 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3185 QUERY_UINT (msg, info5.bad_password_count, "badPwdCount");