r17516: Change helper function names to make more clear what they are meant to do
[Samba/ekacnet.git] / source4 / rpc_server / samr / dcesrv_samr.c
blobb3dadd14e17dd1f690d5bac11ed60ebbcfbfad37
1 /*
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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
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 "ads.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "libcli/ldap/ldap.h"
36 #include "libcli/security/security.h"
37 #include "rpc_server/samr/proto.h"
38 #include "db_wrap.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 = 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_NTTIME(msg, field, attr) \
49 r->out.info->field = samdb_result_nttime(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(mod, field, attr) do { \
65 if (r->in.info->field == NULL) return NT_STATUS_INVALID_PARAMETER; \
66 if (samdb_msg_add_string(sam_ctx, mem_ctx, mod, attr, r->in.info->field) != 0) { \
67 return NT_STATUS_NO_MEMORY; \
68 } \
69 } while (0)
71 #define SET_UINT(mod, field, attr) do { \
72 if (samdb_msg_add_uint(sam_ctx, mem_ctx, mod, attr, r->in.info->field) != 0) { \
73 return NT_STATUS_NO_MEMORY; \
74 } \
75 } while (0)
77 #define SET_INT64(mod, field, attr) do { \
78 if (samdb_msg_add_int64(sam_ctx, mem_ctx, mod, attr, r->in.info->field) != 0) { \
79 return NT_STATUS_NO_MEMORY; \
80 } \
81 } while (0)
83 #define SET_UINT64(mod, field, attr) do { \
84 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, mod, attr, r->in.info->field) != 0) { \
85 return NT_STATUS_NO_MEMORY; \
86 } \
87 } while (0)
89 #define SET_AFLAGS(msg, field, attr) do { \
90 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
91 return NT_STATUS_NO_MEMORY; \
92 } \
93 } while (0)
95 #define SET_LHOURS(msg, field, attr) do { \
96 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != 0) { \
97 return NT_STATUS_NO_MEMORY; \
98 } \
99 } while (0)
103 samr_Connect
105 create a connection to the SAM database
107 static NTSTATUS samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
108 struct samr_Connect *r)
110 struct samr_connect_state *c_state;
111 struct dcesrv_handle *handle;
113 ZERO_STRUCTP(r->out.connect_handle);
115 c_state = talloc(dce_call->conn, struct samr_connect_state);
116 if (!c_state) {
117 return NT_STATUS_NO_MEMORY;
120 /* make sure the sam database is accessible */
121 c_state->sam_ctx = samdb_connect(c_state, dce_call->conn->auth_state.session_info);
122 if (c_state->sam_ctx == NULL) {
123 talloc_free(c_state);
124 return NT_STATUS_INVALID_SYSTEM_SERVICE;
128 handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
129 if (!handle) {
130 talloc_free(c_state);
131 return NT_STATUS_NO_MEMORY;
134 handle->data = talloc_steal(handle, c_state);
136 c_state->access_mask = r->in.access_mask;
137 *r->out.connect_handle = handle->wire_handle;
139 return NT_STATUS_OK;
144 samr_Close
146 static NTSTATUS samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
147 struct samr_Close *r)
149 struct dcesrv_handle *h;
151 *r->out.handle = *r->in.handle;
153 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
155 talloc_free(h);
157 ZERO_STRUCTP(r->out.handle);
159 return NT_STATUS_OK;
164 samr_SetSecurity
166 static NTSTATUS samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
167 struct samr_SetSecurity *r)
169 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
174 samr_QuerySecurity
176 static NTSTATUS samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
177 struct samr_QuerySecurity *r)
179 struct dcesrv_handle *h;
180 struct sec_desc_buf *sd;
182 r->out.sdbuf = NULL;
184 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
186 sd = talloc(mem_ctx, struct sec_desc_buf);
187 if (sd == NULL) {
188 return NT_STATUS_NO_MEMORY;
191 sd->sd = samdb_default_security_descriptor(mem_ctx);
193 r->out.sdbuf = sd;
195 return NT_STATUS_OK;
200 samr_Shutdown
202 we refuse this operation completely. If a admin wants to shutdown samr
203 in Samba then they should use the samba admin tools to disable the samr pipe
205 static NTSTATUS samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
206 struct samr_Shutdown *r)
208 return NT_STATUS_ACCESS_DENIED;
213 samr_LookupDomain
215 this maps from a domain name to a SID
217 static NTSTATUS samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
218 struct samr_LookupDomain *r)
220 struct samr_connect_state *c_state;
221 struct dcesrv_handle *h;
222 struct dom_sid *sid;
223 const char * const dom_attrs[] = { "objectSid", NULL};
224 const char * const ref_attrs[] = { "ncName", NULL};
225 struct ldb_message **dom_msgs;
226 struct ldb_message **ref_msgs;
227 int ret;
228 const struct ldb_dn *partitions_basedn = ldb_dn_string_compose(mem_ctx, samdb_base_dn(mem_ctx), "CN=Partitions,CN=Configuration");
230 r->out.sid = NULL;
232 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
234 c_state = h->data;
236 if (r->in.domain_name->string == NULL) {
237 return NT_STATUS_INVALID_PARAMETER;
240 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
241 ret = gendb_search(c_state->sam_ctx,
242 mem_ctx, samdb_base_dn(mem_ctx), &dom_msgs, dom_attrs,
243 "(objectClass=builtinDomain)");
244 } else {
245 ret = gendb_search(c_state->sam_ctx,
246 mem_ctx, partitions_basedn, &ref_msgs, ref_attrs,
247 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
248 ldb_binary_encode_string(mem_ctx, r->in.domain_name->string));
249 if (ret != 1) {
250 return NT_STATUS_NO_SUCH_DOMAIN;
253 ret = gendb_search_dn(c_state->sam_ctx, mem_ctx,
254 samdb_result_dn(mem_ctx,
255 ref_msgs[0], "ncName", NULL),
256 &dom_msgs, dom_attrs);
259 if (ret != 1) {
260 return NT_STATUS_NO_SUCH_DOMAIN;
263 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
264 "objectSid");
266 if (sid == NULL) {
267 return NT_STATUS_NO_SUCH_DOMAIN;
270 r->out.sid = sid;
272 return NT_STATUS_OK;
277 samr_EnumDomains
279 list the domains in the SAM
281 static NTSTATUS samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
282 struct samr_EnumDomains *r)
284 struct samr_connect_state *c_state;
285 struct dcesrv_handle *h;
286 struct samr_SamArray *array;
287 int count, i, start_i;
288 const char * const dom_attrs[] = { "cn", NULL};
289 const char * const ref_attrs[] = { "nETBIOSName", NULL};
290 struct ldb_message **dom_msgs;
291 struct ldb_message **ref_msgs;
292 const struct ldb_dn *partitions_basedn = ldb_dn_string_compose(mem_ctx, samdb_base_dn(mem_ctx), "CN=Partitions,CN=Configuration");
294 *r->out.resume_handle = 0;
295 r->out.sam = NULL;
296 r->out.num_entries = 0;
298 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
300 c_state = h->data;
302 count = gendb_search(c_state->sam_ctx,
303 mem_ctx, samdb_base_dn(mem_ctx), &dom_msgs, dom_attrs,
304 "(objectClass=domain)");
305 if (count == -1) {
306 DEBUG(0,("samdb: no domains found in EnumDomains\n"));
307 return NT_STATUS_INTERNAL_DB_CORRUPTION;
310 *r->out.resume_handle = count;
312 start_i = *r->in.resume_handle;
314 if (start_i >= count) {
315 /* search past end of list is not an error for this call */
316 return NT_STATUS_OK;
319 array = talloc(mem_ctx, struct samr_SamArray);
320 if (array == NULL) {
321 return NT_STATUS_NO_MEMORY;
324 array->count = 0;
325 array->entries = NULL;
327 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, count - start_i);
328 if (array->entries == NULL) {
329 return NT_STATUS_NO_MEMORY;
332 for (i=0;i<count-start_i;i++) {
333 int ret;
334 array->entries[i].idx = start_i + i;
335 /* try and find the domain */
336 ret = gendb_search(c_state->sam_ctx, mem_ctx, partitions_basedn,
337 &ref_msgs, ref_attrs,
338 "(&(objectClass=crossRef)(ncName=%s))",
339 ldb_dn_linearize(mem_ctx, dom_msgs[i]->dn));
340 if (ret == 1) {
341 array->entries[i].name.string = samdb_result_string(ref_msgs[0], "nETBIOSName", NULL);
342 } else {
343 array->entries[i].name.string = samdb_result_string(dom_msgs[i], "cn", NULL);
347 r->out.sam = array;
348 r->out.num_entries = i;
349 array->count = r->out.num_entries;
351 return NT_STATUS_OK;
356 samr_OpenDomain
358 static NTSTATUS samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
359 struct samr_OpenDomain *r)
361 struct dcesrv_handle *h_conn, *h_domain;
362 const char *domain_name;
363 struct samr_connect_state *c_state;
364 struct samr_domain_state *d_state;
365 const char * const dom_attrs[] = { "cn", NULL};
366 const char * const ref_attrs[] = { "nETBIOSName", NULL};
367 struct ldb_message **dom_msgs;
368 struct ldb_message **ref_msgs;
369 int ret;
370 const struct ldb_dn *partitions_basedn = ldb_dn_string_compose(mem_ctx, samdb_base_dn(mem_ctx), "CN=Partitions,CN=Configuration");
372 ZERO_STRUCTP(r->out.domain_handle);
374 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
376 c_state = h_conn->data;
378 if (r->in.sid == NULL) {
379 return NT_STATUS_INVALID_PARAMETER;
382 ret = gendb_search(c_state->sam_ctx,
383 mem_ctx, samdb_base_dn(mem_ctx), &dom_msgs, dom_attrs,
384 "(&(objectSid=%s)(&(objectclass=domain)))",
385 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
386 if (ret != 1) {
387 return NT_STATUS_INTERNAL_DB_CORRUPTION;
388 } else {
389 ret = gendb_search(c_state->sam_ctx,
390 mem_ctx, partitions_basedn, &ref_msgs, ref_attrs,
391 "(&(&(nETBIOSName=*)(objectclass=crossRef))(ncName=%s))",
392 ldb_dn_linearize(mem_ctx, dom_msgs[0]->dn));
393 if (ret == 0) {
394 domain_name = ldb_msg_find_attr_as_string(dom_msgs[0], "cn", NULL);
395 if (domain_name == NULL) {
396 return NT_STATUS_NO_SUCH_DOMAIN;
398 } else if (ret == 1) {
400 domain_name = ldb_msg_find_attr_as_string(ref_msgs[0], "nETBIOSName", NULL);
401 if (domain_name == NULL) {
402 return NT_STATUS_NO_SUCH_DOMAIN;
404 } else {
405 return NT_STATUS_NO_SUCH_DOMAIN;
409 d_state = talloc(c_state, struct samr_domain_state);
410 if (!d_state) {
411 return NT_STATUS_NO_MEMORY;
414 d_state->connect_state = talloc_reference(d_state, c_state);
415 d_state->sam_ctx = c_state->sam_ctx;
416 d_state->domain_sid = dom_sid_dup(d_state, r->in.sid);
417 d_state->domain_name = talloc_strdup(d_state, domain_name);
418 d_state->domain_dn = ldb_dn_copy(d_state, dom_msgs[0]->dn);
419 if (!d_state->domain_sid || !d_state->domain_name || !d_state->domain_dn) {
420 talloc_free(d_state);
421 return NT_STATUS_NO_MEMORY;
423 d_state->access_mask = r->in.access_mask;
425 h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
426 if (!h_domain) {
427 talloc_free(d_state);
428 return NT_STATUS_NO_MEMORY;
431 h_domain->data = talloc_steal(h_domain, d_state);
433 *r->out.domain_handle = h_domain->wire_handle;
435 return NT_STATUS_OK;
439 return DomInfo1
441 static NTSTATUS samr_info_DomInfo1(struct samr_domain_state *state,
442 TALLOC_CTX *mem_ctx,
443 struct ldb_message **dom_msgs,
444 struct samr_DomInfo1 *info)
446 info->min_password_length =
447 samdb_result_uint(dom_msgs[0], "minPwdLength", 0);
448 info->password_history_length =
449 samdb_result_uint(dom_msgs[0], "pwdHistoryLength", 0);
450 info->password_properties =
451 samdb_result_uint(dom_msgs[0], "pwdProperties", 0);
452 info->max_password_age =
453 samdb_result_int64(dom_msgs[0], "maxPwdAge", 0);
454 info->min_password_age =
455 samdb_result_int64(dom_msgs[0], "minPwdAge", 0);
457 return NT_STATUS_OK;
461 return DomInfo2
463 static NTSTATUS samr_info_DomInfo2(struct samr_domain_state *state, TALLOC_CTX *mem_ctx,
464 struct ldb_message **dom_msgs,
465 struct samr_DomInfo2 *info)
467 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
468 0x8000000000000000LL);
470 info->comment.string = samdb_result_string(dom_msgs[0], "comment", NULL);
471 info->domain_name.string = state->domain_name;
473 /* FIXME: We should find the name of the real PDC emulator */
474 info->primary.string = lp_netbios_name();
475 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
478 info->role = lp_server_role();
480 /* TODO: Should these filter on SID, to avoid counting BUILTIN? */
481 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
482 "(objectClass=user)");
483 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
484 "(&(objectClass=group)(sAMAccountType=%u))",
485 ATYPE_GLOBAL_GROUP);
486 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
487 "(&(objectClass=group)(sAMAccountType=%u))",
488 ATYPE_LOCAL_GROUP);
490 return NT_STATUS_OK;
494 return DomInfo3
496 static NTSTATUS samr_info_DomInfo3(struct samr_domain_state *state,
497 TALLOC_CTX *mem_ctx,
498 struct ldb_message **dom_msgs,
499 struct samr_DomInfo3 *info)
501 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
502 0x8000000000000000LL);
504 return NT_STATUS_OK;
508 return DomInfo4
510 static NTSTATUS samr_info_DomInfo4(struct samr_domain_state *state,
511 TALLOC_CTX *mem_ctx,
512 struct ldb_message **dom_msgs,
513 struct samr_DomInfo4 *info)
515 info->comment.string = samdb_result_string(dom_msgs[0], "comment", NULL);
517 return NT_STATUS_OK;
521 return DomInfo5
523 static NTSTATUS samr_info_DomInfo5(struct samr_domain_state *state,
524 TALLOC_CTX *mem_ctx,
525 struct ldb_message **dom_msgs,
526 struct samr_DomInfo5 *info)
528 info->domain_name.string = state->domain_name;
530 return NT_STATUS_OK;
534 return DomInfo6
536 static NTSTATUS samr_info_DomInfo6(struct samr_domain_state *state,
537 TALLOC_CTX *mem_ctx,
538 struct ldb_message **dom_msgs,
539 struct samr_DomInfo6 *info)
542 /* FIXME: We should find the name of the real PDC emulator */
543 info->primary.string = lp_netbios_name();
545 return NT_STATUS_OK;
549 return DomInfo7
551 static NTSTATUS samr_info_DomInfo7(struct samr_domain_state *state,
552 TALLOC_CTX *mem_ctx,
553 struct ldb_message **dom_msgs,
554 struct samr_DomInfo7 *info)
556 info->role = lp_server_role();
558 return NT_STATUS_OK;
562 return DomInfo8
564 static NTSTATUS samr_info_DomInfo8(struct samr_domain_state *state,
565 TALLOC_CTX *mem_ctx,
566 struct ldb_message **dom_msgs,
567 struct samr_DomInfo8 *info)
569 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
570 time(NULL));
572 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
573 0x0LL);
575 return NT_STATUS_OK;
579 return DomInfo9
581 static NTSTATUS samr_info_DomInfo9(struct samr_domain_state *state,
582 TALLOC_CTX *mem_ctx,
583 struct ldb_message **dom_msgs,
584 struct samr_DomInfo9 *info)
586 info->unknown = 1;
588 return NT_STATUS_OK;
592 return DomInfo11
594 static NTSTATUS samr_info_DomInfo11(struct samr_domain_state *state,
595 TALLOC_CTX *mem_ctx,
596 struct ldb_message **dom_msgs,
597 struct samr_DomInfo11 *info)
599 NTSTATUS status;
600 status = samr_info_DomInfo2(state, mem_ctx, dom_msgs, &info->info2);
601 if (!NT_STATUS_IS_OK(status)) {
602 return status;
605 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
606 -18000000000LL);
607 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
608 -18000000000LL);
609 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
611 return NT_STATUS_OK;
615 return DomInfo12
617 static NTSTATUS samr_info_DomInfo12(struct samr_domain_state *state,
618 TALLOC_CTX *mem_ctx,
619 struct ldb_message **dom_msgs,
620 struct samr_DomInfo12 *info)
622 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
623 -18000000000LL);
624 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
625 -18000000000LL);
626 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
628 return NT_STATUS_OK;
632 return DomInfo13
634 static NTSTATUS samr_info_DomInfo13(struct samr_domain_state *state,
635 TALLOC_CTX *mem_ctx,
636 struct ldb_message **dom_msgs,
637 struct samr_DomInfo13 *info)
639 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
640 time(NULL));
642 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
643 0x0LL);
645 info->unknown1 = 0;
646 info->unknown2 = 0;
648 return NT_STATUS_OK;
652 samr_QueryDomainInfo
654 static NTSTATUS samr_QueryDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
655 struct samr_QueryDomainInfo *r)
657 struct dcesrv_handle *h;
658 struct samr_domain_state *d_state;
660 struct ldb_message **dom_msgs;
661 const char * const *attrs = NULL;
663 r->out.info = NULL;
665 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
667 d_state = h->data;
669 r->out.info = talloc(mem_ctx, union samr_DomainInfo);
670 if (!r->out.info) {
671 return NT_STATUS_NO_MEMORY;
674 switch (r->in.level) {
675 case 1:
677 static const char * const attrs2[] = { "minPwdLength", "pwdHistoryLength",
678 "pwdProperties", "maxPwdAge",
679 "minPwdAge", NULL };
680 attrs = attrs2;
681 break;
683 case 2:
685 static const char * const attrs2[] = {"forceLogoff",
686 "comment",
687 "modifiedCount"};
688 attrs = attrs2;
689 break;
691 case 3:
693 static const char * const attrs2[] = {"forceLogoff"};
694 attrs = attrs2;
695 break;
697 case 4:
699 static const char * const attrs2[] = {"comment"};
700 attrs = attrs2;
701 break;
703 case 5:
704 case 6:
705 case 7:
707 attrs = NULL;
708 break;
710 case 8:
712 static const char * const attrs2[] = { "modifiedCount", "creationTime", NULL };
713 attrs = attrs2;
714 break;
716 case 9:
717 attrs = NULL;
718 break;
719 case 11:
721 static const char * const attrs2[] = { "comment", "forceLogoff",
722 "modifiedCount",
723 "lockoutDuration",
724 "lockOutObservationWindow",
725 "lockoutThreshold", NULL};
726 attrs = attrs2;
727 break;
729 case 12:
731 static const char * const attrs2[] = { "lockoutDuration",
732 "lockOutObservationWindow",
733 "lockoutThreshold", NULL};
734 attrs = attrs2;
735 break;
737 case 13:
739 static const char * const attrs2[] = { "modifiedCount", "creationTime", NULL };
740 attrs = attrs2;
741 break;
745 /* some levels don't need a search */
746 if (attrs) {
747 int ret;
748 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
749 d_state->domain_dn, &dom_msgs, attrs);
750 if (ret != 1) {
751 return NT_STATUS_INTERNAL_DB_CORRUPTION;
755 ZERO_STRUCTP(r->out.info);
757 switch (r->in.level) {
758 case 1:
759 return samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
760 &r->out.info->info1);
761 case 2:
762 return samr_info_DomInfo2(d_state, mem_ctx, dom_msgs,
763 &r->out.info->info2);
764 case 3:
765 return samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
766 &r->out.info->info3);
767 case 4:
768 return samr_info_DomInfo4(d_state, mem_ctx, dom_msgs,
769 &r->out.info->info4);
770 case 5:
771 return samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
772 &r->out.info->info5);
773 case 6:
774 return samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
775 &r->out.info->info6);
776 case 7:
777 return samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
778 &r->out.info->info7);
779 case 8:
780 return samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
781 &r->out.info->info8);
782 case 9:
783 return samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
784 &r->out.info->info9);
785 case 11:
786 return samr_info_DomInfo11(d_state, mem_ctx, dom_msgs,
787 &r->out.info->info11);
788 case 12:
789 return samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
790 &r->out.info->info12);
791 case 13:
792 return samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
793 &r->out.info->info13);
796 return NT_STATUS_INVALID_INFO_CLASS;
801 samr_SetDomainInfo
803 static NTSTATUS samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
804 struct samr_SetDomainInfo *r)
806 struct dcesrv_handle *h;
807 struct samr_domain_state *d_state;
808 struct ldb_message *msg;
809 int ret;
810 struct ldb_context *sam_ctx;
812 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
814 d_state = h->data;
815 sam_ctx = d_state->sam_ctx;
817 msg = ldb_msg_new(mem_ctx);
818 if (msg == NULL) {
819 return NT_STATUS_NO_MEMORY;
822 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
823 if (!msg->dn) {
824 return NT_STATUS_NO_MEMORY;
827 switch (r->in.level) {
828 case 1:
829 SET_UINT (msg, info1.min_password_length, "minPwdLength");
830 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
831 SET_UINT (msg, info1.password_properties, "pwdProperties");
832 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
833 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
834 break;
835 case 3:
836 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
837 break;
838 case 4:
839 SET_STRING(msg, info4.comment.string, "comment");
840 break;
842 case 6:
843 case 7:
844 case 9:
845 /* No op, we don't know where to set these */
846 return NT_STATUS_OK;
848 case 12:
850 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
851 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
852 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
853 break;
855 default:
856 /* many info classes are not valid for SetDomainInfo */
857 return NT_STATUS_INVALID_INFO_CLASS;
860 /* modify the samdb record */
861 ret = samdb_replace(sam_ctx, mem_ctx, msg);
862 if (ret != 0) {
863 DEBUG(1,("Failed to modify record %s: %s\n",
864 ldb_dn_linearize(mem_ctx, d_state->domain_dn),
865 ldb_errstring(sam_ctx)));
867 /* we really need samdb.c to return NTSTATUS */
868 return NT_STATUS_UNSUCCESSFUL;
871 return NT_STATUS_OK;
875 samr_CreateDomainGroup
877 static NTSTATUS samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
878 struct samr_CreateDomainGroup *r)
880 struct samr_domain_state *d_state;
881 struct samr_account_state *a_state;
882 struct dcesrv_handle *h;
883 const char *name;
884 struct ldb_message *msg;
885 struct dom_sid *sid;
886 const char *groupname;
887 struct dcesrv_handle *g_handle;
888 int ret;
890 ZERO_STRUCTP(r->out.group_handle);
891 *r->out.rid = 0;
893 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
895 d_state = h->data;
897 groupname = r->in.name->string;
899 if (groupname == NULL) {
900 return NT_STATUS_INVALID_PARAMETER;
903 /* check if the group already exists */
904 name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
905 "sAMAccountName",
906 "(&(sAMAccountName=%s)(objectclass=group))",
907 ldb_binary_encode_string(mem_ctx, groupname));
908 if (name != NULL) {
909 return NT_STATUS_GROUP_EXISTS;
912 msg = ldb_msg_new(mem_ctx);
913 if (msg == NULL) {
914 return NT_STATUS_NO_MEMORY;
917 /* add core elements to the ldb_message for the user */
918 msg->dn = ldb_dn_string_compose(mem_ctx, d_state->domain_dn,
919 "CN=%s,CN=Users", groupname);
920 if (!msg->dn) {
921 return NT_STATUS_NO_MEMORY;
923 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", groupname);
924 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
926 /* create the group */
927 ret = samdb_add(d_state->sam_ctx, mem_ctx, msg);
928 if (ret != 0) {
929 DEBUG(0,("Failed to create group record %s\n",
930 ldb_dn_linearize(mem_ctx, msg->dn)));
931 return NT_STATUS_INTERNAL_DB_CORRUPTION;
934 a_state = talloc(d_state, struct samr_account_state);
935 if (!a_state) {
936 return NT_STATUS_NO_MEMORY;
938 a_state->sam_ctx = d_state->sam_ctx;
939 a_state->access_mask = r->in.access_mask;
940 a_state->domain_state = talloc_reference(a_state, d_state);
941 a_state->account_dn = talloc_steal(a_state, msg->dn);
943 /* retrieve the sid for the group just created */
944 sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
945 msg->dn, "objectSid", NULL);
946 if (sid == NULL) {
947 return NT_STATUS_UNSUCCESSFUL;
950 a_state->account_name = talloc_strdup(a_state, groupname);
951 if (!a_state->account_name) {
952 return NT_STATUS_NO_MEMORY;
955 /* create the policy handle */
956 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
957 if (!g_handle) {
958 return NT_STATUS_NO_MEMORY;
961 g_handle->data = talloc_steal(g_handle, a_state);
963 *r->out.group_handle = g_handle->wire_handle;
964 *r->out.rid = sid->sub_auths[sid->num_auths-1];
966 return NT_STATUS_OK;
971 comparison function for sorting SamEntry array
973 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
975 return e1->idx - e2->idx;
979 samr_EnumDomainGroups
981 static NTSTATUS samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
982 struct samr_EnumDomainGroups *r)
984 struct dcesrv_handle *h;
985 struct samr_domain_state *d_state;
986 struct ldb_message **res;
987 int ldb_cnt, count, i, first;
988 struct samr_SamEntry *entries;
989 const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
991 *r->out.resume_handle = 0;
992 r->out.sam = NULL;
993 r->out.num_entries = 0;
995 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
997 d_state = h->data;
999 /* search for all domain groups in this domain. This could possibly be
1000 cached and resumed based on resume_key */
1001 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1002 d_state->domain_dn, &res, attrs,
1003 d_state->domain_sid,
1004 "(&(grouptype=%d)(objectclass=group))",
1005 GTYPE_SECURITY_GLOBAL_GROUP);
1006 if (ldb_cnt == -1) {
1007 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1009 if (ldb_cnt == 0 || r->in.max_size == 0) {
1010 return NT_STATUS_OK;
1013 /* convert to SamEntry format */
1014 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1015 if (!entries) {
1016 return NT_STATUS_NO_MEMORY;
1019 count = 0;
1021 for (i=0;i<ldb_cnt;i++) {
1022 struct dom_sid *group_sid;
1024 group_sid = samdb_result_dom_sid(mem_ctx, res[i],
1025 "objectSid");
1026 if (group_sid == NULL)
1027 continue;
1029 entries[count].idx =
1030 group_sid->sub_auths[group_sid->num_auths-1];
1031 entries[count].name.string =
1032 samdb_result_string(res[i], "sAMAccountName", "");
1033 count += 1;
1036 /* sort the results by rid */
1037 qsort(entries, count, sizeof(struct samr_SamEntry),
1038 (comparison_fn_t)compare_SamEntry);
1040 /* find the first entry to return */
1041 for (first=0;
1042 first<count && entries[first].idx <= *r->in.resume_handle;
1043 first++) ;
1045 if (first == count) {
1046 return NT_STATUS_OK;
1049 /* return the rest, limit by max_size. Note that we
1050 use the w2k3 element size value of 54 */
1051 r->out.num_entries = count - first;
1052 r->out.num_entries = MIN(r->out.num_entries,
1053 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1055 r->out.sam = talloc(mem_ctx, struct samr_SamArray);
1056 if (!r->out.sam) {
1057 return NT_STATUS_NO_MEMORY;
1060 r->out.sam->entries = entries+first;
1061 r->out.sam->count = r->out.num_entries;
1063 if (r->out.num_entries < count - first) {
1064 *r->out.resume_handle = entries[first+r->out.num_entries-1].idx;
1065 return STATUS_MORE_ENTRIES;
1068 return NT_STATUS_OK;
1073 samr_CreateUser2
1075 This call uses transactions to ensure we don't get a new conflicting
1076 user while we are processing this, and to ensure the user either
1077 completly exists, or does not.
1079 static NTSTATUS samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1080 struct samr_CreateUser2 *r)
1082 struct samr_domain_state *d_state;
1083 struct samr_account_state *a_state;
1084 struct dcesrv_handle *h;
1085 const char *name;
1086 struct ldb_message *msg;
1087 struct dom_sid *sid;
1088 const char *account_name;
1089 struct dcesrv_handle *u_handle;
1090 int ret;
1091 const char *container, *obj_class=NULL;
1092 char *cn_name;
1093 int cn_name_len;
1095 const char *attrs[] = {
1096 "objectSid",
1097 "userAccountControl",
1098 NULL
1101 uint32_t user_account_control;
1103 struct ldb_message **msgs;
1105 ZERO_STRUCTP(r->out.user_handle);
1106 *r->out.access_granted = 0;
1107 *r->out.rid = 0;
1109 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1111 d_state = h->data;
1113 account_name = r->in.account_name->string;
1115 if (account_name == NULL) {
1116 return NT_STATUS_INVALID_PARAMETER;
1119 ret = ldb_transaction_start(d_state->sam_ctx);
1120 if (ret != 0) {
1121 DEBUG(0,("Failed to start a transaction for user creation: %s\n",
1122 ldb_errstring(d_state->sam_ctx)));
1123 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1126 /* check if the user already exists */
1127 name = samdb_search_string(d_state->sam_ctx, mem_ctx, samdb_base_dn(mem_ctx),
1128 "sAMAccountName",
1129 "(&(sAMAccountName=%s)(objectclass=user))",
1130 ldb_binary_encode_string(mem_ctx, account_name));
1131 if (name != NULL) {
1132 ldb_transaction_cancel(d_state->sam_ctx);
1133 return NT_STATUS_USER_EXISTS;
1136 msg = ldb_msg_new(mem_ctx);
1137 if (msg == NULL) {
1138 ldb_transaction_cancel(d_state->sam_ctx);
1139 return NT_STATUS_NO_MEMORY;
1142 cn_name = talloc_strdup(mem_ctx, account_name);
1143 if (!cn_name) {
1144 ldb_transaction_cancel(d_state->sam_ctx);
1145 return NT_STATUS_NO_MEMORY;
1148 cn_name_len = strlen(cn_name);
1150 /* This must be one of these values *only* */
1151 if (r->in.acct_flags == ACB_NORMAL) {
1152 container = "Users";
1153 obj_class = "user";
1155 } else if (r->in.acct_flags == ACB_WSTRUST) {
1156 if (cn_name[cn_name_len - 1] != '$') {
1157 return NT_STATUS_FOOBAR;
1159 cn_name[cn_name_len - 1] = '\0';
1160 container = "Computers";
1161 obj_class = "computer";
1163 } else if (r->in.acct_flags == ACB_SVRTRUST) {
1164 if (cn_name[cn_name_len - 1] != '$') {
1165 return NT_STATUS_FOOBAR;
1167 cn_name[cn_name_len - 1] = '\0';
1168 container = "Domain Controllers";
1169 obj_class = "computer";
1171 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1172 container = "Users";
1173 obj_class = "user";
1175 } else {
1176 ldb_transaction_cancel(d_state->sam_ctx);
1177 return NT_STATUS_INVALID_PARAMETER;
1180 /* add core elements to the ldb_message for the user */
1181 msg->dn = ldb_dn_build_child(mem_ctx, "CN", cn_name, ldb_dn_build_child(mem_ctx, "CN", container, d_state->domain_dn));
1182 if (!msg->dn) {
1183 ldb_transaction_cancel(d_state->sam_ctx);
1184 return NT_STATUS_NO_MEMORY;
1186 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", account_name);
1187 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", obj_class);
1189 /* Start a transaction, so we can query and do a subsequent atomic modify */
1191 /* create the user */
1192 ret = samdb_add(d_state->sam_ctx, mem_ctx, msg);
1193 switch (ret) {
1194 case LDB_SUCCESS:
1195 break;
1196 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1197 ldb_transaction_cancel(d_state->sam_ctx);
1198 DEBUG(0,("Failed to create user record %s: %s\n",
1199 ldb_dn_linearize(mem_ctx, msg->dn),
1200 ldb_errstring(d_state->sam_ctx)));
1201 return NT_STATUS_USER_EXISTS;
1202 default:
1203 ldb_transaction_cancel(d_state->sam_ctx);
1204 DEBUG(0,("Failed to create user record %s: %s\n",
1205 ldb_dn_linearize(mem_ctx, msg->dn),
1206 ldb_errstring(d_state->sam_ctx)));
1207 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1210 a_state = talloc(d_state, struct samr_account_state);
1211 if (!a_state) {
1212 ldb_transaction_cancel(d_state->sam_ctx);
1213 return NT_STATUS_NO_MEMORY;
1215 a_state->sam_ctx = d_state->sam_ctx;
1216 a_state->access_mask = r->in.access_mask;
1217 a_state->domain_state = talloc_reference(a_state, d_state);
1218 a_state->account_dn = talloc_steal(a_state, msg->dn);
1220 /* retrieve the sid and account control bits for the user just created */
1221 ret = gendb_search_dn(d_state->sam_ctx, a_state,
1222 msg->dn, &msgs, attrs);
1224 if (ret != 1) {
1225 ldb_transaction_cancel(d_state->sam_ctx);
1226 DEBUG(0,("Apparently we failed to create an account record, as %s now doesn't exist\n",
1227 ldb_dn_linearize(mem_ctx, msg->dn)));
1228 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1230 sid = samdb_result_dom_sid(mem_ctx, msgs[0], "objectSid");
1231 if (sid == NULL) {
1232 ldb_transaction_cancel(d_state->sam_ctx);
1233 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1236 /* Change the account control to be the correct account type.
1237 * The default is for a workstation account */
1238 user_account_control = samdb_result_uint(msgs[0], "userAccountControl", 0);
1239 user_account_control = (user_account_control &
1240 ~(UF_NORMAL_ACCOUNT |
1241 UF_INTERDOMAIN_TRUST_ACCOUNT |
1242 UF_WORKSTATION_TRUST_ACCOUNT |
1243 UF_SERVER_TRUST_ACCOUNT));
1244 user_account_control |= samdb_acb2uf(r->in.acct_flags);
1246 talloc_free(msg);
1247 msg = ldb_msg_new(mem_ctx);
1248 if (msg == NULL) {
1249 ldb_transaction_cancel(d_state->sam_ctx);
1250 return NT_STATUS_NO_MEMORY;
1253 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
1255 if (samdb_msg_add_uint(a_state->sam_ctx, mem_ctx, msg,
1256 "userAccountControl",
1257 user_account_control) != 0) {
1258 ldb_transaction_cancel(d_state->sam_ctx);
1259 return NT_STATUS_NO_MEMORY;
1262 /* modify the samdb record */
1263 ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
1264 if (ret != 0) {
1265 DEBUG(0,("Failed to modify account record %s to set userAccountControl: %s\n",
1266 ldb_dn_linearize(mem_ctx, msg->dn),
1267 ldb_errstring(d_state->sam_ctx)));
1268 ldb_transaction_cancel(d_state->sam_ctx);
1270 /* we really need samdb.c to return NTSTATUS */
1271 return NT_STATUS_UNSUCCESSFUL;
1274 ret = ldb_transaction_commit(d_state->sam_ctx);
1275 if (ret != 0) {
1276 DEBUG(0,("Failed to commit transaction to add and modify account record %s: %s\n",
1277 ldb_dn_linearize(mem_ctx, msg->dn),
1278 ldb_errstring(d_state->sam_ctx)));
1279 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1282 a_state->account_name = talloc_steal(a_state, account_name);
1283 if (!a_state->account_name) {
1284 return NT_STATUS_NO_MEMORY;
1287 /* create the policy handle */
1288 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
1289 if (!u_handle) {
1290 return NT_STATUS_NO_MEMORY;
1293 u_handle->data = talloc_steal(u_handle, a_state);
1295 *r->out.user_handle = u_handle->wire_handle;
1296 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1298 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1300 return NT_STATUS_OK;
1305 samr_CreateUser
1307 static NTSTATUS samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1308 struct samr_CreateUser *r)
1310 struct samr_CreateUser2 r2;
1311 uint32_t access_granted = 0;
1314 /* a simple wrapper around samr_CreateUser2 works nicely */
1315 r2.in.domain_handle = r->in.domain_handle;
1316 r2.in.account_name = r->in.account_name;
1317 r2.in.acct_flags = ACB_NORMAL;
1318 r2.in.access_mask = r->in.access_mask;
1319 r2.out.user_handle = r->out.user_handle;
1320 r2.out.access_granted = &access_granted;
1321 r2.out.rid = r->out.rid;
1323 return samr_CreateUser2(dce_call, mem_ctx, &r2);
1327 samr_EnumDomainUsers
1329 static NTSTATUS samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1330 struct samr_EnumDomainUsers *r)
1332 struct dcesrv_handle *h;
1333 struct samr_domain_state *d_state;
1334 struct ldb_message **res;
1335 int count, i, first;
1336 struct samr_SamEntry *entries;
1337 const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
1339 *r->out.resume_handle = 0;
1340 r->out.sam = NULL;
1341 r->out.num_entries = 0;
1343 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1345 d_state = h->data;
1347 /* search for all users in this domain. This could possibly be cached and
1348 resumed based on resume_key */
1349 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1350 "objectclass=user");
1351 if (count == -1) {
1352 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1354 if (count == 0 || r->in.max_size == 0) {
1355 return NT_STATUS_OK;
1358 /* convert to SamEntry format */
1359 entries = talloc_array(mem_ctx, struct samr_SamEntry, count);
1360 if (!entries) {
1361 return NT_STATUS_NO_MEMORY;
1363 for (i=0;i<count;i++) {
1364 entries[i].idx = samdb_result_rid_from_sid(mem_ctx, res[i], "objectSid", 0);
1365 entries[i].name.string = samdb_result_string(res[i], "sAMAccountName", "");
1368 /* sort the results by rid */
1369 qsort(entries, count, sizeof(struct samr_SamEntry),
1370 (comparison_fn_t)compare_SamEntry);
1372 /* find the first entry to return */
1373 for (first=0;
1374 first<count && entries[first].idx <= *r->in.resume_handle;
1375 first++) ;
1377 if (first == count) {
1378 return NT_STATUS_OK;
1381 /* return the rest, limit by max_size. Note that we
1382 use the w2k3 element size value of 54 */
1383 r->out.num_entries = count - first;
1384 r->out.num_entries = MIN(r->out.num_entries,
1385 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1387 r->out.sam = talloc(mem_ctx, struct samr_SamArray);
1388 if (!r->out.sam) {
1389 return NT_STATUS_NO_MEMORY;
1392 r->out.sam->entries = entries+first;
1393 r->out.sam->count = r->out.num_entries;
1395 if (r->out.num_entries < count - first) {
1396 *r->out.resume_handle = entries[first+r->out.num_entries-1].idx;
1397 return STATUS_MORE_ENTRIES;
1400 return NT_STATUS_OK;
1405 samr_CreateDomAlias
1407 static NTSTATUS samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1408 struct samr_CreateDomAlias *r)
1410 struct samr_domain_state *d_state;
1411 struct samr_account_state *a_state;
1412 struct dcesrv_handle *h;
1413 const char *alias_name, *name;
1414 struct ldb_message *msg;
1415 struct dom_sid *sid;
1416 struct dcesrv_handle *a_handle;
1417 int ret;
1419 ZERO_STRUCTP(r->out.alias_handle);
1420 *r->out.rid = 0;
1422 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1424 d_state = h->data;
1426 alias_name = r->in.alias_name->string;
1428 if (alias_name == NULL) {
1429 return NT_STATUS_INVALID_PARAMETER;
1432 /* Check if alias already exists */
1433 name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1434 "sAMAccountName",
1435 "(sAMAccountName=%s)(objectclass=group))",
1436 ldb_binary_encode_string(mem_ctx, alias_name));
1438 if (name != NULL) {
1439 return NT_STATUS_ALIAS_EXISTS;
1442 msg = ldb_msg_new(mem_ctx);
1443 if (msg == NULL) {
1444 return NT_STATUS_NO_MEMORY;
1447 /* add core elements to the ldb_message for the alias */
1448 msg->dn = ldb_dn_string_compose(mem_ctx, d_state->domain_dn,
1449 "CN=%s, CN=Users", alias_name);
1450 if (!msg->dn) {
1451 return NT_STATUS_NO_MEMORY;
1454 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", alias_name);
1455 samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
1456 samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "groupType", GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1458 /* create the alias */
1459 ret = samdb_add(d_state->sam_ctx, mem_ctx, msg);
1460 switch (ret) {
1461 case LDB_SUCCESS:
1462 break;
1463 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1464 return NT_STATUS_ALIAS_EXISTS;
1465 default:
1466 DEBUG(0,("Failed to create alias record %s: %s\n",
1467 ldb_dn_linearize(mem_ctx, msg->dn),
1468 ldb_errstring(d_state->sam_ctx)));
1469 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1472 a_state = talloc(d_state, struct samr_account_state);
1473 if (!a_state) {
1474 return NT_STATUS_NO_MEMORY;
1477 a_state->sam_ctx = d_state->sam_ctx;
1478 a_state->access_mask = r->in.access_mask;
1479 a_state->domain_state = talloc_reference(a_state, d_state);
1480 a_state->account_dn = talloc_steal(a_state, msg->dn);
1482 /* retrieve the sid for the alias just created */
1483 sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
1484 msg->dn, "objectSid", NULL);
1486 a_state->account_name = talloc_strdup(a_state, alias_name);
1487 if (!a_state->account_name) {
1488 return NT_STATUS_NO_MEMORY;
1491 /* create the policy handle */
1492 a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1493 if (a_handle == NULL)
1494 return NT_STATUS_NO_MEMORY;
1496 a_handle->data = talloc_steal(a_handle, a_state);
1498 *r->out.alias_handle = a_handle->wire_handle;
1500 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1502 return NT_STATUS_OK;
1507 samr_EnumDomainAliases
1509 static NTSTATUS samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1510 struct samr_EnumDomainAliases *r)
1512 struct dcesrv_handle *h;
1513 struct samr_domain_state *d_state;
1514 struct ldb_message **res;
1515 int ldb_cnt, count, i, first;
1516 struct samr_SamEntry *entries;
1517 const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
1519 *r->out.resume_handle = 0;
1520 r->out.sam = NULL;
1521 r->out.num_entries = 0;
1523 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1525 d_state = h->data;
1527 /* search for all domain groups in this domain. This could possibly be
1528 cached and resumed based on resume_key */
1529 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1530 d_state->domain_dn,
1531 &res, attrs,
1532 d_state->domain_sid,
1533 "(&(|(grouptype=%d)(grouptype=%d)))"
1534 "(objectclass=group))",
1535 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1536 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1537 if (ldb_cnt == -1) {
1538 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1540 if (ldb_cnt == 0) {
1541 return NT_STATUS_OK;
1544 /* convert to SamEntry format */
1545 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1546 if (!entries) {
1547 return NT_STATUS_NO_MEMORY;
1550 count = 0;
1552 for (i=0;i<ldb_cnt;i++) {
1553 struct dom_sid *alias_sid;
1555 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1556 "objectSid");
1558 if (alias_sid == NULL)
1559 continue;
1561 entries[count].idx =
1562 alias_sid->sub_auths[alias_sid->num_auths-1];
1563 entries[count].name.string =
1564 samdb_result_string(res[i], "sAMAccountName", "");
1565 count += 1;
1568 /* sort the results by rid */
1569 qsort(entries, count, sizeof(struct samr_SamEntry),
1570 (comparison_fn_t)compare_SamEntry);
1572 /* find the first entry to return */
1573 for (first=0;
1574 first<count && entries[first].idx <= *r->in.resume_handle;
1575 first++) ;
1577 if (first == count) {
1578 return NT_STATUS_OK;
1581 r->out.num_entries = count - first;
1582 r->out.num_entries = MIN(r->out.num_entries, 1000);
1584 r->out.sam = talloc(mem_ctx, struct samr_SamArray);
1585 if (!r->out.sam) {
1586 return NT_STATUS_NO_MEMORY;
1589 r->out.sam->entries = entries+first;
1590 r->out.sam->count = r->out.num_entries;
1592 if (r->out.num_entries < count - first) {
1593 *r->out.resume_handle =
1594 entries[first+r->out.num_entries-1].idx;
1595 return STATUS_MORE_ENTRIES;
1598 return NT_STATUS_OK;
1603 samr_GetAliasMembership
1605 static NTSTATUS samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1606 struct samr_GetAliasMembership *r)
1608 struct dcesrv_handle *h;
1609 struct samr_domain_state *d_state;
1610 struct ldb_message **res;
1611 int i, count = 0;
1613 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1615 d_state = h->data;
1617 if (r->in.sids->num_sids > 0) {
1618 const char *filter;
1619 const char * const attrs[2] = { "objectSid", NULL };
1621 filter = talloc_asprintf(mem_ctx,
1622 "(&(|(grouptype=%d)(grouptype=%d))"
1623 "(objectclass=group)(|",
1624 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1625 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1626 if (filter == NULL)
1627 return NT_STATUS_NO_MEMORY;
1629 for (i=0; i<r->in.sids->num_sids; i++) {
1630 const char *memberdn;
1632 memberdn =
1633 samdb_search_string(d_state->sam_ctx,
1634 mem_ctx, samdb_base_dn(mem_ctx), "distinguishedName",
1635 "(objectSid=%s)",
1636 ldap_encode_ndr_dom_sid(mem_ctx,
1637 r->in.sids->sids[i].sid));
1639 if (memberdn == NULL)
1640 continue;
1642 filter = talloc_asprintf(mem_ctx, "%s(member=%s)",
1643 filter, memberdn);
1644 if (filter == NULL)
1645 return NT_STATUS_NO_MEMORY;
1648 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1649 d_state->domain_dn, &res, attrs,
1650 d_state->domain_sid, "%s))", filter);
1651 if (count < 0)
1652 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1655 r->out.rids->count = 0;
1656 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1657 if (r->out.rids->ids == NULL)
1658 return NT_STATUS_NO_MEMORY;
1660 for (i=0; i<count; i++) {
1661 struct dom_sid *alias_sid;
1663 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1665 if (alias_sid == NULL) {
1666 DEBUG(0, ("Could not find objectSid\n"));
1667 continue;
1670 r->out.rids->ids[r->out.rids->count] =
1671 alias_sid->sub_auths[alias_sid->num_auths-1];
1672 r->out.rids->count += 1;
1675 return NT_STATUS_OK;
1680 samr_LookupNames
1682 static NTSTATUS samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1683 struct samr_LookupNames *r)
1685 struct dcesrv_handle *h;
1686 struct samr_domain_state *d_state;
1687 int i;
1688 NTSTATUS status = NT_STATUS_OK;
1689 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1690 int count;
1692 ZERO_STRUCT(r->out.rids);
1693 ZERO_STRUCT(r->out.types);
1695 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1697 d_state = h->data;
1699 if (r->in.num_names == 0) {
1700 return NT_STATUS_OK;
1703 r->out.rids.ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1704 r->out.types.ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1705 if (!r->out.rids.ids || !r->out.types.ids) {
1706 return NT_STATUS_NO_MEMORY;
1708 r->out.rids.count = r->in.num_names;
1709 r->out.types.count = r->in.num_names;
1711 for (i=0;i<r->in.num_names;i++) {
1712 struct ldb_message **res;
1713 struct dom_sid *sid;
1714 uint32_t atype, rtype;
1716 r->out.rids.ids[i] = 0;
1717 r->out.types.ids[i] = SID_NAME_UNKNOWN;
1719 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1720 "sAMAccountName=%s",
1721 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
1722 if (count != 1) {
1723 status = STATUS_SOME_UNMAPPED;
1724 continue;
1727 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
1728 if (sid == NULL) {
1729 status = STATUS_SOME_UNMAPPED;
1730 continue;
1733 atype = samdb_result_uint(res[0], "sAMAccountType", 0);
1734 if (atype == 0) {
1735 status = STATUS_SOME_UNMAPPED;
1736 continue;
1739 rtype = samdb_atype_map(atype);
1741 if (rtype == SID_NAME_UNKNOWN) {
1742 status = STATUS_SOME_UNMAPPED;
1743 continue;
1746 r->out.rids.ids[i] = sid->sub_auths[sid->num_auths-1];
1747 r->out.types.ids[i] = rtype;
1751 return status;
1756 samr_LookupRids
1758 static NTSTATUS samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1759 struct samr_LookupRids *r)
1761 struct dcesrv_handle *h;
1762 struct samr_domain_state *d_state;
1763 int i, total;
1764 NTSTATUS status = NT_STATUS_OK;
1765 struct lsa_String *names;
1766 uint32_t *ids;
1768 ZERO_STRUCT(r->out.names);
1769 ZERO_STRUCT(r->out.types);
1771 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1773 d_state = h->data;
1775 if (r->in.num_rids == 0)
1776 return NT_STATUS_OK;
1778 names = talloc_array(mem_ctx, struct lsa_String, r->in.num_rids);
1779 ids = talloc_array(mem_ctx, uint32_t, r->in.num_rids);
1781 if ((names == NULL) || (ids == NULL))
1782 return NT_STATUS_NO_MEMORY;
1784 total = 0;
1786 for (i=0; i<r->in.num_rids; i++) {
1787 struct ldb_message **res;
1788 int count;
1789 const char * const attrs[] = { "sAMAccountType",
1790 "sAMAccountName", NULL };
1791 uint32_t atype;
1792 struct dom_sid *sid;
1794 ids[i] = SID_NAME_UNKNOWN;
1796 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rids[i]);
1797 if (sid == NULL) {
1798 names[i].string = NULL;
1799 status = STATUS_SOME_UNMAPPED;
1800 continue;
1803 count = gendb_search(d_state->sam_ctx, mem_ctx,
1804 d_state->domain_dn, &res, attrs,
1805 "(objectSid=%s)",
1806 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1807 if (count != 1) {
1808 names[i].string = NULL;
1809 status = STATUS_SOME_UNMAPPED;
1810 continue;
1813 names[i].string = samdb_result_string(res[0], "sAMAccountName",
1814 NULL);
1816 atype = samdb_result_uint(res[0], "sAMAccountType", 0);
1817 if (atype == 0) {
1818 status = STATUS_SOME_UNMAPPED;
1819 continue;
1822 ids[i] = samdb_atype_map(atype);
1824 if (ids[i] == SID_NAME_UNKNOWN) {
1825 status = STATUS_SOME_UNMAPPED;
1826 continue;
1830 r->out.names.names = names;
1831 r->out.names.count = r->in.num_rids;
1833 r->out.types.ids = ids;
1834 r->out.types.count = r->in.num_rids;
1836 return status;
1841 samr_OpenGroup
1843 static NTSTATUS samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1844 struct samr_OpenGroup *r)
1846 struct samr_domain_state *d_state;
1847 struct samr_account_state *a_state;
1848 struct dcesrv_handle *h;
1849 const char *groupname;
1850 struct dom_sid *sid;
1851 struct ldb_message **msgs;
1852 struct dcesrv_handle *g_handle;
1853 const char * const attrs[2] = { "sAMAccountName", NULL };
1854 int ret;
1856 ZERO_STRUCTP(r->out.group_handle);
1858 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1860 d_state = h->data;
1862 /* form the group SID */
1863 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1864 if (!sid) {
1865 return NT_STATUS_NO_MEMORY;
1868 /* search for the group record */
1869 ret = gendb_search(d_state->sam_ctx,
1870 mem_ctx, d_state->domain_dn, &msgs, attrs,
1871 "(&(objectSid=%s)(objectclass=group)"
1872 "(grouptype=%d))",
1873 ldap_encode_ndr_dom_sid(mem_ctx, sid),
1874 GTYPE_SECURITY_GLOBAL_GROUP);
1875 if (ret == 0) {
1876 return NT_STATUS_NO_SUCH_GROUP;
1878 if (ret != 1) {
1879 DEBUG(0,("Found %d records matching sid %s\n",
1880 ret, dom_sid_string(mem_ctx, sid)));
1881 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1884 groupname = samdb_result_string(msgs[0], "sAMAccountName", NULL);
1885 if (groupname == NULL) {
1886 DEBUG(0,("sAMAccountName field missing for sid %s\n",
1887 dom_sid_string(mem_ctx, sid)));
1888 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1891 a_state = talloc(d_state, struct samr_account_state);
1892 if (!a_state) {
1893 return NT_STATUS_NO_MEMORY;
1895 a_state->sam_ctx = d_state->sam_ctx;
1896 a_state->access_mask = r->in.access_mask;
1897 a_state->domain_state = talloc_reference(a_state, d_state);
1898 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
1899 a_state->account_sid = talloc_steal(a_state, sid);
1900 a_state->account_name = talloc_strdup(a_state, groupname);
1901 if (!a_state->account_name) {
1902 return NT_STATUS_NO_MEMORY;
1905 /* create the policy handle */
1906 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1907 if (!g_handle) {
1908 return NT_STATUS_NO_MEMORY;
1911 g_handle->data = talloc_steal(g_handle, a_state);
1913 *r->out.group_handle = g_handle->wire_handle;
1915 return NT_STATUS_OK;
1919 samr_QueryGroupInfo
1921 static NTSTATUS samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1922 struct samr_QueryGroupInfo *r)
1924 struct dcesrv_handle *h;
1925 struct samr_account_state *a_state;
1926 struct ldb_message *msg, **res;
1927 const char * const attrs[4] = { "sAMAccountName", "description",
1928 "numMembers", NULL };
1929 int ret;
1931 r->out.info = NULL;
1933 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1935 a_state = h->data;
1937 /* pull all the group attributes */
1938 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
1939 a_state->account_dn, &res, attrs);
1940 if (ret != 1) {
1941 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1943 msg = res[0];
1945 /* allocate the info structure */
1946 r->out.info = talloc(mem_ctx, union samr_GroupInfo);
1947 if (r->out.info == NULL) {
1948 return NT_STATUS_NO_MEMORY;
1950 ZERO_STRUCTP(r->out.info);
1952 /* Fill in the level */
1953 switch (r->in.level) {
1954 case GROUPINFOALL:
1955 QUERY_STRING(msg, all.name.string, "sAMAccountName");
1956 r->out.info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1957 QUERY_UINT (msg, all.num_members, "numMembers")
1958 QUERY_STRING(msg, all.description.string, "description");
1959 break;
1960 case GROUPINFONAME:
1961 QUERY_STRING(msg, name.string, "sAMAccountName");
1962 break;
1963 case GROUPINFOATTRIBUTES:
1964 r->out.info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1965 break;
1966 case GROUPINFODESCRIPTION:
1967 QUERY_STRING(msg, description.string, "description");
1968 break;
1969 case GROUPINFOALL2:
1970 QUERY_STRING(msg, all2.name.string, "sAMAccountName");
1971 r->out.info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1972 QUERY_UINT (msg, all2.num_members, "numMembers")
1973 QUERY_STRING(msg, all2.description.string, "description");
1974 break;
1975 default:
1976 r->out.info = NULL;
1977 return NT_STATUS_INVALID_INFO_CLASS;
1980 return NT_STATUS_OK;
1985 samr_SetGroupInfo
1987 static NTSTATUS samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1988 struct samr_SetGroupInfo *r)
1990 struct dcesrv_handle *h;
1991 struct samr_account_state *g_state;
1992 struct ldb_message *msg;
1993 struct ldb_context *sam_ctx;
1994 int ret;
1996 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1998 g_state = h->data;
1999 sam_ctx = g_state->sam_ctx;
2001 msg = ldb_msg_new(mem_ctx);
2002 if (msg == NULL) {
2003 return NT_STATUS_NO_MEMORY;
2006 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2007 if (!msg->dn) {
2008 return NT_STATUS_NO_MEMORY;
2011 switch (r->in.level) {
2012 case GROUPINFODESCRIPTION:
2013 SET_STRING(msg, description.string, "description");
2014 break;
2015 case GROUPINFONAME:
2016 /* On W2k3 this does not change the name, it changes the
2017 * sAMAccountName attribute */
2018 SET_STRING(msg, name.string, "sAMAccountName");
2019 break;
2020 case GROUPINFOATTRIBUTES:
2021 /* This does not do anything obviously visible in W2k3 LDAP */
2022 break;
2023 default:
2024 return NT_STATUS_INVALID_INFO_CLASS;
2027 /* modify the samdb record */
2028 ret = samdb_replace(g_state->sam_ctx, mem_ctx, msg);
2029 if (ret != 0) {
2030 /* we really need samdb.c to return NTSTATUS */
2031 return NT_STATUS_UNSUCCESSFUL;
2034 return NT_STATUS_OK;
2039 samr_AddGroupMember
2041 static NTSTATUS samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2042 struct samr_AddGroupMember *r)
2044 struct dcesrv_handle *h;
2045 struct samr_account_state *a_state;
2046 struct samr_domain_state *d_state;
2047 struct ldb_message *mod;
2048 struct dom_sid *membersid;
2049 const char *memberdn;
2050 struct ldb_result *res;
2051 const char * const attrs[] = { NULL };
2052 const char *filter;
2053 int ret;
2055 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2057 a_state = h->data;
2058 d_state = a_state->domain_state;
2060 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2061 if (membersid == NULL)
2062 return NT_STATUS_NO_MEMORY;
2064 filter = talloc_asprintf(mem_ctx, "(&(objectSid=%s)(objectclass=user))",
2065 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2067 /* In native mode, AD can also nest domain groups. Not sure yet
2068 * whether this is also available via RPC. */
2069 ret = ldb_search(d_state->sam_ctx, d_state->domain_dn, LDB_SCOPE_SUBTREE,
2070 filter, attrs, &res);
2072 if (ret != 0) {
2073 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2075 talloc_steal(mem_ctx, res);
2077 if (res->count == 0) {
2078 return NT_STATUS_NO_SUCH_USER;
2081 if (res->count > 1) {
2082 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2085 memberdn = ldb_dn_linearize(mem_ctx, res->msgs[0]->dn);
2087 if (memberdn == NULL)
2088 return NT_STATUS_NO_MEMORY;
2090 mod = ldb_msg_new(mem_ctx);
2091 if (mod == NULL) {
2092 return NT_STATUS_NO_MEMORY;
2095 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2097 if (samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2098 memberdn) != 0)
2099 return NT_STATUS_UNSUCCESSFUL;
2101 ret = samdb_modify(a_state->sam_ctx, mem_ctx, mod);
2102 switch (ret) {
2103 case LDB_SUCCESS:
2104 return NT_STATUS_OK;
2105 case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
2106 return NT_STATUS_MEMBER_IN_GROUP;
2107 default:
2108 return NT_STATUS_UNSUCCESSFUL;
2115 samr_DeleteDomainGroup
2117 static NTSTATUS samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2118 struct samr_DeleteDomainGroup *r)
2120 struct dcesrv_handle *h;
2121 struct samr_account_state *a_state;
2122 int ret;
2124 *r->out.group_handle = *r->in.group_handle;
2126 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2128 a_state = h->data;
2130 ret = samdb_delete(a_state->sam_ctx, mem_ctx, a_state->account_dn);
2131 if (ret != 0) {
2132 return NT_STATUS_UNSUCCESSFUL;
2135 ZERO_STRUCTP(r->out.group_handle);
2137 return NT_STATUS_OK;
2142 samr_DeleteGroupMember
2144 static NTSTATUS samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2145 struct samr_DeleteGroupMember *r)
2147 struct dcesrv_handle *h;
2148 struct samr_account_state *a_state;
2149 struct samr_domain_state *d_state;
2150 struct ldb_message *mod;
2151 struct dom_sid *membersid;
2152 const char *memberdn;
2153 struct ldb_result *res;
2154 const char * const attrs[] = { NULL };
2155 const char *filter;
2156 int ret;
2158 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2160 a_state = h->data;
2161 d_state = a_state->domain_state;
2163 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2164 if (membersid == NULL)
2165 return NT_STATUS_NO_MEMORY;
2167 filter = talloc_asprintf(mem_ctx, "(&(objectSid=%s)(objectclass=user))",
2168 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2170 /* In native mode, AD can also nest domain groups. Not sure yet
2171 * whether this is also available via RPC. */
2172 ret = ldb_search(d_state->sam_ctx, d_state->domain_dn, LDB_SCOPE_SUBTREE,
2173 filter, attrs, &res);
2175 if (ret != 0) {
2176 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2178 talloc_steal(mem_ctx, res);
2180 if (res->count == 0) {
2181 return NT_STATUS_NO_SUCH_USER;
2184 if (res->count > 1) {
2185 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2188 memberdn = ldb_dn_linearize(mem_ctx, res->msgs[0]->dn);
2190 if (memberdn == NULL)
2191 return NT_STATUS_NO_MEMORY;
2193 mod = ldb_msg_new(mem_ctx);
2194 if (mod == NULL) {
2195 return NT_STATUS_NO_MEMORY;
2198 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2200 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2201 memberdn) != 0) {
2202 return NT_STATUS_NO_MEMORY;
2205 ret = samdb_modify(a_state->sam_ctx, mem_ctx, mod);
2206 switch (ret) {
2207 case LDB_SUCCESS:
2208 return NT_STATUS_OK;
2209 case LDB_ERR_NO_SUCH_ATTRIBUTE:
2210 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2211 default:
2212 return NT_STATUS_UNSUCCESSFUL;
2219 samr_QueryGroupMember
2221 static NTSTATUS samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2222 struct samr_QueryGroupMember *r)
2224 struct dcesrv_handle *h;
2225 struct samr_account_state *a_state;
2226 struct ldb_message **res;
2227 struct ldb_message_element *el;
2228 struct samr_RidTypeArray *array;
2229 const char * const attrs[2] = { "member", NULL };
2230 int ret;
2232 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2234 a_state = h->data;
2236 /* pull the member attribute */
2237 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2238 a_state->account_dn, &res, attrs);
2240 if (ret != 1) {
2241 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2244 array = talloc(mem_ctx, struct samr_RidTypeArray);
2246 if (array == NULL)
2247 return NT_STATUS_NO_MEMORY;
2249 ZERO_STRUCTP(array);
2251 el = ldb_msg_find_element(res[0], "member");
2253 if (el != NULL) {
2254 int i;
2256 array->count = el->num_values;
2258 array->rids = talloc_array(mem_ctx, uint32_t,
2259 el->num_values);
2260 if (array->rids == NULL)
2261 return NT_STATUS_NO_MEMORY;
2263 array->types = talloc_array(mem_ctx, uint32_t,
2264 el->num_values);
2265 if (array->types == NULL)
2266 return NT_STATUS_NO_MEMORY;
2268 for (i=0; i<el->num_values; i++) {
2269 struct ldb_message **res2;
2270 const char * const attrs2[2] = { "objectSid", NULL };
2271 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2272 ldb_dn_explode(mem_ctx, (const char *)el->values[i].data),
2273 &res2, attrs2);
2274 if (ret != 1)
2275 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2277 array->rids[i] =
2278 samdb_result_rid_from_sid(mem_ctx, res2[0],
2279 "objectSid", 0);
2281 if (array->rids[i] == 0)
2282 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2284 array->types[i] = 7; /* RID type of some kind, not sure what the value means. */
2288 r->out.rids = array;
2290 return NT_STATUS_OK;
2295 samr_SetMemberAttributesOfGroup
2297 static NTSTATUS samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2298 struct samr_SetMemberAttributesOfGroup *r)
2300 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2305 samr_OpenAlias
2307 static NTSTATUS samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2308 struct samr_OpenAlias *r)
2310 struct samr_domain_state *d_state;
2311 struct samr_account_state *a_state;
2312 struct dcesrv_handle *h;
2313 const char *alias_name;
2314 struct dom_sid *sid;
2315 struct ldb_message **msgs;
2316 struct dcesrv_handle *g_handle;
2317 const char * const attrs[2] = { "sAMAccountName", NULL };
2318 int ret;
2320 ZERO_STRUCTP(r->out.alias_handle);
2322 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2324 d_state = h->data;
2326 /* form the alias SID */
2327 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2328 if (sid == NULL)
2329 return NT_STATUS_NO_MEMORY;
2331 /* search for the group record */
2332 ret = gendb_search(d_state->sam_ctx,
2333 mem_ctx, d_state->domain_dn, &msgs, attrs,
2334 "(&(objectSid=%s)(objectclass=group)"
2335 "(|(grouptype=%d)(grouptype=%d)))",
2336 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2337 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2338 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2339 if (ret == 0) {
2340 return NT_STATUS_NO_SUCH_ALIAS;
2342 if (ret != 1) {
2343 DEBUG(0,("Found %d records matching sid %s\n",
2344 ret, dom_sid_string(mem_ctx, sid)));
2345 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2348 alias_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
2349 if (alias_name == NULL) {
2350 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2351 dom_sid_string(mem_ctx, sid)));
2352 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2355 a_state = talloc(d_state, struct samr_account_state);
2356 if (!a_state) {
2357 return NT_STATUS_NO_MEMORY;
2359 a_state->sam_ctx = d_state->sam_ctx;
2360 a_state->access_mask = r->in.access_mask;
2361 a_state->domain_state = talloc_reference(a_state, d_state);
2362 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2363 a_state->account_sid = talloc_steal(a_state, sid);
2364 a_state->account_name = talloc_strdup(a_state, alias_name);
2365 if (!a_state->account_name) {
2366 return NT_STATUS_NO_MEMORY;
2369 /* create the policy handle */
2370 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2371 if (!g_handle) {
2372 return NT_STATUS_NO_MEMORY;
2375 g_handle->data = talloc_steal(g_handle, a_state);
2377 *r->out.alias_handle = g_handle->wire_handle;
2379 return NT_STATUS_OK;
2384 samr_QueryAliasInfo
2386 static NTSTATUS samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2387 struct samr_QueryAliasInfo *r)
2389 struct dcesrv_handle *h;
2390 struct samr_account_state *a_state;
2391 struct ldb_message *msg, **res;
2392 const char * const attrs[4] = { "sAMAccountName", "description",
2393 "numMembers", NULL };
2394 int ret;
2396 r->out.info = NULL;
2398 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2400 a_state = h->data;
2402 /* pull all the alias attributes */
2403 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2404 a_state->account_dn ,&res, attrs);
2405 if (ret != 1) {
2406 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2408 msg = res[0];
2410 /* allocate the info structure */
2411 r->out.info = talloc(mem_ctx, union samr_AliasInfo);
2412 if (r->out.info == NULL) {
2413 return NT_STATUS_NO_MEMORY;
2415 ZERO_STRUCTP(r->out.info);
2417 switch(r->in.level) {
2418 case ALIASINFOALL:
2419 QUERY_STRING(msg, all.name.string, "sAMAccountName");
2420 QUERY_UINT (msg, all.num_members, "numMembers");
2421 QUERY_STRING(msg, all.description.string, "description");
2422 break;
2423 case ALIASINFONAME:
2424 QUERY_STRING(msg, name.string, "sAMAccountName");
2425 break;
2426 case ALIASINFODESCRIPTION:
2427 QUERY_STRING(msg, description.string, "description");
2428 break;
2429 default:
2430 r->out.info = NULL;
2431 return NT_STATUS_INVALID_INFO_CLASS;
2434 return NT_STATUS_OK;
2439 samr_SetAliasInfo
2441 static NTSTATUS samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2442 struct samr_SetAliasInfo *r)
2444 struct dcesrv_handle *h;
2445 struct samr_account_state *a_state;
2446 struct ldb_message *msg;
2447 struct ldb_context *sam_ctx;
2448 int ret;
2450 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2452 a_state = h->data;
2453 sam_ctx = a_state->sam_ctx;
2455 msg = ldb_msg_new(mem_ctx);
2456 if (msg == NULL) {
2457 return NT_STATUS_NO_MEMORY;
2460 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2461 if (!msg->dn) {
2462 return NT_STATUS_NO_MEMORY;
2465 switch (r->in.level) {
2466 case ALIASINFODESCRIPTION:
2467 SET_STRING(msg, description.string, "description");
2468 break;
2469 case ALIASINFONAME:
2470 /* On W2k3 this does not change the name, it changes the
2471 * sAMAccountName attribute */
2472 SET_STRING(msg, name.string, "sAMAccountName");
2473 break;
2474 default:
2475 return NT_STATUS_INVALID_INFO_CLASS;
2478 /* modify the samdb record */
2479 ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
2480 if (ret != 0) {
2481 /* we really need samdb.c to return NTSTATUS */
2482 return NT_STATUS_UNSUCCESSFUL;
2485 return NT_STATUS_OK;
2490 samr_DeleteDomAlias
2492 static NTSTATUS samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2493 struct samr_DeleteDomAlias *r)
2495 struct dcesrv_handle *h;
2496 struct samr_account_state *a_state;
2497 int ret;
2499 *r->out.alias_handle = *r->in.alias_handle;
2501 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2503 a_state = h->data;
2505 ret = samdb_delete(a_state->sam_ctx, mem_ctx, a_state->account_dn);
2506 if (ret != 0) {
2507 return NT_STATUS_UNSUCCESSFUL;
2510 ZERO_STRUCTP(r->out.alias_handle);
2512 return NT_STATUS_OK;
2517 samr_AddAliasMember
2519 static NTSTATUS samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2520 struct samr_AddAliasMember *r)
2522 struct dcesrv_handle *h;
2523 struct samr_account_state *a_state;
2524 struct samr_domain_state *d_state;
2525 struct ldb_message *mod;
2526 struct ldb_message **msgs;
2527 const char * const attrs[] = { NULL };
2528 struct ldb_dn *memberdn = NULL;
2529 int ret;
2530 NTSTATUS status;
2532 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2534 a_state = h->data;
2535 d_state = a_state->domain_state;
2537 ret = gendb_search(d_state->sam_ctx, mem_ctx, samdb_base_dn(mem_ctx),
2538 &msgs, attrs, "(objectsid=%s)",
2539 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2541 if (ret == 1) {
2542 memberdn = msgs[0]->dn;
2543 } else if (ret > 1) {
2544 DEBUG(0,("Found %d records matching sid %s\n",
2545 ret, dom_sid_string(mem_ctx, r->in.sid)));
2546 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2547 } else if (ret == 0) {
2548 status = samdb_create_foreign_security_principal(d_state->sam_ctx, mem_ctx,
2549 r->in.sid, &memberdn);
2550 if (!NT_STATUS_IS_OK(status)) {
2551 return status;
2553 } else {
2554 DEBUG(0, ("samdb_search returned %d: %s\n", ret, ldb_errstring(d_state->sam_ctx)));
2557 if (memberdn == NULL) {
2558 DEBUG(0, ("Could not find memberdn\n"));
2559 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2562 mod = ldb_msg_new(mem_ctx);
2563 if (mod == NULL) {
2564 return NT_STATUS_NO_MEMORY;
2567 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2569 if (samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2570 ldb_dn_linearize(mem_ctx, memberdn)) != 0)
2571 return NT_STATUS_UNSUCCESSFUL;
2573 if (samdb_modify(a_state->sam_ctx, mem_ctx, mod) != 0)
2574 return NT_STATUS_UNSUCCESSFUL;
2576 return NT_STATUS_OK;
2581 samr_DeleteAliasMember
2583 static NTSTATUS samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2584 struct samr_DeleteAliasMember *r)
2586 struct dcesrv_handle *h;
2587 struct samr_account_state *a_state;
2588 struct samr_domain_state *d_state;
2589 struct ldb_message *mod;
2590 const char *memberdn;
2592 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2594 a_state = h->data;
2595 d_state = a_state->domain_state;
2597 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, samdb_base_dn(mem_ctx),
2598 "distinguishedName", "(objectSid=%s)",
2599 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2601 if (memberdn == NULL)
2602 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2604 mod = ldb_msg_new(mem_ctx);
2605 if (mod == NULL) {
2606 return NT_STATUS_NO_MEMORY;
2609 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2611 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2612 memberdn) != 0)
2613 return NT_STATUS_UNSUCCESSFUL;
2615 if (samdb_modify(a_state->sam_ctx, mem_ctx, mod) != 0)
2616 return NT_STATUS_UNSUCCESSFUL;
2618 return NT_STATUS_OK;
2623 samr_GetMembersInAlias
2625 static NTSTATUS samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2626 struct samr_GetMembersInAlias *r)
2628 struct dcesrv_handle *h;
2629 struct samr_account_state *a_state;
2630 struct samr_domain_state *d_state;
2631 struct ldb_message **msgs;
2632 struct lsa_SidPtr *sids;
2633 struct ldb_message_element *el;
2634 const char * const attrs[2] = { "member", NULL};
2635 int ret;
2637 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2639 a_state = h->data;
2640 d_state = a_state->domain_state;
2642 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
2643 a_state->account_dn, &msgs, attrs);
2645 if (ret != 1)
2646 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2648 r->out.sids->num_sids = 0;
2649 r->out.sids->sids = NULL;
2651 el = ldb_msg_find_element(msgs[0], "member");
2653 if (el != NULL) {
2654 int i;
2656 sids = talloc_array(mem_ctx, struct lsa_SidPtr,
2657 el->num_values);
2659 if (sids == NULL)
2660 return NT_STATUS_NO_MEMORY;
2662 for (i=0; i<el->num_values; i++) {
2663 struct ldb_message **msgs2;
2664 const char * const attrs2[2] = { "objectSid", NULL };
2665 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2666 ldb_dn_explode(mem_ctx, (const char *)el->values[i].data),
2667 &msgs2, attrs2);
2668 if (ret != 1)
2669 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2671 sids[i].sid = samdb_result_dom_sid(mem_ctx, msgs2[0],
2672 "objectSid");
2674 if (sids[i].sid == NULL)
2675 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2677 r->out.sids->num_sids = el->num_values;
2678 r->out.sids->sids = sids;
2681 return NT_STATUS_OK;
2685 samr_OpenUser
2687 static NTSTATUS samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2688 struct samr_OpenUser *r)
2690 struct samr_domain_state *d_state;
2691 struct samr_account_state *a_state;
2692 struct dcesrv_handle *h;
2693 const char *account_name;
2694 struct dom_sid *sid;
2695 struct ldb_message **msgs;
2696 struct dcesrv_handle *u_handle;
2697 const char * const attrs[2] = { "sAMAccountName", NULL };
2698 int ret;
2700 ZERO_STRUCTP(r->out.user_handle);
2702 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2704 d_state = h->data;
2706 /* form the users SID */
2707 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2708 if (!sid) {
2709 return NT_STATUS_NO_MEMORY;
2712 /* search for the user record */
2713 ret = gendb_search(d_state->sam_ctx,
2714 mem_ctx, d_state->domain_dn, &msgs, attrs,
2715 "(&(objectSid=%s)(objectclass=user))",
2716 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2717 if (ret == 0) {
2718 return NT_STATUS_NO_SUCH_USER;
2720 if (ret != 1) {
2721 DEBUG(0,("Found %d records matching sid %s\n", ret,
2722 dom_sid_string(mem_ctx, sid)));
2723 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2726 account_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
2727 if (account_name == NULL) {
2728 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2729 dom_sid_string(mem_ctx, sid)));
2730 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2733 a_state = talloc(mem_ctx, struct samr_account_state);
2734 if (!a_state) {
2735 return NT_STATUS_NO_MEMORY;
2737 a_state->sam_ctx = d_state->sam_ctx;
2738 a_state->access_mask = r->in.access_mask;
2739 a_state->domain_state = talloc_reference(a_state, d_state);
2740 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2741 a_state->account_sid = talloc_steal(a_state, sid);
2742 a_state->account_name = talloc_strdup(a_state, account_name);
2743 if (!a_state->account_name) {
2744 return NT_STATUS_NO_MEMORY;
2747 /* create the policy handle */
2748 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
2749 if (!u_handle) {
2750 return NT_STATUS_NO_MEMORY;
2753 u_handle->data = talloc_steal(u_handle, a_state);
2755 *r->out.user_handle = u_handle->wire_handle;
2757 return NT_STATUS_OK;
2763 samr_DeleteUser
2765 static NTSTATUS samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2766 struct samr_DeleteUser *r)
2768 struct dcesrv_handle *h;
2769 struct samr_account_state *a_state;
2770 int ret;
2772 *r->out.user_handle = *r->in.user_handle;
2774 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2776 a_state = h->data;
2778 ret = samdb_delete(a_state->sam_ctx, mem_ctx, a_state->account_dn);
2779 if (ret != 0) {
2780 return NT_STATUS_UNSUCCESSFUL;
2783 ZERO_STRUCTP(r->out.user_handle);
2785 return NT_STATUS_OK;
2790 samr_QueryUserInfo
2792 static NTSTATUS samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2793 struct samr_QueryUserInfo *r)
2795 struct dcesrv_handle *h;
2796 struct samr_account_state *a_state;
2797 struct ldb_message *msg, **res;
2798 int ret;
2799 struct ldb_context *sam_ctx;
2801 const char * const *attrs = NULL;
2803 r->out.info = NULL;
2805 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2807 a_state = h->data;
2808 sam_ctx = a_state->sam_ctx;
2810 /* fill in the reply */
2811 switch (r->in.level) {
2812 case 1:
2814 static const char * const attrs2[] = {"sAMAccountName", "displayName",
2815 "primaryGroupID", "description",
2816 "comment", NULL};
2817 attrs = attrs2;
2818 break;
2820 case 2:
2822 static const char * const attrs2[] = {"comment", "countryCode", "codePage", NULL};
2823 attrs = attrs2;
2824 break;
2826 case 3:
2828 static const char * const attrs2[] = {"sAMAccountName",
2829 "displayName",
2830 "objectSid",
2831 "primaryGroupID",
2832 "homeDirectory",
2833 "homeDrive",
2834 "scriptPath",
2835 "profilePath",
2836 "userWorkstations",
2837 "lastLogon",
2838 "lastLogoff",
2839 "pwdLastSet",
2840 "logonHours",
2841 "badPwdCount",
2842 "logonCount",
2843 "userAccountControl", NULL};
2844 attrs = attrs2;
2845 break;
2847 case 4:
2849 static const char * const attrs2[] = {"logonHours", NULL};
2850 attrs = attrs2;
2851 break;
2853 case 5:
2855 static const char * const attrs2[] = {"sAMAccountName",
2856 "displayName",
2857 "objectSid",
2858 "primaryGroupID",
2859 "homeDirectory",
2860 "homeDrive",
2861 "scriptPath",
2862 "profilePath",
2863 "description",
2864 "userWorkstations",
2865 "lastLogon",
2866 "lastLogoff",
2867 "logonHours",
2868 "badPwdCount",
2869 "logonCount",
2870 "pwdLastSet",
2871 "accountExpires",
2872 "userAccountControl",
2873 NULL};
2874 attrs = attrs2;
2875 break;
2877 case 6:
2879 static const char * const attrs2[] = {"sAMAccountName", "displayName", NULL};
2880 attrs = attrs2;
2881 break;
2883 case 7:
2885 static const char * const attrs2[] = {"sAMAccountName", NULL};
2886 attrs = attrs2;
2887 break;
2889 case 8:
2891 static const char * const attrs2[] = {"displayName", NULL};
2892 attrs = attrs2;
2893 break;
2895 case 9:
2897 static const char * const attrs2[] = {"primaryGroupID", NULL};
2898 attrs = attrs2;
2899 break;
2901 case 10:
2903 static const char * const attrs2[] = {"homeDirectory", "homeDrive", NULL};
2904 attrs = attrs2;
2905 break;
2907 case 11:
2909 static const char * const attrs2[] = {"scriptPath", NULL};
2910 attrs = attrs2;
2911 break;
2913 case 12:
2915 static const char * const attrs2[] = {"profilePath", NULL};
2916 attrs = attrs2;
2917 break;
2919 case 13:
2921 static const char * const attrs2[] = {"description", NULL};
2922 attrs = attrs2;
2923 break;
2925 case 14:
2927 static const char * const attrs2[] = {"userWorkstations", NULL};
2928 attrs = attrs2;
2929 break;
2931 case 16:
2933 static const char * const attrs2[] = {"userAccountControl", NULL};
2934 attrs = attrs2;
2935 break;
2937 case 17:
2939 static const char * const attrs2[] = {"accountExpires", NULL};
2940 attrs = attrs2;
2941 break;
2943 case 20:
2945 static const char * const attrs2[] = {"userParameters", NULL};
2946 attrs = attrs2;
2947 break;
2949 case 21:
2951 static const char * const attrs2[] = {"lastLogon",
2952 "lastLogoff",
2953 "pwdLastSet",
2954 "accountExpires",
2955 "sAMAccountName",
2956 "displayName",
2957 "homeDirectory",
2958 "homeDrive",
2959 "scriptPath",
2960 "profilePath",
2961 "description",
2962 "userWorkstations",
2963 "comment",
2964 "userParameters",
2965 "objectSid",
2966 "primaryGroupID",
2967 "userAccountControl",
2968 "logonHours",
2969 "badPwdCount",
2970 "logonCount",
2971 "countryCode",
2972 "codePage",
2973 NULL};
2974 attrs = attrs2;
2975 break;
2979 /* pull all the user attributes */
2980 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2981 a_state->account_dn ,&res, attrs);
2982 if (ret != 1) {
2983 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2985 msg = res[0];
2987 /* allocate the info structure */
2988 r->out.info = talloc(mem_ctx, union samr_UserInfo);
2989 if (r->out.info == NULL) {
2990 return NT_STATUS_NO_MEMORY;
2992 ZERO_STRUCTP(r->out.info);
2994 /* fill in the reply */
2995 switch (r->in.level) {
2996 case 1:
2997 QUERY_STRING(msg, info1.account_name.string, "sAMAccountName");
2998 QUERY_STRING(msg, info1.full_name.string, "displayName");
2999 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3000 QUERY_STRING(msg, info1.description.string, "description");
3001 QUERY_STRING(msg, info1.comment.string, "comment");
3002 break;
3004 case 2:
3005 QUERY_STRING(msg, info2.comment.string, "comment");
3006 QUERY_UINT (msg, info2.country_code, "countryCode");
3007 QUERY_UINT (msg, info2.code_page, "codePage");
3008 break;
3010 case 3:
3011 QUERY_STRING(msg, info3.account_name.string, "sAMAccountName");
3012 QUERY_STRING(msg, info3.full_name.string, "displayName");
3013 QUERY_RID (msg, info3.rid, "objectSid");
3014 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3015 QUERY_STRING(msg, info3.home_directory.string, "homeDirectory");
3016 QUERY_STRING(msg, info3.home_drive.string, "homeDrive");
3017 QUERY_STRING(msg, info3.logon_script.string, "scriptPath");
3018 QUERY_STRING(msg, info3.profile_path.string, "profilePath");
3019 QUERY_STRING(msg, info3.workstations.string, "userWorkstations");
3020 QUERY_NTTIME(msg, info3.last_logon, "lastLogon");
3021 QUERY_NTTIME(msg, info3.last_logoff, "lastLogoff");
3022 QUERY_NTTIME(msg, info3.last_password_change, "pwdLastSet");
3023 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3024 QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
3025 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3026 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3027 QUERY_UINT (msg, info3.logon_count, "logonCount");
3028 QUERY_AFLAGS(msg, info3.acct_flags, "userAccountControl");
3029 break;
3031 case 4:
3032 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3033 break;
3035 case 5:
3036 QUERY_STRING(msg, info5.account_name.string, "sAMAccountName");
3037 QUERY_STRING(msg, info5.full_name.string, "displayName");
3038 QUERY_RID (msg, info5.rid, "objectSid");
3039 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3040 QUERY_STRING(msg, info5.home_directory.string, "homeDirectory");
3041 QUERY_STRING(msg, info5.home_drive.string, "homeDrive");
3042 QUERY_STRING(msg, info5.logon_script.string, "scriptPath");
3043 QUERY_STRING(msg, info5.profile_path.string, "profilePath");
3044 QUERY_STRING(msg, info5.description.string, "description");
3045 QUERY_STRING(msg, info5.workstations.string, "userWorkstations");
3046 QUERY_NTTIME(msg, info5.last_logon, "lastLogon");
3047 QUERY_NTTIME(msg, info5.last_logoff, "lastLogoff");
3048 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3049 QUERY_UINT (msg, info5.bad_password_count, "badPwdCount");
3050 QUERY_UINT (msg, info5.logon_count, "logonCount");
3051 QUERY_NTTIME(msg, info5.last_password_change, "pwdLastSet");
3052 QUERY_NTTIME(msg, info5.acct_expiry, "accountExpires");
3053 QUERY_AFLAGS(msg, info5.acct_flags, "userAccountControl");
3054 break;
3056 case 6:
3057 QUERY_STRING(msg, info6.account_name.string, "sAMAccountName");
3058 QUERY_STRING(msg, info6.full_name.string, "displayName");
3059 break;
3061 case 7:
3062 QUERY_STRING(msg, info7.account_name.string, "sAMAccountName");
3063 break;
3065 case 8:
3066 QUERY_STRING(msg, info8.full_name.string, "displayName");
3067 break;
3069 case 9:
3070 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3071 break;
3073 case 10:
3074 QUERY_STRING(msg, info10.home_directory.string,"homeDirectory");
3075 QUERY_STRING(msg, info10.home_drive.string, "homeDrive");
3076 break;
3078 case 11:
3079 QUERY_STRING(msg, info11.logon_script.string, "scriptPath");
3080 break;
3082 case 12:
3083 QUERY_STRING(msg, info12.profile_path.string, "profilePath");
3084 break;
3086 case 13:
3087 QUERY_STRING(msg, info13.description.string, "description");
3088 break;
3090 case 14:
3091 QUERY_STRING(msg, info14.workstations.string, "userWorkstations");
3092 break;
3094 case 16:
3095 QUERY_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3096 break;
3098 case 17:
3099 QUERY_NTTIME(msg, info17.acct_expiry, "accountExpires");
3101 case 20:
3102 QUERY_STRING(msg, info20.parameters.string, "userParameters");
3103 break;
3105 case 21:
3106 QUERY_NTTIME(msg, info21.last_logon, "lastLogon");
3107 QUERY_NTTIME(msg, info21.last_logoff, "lastLogoff");
3108 QUERY_NTTIME(msg, info21.last_password_change, "pwdLastSet");
3109 QUERY_NTTIME(msg, info21.acct_expiry, "accountExpires");
3110 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3111 QUERY_FPASSC(msg, info21.force_password_change,"pwdLastSet");
3112 QUERY_STRING(msg, info21.account_name.string, "sAMAccountName");
3113 QUERY_STRING(msg, info21.full_name.string, "displayName");
3114 QUERY_STRING(msg, info21.home_directory.string,"homeDirectory");
3115 QUERY_STRING(msg, info21.home_drive.string, "homeDrive");
3116 QUERY_STRING(msg, info21.logon_script.string, "scriptPath");
3117 QUERY_STRING(msg, info21.profile_path.string, "profilePath");
3118 QUERY_STRING(msg, info21.description.string, "description");
3119 QUERY_STRING(msg, info21.workstations.string, "userWorkstations");
3120 QUERY_STRING(msg, info21.comment.string, "comment");
3121 QUERY_STRING(msg, info21.parameters.string, "userParameters");
3122 QUERY_RID (msg, info21.rid, "objectSid");
3123 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3124 QUERY_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3125 r->out.info->info21.fields_present = 0x00FFFFFF;
3126 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3127 QUERY_UINT (msg, info21.bad_password_count, "badPwdCount");
3128 QUERY_UINT (msg, info21.logon_count, "logonCount");
3129 QUERY_UINT (msg, info21.country_code, "countryCode");
3130 QUERY_UINT (msg, info21.code_page, "codePage");
3131 break;
3134 default:
3135 r->out.info = NULL;
3136 return NT_STATUS_INVALID_INFO_CLASS;
3139 return NT_STATUS_OK;
3144 samr_SetUserInfo
3146 static NTSTATUS samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3147 struct samr_SetUserInfo *r)
3149 struct dcesrv_handle *h;
3150 struct samr_account_state *a_state;
3151 struct ldb_message *msg;
3152 int ret;
3153 NTSTATUS status = NT_STATUS_OK;
3154 struct ldb_context *sam_ctx;
3156 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3158 a_state = h->data;
3159 sam_ctx = a_state->sam_ctx;
3161 msg = ldb_msg_new(mem_ctx);
3162 if (msg == NULL) {
3163 return NT_STATUS_NO_MEMORY;
3166 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3167 if (!msg->dn) {
3168 return NT_STATUS_NO_MEMORY;
3171 switch (r->in.level) {
3172 case 2:
3173 SET_STRING(msg, info2.comment.string, "comment");
3174 SET_UINT (msg, info2.country_code, "countryCode");
3175 SET_UINT (msg, info2.code_page, "codePage");
3176 break;
3178 case 4:
3179 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3180 break;
3182 case 6:
3183 SET_STRING(msg, info6.full_name.string, "displayName");
3184 break;
3186 case 7:
3187 SET_STRING(msg, info7.account_name.string, "samAccountName");
3188 break;
3190 case 8:
3191 SET_STRING(msg, info8.full_name.string, "displayName");
3192 break;
3194 case 9:
3195 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3196 break;
3198 case 10:
3199 SET_STRING(msg, info10.home_directory.string, "homeDirectory");
3200 SET_STRING(msg, info10.home_drive.string, "homeDrive");
3201 break;
3203 case 11:
3204 SET_STRING(msg, info11.logon_script.string, "scriptPath");
3205 break;
3207 case 12:
3208 SET_STRING(msg, info12.profile_path.string, "profilePath");
3209 break;
3211 case 13:
3212 SET_STRING(msg, info13.description.string, "description");
3213 break;
3215 case 14:
3216 SET_STRING(msg, info14.workstations.string, "userWorkstations");
3217 break;
3219 case 16:
3220 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3221 break;
3223 case 20:
3224 SET_STRING(msg, info20.parameters.string, "userParameters");
3225 break;
3227 case 21:
3228 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3229 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3230 SET_STRING(msg, info21.account_name.string, "samAccountName");
3231 IFSET(SAMR_FIELD_FULL_NAME)
3232 SET_STRING(msg, info21.full_name.string, "displayName");
3233 IFSET(SAMR_FIELD_DESCRIPTION)
3234 SET_STRING(msg, info21.description.string, "description");
3235 IFSET(SAMR_FIELD_COMMENT)
3236 SET_STRING(msg, info21.comment.string, "comment");
3237 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3238 SET_STRING(msg, info21.logon_script.string, "scriptPath");
3239 IFSET(SAMR_FIELD_PROFILE_PATH)
3240 SET_STRING(msg, info21.profile_path.string, "profilePath");
3241 IFSET(SAMR_FIELD_WORKSTATIONS)
3242 SET_STRING(msg, info21.workstations.string, "userWorkstations");
3243 IFSET(SAMR_FIELD_LOGON_HOURS)
3244 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3245 IFSET(SAMR_FIELD_ACCT_FLAGS)
3246 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3247 IFSET(SAMR_FIELD_PARAMETERS)
3248 SET_STRING(msg, info21.parameters.string, "userParameters");
3249 IFSET(SAMR_FIELD_COUNTRY_CODE)
3250 SET_UINT (msg, info21.country_code, "countryCode");
3251 IFSET(SAMR_FIELD_CODE_PAGE)
3252 SET_UINT (msg, info21.code_page, "codePage");
3255 /* Any reason the rest of these can't be set? */
3256 #undef IFSET
3257 break;
3259 case 23:
3260 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3261 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3262 SET_STRING(msg, info23.info.account_name.string, "samAccountName");
3263 IFSET(SAMR_FIELD_FULL_NAME)
3264 SET_STRING(msg, info23.info.full_name.string, "displayName");
3265 IFSET(SAMR_FIELD_DESCRIPTION)
3266 SET_STRING(msg, info23.info.description.string, "description");
3267 IFSET(SAMR_FIELD_COMMENT)
3268 SET_STRING(msg, info23.info.comment.string, "comment");
3269 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3270 SET_STRING(msg, info23.info.logon_script.string, "scriptPath");
3271 IFSET(SAMR_FIELD_PROFILE_PATH)
3272 SET_STRING(msg, info23.info.profile_path.string, "profilePath");
3273 IFSET(SAMR_FIELD_WORKSTATIONS)
3274 SET_STRING(msg, info23.info.workstations.string, "userWorkstations");
3275 IFSET(SAMR_FIELD_LOGON_HOURS)
3276 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3277 IFSET(SAMR_FIELD_ACCT_FLAGS)
3278 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3279 IFSET(SAMR_FIELD_PARAMETERS)
3280 SET_STRING(msg, info23.info.parameters.string, "userParameters");
3281 IFSET(SAMR_FIELD_COUNTRY_CODE)
3282 SET_UINT (msg, info23.info.country_code, "countryCode");
3283 IFSET(SAMR_FIELD_CODE_PAGE)
3284 SET_UINT (msg, info23.info.code_page, "codePage");
3285 IFSET(SAMR_FIELD_PASSWORD) {
3286 status = samr_set_password(dce_call,
3287 a_state->sam_ctx,
3288 a_state->account_dn,
3289 a_state->domain_state->domain_dn,
3290 mem_ctx, msg,
3291 &r->in.info->info23.password);
3292 } else IFSET(SAMR_FIELD_PASSWORD2) {
3293 status = samr_set_password(dce_call,
3294 a_state->sam_ctx,
3295 a_state->account_dn,
3296 a_state->domain_state->domain_dn,
3297 mem_ctx, msg,
3298 &r->in.info->info23.password);
3300 #undef IFSET
3301 break;
3303 /* the set password levels are handled separately */
3304 case 24:
3305 status = samr_set_password(dce_call,
3306 a_state->sam_ctx,
3307 a_state->account_dn,
3308 a_state->domain_state->domain_dn,
3309 mem_ctx, msg,
3310 &r->in.info->info24.password);
3311 break;
3313 case 25:
3314 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3315 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3316 SET_STRING(msg, info25.info.account_name.string, "samAccountName");
3317 IFSET(SAMR_FIELD_FULL_NAME)
3318 SET_STRING(msg, info25.info.full_name.string, "displayName");
3319 IFSET(SAMR_FIELD_DESCRIPTION)
3320 SET_STRING(msg, info25.info.description.string, "description");
3321 IFSET(SAMR_FIELD_COMMENT)
3322 SET_STRING(msg, info25.info.comment.string, "comment");
3323 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3324 SET_STRING(msg, info25.info.logon_script.string, "scriptPath");
3325 IFSET(SAMR_FIELD_PROFILE_PATH)
3326 SET_STRING(msg, info25.info.profile_path.string, "profilePath");
3327 IFSET(SAMR_FIELD_WORKSTATIONS)
3328 SET_STRING(msg, info25.info.workstations.string, "userWorkstations");
3329 IFSET(SAMR_FIELD_LOGON_HOURS)
3330 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3331 IFSET(SAMR_FIELD_ACCT_FLAGS)
3332 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3333 IFSET(SAMR_FIELD_PARAMETERS)
3334 SET_STRING(msg, info25.info.parameters.string, "userParameters");
3335 IFSET(SAMR_FIELD_COUNTRY_CODE)
3336 SET_UINT (msg, info25.info.country_code, "countryCode");
3337 IFSET(SAMR_FIELD_CODE_PAGE)
3338 SET_UINT (msg, info25.info.code_page, "codePage");
3339 IFSET(SAMR_FIELD_PASSWORD) {
3340 status = samr_set_password_ex(dce_call,
3341 a_state->sam_ctx,
3342 a_state->account_dn,
3343 a_state->domain_state->domain_dn,
3344 mem_ctx, msg,
3345 &r->in.info->info25.password);
3346 } else IFSET(SAMR_FIELD_PASSWORD2) {
3347 status = samr_set_password_ex(dce_call,
3348 a_state->sam_ctx,
3349 a_state->account_dn,
3350 a_state->domain_state->domain_dn,
3351 mem_ctx, msg,
3352 &r->in.info->info25.password);
3354 #undef IFSET
3355 break;
3357 /* the set password levels are handled separately */
3358 case 26:
3359 status = samr_set_password_ex(dce_call,
3360 a_state->sam_ctx,
3361 a_state->account_dn,
3362 a_state->domain_state->domain_dn,
3363 mem_ctx, msg,
3364 &r->in.info->info26.password);
3365 break;
3368 default:
3369 /* many info classes are not valid for SetUserInfo */
3370 return NT_STATUS_INVALID_INFO_CLASS;
3373 if (!NT_STATUS_IS_OK(status)) {
3374 return status;
3377 /* modify the samdb record */
3378 ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
3379 if (ret != 0) {
3380 DEBUG(1,("Failed to modify record %s: %s\n",
3381 ldb_dn_linearize(mem_ctx, a_state->account_dn),
3382 ldb_errstring(a_state->sam_ctx)));
3384 /* we really need samdb.c to return NTSTATUS */
3385 return NT_STATUS_UNSUCCESSFUL;
3388 return NT_STATUS_OK;
3393 samr_GetGroupsForUser
3395 static NTSTATUS samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3396 struct samr_GetGroupsForUser *r)
3398 struct dcesrv_handle *h;
3399 struct samr_account_state *a_state;
3400 struct samr_domain_state *d_state;
3401 struct ldb_message **res;
3402 const char * const attrs[2] = { "objectSid", NULL };
3403 struct samr_RidWithAttributeArray *array;
3404 int count;
3406 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3408 a_state = h->data;
3409 d_state = a_state->domain_state;
3411 count = samdb_search_domain(a_state->sam_ctx, mem_ctx, d_state->domain_dn, &res,
3412 attrs, d_state->domain_sid,
3413 "(&(member=%s)(grouptype=%d)(objectclass=group))",
3414 ldb_dn_linearize(mem_ctx, a_state->account_dn),
3415 GTYPE_SECURITY_GLOBAL_GROUP);
3416 if (count < 0)
3417 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3419 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
3420 if (array == NULL)
3421 return NT_STATUS_NO_MEMORY;
3423 array->count = 0;
3424 array->rids = NULL;
3426 if (count > 0) {
3427 int i;
3428 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
3429 count);
3431 if (array->rids == NULL)
3432 return NT_STATUS_NO_MEMORY;
3434 for (i=0; i<count; i++) {
3435 struct dom_sid *group_sid;
3437 group_sid = samdb_result_dom_sid(mem_ctx, res[i],
3438 "objectSid");
3439 if (group_sid == NULL) {
3440 DEBUG(0, ("Couldn't find objectSid attrib\n"));
3441 continue;
3444 array->rids[array->count].rid =
3445 group_sid->sub_auths[group_sid->num_auths-1];
3446 array->rids[array->count].attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3447 array->count += 1;
3451 r->out.rids = array;
3453 return NT_STATUS_OK;
3458 samr_QueryDisplayInfo
3460 static NTSTATUS samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3461 struct samr_QueryDisplayInfo *r)
3463 struct dcesrv_handle *h;
3464 struct samr_domain_state *d_state;
3465 struct ldb_message **res;
3466 int ldb_cnt, count, i;
3467 const char * const attrs[4] = { "objectSid", "sAMAccountName",
3468 "description", NULL };
3469 struct samr_DispEntryFull *entriesFull = NULL;
3470 struct samr_DispEntryAscii *entriesAscii = NULL;
3471 struct samr_DispEntryGeneral * entriesGeneral = NULL;
3472 const char *filter;
3474 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3476 d_state = h->data;
3478 switch (r->in.level) {
3479 case 1:
3480 case 4:
3481 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3482 "(sAMAccountType=%u))",
3483 ATYPE_NORMAL_ACCOUNT);
3484 break;
3485 case 2:
3486 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3487 "(sAMAccountType=%u))",
3488 ATYPE_WORKSTATION_TRUST);
3489 break;
3490 case 3:
3491 case 5:
3492 filter = talloc_asprintf(mem_ctx, "(&(grouptype=%d)"
3493 "(objectclass=group))",
3494 GTYPE_SECURITY_GLOBAL_GROUP);
3495 break;
3496 default:
3497 return NT_STATUS_INVALID_INFO_CLASS;
3500 /* search for all requested objects in this domain. This could
3501 possibly be cached and resumed based on resume_key */
3502 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
3503 d_state->domain_dn, &res, attrs,
3504 d_state->domain_sid, "%s", filter);
3505 if (ldb_cnt == -1) {
3506 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3508 if (ldb_cnt == 0 || r->in.max_entries == 0) {
3509 return NT_STATUS_OK;
3512 switch (r->in.level) {
3513 case 1:
3514 entriesGeneral = talloc_array(mem_ctx,
3515 struct samr_DispEntryGeneral,
3516 ldb_cnt);
3517 break;
3518 case 2:
3519 case 3:
3520 entriesFull = talloc_array(mem_ctx,
3521 struct samr_DispEntryFull,
3522 ldb_cnt);
3523 break;
3524 case 4:
3525 case 5:
3526 entriesAscii = talloc_array(mem_ctx,
3527 struct samr_DispEntryAscii,
3528 ldb_cnt);
3529 break;
3532 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
3533 (entriesAscii == NULL))
3534 return NT_STATUS_NO_MEMORY;
3536 count = 0;
3538 for (i=0; i<ldb_cnt; i++) {
3539 struct dom_sid *objectsid;
3541 objectsid = samdb_result_dom_sid(mem_ctx, res[i],
3542 "objectSid");
3543 if (objectsid == NULL)
3544 continue;
3546 switch(r->in.level) {
3547 case 1:
3548 entriesGeneral[count].idx = count + 1;
3549 entriesGeneral[count].rid =
3550 objectsid->sub_auths[objectsid->num_auths-1];
3551 entriesGeneral[count].acct_flags =
3552 samdb_result_acct_flags(res[i],
3553 "userAccountControl");
3554 entriesGeneral[count].account_name.string =
3555 samdb_result_string(res[i],
3556 "sAMAccountName", "");
3557 entriesGeneral[count].full_name.string =
3558 samdb_result_string(res[i], "displayName", "");
3559 entriesGeneral[count].description.string =
3560 samdb_result_string(res[i], "description", "");
3561 break;
3562 case 2:
3563 case 3:
3564 entriesFull[count].idx = count + 1;
3565 entriesFull[count].rid =
3566 objectsid->sub_auths[objectsid->num_auths-1];
3567 entriesFull[count].acct_flags =
3568 samdb_result_acct_flags(res[i],
3569 "userAccountControl");
3570 if (r->in.level == 3) {
3571 /* We get a "7" here for groups */
3572 entriesFull[count].acct_flags = 7;
3574 entriesFull[count].account_name.string =
3575 samdb_result_string(res[i], "sAMAccountName",
3576 "");
3577 entriesFull[count].description.string =
3578 samdb_result_string(res[i], "description", "");
3579 break;
3580 case 4:
3581 case 5:
3582 entriesAscii[count].idx = count + 1;
3583 entriesAscii[count].account_name.string =
3584 samdb_result_string(res[i], "sAMAccountName",
3585 "");
3586 break;
3589 count += 1;
3592 r->out.total_size = count;
3594 if (r->in.start_idx >= count) {
3595 r->out.returned_size = 0;
3596 switch(r->in.level) {
3597 case 1:
3598 r->out.info.info1.count = r->out.returned_size;
3599 r->out.info.info1.entries = NULL;
3600 break;
3601 case 2:
3602 r->out.info.info2.count = r->out.returned_size;
3603 r->out.info.info2.entries = NULL;
3604 break;
3605 case 3:
3606 r->out.info.info3.count = r->out.returned_size;
3607 r->out.info.info3.entries = NULL;
3608 break;
3609 case 4:
3610 r->out.info.info4.count = r->out.returned_size;
3611 r->out.info.info4.entries = NULL;
3612 break;
3613 case 5:
3614 r->out.info.info5.count = r->out.returned_size;
3615 r->out.info.info5.entries = NULL;
3616 break;
3618 } else {
3619 r->out.returned_size = MIN(count - r->in.start_idx,
3620 r->in.max_entries);
3621 switch(r->in.level) {
3622 case 1:
3623 r->out.info.info1.count = r->out.returned_size;
3624 r->out.info.info1.entries =
3625 &(entriesGeneral[r->in.start_idx]);
3626 break;
3627 case 2:
3628 r->out.info.info2.count = r->out.returned_size;
3629 r->out.info.info2.entries =
3630 &(entriesFull[r->in.start_idx]);
3631 break;
3632 case 3:
3633 r->out.info.info3.count = r->out.returned_size;
3634 r->out.info.info3.entries =
3635 &(entriesFull[r->in.start_idx]);
3636 break;
3637 case 4:
3638 r->out.info.info4.count = r->out.returned_size;
3639 r->out.info.info4.entries =
3640 &(entriesAscii[r->in.start_idx]);
3641 break;
3642 case 5:
3643 r->out.info.info5.count = r->out.returned_size;
3644 r->out.info.info5.entries =
3645 &(entriesAscii[r->in.start_idx]);
3646 break;
3650 return (r->out.returned_size < (count - r->in.start_idx)) ?
3651 STATUS_MORE_ENTRIES : NT_STATUS_OK;
3656 samr_GetDisplayEnumerationIndex
3658 static NTSTATUS samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3659 struct samr_GetDisplayEnumerationIndex *r)
3661 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3666 samr_TestPrivateFunctionsDomain
3668 static NTSTATUS samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3669 struct samr_TestPrivateFunctionsDomain *r)
3671 return NT_STATUS_NOT_IMPLEMENTED;
3676 samr_TestPrivateFunctionsUser
3678 static NTSTATUS samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3679 struct samr_TestPrivateFunctionsUser *r)
3681 return NT_STATUS_NOT_IMPLEMENTED;
3686 samr_GetUserPwInfo
3688 static NTSTATUS samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3689 struct samr_GetUserPwInfo *r)
3691 struct dcesrv_handle *h;
3692 struct samr_account_state *a_state;
3694 ZERO_STRUCT(r->out.info);
3696 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3698 a_state = h->data;
3700 r->out.info.min_password_length = samdb_search_uint(a_state->sam_ctx, mem_ctx, 0,
3701 a_state->domain_state->domain_dn, "minPwdLength",
3702 NULL);
3703 r->out.info.password_properties = samdb_search_uint(a_state->sam_ctx, mem_ctx, 0,
3704 a_state->account_dn,
3705 "pwdProperties", NULL);
3706 return NT_STATUS_OK;
3711 samr_RemoveMemberFromForeignDomain
3713 static NTSTATUS samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3714 struct samr_RemoveMemberFromForeignDomain *r)
3716 struct dcesrv_handle *h;
3717 struct samr_domain_state *d_state;
3718 const char *memberdn;
3719 struct ldb_message **res;
3720 const char * const attrs[3] = { "distinguishedName", "objectSid", NULL };
3721 int i, count;
3723 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3725 d_state = h->data;
3727 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3728 "distinguishedName", "(objectSid=%s)",
3729 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3730 /* Nothing to do */
3731 if (memberdn == NULL) {
3732 return NT_STATUS_OK;
3735 /* TODO: Does this call only remove alias members, or does it do this
3736 * for domain groups as well? */
3738 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
3739 d_state->domain_dn, &res, attrs,
3740 d_state->domain_sid,
3741 "(&(member=%s)(objectClass=group)"
3742 "(|(groupType=%d)(groupType=%d)))",
3743 memberdn,
3744 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
3745 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
3747 if (count < 0)
3748 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3750 for (i=0; i<count; i++) {
3751 struct ldb_message *mod;
3753 mod = ldb_msg_new(mem_ctx);
3754 if (mod == NULL) {
3755 return NT_STATUS_NO_MEMORY;
3758 mod->dn = samdb_result_dn(mod, res[i], "distinguishedName", NULL);
3759 if (mod->dn == NULL) {
3760 talloc_free(mod);
3761 continue;
3764 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
3765 "member", memberdn) != 0)
3766 return NT_STATUS_NO_MEMORY;
3768 if (samdb_modify(d_state->sam_ctx, mem_ctx, mod) != 0)
3769 return NT_STATUS_UNSUCCESSFUL;
3771 talloc_free(mod);
3774 return NT_STATUS_OK;
3779 samr_QueryDomainInfo2
3781 just an alias for samr_QueryDomainInfo
3783 static NTSTATUS samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3784 struct samr_QueryDomainInfo2 *r)
3786 struct samr_QueryDomainInfo r1;
3787 NTSTATUS status;
3789 ZERO_STRUCT(r1.out);
3790 r1.in.domain_handle = r->in.domain_handle;
3791 r1.in.level = r->in.level;
3793 status = samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
3795 r->out.info = r1.out.info;
3797 return status;
3802 samr_QueryUserInfo2
3804 just an alias for samr_QueryUserInfo
3806 static NTSTATUS samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3807 struct samr_QueryUserInfo2 *r)
3809 struct samr_QueryUserInfo r1;
3810 NTSTATUS status;
3812 ZERO_STRUCT(r1.out);
3813 r1.in.user_handle = r->in.user_handle;
3814 r1.in.level = r->in.level;
3816 status = samr_QueryUserInfo(dce_call, mem_ctx, &r1);
3818 r->out.info = r1.out.info;
3820 return status;
3825 samr_QueryDisplayInfo2
3827 static NTSTATUS samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3828 struct samr_QueryDisplayInfo2 *r)
3830 struct samr_QueryDisplayInfo q;
3831 NTSTATUS result;
3833 q.in.domain_handle = r->in.domain_handle;
3834 q.in.level = r->in.level;
3835 q.in.start_idx = r->in.start_idx;
3836 q.in.max_entries = r->in.max_entries;
3837 q.in.buf_size = r->in.buf_size;
3838 ZERO_STRUCT(q.out);
3840 result = samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
3842 r->out.total_size = q.out.total_size;
3843 r->out.returned_size = q.out.returned_size;
3844 r->out.info = q.out.info;
3846 return result;
3851 samr_GetDisplayEnumerationIndex2
3853 static NTSTATUS samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3854 struct samr_GetDisplayEnumerationIndex2 *r)
3856 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3861 samr_QueryDisplayInfo3
3863 static NTSTATUS samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3864 struct samr_QueryDisplayInfo3 *r)
3866 struct samr_QueryDisplayInfo q;
3867 NTSTATUS result;
3869 q.in.domain_handle = r->in.domain_handle;
3870 q.in.level = r->in.level;
3871 q.in.start_idx = r->in.start_idx;
3872 q.in.max_entries = r->in.max_entries;
3873 q.in.buf_size = r->in.buf_size;
3874 ZERO_STRUCT(q.out);
3876 result = samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
3878 r->out.total_size = q.out.total_size;
3879 r->out.returned_size = q.out.returned_size;
3880 r->out.info = q.out.info;
3882 return result;
3887 samr_AddMultipleMembersToAlias
3889 static NTSTATUS samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3890 struct samr_AddMultipleMembersToAlias *r)
3892 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3897 samr_RemoveMultipleMembersFromAlias
3899 static NTSTATUS samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3900 struct samr_RemoveMultipleMembersFromAlias *r)
3902 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3907 samr_GetDomPwInfo
3909 this fetches the default password properties for a domain
3911 note that w2k3 completely ignores the domain name in this call, and
3912 always returns the information for the servers primary domain
3914 static NTSTATUS samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3915 struct samr_GetDomPwInfo *r)
3917 struct ldb_message **msgs;
3918 int ret;
3919 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
3920 struct ldb_context *sam_ctx;
3922 ZERO_STRUCT(r->out.info);
3924 sam_ctx = samdb_connect(mem_ctx, dce_call->conn->auth_state.session_info);
3925 if (sam_ctx == NULL) {
3926 return NT_STATUS_INVALID_SYSTEM_SERVICE;
3929 /* The domain name in this call is ignored */
3930 ret = gendb_search_dn(sam_ctx,
3931 mem_ctx, samdb_base_dn(mem_ctx), &msgs, attrs);
3932 if (ret <= 0) {
3933 return NT_STATUS_NO_SUCH_DOMAIN;
3935 if (ret > 1) {
3936 talloc_free(msgs);
3937 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3940 r->out.info.min_password_length = samdb_result_uint(msgs[0], "minPwdLength", 0);
3941 r->out.info.password_properties = samdb_result_uint(msgs[0], "pwdProperties", 1);
3943 talloc_free(msgs);
3945 talloc_free(sam_ctx);
3946 return NT_STATUS_OK;
3951 samr_Connect2
3953 static NTSTATUS samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3954 struct samr_Connect2 *r)
3956 struct samr_Connect c;
3958 c.in.system_name = NULL;
3959 c.in.access_mask = r->in.access_mask;
3960 c.out.connect_handle = r->out.connect_handle;
3962 return samr_Connect(dce_call, mem_ctx, &c);
3967 samr_SetUserInfo2
3969 just an alias for samr_SetUserInfo
3971 static NTSTATUS samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3972 struct samr_SetUserInfo2 *r)
3974 struct samr_SetUserInfo r2;
3976 r2.in.user_handle = r->in.user_handle;
3977 r2.in.level = r->in.level;
3978 r2.in.info = r->in.info;
3980 return samr_SetUserInfo(dce_call, mem_ctx, &r2);
3985 samr_SetBootKeyInformation
3987 static NTSTATUS samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3988 struct samr_SetBootKeyInformation *r)
3990 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3995 samr_GetBootKeyInformation
3997 static NTSTATUS samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3998 struct samr_GetBootKeyInformation *r)
4000 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4005 samr_Connect3
4007 static NTSTATUS samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4008 struct samr_Connect3 *r)
4010 struct samr_Connect c;
4012 c.in.system_name = NULL;
4013 c.in.access_mask = r->in.access_mask;
4014 c.out.connect_handle = r->out.connect_handle;
4016 return samr_Connect(dce_call, mem_ctx, &c);
4021 samr_Connect4
4023 static NTSTATUS samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4024 struct samr_Connect4 *r)
4026 struct samr_Connect c;
4028 c.in.system_name = NULL;
4029 c.in.access_mask = r->in.access_mask;
4030 c.out.connect_handle = r->out.connect_handle;
4032 return samr_Connect(dce_call, mem_ctx, &c);
4037 samr_Connect5
4039 static NTSTATUS samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4040 struct samr_Connect5 *r)
4042 struct samr_Connect c;
4043 NTSTATUS status;
4045 c.in.system_name = NULL;
4046 c.in.access_mask = r->in.access_mask;
4047 c.out.connect_handle = r->out.connect_handle;
4049 status = samr_Connect(dce_call, mem_ctx, &c);
4051 r->out.info->info1.unknown1 = 3;
4052 r->out.info->info1.unknown2 = 0;
4053 r->out.level = r->in.level;
4055 return status;
4060 samr_RidToSid
4062 static NTSTATUS samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4063 struct samr_RidToSid *r)
4065 struct samr_domain_state *d_state;
4066 struct dcesrv_handle *h;
4068 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4070 d_state = h->data;
4072 /* form the users SID */
4073 r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4074 if (!r->out.sid) {
4075 return NT_STATUS_NO_MEMORY;
4078 return NT_STATUS_OK;
4083 samr_SetDsrmPassword
4085 static NTSTATUS samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4086 struct samr_SetDsrmPassword *r)
4088 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4093 samr_ValidatePassword
4095 static NTSTATUS samr_ValidatePassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4096 struct samr_ValidatePassword *r)
4098 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4102 /* include the generated boilerplate */
4103 #include "librpc/gen_ndr/ndr_samr_s.c"