s4:dsdb: Remove unused ‘domain_dn’ parameter
[Samba.git] / source4 / rpc_server / samr / dcesrv_samr.c
blob69ac0798082a572ebe4ea65834adfa4056bbaa88
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
9 Copyright (C) Matthias Dieter Wallnöfer 2009
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
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 <ldb.h>
32 #include <ldb_errors.h>
33 #include "../libds/common/flags.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "dsdb/common/util.h"
36 #include "libcli/ldap/ldap_ndr.h"
37 #include "libcli/security/security.h"
38 #include "rpc_server/samr/proto.h"
39 #include "../lib/util/util_ldb.h"
40 #include "param/param.h"
41 #include "lib/util/tsort.h"
42 #include "libds/common/flag_mapping.h"
44 #undef strcasecmp
46 #define DCESRV_INTERFACE_SAMR_BIND(context, iface) \
47 dcesrv_interface_samr_bind(context, iface)
48 static NTSTATUS dcesrv_interface_samr_bind(struct dcesrv_connection_context *context,
49 const struct dcesrv_interface *iface)
51 return dcesrv_interface_bind_reject_connect(context, iface);
54 /* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
56 #define QUERY_STRING(msg, field, attr) \
57 info->field.string = ldb_msg_find_attr_as_string(msg, attr, "");
58 #define QUERY_UINT(msg, field, attr) \
59 info->field = ldb_msg_find_attr_as_uint(msg, attr, 0);
60 #define QUERY_RID(msg, field, attr) \
61 info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
62 #define QUERY_UINT64(msg, field, attr) \
63 info->field = ldb_msg_find_attr_as_uint64(msg, attr, 0);
64 #define QUERY_APASSC(msg, field, attr) \
65 info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
66 a_state->domain_state->domain_dn, msg, attr);
67 #define QUERY_BPWDCT(msg, field, attr) \
68 info->field = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, \
69 a_state->domain_state->domain_dn, msg);
70 #define QUERY_LHOURS(msg, field, attr) \
71 info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
72 #define QUERY_AFLAGS(msg, field, attr) \
73 info->field = samdb_result_acct_flags(msg, attr);
76 /* these are used to make the Set[User|Group]Info code easier to follow */
78 #define SET_STRING(msg, field, attr) do { \
79 struct ldb_message_element *set_el; \
80 if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
81 if (r->in.info->field.string[0] == '\0') { \
82 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
83 return NT_STATUS_NO_MEMORY; \
84 } \
85 } \
86 if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
87 return NT_STATUS_NO_MEMORY; \
88 } \
89 set_el = ldb_msg_find_element(msg, attr); \
90 set_el->flags = LDB_FLAG_MOD_REPLACE; \
91 } while (0)
93 #define SET_UINT(msg, field, attr) do { \
94 struct ldb_message_element *set_el; \
95 if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
96 return NT_STATUS_NO_MEMORY; \
97 } \
98 set_el = ldb_msg_find_element(msg, attr); \
99 set_el->flags = LDB_FLAG_MOD_REPLACE; \
100 } while (0)
102 #define SET_INT64(msg, field, attr) do { \
103 struct ldb_message_element *set_el; \
104 if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
105 return NT_STATUS_NO_MEMORY; \
107 set_el = ldb_msg_find_element(msg, attr); \
108 set_el->flags = LDB_FLAG_MOD_REPLACE; \
109 } while (0)
111 #define SET_UINT64(msg, field, attr) do { \
112 struct ldb_message_element *set_el; \
113 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
114 return NT_STATUS_NO_MEMORY; \
116 set_el = ldb_msg_find_element(msg, attr); \
117 set_el->flags = LDB_FLAG_MOD_REPLACE; \
118 } while (0)
120 /* Set account flags, discarding flags that cannot be set with SAMR */
121 #define SET_AFLAGS(msg, field, attr) do { \
122 struct ldb_message_element *set_el; \
123 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
124 return NT_STATUS_NO_MEMORY; \
126 set_el = ldb_msg_find_element(msg, attr); \
127 set_el->flags = LDB_FLAG_MOD_REPLACE; \
128 } while (0)
130 #define SET_LHOURS(msg, field, attr) do { \
131 struct ldb_message_element *set_el; \
132 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
133 return NT_STATUS_NO_MEMORY; \
135 set_el = ldb_msg_find_element(msg, attr); \
136 set_el->flags = LDB_FLAG_MOD_REPLACE; \
137 } while (0)
139 #define SET_PARAMETERS(msg, field, attr) do { \
140 struct ldb_message_element *set_el; \
141 if (r->in.info->field.length != 0) { \
142 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
143 return NT_STATUS_NO_MEMORY; \
145 set_el = ldb_msg_find_element(msg, attr); \
146 set_el->flags = LDB_FLAG_MOD_REPLACE; \
148 } while (0)
151 * Clear a GUID cache
153 static void clear_guid_cache(struct samr_guid_cache *cache)
155 cache->handle = 0;
156 cache->size = 0;
157 TALLOC_FREE(cache->entries);
161 * initialize a GUID cache
163 static void initialize_guid_cache(struct samr_guid_cache *cache)
165 cache->handle = 0;
166 cache->size = 0;
167 cache->entries = NULL;
170 static NTSTATUS load_guid_cache(
171 struct samr_guid_cache *cache,
172 struct samr_domain_state *d_state,
173 unsigned int ldb_cnt,
174 struct ldb_message **res)
176 NTSTATUS status = NT_STATUS_OK;
177 unsigned int i;
178 TALLOC_CTX *frame = talloc_stackframe();
180 clear_guid_cache(cache);
183 * Store the GUID's in the cache.
185 cache->handle = 0;
186 cache->size = ldb_cnt;
187 cache->entries = talloc_array(d_state, struct GUID, ldb_cnt);
188 if (cache->entries == NULL) {
189 clear_guid_cache(cache);
190 status = NT_STATUS_NO_MEMORY;
191 goto exit;
195 * Extract a list of the GUIDs for all the matching objects
196 * we cache just the GUIDS to reduce the memory overhead of
197 * the result cache.
199 for (i = 0; i < ldb_cnt; i++) {
200 cache->entries[i] = samdb_result_guid(res[i], "objectGUID");
202 exit:
203 TALLOC_FREE(frame);
204 return status;
208 samr_Connect
210 create a connection to the SAM database
212 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
213 struct samr_Connect *r)
215 struct samr_connect_state *c_state;
216 struct dcesrv_handle *handle;
218 ZERO_STRUCTP(r->out.connect_handle);
220 c_state = talloc(mem_ctx, struct samr_connect_state);
221 if (!c_state) {
222 return NT_STATUS_NO_MEMORY;
225 /* make sure the sam database is accessible */
226 c_state->sam_ctx = dcesrv_samdb_connect_as_user(c_state, dce_call);
227 if (c_state->sam_ctx == NULL) {
228 talloc_free(c_state);
229 return NT_STATUS_INVALID_SYSTEM_SERVICE;
232 handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_CONNECT);
233 if (!handle) {
234 talloc_free(c_state);
235 return NT_STATUS_NO_MEMORY;
238 handle->data = talloc_steal(handle, c_state);
240 c_state->access_mask = r->in.access_mask;
241 *r->out.connect_handle = handle->wire_handle;
243 return NT_STATUS_OK;
248 samr_Close
250 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
251 struct samr_Close *r)
253 struct dcesrv_handle *h;
255 *r->out.handle = *r->in.handle;
257 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
259 talloc_free(h);
261 ZERO_STRUCTP(r->out.handle);
263 return NT_STATUS_OK;
268 samr_SetSecurity
270 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
271 struct samr_SetSecurity *r)
273 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
278 samr_QuerySecurity
280 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
281 struct samr_QuerySecurity *r)
283 struct dcesrv_handle *h;
284 struct sec_desc_buf *sd;
286 *r->out.sdbuf = NULL;
288 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
290 sd = talloc(mem_ctx, struct sec_desc_buf);
291 if (sd == NULL) {
292 return NT_STATUS_NO_MEMORY;
295 sd->sd = samdb_default_security_descriptor(mem_ctx);
297 *r->out.sdbuf = sd;
299 return NT_STATUS_OK;
304 samr_Shutdown
306 we refuse this operation completely. If a admin wants to shutdown samr
307 in Samba then they should use the samba admin tools to disable the samr pipe
309 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
310 struct samr_Shutdown *r)
312 return NT_STATUS_ACCESS_DENIED;
317 samr_LookupDomain
319 this maps from a domain name to a SID
321 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
322 struct samr_LookupDomain *r)
324 struct samr_connect_state *c_state;
325 struct dcesrv_handle *h;
326 struct dom_sid *sid;
327 const char * const dom_attrs[] = { "objectSid", NULL};
328 struct ldb_message **dom_msgs;
329 int ret;
331 *r->out.sid = NULL;
333 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
335 c_state = h->data;
337 if (r->in.domain_name->string == NULL) {
338 return NT_STATUS_INVALID_PARAMETER;
341 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
342 ret = gendb_search(c_state->sam_ctx,
343 mem_ctx, NULL, &dom_msgs, dom_attrs,
344 "(objectClass=builtinDomain)");
345 } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
346 ret = gendb_search_dn(c_state->sam_ctx,
347 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
348 &dom_msgs, dom_attrs);
349 } else {
350 return NT_STATUS_NO_SUCH_DOMAIN;
352 if (ret != 1) {
353 return NT_STATUS_NO_SUCH_DOMAIN;
356 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
357 "objectSid");
359 if (sid == NULL) {
360 return NT_STATUS_NO_SUCH_DOMAIN;
363 *r->out.sid = sid;
365 return NT_STATUS_OK;
370 samr_EnumDomains
372 list the domains in the SAM
374 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
375 struct samr_EnumDomains *r)
377 struct dcesrv_handle *h;
378 struct samr_SamArray *array;
379 uint32_t i, start_i;
381 *r->out.resume_handle = 0;
382 *r->out.sam = NULL;
383 *r->out.num_entries = 0;
385 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
387 *r->out.resume_handle = 2;
389 start_i = *r->in.resume_handle;
391 if (start_i >= 2) {
392 /* search past end of list is not an error for this call */
393 return NT_STATUS_OK;
396 array = talloc(mem_ctx, struct samr_SamArray);
397 if (array == NULL) {
398 return NT_STATUS_NO_MEMORY;
401 array->count = 0;
402 array->entries = NULL;
404 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
405 if (array->entries == NULL) {
406 return NT_STATUS_NO_MEMORY;
409 for (i=0;i<2-start_i;i++) {
410 array->entries[i].idx = start_i + i;
411 if (i == 0) {
412 array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
413 } else {
414 array->entries[i].name.string = "BUILTIN";
418 *r->out.sam = array;
419 *r->out.num_entries = i;
420 array->count = *r->out.num_entries;
422 return NT_STATUS_OK;
427 samr_OpenDomain
429 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
430 struct samr_OpenDomain *r)
432 struct dcesrv_handle *h_conn, *h_domain;
433 struct samr_connect_state *c_state;
434 struct samr_domain_state *d_state;
435 const char * const dom_attrs[] = { "cn", NULL};
436 struct ldb_message **dom_msgs;
437 int ret;
438 unsigned int i;
440 ZERO_STRUCTP(r->out.domain_handle);
442 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
444 c_state = h_conn->data;
446 if (r->in.sid == NULL) {
447 return NT_STATUS_INVALID_PARAMETER;
450 d_state = talloc(mem_ctx, struct samr_domain_state);
451 if (!d_state) {
452 return NT_STATUS_NO_MEMORY;
455 d_state->domain_sid = talloc_steal(d_state, r->in.sid);
457 if (dom_sid_equal(d_state->domain_sid, &global_sid_Builtin)) {
458 d_state->builtin = true;
459 d_state->domain_name = "BUILTIN";
460 } else {
461 d_state->builtin = false;
462 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
465 ret = gendb_search(c_state->sam_ctx,
466 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
467 "(objectSid=%s)",
468 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
470 if (ret == 0) {
471 talloc_free(d_state);
472 return NT_STATUS_NO_SUCH_DOMAIN;
473 } else if (ret > 1) {
474 talloc_free(d_state);
475 return NT_STATUS_INTERNAL_DB_CORRUPTION;
476 } else if (ret == -1) {
477 talloc_free(d_state);
478 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
479 return NT_STATUS_INTERNAL_DB_CORRUPTION;
482 d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
483 d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
484 d_state->connect_state = talloc_reference(d_state, c_state);
485 d_state->sam_ctx = c_state->sam_ctx;
486 d_state->access_mask = r->in.access_mask;
487 d_state->domain_users_cached = NULL;
489 d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
491 for (i = 0; i < SAMR_LAST_CACHE; i++) {
492 initialize_guid_cache(&d_state->guid_caches[i]);
495 h_domain = dcesrv_handle_create(dce_call, SAMR_HANDLE_DOMAIN);
496 if (!h_domain) {
497 talloc_free(d_state);
498 return NT_STATUS_NO_MEMORY;
501 h_domain->data = talloc_steal(h_domain, d_state);
503 *r->out.domain_handle = h_domain->wire_handle;
505 return NT_STATUS_OK;
509 return DomInfo1
511 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
512 TALLOC_CTX *mem_ctx,
513 struct ldb_message **dom_msgs,
514 struct samr_DomInfo1 *info)
516 info->min_password_length =
517 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
518 info->password_history_length =
519 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
520 info->password_properties =
521 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
522 info->max_password_age =
523 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
524 info->min_password_age =
525 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
527 return NT_STATUS_OK;
531 return DomInfo2
533 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
534 TALLOC_CTX *mem_ctx,
535 struct ldb_message **dom_msgs,
536 struct samr_DomGeneralInformation *info)
538 size_t count = 0;
539 const enum ldb_scope scope = LDB_SCOPE_SUBTREE;
540 int ret = 0;
542 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
543 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
544 "domainReplica",
545 "");
547 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
548 0x8000000000000000LL);
550 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
551 "oEMInformation",
552 "");
553 info->domain_name.string = state->domain_name;
555 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
557 switch (state->role) {
558 case ROLE_ACTIVE_DIRECTORY_DC:
559 /* This pulls the NetBIOS name from the
560 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
561 string */
562 if (samdb_is_pdc(state->sam_ctx)) {
563 info->role = SAMR_ROLE_DOMAIN_PDC;
564 } else {
565 info->role = SAMR_ROLE_DOMAIN_BDC;
567 break;
568 case ROLE_DOMAIN_PDC:
569 case ROLE_DOMAIN_BDC:
570 case ROLE_IPA_DC:
571 case ROLE_AUTO:
572 return NT_STATUS_INTERNAL_ERROR;
573 case ROLE_DOMAIN_MEMBER:
574 info->role = SAMR_ROLE_DOMAIN_MEMBER;
575 break;
576 case ROLE_STANDALONE:
577 info->role = SAMR_ROLE_STANDALONE;
578 break;
582 * Users are not meant to be in BUILTIN
583 * so to speed up the query we do not filter on domain_sid
585 ret = dsdb_domain_count(
586 state->sam_ctx,
587 &count,
588 state->domain_dn,
589 NULL,
590 scope,
591 "(objectClass=user)");
592 if (ret != LDB_SUCCESS || count > UINT32_MAX) {
593 goto error;
595 info->num_users = count;
598 * Groups are not meant to be in BUILTIN
599 * so to speed up the query we do not filter on domain_sid
601 ret = dsdb_domain_count(
602 state->sam_ctx,
603 &count,
604 state->domain_dn,
605 NULL,
606 scope,
607 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
608 GTYPE_SECURITY_UNIVERSAL_GROUP,
609 GTYPE_SECURITY_GLOBAL_GROUP);
610 if (ret != LDB_SUCCESS || count > UINT32_MAX) {
611 goto error;
613 info->num_groups = count;
615 ret = dsdb_domain_count(
616 state->sam_ctx,
617 &count,
618 state->domain_dn,
619 state->domain_sid,
620 scope,
621 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
622 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
623 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
624 if (ret != LDB_SUCCESS || count > UINT32_MAX) {
625 goto error;
627 info->num_aliases = count;
629 return NT_STATUS_OK;
631 error:
632 if (count > UINT32_MAX) {
633 return NT_STATUS_INTEGER_OVERFLOW;
635 return dsdb_ldb_err_to_ntstatus(ret);
640 return DomInfo3
642 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
643 TALLOC_CTX *mem_ctx,
644 struct ldb_message **dom_msgs,
645 struct samr_DomInfo3 *info)
647 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
648 0x8000000000000000LL);
650 return NT_STATUS_OK;
654 return DomInfo4
656 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
657 TALLOC_CTX *mem_ctx,
658 struct ldb_message **dom_msgs,
659 struct samr_DomOEMInformation *info)
661 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
662 "oEMInformation",
663 "");
665 return NT_STATUS_OK;
669 return DomInfo5
671 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
672 TALLOC_CTX *mem_ctx,
673 struct ldb_message **dom_msgs,
674 struct samr_DomInfo5 *info)
676 info->domain_name.string = state->domain_name;
678 return NT_STATUS_OK;
682 return DomInfo6
684 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
685 TALLOC_CTX *mem_ctx,
686 struct ldb_message **dom_msgs,
687 struct samr_DomInfo6 *info)
689 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
690 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
691 "domainReplica",
692 "");
694 return NT_STATUS_OK;
698 return DomInfo7
700 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
701 TALLOC_CTX *mem_ctx,
702 struct ldb_message **dom_msgs,
703 struct samr_DomInfo7 *info)
706 switch (state->role) {
707 case ROLE_ACTIVE_DIRECTORY_DC:
708 /* This pulls the NetBIOS name from the
709 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
710 string */
711 if (samdb_is_pdc(state->sam_ctx)) {
712 info->role = SAMR_ROLE_DOMAIN_PDC;
713 } else {
714 info->role = SAMR_ROLE_DOMAIN_BDC;
716 break;
717 case ROLE_DOMAIN_PDC:
718 case ROLE_DOMAIN_BDC:
719 case ROLE_IPA_DC:
720 case ROLE_AUTO:
721 return NT_STATUS_INTERNAL_ERROR;
722 case ROLE_DOMAIN_MEMBER:
723 info->role = SAMR_ROLE_DOMAIN_MEMBER;
724 break;
725 case ROLE_STANDALONE:
726 info->role = SAMR_ROLE_STANDALONE;
727 break;
730 return NT_STATUS_OK;
734 return DomInfo8
736 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
737 TALLOC_CTX *mem_ctx,
738 struct ldb_message **dom_msgs,
739 struct samr_DomInfo8 *info)
741 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
742 time(NULL));
744 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
745 0x0LL);
747 return NT_STATUS_OK;
751 return DomInfo9
753 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
754 TALLOC_CTX *mem_ctx,
755 struct ldb_message **dom_msgs,
756 struct samr_DomInfo9 *info)
758 info->domain_server_state = DOMAIN_SERVER_ENABLED;
760 return NT_STATUS_OK;
764 return DomInfo11
766 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
767 TALLOC_CTX *mem_ctx,
768 struct ldb_message **dom_msgs,
769 struct samr_DomGeneralInformation2 *info)
771 NTSTATUS status;
772 status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
773 if (!NT_STATUS_IS_OK(status)) {
774 return status;
777 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
778 -18000000000LL);
779 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
780 -18000000000LL);
781 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
783 return NT_STATUS_OK;
787 return DomInfo12
789 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
790 TALLOC_CTX *mem_ctx,
791 struct ldb_message **dom_msgs,
792 struct samr_DomInfo12 *info)
794 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
795 -18000000000LL);
796 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
797 -18000000000LL);
798 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
800 return NT_STATUS_OK;
804 return DomInfo13
806 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
807 TALLOC_CTX *mem_ctx,
808 struct ldb_message **dom_msgs,
809 struct samr_DomInfo13 *info)
811 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
812 time(NULL));
814 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
815 0x0LL);
817 info->modified_count_at_last_promotion = 0;
819 return NT_STATUS_OK;
823 samr_QueryDomainInfo
825 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
826 TALLOC_CTX *mem_ctx,
827 struct samr_QueryDomainInfo *r)
829 struct dcesrv_handle *h;
830 struct samr_domain_state *d_state;
831 union samr_DomainInfo *info;
833 struct ldb_message **dom_msgs;
834 const char * const *attrs = NULL;
836 *r->out.info = NULL;
838 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
840 d_state = h->data;
842 switch (r->in.level) {
843 case 1:
845 static const char * const attrs2[] = { "minPwdLength",
846 "pwdHistoryLength",
847 "pwdProperties",
848 "maxPwdAge",
849 "minPwdAge",
850 NULL };
851 attrs = attrs2;
852 break;
854 case 2:
856 static const char * const attrs2[] = {"forceLogoff",
857 "oEMInformation",
858 "modifiedCount",
859 "domainReplica",
860 NULL};
861 attrs = attrs2;
862 break;
864 case 3:
866 static const char * const attrs2[] = {"forceLogoff",
867 NULL};
868 attrs = attrs2;
869 break;
871 case 4:
873 static const char * const attrs2[] = {"oEMInformation",
874 NULL};
875 attrs = attrs2;
876 break;
878 case 5:
880 attrs = NULL;
881 break;
883 case 6:
885 static const char * const attrs2[] = { "domainReplica",
886 NULL };
887 attrs = attrs2;
888 break;
890 case 7:
892 attrs = NULL;
893 break;
895 case 8:
897 static const char * const attrs2[] = { "modifiedCount",
898 "creationTime",
899 NULL };
900 attrs = attrs2;
901 break;
903 case 9:
905 attrs = NULL;
906 break;
908 case 11:
910 static const char * const attrs2[] = { "oEMInformation",
911 "forceLogoff",
912 "modifiedCount",
913 "lockoutDuration",
914 "lockOutObservationWindow",
915 "lockoutThreshold",
916 NULL};
917 attrs = attrs2;
918 break;
920 case 12:
922 static const char * const attrs2[] = { "lockoutDuration",
923 "lockOutObservationWindow",
924 "lockoutThreshold",
925 NULL};
926 attrs = attrs2;
927 break;
929 case 13:
931 static const char * const attrs2[] = { "modifiedCount",
932 "creationTime",
933 NULL };
934 attrs = attrs2;
935 break;
937 default:
939 return NT_STATUS_INVALID_INFO_CLASS;
943 /* some levels don't need a search */
944 if (attrs) {
945 int ret;
946 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
947 d_state->domain_dn, &dom_msgs, attrs);
948 if (ret == 0) {
949 return NT_STATUS_NO_SUCH_DOMAIN;
951 if (ret != 1) {
952 return NT_STATUS_INTERNAL_DB_CORRUPTION;
956 /* allocate the info structure */
957 info = talloc_zero(mem_ctx, union samr_DomainInfo);
958 if (info == NULL) {
959 return NT_STATUS_NO_MEMORY;
962 *r->out.info = info;
964 switch (r->in.level) {
965 case 1:
966 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
967 &info->info1);
968 case 2:
969 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
970 &info->general);
971 case 3:
972 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
973 &info->info3);
974 case 4:
975 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
976 &info->oem);
977 case 5:
978 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
979 &info->info5);
980 case 6:
981 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
982 &info->info6);
983 case 7:
984 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
985 &info->info7);
986 case 8:
987 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
988 &info->info8);
989 case 9:
990 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
991 &info->info9);
992 case 11:
993 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
994 &info->general2);
995 case 12:
996 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
997 &info->info12);
998 case 13:
999 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
1000 &info->info13);
1001 default:
1002 return NT_STATUS_INVALID_INFO_CLASS;
1008 samr_SetDomainInfo
1010 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1011 struct samr_SetDomainInfo *r)
1013 struct dcesrv_handle *h;
1014 struct samr_domain_state *d_state;
1015 struct ldb_message *msg;
1016 int ret;
1017 struct ldb_context *sam_ctx;
1019 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1021 d_state = h->data;
1022 sam_ctx = d_state->sam_ctx;
1024 msg = ldb_msg_new(mem_ctx);
1025 if (msg == NULL) {
1026 return NT_STATUS_NO_MEMORY;
1029 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
1030 if (!msg->dn) {
1031 return NT_STATUS_NO_MEMORY;
1034 switch (r->in.level) {
1035 case 1:
1036 SET_UINT (msg, info1.min_password_length, "minPwdLength");
1037 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
1038 SET_UINT (msg, info1.password_properties, "pwdProperties");
1039 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
1040 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
1041 break;
1042 case 3:
1043 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
1044 break;
1045 case 4:
1046 SET_STRING(msg, oem.oem_information, "oEMInformation");
1047 break;
1049 case 6:
1050 case 7:
1051 case 9:
1052 /* No op, we don't know where to set these */
1053 return NT_STATUS_OK;
1055 case 12:
1057 * It is not possible to set lockout_duration < lockout_window.
1058 * (The test is the other way around since the negative numbers
1059 * are stored...)
1061 * TODO:
1062 * This check should be moved to the backend, i.e. to some
1063 * ldb module under dsdb/samdb/ldb_modules/ .
1065 * This constraint is documented here for the samr rpc service:
1066 * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
1067 * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
1069 * And here for the ldap backend:
1070 * MS-ADTS 3.1.1.5.3.2 Constraints
1071 * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
1073 if (r->in.info->info12.lockout_duration >
1074 r->in.info->info12.lockout_window)
1076 return NT_STATUS_INVALID_PARAMETER;
1078 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
1079 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
1080 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
1081 break;
1083 default:
1084 /* many info classes are not valid for SetDomainInfo */
1085 return NT_STATUS_INVALID_INFO_CLASS;
1088 /* modify the samdb record */
1089 ret = ldb_modify(sam_ctx, msg);
1090 if (ret != LDB_SUCCESS) {
1091 DEBUG(1,("Failed to modify record %s: %s\n",
1092 ldb_dn_get_linearized(d_state->domain_dn),
1093 ldb_errstring(sam_ctx)));
1094 return dsdb_ldb_err_to_ntstatus(ret);
1097 return NT_STATUS_OK;
1101 samr_CreateDomainGroup
1103 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1104 struct samr_CreateDomainGroup *r)
1106 NTSTATUS status;
1107 struct samr_domain_state *d_state;
1108 struct samr_account_state *a_state;
1109 struct dcesrv_handle *h;
1110 const char *groupname;
1111 struct dom_sid *group_sid;
1112 struct ldb_dn *group_dn;
1113 struct dcesrv_handle *g_handle;
1115 ZERO_STRUCTP(r->out.group_handle);
1116 *r->out.rid = 0;
1118 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1120 d_state = h->data;
1122 if (d_state->builtin) {
1123 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain\n"));
1124 return NT_STATUS_ACCESS_DENIED;
1127 groupname = r->in.name->string;
1129 if (groupname == NULL) {
1130 return NT_STATUS_INVALID_PARAMETER;
1133 status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1134 if (!NT_STATUS_IS_OK(status)) {
1135 return status;
1138 a_state = talloc(mem_ctx, struct samr_account_state);
1139 if (!a_state) {
1140 return NT_STATUS_NO_MEMORY;
1142 a_state->sam_ctx = d_state->sam_ctx;
1143 a_state->access_mask = r->in.access_mask;
1144 a_state->domain_state = talloc_reference(a_state, d_state);
1145 a_state->account_dn = talloc_steal(a_state, group_dn);
1147 a_state->account_name = talloc_steal(a_state, groupname);
1149 /* create the policy handle */
1150 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
1151 if (!g_handle) {
1152 return NT_STATUS_NO_MEMORY;
1155 g_handle->data = talloc_steal(g_handle, a_state);
1157 *r->out.group_handle = g_handle->wire_handle;
1158 *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1160 return NT_STATUS_OK;
1165 comparison function for sorting SamEntry array
1167 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1169 return e1->idx - e2->idx;
1172 static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) {
1173 struct dom_sid *sid1 = NULL;
1174 struct dom_sid *sid2 = NULL;
1175 uint32_t rid1;
1176 uint32_t rid2;
1177 int res = 0;
1178 NTSTATUS status;
1179 TALLOC_CTX *frame = talloc_stackframe();
1181 sid1 = samdb_result_dom_sid(frame, *m1, "objectSid");
1182 sid2 = samdb_result_dom_sid(frame, *m2, "objectSid");
1185 * If entries don't have a SID we want to sort them to the end of
1186 * the list.
1188 if (sid1 == NULL && sid2 == NULL) {
1189 res = 0;
1190 goto exit;
1191 } else if (sid2 == NULL) {
1192 res = 1;
1193 goto exit;
1194 } else if (sid1 == NULL) {
1195 res = -1;
1196 goto exit;
1200 * Get and compare the rids, if we fail to extract a rid treat it as a
1201 * missing SID and sort to the end of the list
1203 status = dom_sid_split_rid(NULL, sid1, NULL, &rid1);
1204 if (!NT_STATUS_IS_OK(status)) {
1205 res = 1;
1206 goto exit;
1209 status = dom_sid_split_rid(NULL, sid2, NULL, &rid2);
1210 if (!NT_STATUS_IS_OK(status)) {
1211 res = -1;
1212 goto exit;
1215 if (rid1 == rid2) {
1216 res = 0;
1218 else if (rid1 > rid2) {
1219 res = 1;
1221 else {
1222 res = -1;
1224 exit:
1225 TALLOC_FREE(frame);
1226 return res;
1230 samr_EnumDomainGroups
1232 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1233 struct samr_EnumDomainGroups *r)
1235 struct dcesrv_handle *h;
1236 struct samr_domain_state *d_state;
1237 struct ldb_message **res;
1238 uint32_t i;
1239 uint32_t count;
1240 uint32_t results;
1241 uint32_t max_entries;
1242 uint32_t remaining_entries;
1243 uint32_t resume_handle;
1244 struct samr_SamEntry *entries;
1245 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1246 const char * const cache_attrs[] = { "objectSid", "objectGUID", NULL };
1247 struct samr_SamArray *sam;
1248 struct samr_guid_cache *cache = NULL;
1250 *r->out.resume_handle = 0;
1251 *r->out.sam = NULL;
1252 *r->out.num_entries = 0;
1254 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1256 d_state = h->data;
1257 cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_GROUPS_CACHE];
1260 * If the resume_handle is zero, query the database and cache the
1261 * matching GUID's
1263 if (*r->in.resume_handle == 0) {
1264 NTSTATUS status;
1265 int ldb_cnt;
1266 clear_guid_cache(cache);
1268 * search for all domain groups in this domain.
1270 ldb_cnt = samdb_search_domain(
1271 d_state->sam_ctx,
1272 mem_ctx,
1273 d_state->domain_dn,
1274 &res,
1275 cache_attrs,
1276 d_state->domain_sid,
1277 "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1278 GTYPE_SECURITY_UNIVERSAL_GROUP,
1279 GTYPE_SECURITY_GLOBAL_GROUP);
1280 if (ldb_cnt < 0) {
1281 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1284 * Sort the results into RID order, while the spec states there
1285 * is no order, Windows appears to sort the results by RID and
1286 * so it is possible that there are clients that depend on
1287 * this ordering
1289 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1292 * cache the sorted GUID's
1294 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1295 TALLOC_FREE(res);
1296 if (!NT_STATUS_IS_OK(status)) {
1297 return status;
1299 cache->handle = 0;
1304 * If the resume handle is out of range we return an empty response
1305 * and invalidate the cache.
1307 * From the specification:
1308 * Servers SHOULD validate that EnumerationContext is an expected
1309 * value for the server's implementation. Windows does NOT validate
1310 * the input, though the result of malformed information merely results
1311 * in inconsistent output to the client.
1313 if (*r->in.resume_handle >= cache->size) {
1314 clear_guid_cache(cache);
1315 sam = talloc(mem_ctx, struct samr_SamArray);
1316 if (!sam) {
1317 return NT_STATUS_NO_MEMORY;
1319 sam->entries = NULL;
1320 sam->count = 0;
1322 *r->out.sam = sam;
1323 *r->out.resume_handle = 0;
1324 return NT_STATUS_OK;
1329 * Calculate the number of entries to return limit by max_size.
1330 * Note that we use the w2k3 element size value of 54
1332 max_entries = 1 + (r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER);
1333 remaining_entries = cache->size - *r->in.resume_handle;
1334 results = MIN(remaining_entries, max_entries);
1337 * Process the list of result GUID's.
1338 * Read the details of each object and populate the Entries
1339 * for the current level.
1341 count = 0;
1342 resume_handle = *r->in.resume_handle;
1343 entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1344 if (entries == NULL) {
1345 clear_guid_cache(cache);
1346 return NT_STATUS_NO_MEMORY;
1348 for (i = 0; i < results; i++) {
1349 struct dom_sid *objectsid;
1350 uint32_t rid;
1351 struct ldb_result *rec;
1352 const uint32_t idx = *r->in.resume_handle + i;
1353 int ret;
1354 NTSTATUS status;
1355 const char *name = NULL;
1356 resume_handle++;
1358 * Read an object from disk using the GUID as the key
1360 * If the object can not be read, or it does not have a SID
1361 * it is ignored.
1363 * As a consequence of this, if all the remaining GUID's
1364 * have been deleted an empty result will be returned.
1365 * i.e. even if the previous call returned a non zero
1366 * resume_handle it is possible for no results to be returned.
1369 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1370 mem_ctx,
1371 &rec,
1372 &cache->entries[idx],
1373 attrs,
1375 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1376 struct GUID_txt_buf guid_buf;
1377 DBG_WARNING(
1378 "GUID [%s] not found\n",
1379 GUID_buf_string(&cache->entries[idx], &guid_buf));
1380 continue;
1381 } else if (ret != LDB_SUCCESS) {
1382 clear_guid_cache(cache);
1383 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1386 objectsid = samdb_result_dom_sid(mem_ctx,
1387 rec->msgs[0],
1388 "objectSID");
1389 if (objectsid == NULL) {
1390 struct GUID_txt_buf guid_buf;
1391 DBG_WARNING(
1392 "objectSID for GUID [%s] not found\n",
1393 GUID_buf_string(&cache->entries[idx], &guid_buf));
1394 continue;
1396 status = dom_sid_split_rid(NULL,
1397 objectsid,
1398 NULL,
1399 &rid);
1400 if (!NT_STATUS_IS_OK(status)) {
1401 struct dom_sid_buf sid_buf;
1402 struct GUID_txt_buf guid_buf;
1403 DBG_WARNING(
1404 "objectSID [%s] for GUID [%s] invalid\n",
1405 dom_sid_str_buf(objectsid, &sid_buf),
1406 GUID_buf_string(&cache->entries[idx], &guid_buf));
1407 continue;
1410 entries[count].idx = rid;
1411 name = ldb_msg_find_attr_as_string(
1412 rec->msgs[0], "sAMAccountName", "");
1413 entries[count].name.string = talloc_strdup(entries, name);
1414 count++;
1417 sam = talloc(mem_ctx, struct samr_SamArray);
1418 if (!sam) {
1419 clear_guid_cache(cache);
1420 return NT_STATUS_NO_MEMORY;
1423 sam->entries = entries;
1424 sam->count = count;
1426 *r->out.sam = sam;
1427 *r->out.resume_handle = resume_handle;
1428 *r->out.num_entries = count;
1431 * Signal no more results by returning zero resume handle,
1432 * the cache is also cleared at this point
1434 if (*r->out.resume_handle >= cache->size) {
1435 *r->out.resume_handle = 0;
1436 clear_guid_cache(cache);
1437 return NT_STATUS_OK;
1440 * There are more results to be returned.
1442 return STATUS_MORE_ENTRIES;
1447 samr_CreateUser2
1449 This call uses transactions to ensure we don't get a new conflicting
1450 user while we are processing this, and to ensure the user either
1451 completely exists, or does not.
1453 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1454 struct samr_CreateUser2 *r)
1456 NTSTATUS status;
1457 struct samr_domain_state *d_state;
1458 struct samr_account_state *a_state;
1459 struct dcesrv_handle *h;
1460 struct ldb_dn *dn;
1461 struct dom_sid *sid;
1462 struct dcesrv_handle *u_handle;
1463 const char *account_name;
1465 ZERO_STRUCTP(r->out.user_handle);
1466 *r->out.access_granted = 0;
1467 *r->out.rid = 0;
1469 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1471 d_state = h->data;
1473 if (d_state->builtin) {
1474 DEBUG(5, ("Cannot create a user in the BUILTIN domain\n"));
1475 return NT_STATUS_ACCESS_DENIED;
1476 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1477 /* Domain trust accounts must be created by the LSA calls */
1478 return NT_STATUS_ACCESS_DENIED;
1480 account_name = r->in.account_name->string;
1482 if (account_name == NULL) {
1483 return NT_STATUS_INVALID_PARAMETER;
1486 status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1487 &sid, &dn);
1488 if (!NT_STATUS_IS_OK(status)) {
1489 return status;
1491 a_state = talloc(mem_ctx, struct samr_account_state);
1492 if (!a_state) {
1493 return NT_STATUS_NO_MEMORY;
1495 a_state->sam_ctx = d_state->sam_ctx;
1496 a_state->access_mask = r->in.access_mask;
1497 a_state->domain_state = talloc_reference(a_state, d_state);
1498 a_state->account_dn = talloc_steal(a_state, dn);
1500 a_state->account_name = talloc_steal(a_state, account_name);
1501 if (!a_state->account_name) {
1502 return NT_STATUS_NO_MEMORY;
1505 /* create the policy handle */
1506 u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
1507 if (!u_handle) {
1508 return NT_STATUS_NO_MEMORY;
1511 u_handle->data = talloc_steal(u_handle, a_state);
1513 *r->out.user_handle = u_handle->wire_handle;
1514 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1516 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1518 return NT_STATUS_OK;
1523 samr_CreateUser
1525 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1526 struct samr_CreateUser *r)
1528 struct samr_CreateUser2 r2;
1529 uint32_t access_granted = 0;
1532 /* a simple wrapper around samr_CreateUser2 works nicely */
1534 r2 = (struct samr_CreateUser2) {
1535 .in.domain_handle = r->in.domain_handle,
1536 .in.account_name = r->in.account_name,
1537 .in.acct_flags = ACB_NORMAL,
1538 .in.access_mask = r->in.access_mask,
1539 .out.user_handle = r->out.user_handle,
1540 .out.access_granted = &access_granted,
1541 .out.rid = r->out.rid
1544 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1547 struct enum_dom_users_ctx {
1548 struct samr_SamEntry *entries;
1549 uint32_t num_entries;
1550 uint32_t acct_flags;
1551 struct dom_sid *domain_sid;
1554 static int user_iterate_callback(struct ldb_request *req,
1555 struct ldb_reply *ares);
1558 * Iterate users and add all those that match a domain SID and pass an acct
1559 * flags check to an array of SamEntry objects.
1561 static int user_iterate_callback(struct ldb_request *req,
1562 struct ldb_reply *ares)
1564 struct enum_dom_users_ctx *ac =\
1565 talloc_get_type(req->context, struct enum_dom_users_ctx);
1566 int ret = LDB_ERR_OPERATIONS_ERROR;
1568 if (!ares) {
1569 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1571 if (ares->error != LDB_SUCCESS) {
1572 return ldb_request_done(req, ares->error);
1575 switch (ares->type) {
1576 case LDB_REPLY_ENTRY:
1578 struct ldb_message *msg = ares->message;
1579 const struct ldb_val *val;
1580 struct samr_SamEntry *ent;
1581 struct dom_sid objectsid;
1582 uint32_t rid;
1583 size_t entries_array_len = 0;
1584 NTSTATUS status;
1585 ssize_t sid_size;
1587 if (ac->acct_flags && ((samdb_result_acct_flags(msg, NULL) &
1588 ac->acct_flags) == 0)) {
1589 ret = LDB_SUCCESS;
1590 break;
1593 val = ldb_msg_find_ldb_val(msg, "objectSID");
1594 if (val == NULL) {
1595 DBG_WARNING("objectSID for DN %s not found\n",
1596 ldb_dn_get_linearized(msg->dn));
1597 ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1598 break;
1601 sid_size = sid_parse(val->data, val->length, &objectsid);
1602 if (sid_size == -1) {
1603 struct dom_sid_buf sid_buf;
1604 DBG_WARNING("objectsid [%s] for DN [%s] invalid\n",
1605 dom_sid_str_buf(&objectsid, &sid_buf),
1606 ldb_dn_get_linearized(msg->dn));
1607 ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1608 break;
1611 if (!dom_sid_in_domain(ac->domain_sid, &objectsid)) {
1612 /* Ignore if user isn't in the domain */
1613 ret = LDB_SUCCESS;
1614 break;
1617 status = dom_sid_split_rid(ares, &objectsid, NULL, &rid);
1618 if (!NT_STATUS_IS_OK(status)) {
1619 struct dom_sid_buf sid_buf;
1620 DBG_WARNING("Couldn't split RID from "
1621 "SID [%s] of DN [%s]\n",
1622 dom_sid_str_buf(&objectsid, &sid_buf),
1623 ldb_dn_get_linearized(msg->dn));
1624 ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1625 break;
1628 entries_array_len = talloc_array_length(ac->entries);
1629 if (ac->num_entries >= entries_array_len) {
1630 if (entries_array_len * 2 < entries_array_len) {
1631 ret = ldb_request_done(req,
1632 LDB_ERR_OPERATIONS_ERROR);
1633 break;
1635 ac->entries = talloc_realloc(ac,
1636 ac->entries,
1637 struct samr_SamEntry,
1638 entries_array_len * 2);
1639 if (ac->entries == NULL) {
1640 ret = ldb_request_done(req,
1641 LDB_ERR_OPERATIONS_ERROR);
1642 break;
1646 ent = &(ac->entries[ac->num_entries++]);
1647 val = ldb_msg_find_ldb_val(msg, "samaccountname");
1648 if (val == NULL) {
1649 DBG_WARNING("samaccountname attribute not found\n");
1650 ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1651 break;
1653 ent->name.string = talloc_steal(ac->entries,
1654 (char *)val->data);
1655 ent->idx = rid;
1656 ret = LDB_SUCCESS;
1657 break;
1659 case LDB_REPLY_DONE:
1661 if (ac->num_entries != 0 &&
1662 ac->num_entries != talloc_array_length(ac->entries)) {
1663 ac->entries = talloc_realloc(ac,
1664 ac->entries,
1665 struct samr_SamEntry,
1666 ac->num_entries);
1667 if (ac->entries == NULL) {
1668 ret = ldb_request_done(req,
1669 LDB_ERR_OPERATIONS_ERROR);
1670 break;
1673 ret = ldb_request_done(req, LDB_SUCCESS);
1674 break;
1676 case LDB_REPLY_REFERRAL:
1678 ret = LDB_SUCCESS;
1679 break;
1681 default:
1682 /* Doesn't happen */
1683 ret = LDB_ERR_OPERATIONS_ERROR;
1685 TALLOC_FREE(ares);
1687 return ret;
1691 * samr_EnumDomainUsers
1692 * The previous implementation did an initial search and stored a list of
1693 * matching GUIDs on the connection handle's domain state, then did direct
1694 * GUID lookups for each record in a page indexed by resume_handle. That
1695 * approach was memory efficient, requiring only 16 bytes per record, but
1696 * was too slow for winbind which needs this RPC call for getpwent.
1698 * Now we use an iterate pattern to populate a cached list of the rids and
1699 * names for each record. This improves runtime performance but requires
1700 * about 200 bytes per record which will mean for a 100k database we use
1701 * about 2MB, which is fine. The speedup achieved by this new approach is
1702 * around 50%.
1704 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call,
1705 TALLOC_CTX *mem_ctx,
1706 struct samr_EnumDomainUsers *r)
1708 struct dcesrv_handle *h;
1709 struct samr_domain_state *d_state;
1710 uint32_t results;
1711 uint32_t max_entries;
1712 uint32_t num_entries;
1713 uint32_t remaining_entries;
1714 struct samr_SamEntry *entries;
1715 const char * const attrs[] = { "objectSid", "sAMAccountName",
1716 "userAccountControl", NULL };
1717 struct samr_SamArray *sam;
1718 struct ldb_request *req;
1720 *r->out.resume_handle = 0;
1721 *r->out.sam = NULL;
1722 *r->out.num_entries = 0;
1724 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1726 d_state = h->data;
1727 entries = d_state->domain_users_cached;
1730 * If the resume_handle is zero, query the database and cache the
1731 * matching entries.
1733 if (*r->in.resume_handle == 0) {
1734 int ret;
1735 struct enum_dom_users_ctx *ac;
1736 if (entries != NULL) {
1737 talloc_free(entries);
1738 d_state->domain_users_cached = NULL;
1741 ac = talloc(mem_ctx, struct enum_dom_users_ctx);
1742 ac->num_entries = 0;
1743 ac->domain_sid = d_state->domain_sid;
1744 ac->entries = talloc_array(ac,
1745 struct samr_SamEntry,
1746 100);
1747 if (ac->entries == NULL) {
1748 talloc_free(ac);
1749 return NT_STATUS_NO_MEMORY;
1751 ac->acct_flags = r->in.acct_flags;
1753 ret = ldb_build_search_req(&req,
1754 d_state->sam_ctx,
1755 mem_ctx,
1756 d_state->domain_dn,
1757 LDB_SCOPE_SUBTREE,
1758 "(objectClass=user)",
1759 attrs,
1760 NULL,
1762 user_iterate_callback,
1763 NULL);
1764 if (ret != LDB_SUCCESS) {
1765 talloc_free(ac);
1766 return dsdb_ldb_err_to_ntstatus(ret);
1769 ret = ldb_request(d_state->sam_ctx, req);
1770 if (ret != LDB_SUCCESS) {
1771 talloc_free(ac);
1772 return dsdb_ldb_err_to_ntstatus(ret);
1775 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1776 if (ret != LDB_SUCCESS) {
1777 return dsdb_ldb_err_to_ntstatus(ret);
1780 if (ac->num_entries == 0) {
1781 DBG_WARNING("No users in domain %s\n",
1782 ldb_dn_get_linearized(d_state->domain_dn));
1783 talloc_free(ac);
1786 * test_EnumDomainUsers_all() expects that r.out.sam
1787 * should be non-NULL, even if we have no entries.
1789 sam = talloc_zero(mem_ctx, struct samr_SamArray);
1790 if (sam == NULL) {
1791 return NT_STATUS_NO_MEMORY;
1793 *r->out.sam = sam;
1795 return NT_STATUS_OK;
1798 entries = talloc_steal(d_state, ac->entries);
1799 d_state->domain_users_cached = entries;
1800 num_entries = ac->num_entries;
1801 talloc_free(ac);
1804 * Sort the entries into RID order, while the spec states there
1805 * is no order, Windows appears to sort the results by RID and
1806 * so it is possible that there are clients that depend on
1807 * this ordering
1809 TYPESAFE_QSORT(entries, num_entries, compare_SamEntry);
1810 } else {
1811 num_entries = talloc_array_length(entries);
1815 * If the resume handle is out of range we return an empty response
1816 * and invalidate the cache.
1818 * From the specification:
1819 * Servers SHOULD validate that EnumerationContext is an expected
1820 * value for the server's implementation. Windows does NOT validate
1821 * the input, though the result of malformed information merely results
1822 * in inconsistent output to the client.
1824 if (*r->in.resume_handle >= num_entries) {
1825 talloc_free(entries);
1826 d_state->domain_users_cached = NULL;
1827 sam = talloc(mem_ctx, struct samr_SamArray);
1828 if (!sam) {
1829 return NT_STATUS_NO_MEMORY;
1831 sam->entries = NULL;
1832 sam->count = 0;
1834 *r->out.sam = sam;
1835 *r->out.resume_handle = 0;
1836 return NT_STATUS_OK;
1840 * Calculate the number of entries to return limit by max_size.
1841 * Note that we use the w2k3 element size value of 54
1843 max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER);
1844 remaining_entries = num_entries - *r->in.resume_handle;
1845 results = MIN(remaining_entries, max_entries);
1847 sam = talloc(mem_ctx, struct samr_SamArray);
1848 if (!sam) {
1849 d_state->domain_users_cached = NULL;
1850 return NT_STATUS_NO_MEMORY;
1853 sam->entries = entries + *r->in.resume_handle;
1854 sam->count = results;
1856 *r->out.sam = sam;
1857 *r->out.resume_handle = *r->in.resume_handle + results;
1858 *r->out.num_entries = results;
1861 * Signal no more results by returning zero resume handle,
1862 * the cache is also cleared at this point
1864 if (*r->out.resume_handle >= num_entries) {
1865 *r->out.resume_handle = 0;
1866 return NT_STATUS_OK;
1869 * There are more results to be returned.
1871 return STATUS_MORE_ENTRIES;
1876 samr_CreateDomAlias
1878 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1879 struct samr_CreateDomAlias *r)
1881 struct samr_domain_state *d_state;
1882 struct samr_account_state *a_state;
1883 struct dcesrv_handle *h;
1884 const char *alias_name;
1885 struct dom_sid *sid;
1886 struct dcesrv_handle *a_handle;
1887 struct ldb_dn *dn;
1888 NTSTATUS status;
1890 ZERO_STRUCTP(r->out.alias_handle);
1891 *r->out.rid = 0;
1893 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1895 d_state = h->data;
1897 if (d_state->builtin) {
1898 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain\n"));
1899 return NT_STATUS_ACCESS_DENIED;
1902 alias_name = r->in.alias_name->string;
1904 if (alias_name == NULL) {
1905 return NT_STATUS_INVALID_PARAMETER;
1908 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1909 if (!NT_STATUS_IS_OK(status)) {
1910 return status;
1913 a_state = talloc(mem_ctx, struct samr_account_state);
1914 if (!a_state) {
1915 return NT_STATUS_NO_MEMORY;
1918 a_state->sam_ctx = d_state->sam_ctx;
1919 a_state->access_mask = r->in.access_mask;
1920 a_state->domain_state = talloc_reference(a_state, d_state);
1921 a_state->account_dn = talloc_steal(a_state, dn);
1923 a_state->account_name = talloc_steal(a_state, alias_name);
1925 /* create the policy handle */
1926 a_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
1927 if (a_handle == NULL)
1928 return NT_STATUS_NO_MEMORY;
1930 a_handle->data = talloc_steal(a_handle, a_state);
1932 *r->out.alias_handle = a_handle->wire_handle;
1934 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1936 return NT_STATUS_OK;
1941 samr_EnumDomainAliases
1943 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1944 struct samr_EnumDomainAliases *r)
1946 struct dcesrv_handle *h;
1947 struct samr_domain_state *d_state;
1948 struct ldb_message **res;
1949 int i, ldb_cnt;
1950 uint32_t first, count;
1951 struct samr_SamEntry *entries;
1952 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1953 struct samr_SamArray *sam;
1955 *r->out.resume_handle = 0;
1956 *r->out.sam = NULL;
1957 *r->out.num_entries = 0;
1959 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1961 d_state = h->data;
1963 /* search for all domain aliases in this domain. This could possibly be
1964 cached and resumed based on resume_key */
1965 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1966 &res, attrs,
1967 d_state->domain_sid,
1968 "(&(|(grouptype=%d)(grouptype=%d)))"
1969 "(objectclass=group))",
1970 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1971 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1972 if (ldb_cnt < 0) {
1973 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1976 /* convert to SamEntry format */
1977 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1978 if (!entries) {
1979 return NT_STATUS_NO_MEMORY;
1982 count = 0;
1984 for (i=0;i<ldb_cnt;i++) {
1985 struct dom_sid *alias_sid;
1987 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1988 "objectSid");
1990 if (alias_sid == NULL) {
1991 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1994 entries[count].idx =
1995 alias_sid->sub_auths[alias_sid->num_auths-1];
1996 entries[count].name.string =
1997 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1998 count += 1;
2001 /* sort the results by rid */
2002 TYPESAFE_QSORT(entries, count, compare_SamEntry);
2004 /* find the first entry to return */
2005 for (first=0;
2006 first<count && entries[first].idx <= *r->in.resume_handle;
2007 first++) ;
2009 /* return the rest, limit by max_size. Note that we
2010 use the w2k3 element size value of 54 */
2011 *r->out.num_entries = count - first;
2012 *r->out.num_entries = MIN(*r->out.num_entries,
2013 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
2015 sam = talloc(mem_ctx, struct samr_SamArray);
2016 if (!sam) {
2017 return NT_STATUS_NO_MEMORY;
2020 sam->entries = entries+first;
2021 sam->count = *r->out.num_entries;
2023 *r->out.sam = sam;
2025 if (first == count) {
2026 return NT_STATUS_OK;
2029 if (*r->out.num_entries < count - first) {
2030 *r->out.resume_handle =
2031 entries[first+*r->out.num_entries-1].idx;
2032 return STATUS_MORE_ENTRIES;
2035 return NT_STATUS_OK;
2040 samr_GetAliasMembership
2042 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2043 struct samr_GetAliasMembership *r)
2045 struct dcesrv_handle *h;
2046 struct samr_domain_state *d_state;
2047 char *filter;
2048 const char * const attrs[] = { "objectSid", NULL };
2049 struct ldb_message **res;
2050 uint32_t i;
2051 int count = 0;
2053 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2055 d_state = h->data;
2057 filter = talloc_asprintf(mem_ctx,
2058 "(&(|(grouptype=%d)(grouptype=%d))"
2059 "(objectclass=group)(|",
2060 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2061 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2062 if (filter == NULL) {
2063 return NT_STATUS_NO_MEMORY;
2066 for (i=0; i<r->in.sids->num_sids; i++) {
2067 struct dom_sid_buf buf;
2069 filter = talloc_asprintf_append(
2070 filter,
2071 "(member=<SID=%s>)",
2072 dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
2074 if (filter == NULL) {
2075 return NT_STATUS_NO_MEMORY;
2079 /* Find out if we had at least one valid member SID passed - otherwise
2080 * just skip the search. */
2081 if (strstr(filter, "member") != NULL) {
2082 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
2083 &res, attrs, d_state->domain_sid,
2084 "%s))", filter);
2085 if (count < 0) {
2086 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2090 r->out.rids->count = 0;
2091 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
2092 if (r->out.rids->ids == NULL)
2093 return NT_STATUS_NO_MEMORY;
2095 for (i=0; i<count; i++) {
2096 struct dom_sid *alias_sid;
2098 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
2099 if (alias_sid == NULL) {
2100 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2103 r->out.rids->ids[r->out.rids->count] =
2104 alias_sid->sub_auths[alias_sid->num_auths-1];
2105 r->out.rids->count += 1;
2108 return NT_STATUS_OK;
2113 samr_LookupNames
2115 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2116 struct samr_LookupNames *r)
2118 struct dcesrv_handle *h;
2119 struct samr_domain_state *d_state;
2120 uint32_t i, num_mapped;
2121 NTSTATUS status = NT_STATUS_OK;
2122 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
2123 int count;
2125 ZERO_STRUCTP(r->out.rids);
2126 ZERO_STRUCTP(r->out.types);
2128 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2130 d_state = h->data;
2132 if (r->in.num_names == 0) {
2133 return NT_STATUS_OK;
2136 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
2137 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
2138 if (!r->out.rids->ids || !r->out.types->ids) {
2139 return NT_STATUS_NO_MEMORY;
2141 r->out.rids->count = r->in.num_names;
2142 r->out.types->count = r->in.num_names;
2144 num_mapped = 0;
2146 for (i=0;i<r->in.num_names;i++) {
2147 struct ldb_message **res;
2148 struct dom_sid *sid;
2149 uint32_t atype, rtype;
2151 r->out.rids->ids[i] = 0;
2152 r->out.types->ids[i] = SID_NAME_UNKNOWN;
2154 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
2155 "sAMAccountName=%s",
2156 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
2157 if (count != 1) {
2158 status = STATUS_SOME_UNMAPPED;
2159 continue;
2162 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
2163 if (sid == NULL) {
2164 status = STATUS_SOME_UNMAPPED;
2165 continue;
2168 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
2169 if (atype == 0) {
2170 status = STATUS_SOME_UNMAPPED;
2171 continue;
2174 rtype = ds_atype_map(atype);
2176 if (rtype == SID_NAME_UNKNOWN) {
2177 status = STATUS_SOME_UNMAPPED;
2178 continue;
2181 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
2182 r->out.types->ids[i] = rtype;
2183 num_mapped++;
2186 if (num_mapped == 0) {
2187 return NT_STATUS_NONE_MAPPED;
2189 return status;
2194 samr_LookupRids
2196 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2197 struct samr_LookupRids *r)
2199 NTSTATUS status;
2200 struct dcesrv_handle *h;
2201 struct samr_domain_state *d_state;
2202 const char **names;
2203 struct lsa_String *lsa_names;
2204 enum lsa_SidType *ids;
2206 ZERO_STRUCTP(r->out.names);
2207 ZERO_STRUCTP(r->out.types);
2209 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2211 d_state = h->data;
2213 if (r->in.num_rids == 0)
2214 return NT_STATUS_OK;
2216 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
2217 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
2218 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
2220 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
2221 return NT_STATUS_NO_MEMORY;
2223 r->out.names->names = lsa_names;
2224 r->out.names->count = r->in.num_rids;
2226 r->out.types->ids = (uint32_t *) ids;
2227 r->out.types->count = r->in.num_rids;
2229 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
2230 r->in.num_rids, r->in.rids, names, ids);
2231 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
2232 uint32_t i;
2233 for (i = 0; i < r->in.num_rids; i++) {
2234 lsa_names[i].string = names[i];
2237 return status;
2242 samr_OpenGroup
2244 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2245 struct samr_OpenGroup *r)
2247 struct samr_domain_state *d_state;
2248 struct samr_account_state *a_state;
2249 struct dcesrv_handle *h;
2250 const char *groupname;
2251 struct dom_sid *sid;
2252 struct ldb_message **msgs;
2253 struct dcesrv_handle *g_handle;
2254 const char * const attrs[2] = { "sAMAccountName", NULL };
2255 int ret;
2257 ZERO_STRUCTP(r->out.group_handle);
2259 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2261 d_state = h->data;
2263 /* form the group SID */
2264 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2265 if (!sid) {
2266 return NT_STATUS_NO_MEMORY;
2269 /* search for the group record */
2270 if (d_state->builtin) {
2271 ret = gendb_search(d_state->sam_ctx,
2272 mem_ctx, d_state->domain_dn, &msgs, attrs,
2273 "(&(objectSid=%s)(objectClass=group)"
2274 "(groupType=%d))",
2275 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2276 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
2277 } else {
2278 ret = gendb_search(d_state->sam_ctx,
2279 mem_ctx, d_state->domain_dn, &msgs, attrs,
2280 "(&(objectSid=%s)(objectClass=group)"
2281 "(|(groupType=%d)(groupType=%d)))",
2282 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2283 GTYPE_SECURITY_UNIVERSAL_GROUP,
2284 GTYPE_SECURITY_GLOBAL_GROUP);
2286 if (ret == 0) {
2287 return NT_STATUS_NO_SUCH_GROUP;
2289 if (ret != 1) {
2290 DEBUG(0,("Found %d records matching sid %s\n",
2291 ret, dom_sid_string(mem_ctx, sid)));
2292 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2295 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2296 if (groupname == NULL) {
2297 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2298 dom_sid_string(mem_ctx, sid)));
2299 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2302 a_state = talloc(mem_ctx, struct samr_account_state);
2303 if (!a_state) {
2304 return NT_STATUS_NO_MEMORY;
2306 a_state->sam_ctx = d_state->sam_ctx;
2307 a_state->access_mask = r->in.access_mask;
2308 a_state->domain_state = talloc_reference(a_state, d_state);
2309 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2310 a_state->account_sid = talloc_steal(a_state, sid);
2311 a_state->account_name = talloc_strdup(a_state, groupname);
2312 if (!a_state->account_name) {
2313 return NT_STATUS_NO_MEMORY;
2316 /* create the policy handle */
2317 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
2318 if (!g_handle) {
2319 return NT_STATUS_NO_MEMORY;
2322 g_handle->data = talloc_steal(g_handle, a_state);
2324 *r->out.group_handle = g_handle->wire_handle;
2326 return NT_STATUS_OK;
2330 samr_QueryGroupInfo
2332 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2333 struct samr_QueryGroupInfo *r)
2335 struct dcesrv_handle *h;
2336 struct samr_account_state *a_state;
2337 struct ldb_message *msg, **res;
2338 const char * const attrs[4] = { "sAMAccountName", "description",
2339 "numMembers", NULL };
2340 int ret;
2341 union samr_GroupInfo *info;
2343 *r->out.info = NULL;
2345 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2347 a_state = h->data;
2349 /* pull all the group attributes */
2350 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2351 a_state->account_dn, &res, attrs);
2352 if (ret == 0) {
2353 return NT_STATUS_NO_SUCH_GROUP;
2355 if (ret != 1) {
2356 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2358 msg = res[0];
2360 /* allocate the info structure */
2361 info = talloc_zero(mem_ctx, union samr_GroupInfo);
2362 if (info == NULL) {
2363 return NT_STATUS_NO_MEMORY;
2366 /* Fill in the level */
2367 switch (r->in.level) {
2368 case GROUPINFOALL:
2369 QUERY_STRING(msg, all.name, "sAMAccountName");
2370 info->all.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2371 QUERY_UINT (msg, all.num_members, "numMembers")
2372 QUERY_STRING(msg, all.description, "description");
2373 break;
2374 case GROUPINFONAME:
2375 QUERY_STRING(msg, name, "sAMAccountName");
2376 break;
2377 case GROUPINFOATTRIBUTES:
2378 info->attributes.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2379 break;
2380 case GROUPINFODESCRIPTION:
2381 QUERY_STRING(msg, description, "description");
2382 break;
2383 case GROUPINFOALL2:
2384 QUERY_STRING(msg, all2.name, "sAMAccountName");
2385 info->all.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2386 QUERY_UINT (msg, all2.num_members, "numMembers")
2387 QUERY_STRING(msg, all2.description, "description");
2388 break;
2389 default:
2390 talloc_free(info);
2391 return NT_STATUS_INVALID_INFO_CLASS;
2394 *r->out.info = info;
2396 return NT_STATUS_OK;
2401 samr_SetGroupInfo
2403 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2404 struct samr_SetGroupInfo *r)
2406 struct dcesrv_handle *h;
2407 struct samr_account_state *g_state;
2408 struct ldb_message *msg;
2409 int ret;
2411 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2413 g_state = h->data;
2415 msg = ldb_msg_new(mem_ctx);
2416 if (msg == NULL) {
2417 return NT_STATUS_NO_MEMORY;
2420 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2421 if (!msg->dn) {
2422 return NT_STATUS_NO_MEMORY;
2425 switch (r->in.level) {
2426 case GROUPINFODESCRIPTION:
2427 SET_STRING(msg, description, "description");
2428 break;
2429 case GROUPINFONAME:
2430 /* On W2k3 this does not change the name, it changes the
2431 * sAMAccountName attribute */
2432 SET_STRING(msg, name, "sAMAccountName");
2433 break;
2434 case GROUPINFOATTRIBUTES:
2435 /* This does not do anything obviously visible in W2k3 LDAP */
2436 return NT_STATUS_OK;
2437 default:
2438 return NT_STATUS_INVALID_INFO_CLASS;
2441 /* modify the samdb record */
2442 ret = ldb_modify(g_state->sam_ctx, msg);
2443 if (ret != LDB_SUCCESS) {
2444 return dsdb_ldb_err_to_ntstatus(ret);
2447 return NT_STATUS_OK;
2452 samr_AddGroupMember
2454 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2455 struct samr_AddGroupMember *r)
2457 struct dcesrv_handle *h;
2458 struct samr_account_state *a_state;
2459 struct samr_domain_state *d_state;
2460 struct ldb_message *mod;
2461 struct dom_sid *membersid;
2462 const char *memberdn;
2463 struct ldb_result *res;
2464 const char * const attrs[] = { NULL };
2465 int ret;
2467 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2469 a_state = h->data;
2470 d_state = a_state->domain_state;
2472 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2473 if (membersid == NULL) {
2474 return NT_STATUS_NO_MEMORY;
2477 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2478 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2479 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2480 "(objectSid=%s)",
2481 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2483 if (ret != LDB_SUCCESS) {
2484 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2487 if (res->count == 0) {
2488 return NT_STATUS_NO_SUCH_USER;
2491 if (res->count > 1) {
2492 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2495 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2497 if (memberdn == NULL)
2498 return NT_STATUS_NO_MEMORY;
2500 mod = ldb_msg_new(mem_ctx);
2501 if (mod == NULL) {
2502 return NT_STATUS_NO_MEMORY;
2505 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2507 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2508 memberdn);
2509 if (ret != LDB_SUCCESS) {
2510 return dsdb_ldb_err_to_ntstatus(ret);
2513 ret = ldb_modify(a_state->sam_ctx, mod);
2514 switch (ret) {
2515 case LDB_SUCCESS:
2516 return NT_STATUS_OK;
2517 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2518 return NT_STATUS_MEMBER_IN_GROUP;
2519 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2520 return NT_STATUS_ACCESS_DENIED;
2521 default:
2522 return dsdb_ldb_err_to_ntstatus(ret);
2528 samr_DeleteDomainGroup
2530 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2531 struct samr_DeleteDomainGroup *r)
2533 struct dcesrv_handle *h;
2534 struct samr_account_state *a_state;
2535 int ret;
2537 *r->out.group_handle = *r->in.group_handle;
2539 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2541 a_state = h->data;
2543 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2544 if (ret != LDB_SUCCESS) {
2545 return dsdb_ldb_err_to_ntstatus(ret);
2548 talloc_free(h);
2549 ZERO_STRUCTP(r->out.group_handle);
2551 return NT_STATUS_OK;
2556 samr_DeleteGroupMember
2558 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2559 struct samr_DeleteGroupMember *r)
2561 struct dcesrv_handle *h;
2562 struct samr_account_state *a_state;
2563 struct samr_domain_state *d_state;
2564 struct ldb_message *mod;
2565 struct dom_sid *membersid;
2566 const char *memberdn;
2567 struct ldb_result *res;
2568 const char * const attrs[] = { NULL };
2569 int ret;
2571 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2573 a_state = h->data;
2574 d_state = a_state->domain_state;
2576 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2577 if (membersid == NULL) {
2578 return NT_STATUS_NO_MEMORY;
2581 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2582 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2583 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2584 "(objectSid=%s)",
2585 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2587 if (ret != LDB_SUCCESS) {
2588 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2591 if (res->count == 0) {
2592 return NT_STATUS_NO_SUCH_USER;
2595 if (res->count > 1) {
2596 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2599 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2601 if (memberdn == NULL)
2602 return NT_STATUS_NO_MEMORY;
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 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2612 memberdn);
2613 if (ret != LDB_SUCCESS) {
2614 return NT_STATUS_NO_MEMORY;
2617 ret = ldb_modify(a_state->sam_ctx, mod);
2618 switch (ret) {
2619 case LDB_SUCCESS:
2620 return NT_STATUS_OK;
2621 case LDB_ERR_UNWILLING_TO_PERFORM:
2622 case LDB_ERR_NO_SUCH_ATTRIBUTE:
2623 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2624 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2625 return NT_STATUS_ACCESS_DENIED;
2626 default:
2627 return dsdb_ldb_err_to_ntstatus(ret);
2633 samr_QueryGroupMember
2635 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2636 struct samr_QueryGroupMember *r)
2638 struct dcesrv_handle *h;
2639 struct samr_account_state *a_state;
2640 struct samr_domain_state *d_state;
2641 struct samr_RidAttrArray *array;
2642 unsigned int i, num_members;
2643 struct dom_sid *members;
2644 NTSTATUS status;
2646 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2648 a_state = h->data;
2649 d_state = a_state->domain_state;
2651 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2652 a_state->account_dn, &members,
2653 &num_members);
2654 if (!NT_STATUS_IS_OK(status)) {
2655 return status;
2658 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2659 if (array == NULL) {
2660 return NT_STATUS_NO_MEMORY;
2663 if (num_members == 0) {
2664 *r->out.rids = array;
2666 return NT_STATUS_OK;
2669 array->rids = talloc_array(array, uint32_t, num_members);
2670 if (array->rids == NULL) {
2671 return NT_STATUS_NO_MEMORY;
2674 array->attributes = talloc_array(array, uint32_t, num_members);
2675 if (array->attributes == NULL) {
2676 return NT_STATUS_NO_MEMORY;
2679 array->count = 0;
2680 for (i=0; i<num_members; i++) {
2681 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2682 continue;
2685 status = dom_sid_split_rid(NULL, &members[i], NULL,
2686 &array->rids[array->count]);
2687 if (!NT_STATUS_IS_OK(status)) {
2688 return status;
2691 array->attributes[array->count] = SE_GROUP_DEFAULT_FLAGS;
2692 array->count++;
2695 *r->out.rids = array;
2697 return NT_STATUS_OK;
2702 samr_SetMemberAttributesOfGroup
2704 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2705 struct samr_SetMemberAttributesOfGroup *r)
2707 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2712 samr_OpenAlias
2714 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2715 struct samr_OpenAlias *r)
2717 struct samr_domain_state *d_state;
2718 struct samr_account_state *a_state;
2719 struct dcesrv_handle *h;
2720 const char *alias_name;
2721 struct dom_sid *sid;
2722 struct ldb_message **msgs;
2723 struct dcesrv_handle *g_handle;
2724 const char * const attrs[2] = { "sAMAccountName", NULL };
2725 int ret;
2727 ZERO_STRUCTP(r->out.alias_handle);
2729 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2731 d_state = h->data;
2733 /* form the alias SID */
2734 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2735 if (sid == NULL)
2736 return NT_STATUS_NO_MEMORY;
2738 /* search for the group record */
2739 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2740 "(&(objectSid=%s)(objectclass=group)"
2741 "(|(grouptype=%d)(grouptype=%d)))",
2742 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2743 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2744 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2745 if (ret == 0) {
2746 return NT_STATUS_NO_SUCH_ALIAS;
2748 if (ret != 1) {
2749 DEBUG(0,("Found %d records matching sid %s\n",
2750 ret, dom_sid_string(mem_ctx, sid)));
2751 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2754 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2755 if (alias_name == NULL) {
2756 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2757 dom_sid_string(mem_ctx, sid)));
2758 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2761 a_state = talloc(mem_ctx, struct samr_account_state);
2762 if (!a_state) {
2763 return NT_STATUS_NO_MEMORY;
2765 a_state->sam_ctx = d_state->sam_ctx;
2766 a_state->access_mask = r->in.access_mask;
2767 a_state->domain_state = talloc_reference(a_state, d_state);
2768 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2769 a_state->account_sid = talloc_steal(a_state, sid);
2770 a_state->account_name = talloc_strdup(a_state, alias_name);
2771 if (!a_state->account_name) {
2772 return NT_STATUS_NO_MEMORY;
2775 /* create the policy handle */
2776 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
2777 if (!g_handle) {
2778 return NT_STATUS_NO_MEMORY;
2781 g_handle->data = talloc_steal(g_handle, a_state);
2783 *r->out.alias_handle = g_handle->wire_handle;
2785 return NT_STATUS_OK;
2790 samr_QueryAliasInfo
2792 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2793 struct samr_QueryAliasInfo *r)
2795 struct dcesrv_handle *h;
2796 struct samr_account_state *a_state;
2797 struct ldb_message *msg, **res;
2798 const char * const attrs[4] = { "sAMAccountName", "description",
2799 "numMembers", NULL };
2800 int ret;
2801 union samr_AliasInfo *info;
2803 *r->out.info = NULL;
2805 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2807 a_state = h->data;
2809 /* pull all the alias attributes */
2810 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2811 a_state->account_dn, &res, attrs);
2812 if (ret == 0) {
2813 return NT_STATUS_NO_SUCH_ALIAS;
2815 if (ret != 1) {
2816 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2818 msg = res[0];
2820 /* allocate the info structure */
2821 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2822 if (info == NULL) {
2823 return NT_STATUS_NO_MEMORY;
2826 switch(r->in.level) {
2827 case ALIASINFOALL:
2828 QUERY_STRING(msg, all.name, "sAMAccountName");
2829 QUERY_UINT (msg, all.num_members, "numMembers");
2830 QUERY_STRING(msg, all.description, "description");
2831 break;
2832 case ALIASINFONAME:
2833 QUERY_STRING(msg, name, "sAMAccountName");
2834 break;
2835 case ALIASINFODESCRIPTION:
2836 QUERY_STRING(msg, description, "description");
2837 break;
2838 default:
2839 talloc_free(info);
2840 return NT_STATUS_INVALID_INFO_CLASS;
2843 *r->out.info = info;
2845 return NT_STATUS_OK;
2850 samr_SetAliasInfo
2852 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2853 struct samr_SetAliasInfo *r)
2855 struct dcesrv_handle *h;
2856 struct samr_account_state *a_state;
2857 struct ldb_message *msg;
2858 int ret;
2860 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2862 a_state = h->data;
2864 msg = ldb_msg_new(mem_ctx);
2865 if (msg == NULL) {
2866 return NT_STATUS_NO_MEMORY;
2869 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2870 if (!msg->dn) {
2871 return NT_STATUS_NO_MEMORY;
2874 switch (r->in.level) {
2875 case ALIASINFODESCRIPTION:
2876 SET_STRING(msg, description, "description");
2877 break;
2878 case ALIASINFONAME:
2879 /* On W2k3 this does not change the name, it changes the
2880 * sAMAccountName attribute */
2881 SET_STRING(msg, name, "sAMAccountName");
2882 break;
2883 default:
2884 return NT_STATUS_INVALID_INFO_CLASS;
2887 /* modify the samdb record */
2888 ret = ldb_modify(a_state->sam_ctx, msg);
2889 if (ret != LDB_SUCCESS) {
2890 return dsdb_ldb_err_to_ntstatus(ret);
2893 return NT_STATUS_OK;
2898 samr_DeleteDomAlias
2900 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2901 struct samr_DeleteDomAlias *r)
2903 struct dcesrv_handle *h;
2904 struct samr_account_state *a_state;
2905 int ret;
2907 *r->out.alias_handle = *r->in.alias_handle;
2909 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2911 a_state = h->data;
2913 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2914 if (ret != LDB_SUCCESS) {
2915 return dsdb_ldb_err_to_ntstatus(ret);
2918 talloc_free(h);
2919 ZERO_STRUCTP(r->out.alias_handle);
2921 return NT_STATUS_OK;
2926 samr_AddAliasMember
2928 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2929 struct samr_AddAliasMember *r)
2931 struct dcesrv_handle *h;
2932 struct samr_account_state *a_state;
2933 struct samr_domain_state *d_state;
2934 struct ldb_message *mod;
2935 struct ldb_message **msgs;
2936 const char * const attrs[] = { NULL };
2937 struct ldb_dn *memberdn = NULL;
2938 int ret;
2939 NTSTATUS status;
2941 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2943 a_state = h->data;
2944 d_state = a_state->domain_state;
2946 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2947 &msgs, attrs, "(objectsid=%s)",
2948 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2950 if (ret == 1) {
2951 memberdn = msgs[0]->dn;
2952 } else if (ret == 0) {
2953 status = samdb_create_foreign_security_principal(
2954 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2955 if (!NT_STATUS_IS_OK(status)) {
2956 return status;
2958 } else {
2959 DEBUG(0,("Found %d records matching sid %s\n",
2960 ret, dom_sid_string(mem_ctx, r->in.sid)));
2961 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2964 if (memberdn == NULL) {
2965 DEBUG(0, ("Could not find memberdn\n"));
2966 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2969 mod = ldb_msg_new(mem_ctx);
2970 if (mod == NULL) {
2971 return NT_STATUS_NO_MEMORY;
2974 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2976 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2977 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2978 if (ret != LDB_SUCCESS) {
2979 return dsdb_ldb_err_to_ntstatus(ret);
2982 ret = ldb_modify(a_state->sam_ctx, mod);
2983 switch (ret) {
2984 case LDB_SUCCESS:
2985 return NT_STATUS_OK;
2986 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2987 return NT_STATUS_MEMBER_IN_GROUP;
2988 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2989 return NT_STATUS_ACCESS_DENIED;
2990 default:
2991 return dsdb_ldb_err_to_ntstatus(ret);
2997 samr_DeleteAliasMember
2999 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3000 struct samr_DeleteAliasMember *r)
3002 struct dcesrv_handle *h;
3003 struct samr_account_state *a_state;
3004 struct samr_domain_state *d_state;
3005 struct ldb_message *mod;
3006 const char *memberdn;
3007 int ret;
3009 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
3011 a_state = h->data;
3012 d_state = a_state->domain_state;
3014 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3015 "distinguishedName", "(objectSid=%s)",
3016 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3017 if (memberdn == NULL) {
3018 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3021 mod = ldb_msg_new(mem_ctx);
3022 if (mod == NULL) {
3023 return NT_STATUS_NO_MEMORY;
3026 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
3028 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
3029 memberdn);
3030 if (ret != LDB_SUCCESS) {
3031 return dsdb_ldb_err_to_ntstatus(ret);
3034 ret = ldb_modify(a_state->sam_ctx, mod);
3035 switch (ret) {
3036 case LDB_SUCCESS:
3037 return NT_STATUS_OK;
3038 case LDB_ERR_UNWILLING_TO_PERFORM:
3039 return NT_STATUS_MEMBER_NOT_IN_GROUP;
3040 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
3041 return NT_STATUS_ACCESS_DENIED;
3042 default:
3043 return dsdb_ldb_err_to_ntstatus(ret);
3049 samr_GetMembersInAlias
3051 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3052 struct samr_GetMembersInAlias *r)
3054 struct dcesrv_handle *h;
3055 struct samr_account_state *a_state;
3056 struct samr_domain_state *d_state;
3057 struct lsa_SidPtr *array;
3058 unsigned int i, num_members;
3059 struct dom_sid *members;
3060 NTSTATUS status;
3062 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
3064 a_state = h->data;
3065 d_state = a_state->domain_state;
3067 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
3068 a_state->account_dn, &members,
3069 &num_members);
3070 if (!NT_STATUS_IS_OK(status)) {
3071 return status;
3074 if (num_members == 0) {
3075 r->out.sids->sids = NULL;
3077 return NT_STATUS_OK;
3080 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
3081 if (array == NULL) {
3082 return NT_STATUS_NO_MEMORY;
3085 for (i=0; i<num_members; i++) {
3086 array[i].sid = &members[i];
3089 r->out.sids->num_sids = num_members;
3090 r->out.sids->sids = array;
3092 return NT_STATUS_OK;
3096 samr_OpenUser
3098 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3099 struct samr_OpenUser *r)
3101 struct samr_domain_state *d_state;
3102 struct samr_account_state *a_state;
3103 struct dcesrv_handle *h;
3104 const char *account_name;
3105 struct dom_sid *sid;
3106 struct ldb_message **msgs;
3107 struct dcesrv_handle *u_handle;
3108 const char * const attrs[2] = { "sAMAccountName", NULL };
3109 int ret;
3111 ZERO_STRUCTP(r->out.user_handle);
3113 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3115 d_state = h->data;
3117 /* form the users SID */
3118 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
3119 if (!sid) {
3120 return NT_STATUS_NO_MEMORY;
3123 /* search for the user record */
3124 ret = gendb_search(d_state->sam_ctx,
3125 mem_ctx, d_state->domain_dn, &msgs, attrs,
3126 "(&(objectSid=%s)(objectclass=user))",
3127 ldap_encode_ndr_dom_sid(mem_ctx, sid));
3128 if (ret == 0) {
3129 return NT_STATUS_NO_SUCH_USER;
3131 if (ret != 1) {
3132 DEBUG(0,("Found %d records matching sid %s\n", ret,
3133 dom_sid_string(mem_ctx, sid)));
3134 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3137 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
3138 if (account_name == NULL) {
3139 DEBUG(0,("sAMAccountName field missing for sid %s\n",
3140 dom_sid_string(mem_ctx, sid)));
3141 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3144 a_state = talloc(mem_ctx, struct samr_account_state);
3145 if (!a_state) {
3146 return NT_STATUS_NO_MEMORY;
3148 a_state->sam_ctx = d_state->sam_ctx;
3149 a_state->access_mask = r->in.access_mask;
3150 a_state->domain_state = talloc_reference(a_state, d_state);
3151 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
3152 a_state->account_sid = talloc_steal(a_state, sid);
3153 a_state->account_name = talloc_strdup(a_state, account_name);
3154 if (!a_state->account_name) {
3155 return NT_STATUS_NO_MEMORY;
3158 /* create the policy handle */
3159 u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
3160 if (!u_handle) {
3161 return NT_STATUS_NO_MEMORY;
3164 u_handle->data = talloc_steal(u_handle, a_state);
3166 *r->out.user_handle = u_handle->wire_handle;
3168 return NT_STATUS_OK;
3174 samr_DeleteUser
3176 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3177 struct samr_DeleteUser *r)
3179 struct dcesrv_handle *h;
3180 struct samr_account_state *a_state;
3181 int ret;
3183 *r->out.user_handle = *r->in.user_handle;
3185 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3187 a_state = h->data;
3189 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
3190 if (ret != LDB_SUCCESS) {
3191 DEBUG(1, ("Failed to delete user: %s: %s\n",
3192 ldb_dn_get_linearized(a_state->account_dn),
3193 ldb_errstring(a_state->sam_ctx)));
3194 return dsdb_ldb_err_to_ntstatus(ret);
3197 talloc_free(h);
3198 ZERO_STRUCTP(r->out.user_handle);
3200 return NT_STATUS_OK;
3205 samr_QueryUserInfo
3207 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3208 struct samr_QueryUserInfo *r)
3210 struct dcesrv_handle *h;
3211 struct samr_account_state *a_state;
3212 struct ldb_message *msg, **res;
3213 int ret;
3214 struct ldb_context *sam_ctx;
3216 const char * const *attrs = NULL;
3217 union samr_UserInfo *info;
3219 NTSTATUS status;
3221 *r->out.info = NULL;
3223 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3225 a_state = h->data;
3226 sam_ctx = a_state->sam_ctx;
3228 /* fill in the reply */
3229 switch (r->in.level) {
3230 case 1:
3232 static const char * const attrs2[] = {"sAMAccountName",
3233 "displayName",
3234 "primaryGroupID",
3235 "description",
3236 "comment",
3237 NULL};
3238 attrs = attrs2;
3239 break;
3241 case 2:
3243 static const char * const attrs2[] = {"comment",
3244 "countryCode",
3245 "codePage",
3246 NULL};
3247 attrs = attrs2;
3248 break;
3250 case 3:
3252 static const char * const attrs2[] = {"sAMAccountName",
3253 "displayName",
3254 "objectSid",
3255 "primaryGroupID",
3256 "homeDirectory",
3257 "homeDrive",
3258 "scriptPath",
3259 "profilePath",
3260 "userWorkstations",
3261 "lastLogon",
3262 "lastLogoff",
3263 "pwdLastSet",
3264 "msDS-UserPasswordExpiryTimeComputed",
3265 "logonHours",
3266 "badPwdCount",
3267 "badPasswordTime",
3268 "logonCount",
3269 "userAccountControl",
3270 "msDS-User-Account-Control-Computed",
3271 NULL};
3272 attrs = attrs2;
3273 break;
3275 case 4:
3277 static const char * const attrs2[] = {"logonHours",
3278 NULL};
3279 attrs = attrs2;
3280 break;
3282 case 5:
3284 static const char * const attrs2[] = {"sAMAccountName",
3285 "displayName",
3286 "objectSid",
3287 "primaryGroupID",
3288 "homeDirectory",
3289 "homeDrive",
3290 "scriptPath",
3291 "profilePath",
3292 "description",
3293 "userWorkstations",
3294 "lastLogon",
3295 "lastLogoff",
3296 "logonHours",
3297 "badPwdCount",
3298 "badPasswordTime",
3299 "logonCount",
3300 "pwdLastSet",
3301 "msDS-ResultantPSO",
3302 "msDS-UserPasswordExpiryTimeComputed",
3303 "accountExpires",
3304 "userAccountControl",
3305 "msDS-User-Account-Control-Computed",
3306 NULL};
3307 attrs = attrs2;
3308 break;
3310 case 6:
3312 static const char * const attrs2[] = {"sAMAccountName",
3313 "displayName",
3314 NULL};
3315 attrs = attrs2;
3316 break;
3318 case 7:
3320 static const char * const attrs2[] = {"sAMAccountName",
3321 NULL};
3322 attrs = attrs2;
3323 break;
3325 case 8:
3327 static const char * const attrs2[] = {"displayName",
3328 NULL};
3329 attrs = attrs2;
3330 break;
3332 case 9:
3334 static const char * const attrs2[] = {"primaryGroupID",
3335 NULL};
3336 attrs = attrs2;
3337 break;
3339 case 10:
3341 static const char * const attrs2[] = {"homeDirectory",
3342 "homeDrive",
3343 NULL};
3344 attrs = attrs2;
3345 break;
3347 case 11:
3349 static const char * const attrs2[] = {"scriptPath",
3350 NULL};
3351 attrs = attrs2;
3352 break;
3354 case 12:
3356 static const char * const attrs2[] = {"profilePath",
3357 NULL};
3358 attrs = attrs2;
3359 break;
3361 case 13:
3363 static const char * const attrs2[] = {"description",
3364 NULL};
3365 attrs = attrs2;
3366 break;
3368 case 14:
3370 static const char * const attrs2[] = {"userWorkstations",
3371 NULL};
3372 attrs = attrs2;
3373 break;
3375 case 16:
3377 static const char * const attrs2[] = {"userAccountControl",
3378 "msDS-User-Account-Control-Computed",
3379 "pwdLastSet",
3380 "msDS-UserPasswordExpiryTimeComputed",
3381 NULL};
3382 attrs = attrs2;
3383 break;
3385 case 17:
3387 static const char * const attrs2[] = {"accountExpires",
3388 NULL};
3389 attrs = attrs2;
3390 break;
3392 case 18:
3394 return NT_STATUS_NOT_SUPPORTED;
3396 case 20:
3398 static const char * const attrs2[] = {"userParameters",
3399 NULL};
3400 attrs = attrs2;
3401 break;
3403 case 21:
3405 static const char * const attrs2[] = {"lastLogon",
3406 "lastLogoff",
3407 "pwdLastSet",
3408 "msDS-ResultantPSO",
3409 "msDS-UserPasswordExpiryTimeComputed",
3410 "accountExpires",
3411 "sAMAccountName",
3412 "displayName",
3413 "homeDirectory",
3414 "homeDrive",
3415 "scriptPath",
3416 "profilePath",
3417 "description",
3418 "userWorkstations",
3419 "comment",
3420 "userParameters",
3421 "objectSid",
3422 "primaryGroupID",
3423 "userAccountControl",
3424 "msDS-User-Account-Control-Computed",
3425 "logonHours",
3426 "badPwdCount",
3427 "badPasswordTime",
3428 "logonCount",
3429 "countryCode",
3430 "codePage",
3431 NULL};
3432 attrs = attrs2;
3433 break;
3435 case 23:
3436 case 24:
3437 case 25:
3438 case 26:
3440 return NT_STATUS_NOT_SUPPORTED;
3442 default:
3444 return NT_STATUS_INVALID_INFO_CLASS;
3448 /* pull all the user attributes */
3449 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3450 a_state->account_dn, &res, attrs);
3451 if (ret == 0) {
3452 return NT_STATUS_NO_SUCH_USER;
3454 if (ret != 1) {
3455 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3457 msg = res[0];
3459 /* allocate the info structure */
3460 info = talloc_zero(mem_ctx, union samr_UserInfo);
3461 if (info == NULL) {
3462 return NT_STATUS_NO_MEMORY;
3465 /* fill in the reply */
3466 switch (r->in.level) {
3467 case 1:
3468 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
3469 QUERY_STRING(msg, info1.full_name, "displayName");
3470 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3471 QUERY_STRING(msg, info1.description, "description");
3472 QUERY_STRING(msg, info1.comment, "comment");
3473 break;
3475 case 2:
3476 QUERY_STRING(msg, info2.comment, "comment");
3477 QUERY_UINT (msg, info2.country_code, "countryCode");
3478 QUERY_UINT (msg, info2.code_page, "codePage");
3479 break;
3481 case 3:
3482 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
3483 QUERY_STRING(msg, info3.full_name, "displayName");
3484 QUERY_RID (msg, info3.rid, "objectSid");
3485 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3486 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
3487 QUERY_STRING(msg, info3.home_drive, "homeDrive");
3488 QUERY_STRING(msg, info3.logon_script, "scriptPath");
3489 QUERY_STRING(msg, info3.profile_path, "profilePath");
3490 QUERY_STRING(msg, info3.workstations, "userWorkstations");
3491 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
3492 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
3493 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
3494 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3495 QUERY_UINT64(msg, info3.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3496 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3497 /* level 3 gives the raw badPwdCount value */
3498 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3499 QUERY_UINT (msg, info3.logon_count, "logonCount");
3500 QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
3501 break;
3503 case 4:
3504 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3505 break;
3507 case 5:
3508 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
3509 QUERY_STRING(msg, info5.full_name, "displayName");
3510 QUERY_RID (msg, info5.rid, "objectSid");
3511 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3512 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
3513 QUERY_STRING(msg, info5.home_drive, "homeDrive");
3514 QUERY_STRING(msg, info5.logon_script, "scriptPath");
3515 QUERY_STRING(msg, info5.profile_path, "profilePath");
3516 QUERY_STRING(msg, info5.description, "description");
3517 QUERY_STRING(msg, info5.workstations, "userWorkstations");
3518 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
3519 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
3520 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3521 QUERY_BPWDCT(msg, info5.bad_password_count, "badPwdCount");
3522 QUERY_UINT (msg, info5.logon_count, "logonCount");
3523 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
3524 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
3525 QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
3526 break;
3528 case 6:
3529 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3530 QUERY_STRING(msg, info6.full_name, "displayName");
3531 break;
3533 case 7:
3534 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3535 break;
3537 case 8:
3538 QUERY_STRING(msg, info8.full_name, "displayName");
3539 break;
3541 case 9:
3542 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3543 break;
3545 case 10:
3546 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3547 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3548 break;
3550 case 11:
3551 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3552 break;
3554 case 12:
3555 QUERY_STRING(msg, info12.profile_path, "profilePath");
3556 break;
3558 case 13:
3559 QUERY_STRING(msg, info13.description, "description");
3560 break;
3562 case 14:
3563 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3564 break;
3566 case 16:
3567 QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3568 break;
3570 case 17:
3571 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3572 break;
3574 case 20:
3575 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
3576 if (!NT_STATUS_IS_OK(status)) {
3577 talloc_free(info);
3578 return status;
3580 break;
3582 case 21:
3583 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3584 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3585 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3586 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3587 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3588 QUERY_UINT64(msg, info21.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3589 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3590 QUERY_STRING(msg, info21.full_name, "displayName");
3591 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3592 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3593 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3594 QUERY_STRING(msg, info21.profile_path, "profilePath");
3595 QUERY_STRING(msg, info21.description, "description");
3596 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3597 QUERY_STRING(msg, info21.comment, "comment");
3598 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
3599 if (!NT_STATUS_IS_OK(status)) {
3600 talloc_free(info);
3601 return status;
3604 QUERY_RID (msg, info21.rid, "objectSid");
3605 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3606 QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3607 info->info21.fields_present = 0x08FFFFFF;
3608 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3609 QUERY_BPWDCT(msg, info21.bad_password_count, "badPwdCount");
3610 QUERY_UINT (msg, info21.logon_count, "logonCount");
3611 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3612 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3613 } else {
3614 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3616 QUERY_UINT (msg, info21.country_code, "countryCode");
3617 QUERY_UINT (msg, info21.code_page, "codePage");
3618 break;
3621 default:
3622 talloc_free(info);
3623 return NT_STATUS_INVALID_INFO_CLASS;
3626 *r->out.info = info;
3628 return NT_STATUS_OK;
3633 samr_SetUserInfo
3635 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3636 struct samr_SetUserInfo *r)
3638 struct dcesrv_handle *h;
3639 struct samr_account_state *a_state;
3640 struct ldb_message *msg;
3641 int ret;
3642 NTSTATUS status = NT_STATUS_OK;
3643 struct ldb_context *sam_ctx;
3644 DATA_BLOB session_key = data_blob_null;
3646 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3648 a_state = h->data;
3649 sam_ctx = a_state->sam_ctx;
3651 msg = ldb_msg_new(mem_ctx);
3652 if (msg == NULL) {
3653 return NT_STATUS_NO_MEMORY;
3656 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3657 if (!msg->dn) {
3658 return NT_STATUS_NO_MEMORY;
3661 ret = ldb_transaction_start(sam_ctx);
3662 if (ret != LDB_SUCCESS) {
3663 DBG_ERR("Failed to start a transaction: %s\n",
3664 ldb_errstring(sam_ctx));
3665 return NT_STATUS_LOCK_NOT_GRANTED;
3668 switch (r->in.level) {
3669 case 2:
3670 SET_STRING(msg, info2.comment, "comment");
3671 SET_UINT (msg, info2.country_code, "countryCode");
3672 SET_UINT (msg, info2.code_page, "codePage");
3673 break;
3675 case 4:
3676 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3677 break;
3679 case 6:
3680 SET_STRING(msg, info6.account_name, "samAccountName");
3681 SET_STRING(msg, info6.full_name, "displayName");
3682 break;
3684 case 7:
3685 SET_STRING(msg, info7.account_name, "samAccountName");
3686 break;
3688 case 8:
3689 SET_STRING(msg, info8.full_name, "displayName");
3690 break;
3692 case 9:
3693 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3694 break;
3696 case 10:
3697 SET_STRING(msg, info10.home_directory, "homeDirectory");
3698 SET_STRING(msg, info10.home_drive, "homeDrive");
3699 break;
3701 case 11:
3702 SET_STRING(msg, info11.logon_script, "scriptPath");
3703 break;
3705 case 12:
3706 SET_STRING(msg, info12.profile_path, "profilePath");
3707 break;
3709 case 13:
3710 SET_STRING(msg, info13.description, "description");
3711 break;
3713 case 14:
3714 SET_STRING(msg, info14.workstations, "userWorkstations");
3715 break;
3717 case 16:
3718 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3719 break;
3721 case 17:
3722 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3723 break;
3725 case 18:
3726 status = samr_set_password_buffers(dce_call,
3727 sam_ctx,
3728 a_state->account_dn,
3729 mem_ctx,
3730 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3731 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3732 if (!NT_STATUS_IS_OK(status)) {
3733 goto done;
3736 if (r->in.info->info18.password_expired > 0) {
3737 struct ldb_message_element *set_el;
3738 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3739 status = NT_STATUS_NO_MEMORY;
3740 goto done;
3742 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3743 set_el->flags = LDB_FLAG_MOD_REPLACE;
3745 break;
3747 case 20:
3748 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3749 break;
3751 case 21:
3752 if (r->in.info->info21.fields_present == 0) {
3753 status = NT_STATUS_INVALID_PARAMETER;
3754 goto done;
3757 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3758 IFSET(SAMR_FIELD_LAST_LOGON)
3759 SET_UINT64(msg, info21.last_logon, "lastLogon");
3760 IFSET(SAMR_FIELD_LAST_LOGOFF)
3761 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3762 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3763 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3764 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3765 SET_STRING(msg, info21.account_name, "samAccountName");
3766 IFSET(SAMR_FIELD_FULL_NAME)
3767 SET_STRING(msg, info21.full_name, "displayName");
3768 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3769 SET_STRING(msg, info21.home_directory, "homeDirectory");
3770 IFSET(SAMR_FIELD_HOME_DRIVE)
3771 SET_STRING(msg, info21.home_drive, "homeDrive");
3772 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3773 SET_STRING(msg, info21.logon_script, "scriptPath");
3774 IFSET(SAMR_FIELD_PROFILE_PATH)
3775 SET_STRING(msg, info21.profile_path, "profilePath");
3776 IFSET(SAMR_FIELD_DESCRIPTION)
3777 SET_STRING(msg, info21.description, "description");
3778 IFSET(SAMR_FIELD_WORKSTATIONS)
3779 SET_STRING(msg, info21.workstations, "userWorkstations");
3780 IFSET(SAMR_FIELD_COMMENT)
3781 SET_STRING(msg, info21.comment, "comment");
3782 IFSET(SAMR_FIELD_PARAMETERS)
3783 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3784 IFSET(SAMR_FIELD_PRIMARY_GID)
3785 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3786 IFSET(SAMR_FIELD_ACCT_FLAGS)
3787 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3788 IFSET(SAMR_FIELD_LOGON_HOURS)
3789 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3790 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3791 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3792 IFSET(SAMR_FIELD_NUM_LOGONS)
3793 SET_UINT (msg, info21.logon_count, "logonCount");
3794 IFSET(SAMR_FIELD_COUNTRY_CODE)
3795 SET_UINT (msg, info21.country_code, "countryCode");
3796 IFSET(SAMR_FIELD_CODE_PAGE)
3797 SET_UINT (msg, info21.code_page, "codePage");
3799 /* password change fields */
3800 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
3801 status = NT_STATUS_ACCESS_DENIED;
3802 goto done;
3805 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3806 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3807 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3809 if (r->in.info->info21.lm_password_set) {
3810 if ((r->in.info->info21.lm_owf_password.length != 16)
3811 || (r->in.info->info21.lm_owf_password.size != 16)) {
3812 status = NT_STATUS_INVALID_PARAMETER;
3813 goto done;
3816 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3818 if (r->in.info->info21.nt_password_set) {
3819 if ((r->in.info->info21.nt_owf_password.length != 16)
3820 || (r->in.info->info21.nt_owf_password.size != 16)) {
3821 status = NT_STATUS_INVALID_PARAMETER;
3822 goto done;
3825 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3827 status = samr_set_password_buffers(dce_call,
3828 sam_ctx,
3829 a_state->account_dn,
3830 mem_ctx,
3831 lm_pwd_hash,
3832 nt_pwd_hash);
3833 if (!NT_STATUS_IS_OK(status)) {
3834 goto done;
3839 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3840 const char *t = "0";
3841 struct ldb_message_element *set_el;
3842 if (r->in.info->info21.password_expired
3843 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3844 t = "-1";
3846 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3847 status = NT_STATUS_NO_MEMORY;
3848 goto done;
3850 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3851 set_el->flags = LDB_FLAG_MOD_REPLACE;
3853 #undef IFSET
3854 break;
3856 case 23:
3857 if (r->in.info->info23.info.fields_present == 0) {
3858 status = NT_STATUS_INVALID_PARAMETER;
3859 goto done;
3862 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3863 IFSET(SAMR_FIELD_LAST_LOGON)
3864 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3865 IFSET(SAMR_FIELD_LAST_LOGOFF)
3866 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3867 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3868 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3869 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3870 SET_STRING(msg, info23.info.account_name, "samAccountName");
3871 IFSET(SAMR_FIELD_FULL_NAME)
3872 SET_STRING(msg, info23.info.full_name, "displayName");
3873 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3874 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3875 IFSET(SAMR_FIELD_HOME_DRIVE)
3876 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3877 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3878 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3879 IFSET(SAMR_FIELD_PROFILE_PATH)
3880 SET_STRING(msg, info23.info.profile_path, "profilePath");
3881 IFSET(SAMR_FIELD_DESCRIPTION)
3882 SET_STRING(msg, info23.info.description, "description");
3883 IFSET(SAMR_FIELD_WORKSTATIONS)
3884 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3885 IFSET(SAMR_FIELD_COMMENT)
3886 SET_STRING(msg, info23.info.comment, "comment");
3887 IFSET(SAMR_FIELD_PARAMETERS)
3888 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3889 IFSET(SAMR_FIELD_PRIMARY_GID)
3890 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3891 IFSET(SAMR_FIELD_ACCT_FLAGS)
3892 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3893 IFSET(SAMR_FIELD_LOGON_HOURS)
3894 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3895 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3896 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3897 IFSET(SAMR_FIELD_NUM_LOGONS)
3898 SET_UINT (msg, info23.info.logon_count, "logonCount");
3900 IFSET(SAMR_FIELD_COUNTRY_CODE)
3901 SET_UINT (msg, info23.info.country_code, "countryCode");
3902 IFSET(SAMR_FIELD_CODE_PAGE)
3903 SET_UINT (msg, info23.info.code_page, "codePage");
3905 /* password change fields */
3906 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
3907 status = NT_STATUS_ACCESS_DENIED;
3908 goto done;
3911 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3912 status = samr_set_password(dce_call,
3913 sam_ctx,
3914 a_state->account_dn,
3915 mem_ctx,
3916 &r->in.info->info23.password);
3917 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3918 status = samr_set_password(dce_call,
3919 sam_ctx,
3920 a_state->account_dn,
3921 mem_ctx,
3922 &r->in.info->info23.password);
3924 if (!NT_STATUS_IS_OK(status)) {
3925 goto done;
3928 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3929 const char *t = "0";
3930 struct ldb_message_element *set_el;
3931 if (r->in.info->info23.info.password_expired
3932 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3933 t = "-1";
3935 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3936 status = NT_STATUS_NO_MEMORY;
3937 goto done;
3939 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3940 set_el->flags = LDB_FLAG_MOD_REPLACE;
3942 #undef IFSET
3943 break;
3945 /* the set password levels are handled separately */
3946 case 24:
3947 status = samr_set_password(dce_call,
3948 sam_ctx,
3949 a_state->account_dn,
3950 mem_ctx,
3951 &r->in.info->info24.password);
3952 if (!NT_STATUS_IS_OK(status)) {
3953 goto done;
3956 if (r->in.info->info24.password_expired > 0) {
3957 struct ldb_message_element *set_el;
3958 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3959 status = NT_STATUS_NO_MEMORY;
3960 goto done;
3962 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3963 set_el->flags = LDB_FLAG_MOD_REPLACE;
3965 break;
3967 case 25:
3968 if (r->in.info->info25.info.fields_present == 0) {
3969 status = NT_STATUS_INVALID_PARAMETER;
3970 goto done;
3973 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3974 IFSET(SAMR_FIELD_LAST_LOGON)
3975 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3976 IFSET(SAMR_FIELD_LAST_LOGOFF)
3977 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3978 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3979 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3980 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3981 SET_STRING(msg, info25.info.account_name, "samAccountName");
3982 IFSET(SAMR_FIELD_FULL_NAME)
3983 SET_STRING(msg, info25.info.full_name, "displayName");
3984 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3985 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3986 IFSET(SAMR_FIELD_HOME_DRIVE)
3987 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3988 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3989 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3990 IFSET(SAMR_FIELD_PROFILE_PATH)
3991 SET_STRING(msg, info25.info.profile_path, "profilePath");
3992 IFSET(SAMR_FIELD_DESCRIPTION)
3993 SET_STRING(msg, info25.info.description, "description");
3994 IFSET(SAMR_FIELD_WORKSTATIONS)
3995 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3996 IFSET(SAMR_FIELD_COMMENT)
3997 SET_STRING(msg, info25.info.comment, "comment");
3998 IFSET(SAMR_FIELD_PARAMETERS)
3999 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
4000 IFSET(SAMR_FIELD_PRIMARY_GID)
4001 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
4002 IFSET(SAMR_FIELD_ACCT_FLAGS)
4003 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
4004 IFSET(SAMR_FIELD_LOGON_HOURS)
4005 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
4006 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
4007 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
4008 IFSET(SAMR_FIELD_NUM_LOGONS)
4009 SET_UINT (msg, info25.info.logon_count, "logonCount");
4010 IFSET(SAMR_FIELD_COUNTRY_CODE)
4011 SET_UINT (msg, info25.info.country_code, "countryCode");
4012 IFSET(SAMR_FIELD_CODE_PAGE)
4013 SET_UINT (msg, info25.info.code_page, "codePage");
4015 /* password change fields */
4016 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
4017 status = NT_STATUS_ACCESS_DENIED;
4018 goto done;
4021 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
4022 status = samr_set_password_ex(dce_call,
4023 sam_ctx,
4024 a_state->account_dn,
4025 mem_ctx,
4026 &r->in.info->info25.password);
4027 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
4028 status = samr_set_password_ex(dce_call,
4029 sam_ctx,
4030 a_state->account_dn,
4031 mem_ctx,
4032 &r->in.info->info25.password);
4034 if (!NT_STATUS_IS_OK(status)) {
4035 goto done;
4038 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
4039 const char *t = "0";
4040 struct ldb_message_element *set_el;
4041 if (r->in.info->info25.info.password_expired
4042 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4043 t = "-1";
4045 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4046 status = NT_STATUS_NO_MEMORY;
4047 goto done;
4049 set_el = ldb_msg_find_element(msg, "pwdLastSet");
4050 set_el->flags = LDB_FLAG_MOD_REPLACE;
4052 #undef IFSET
4053 break;
4055 /* the set password levels are handled separately */
4056 case 26:
4057 status = samr_set_password_ex(dce_call,
4058 sam_ctx,
4059 a_state->account_dn,
4060 mem_ctx,
4061 &r->in.info->info26.password);
4062 if (!NT_STATUS_IS_OK(status)) {
4063 goto done;
4066 if (r->in.info->info26.password_expired > 0) {
4067 const char *t = "0";
4068 struct ldb_message_element *set_el;
4069 if (r->in.info->info26.password_expired
4070 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4071 t = "-1";
4073 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4074 status = NT_STATUS_NO_MEMORY;
4075 goto done;
4077 set_el = ldb_msg_find_element(msg, "pwdLastSet");
4078 set_el->flags = LDB_FLAG_MOD_REPLACE;
4080 break;
4082 case 31:
4083 status = dcesrv_transport_session_key(dce_call, &session_key);
4084 if (!NT_STATUS_IS_OK(status)) {
4085 DBG_NOTICE("samr: failed to get session key: %s\n",
4086 nt_errstr(status));
4087 goto done;
4090 status = samr_set_password_aes(dce_call,
4091 mem_ctx,
4092 &session_key,
4093 sam_ctx,
4094 a_state->account_dn,
4095 &r->in.info->info31.password,
4096 DSDB_PASSWORD_RESET);
4097 if (!NT_STATUS_IS_OK(status)) {
4098 goto done;
4101 if (r->in.info->info31.password_expired > 0) {
4102 const char *t = "0";
4103 struct ldb_message_element *set_el = NULL;
4105 if (r->in.info->info31.password_expired ==
4106 PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4107 t = "-1";
4110 ret = ldb_msg_add_string(msg, "pwdLastSet", t);
4111 if (ret != LDB_SUCCESS) {
4112 status = NT_STATUS_NO_MEMORY;
4113 goto done;
4115 set_el = ldb_msg_find_element(msg, "pwdLastSet");
4116 set_el->flags = LDB_FLAG_MOD_REPLACE;
4119 break;
4120 case 32:
4121 status = dcesrv_transport_session_key(dce_call, &session_key);
4122 if (!NT_STATUS_IS_OK(status)) {
4123 DBG_NOTICE("samr: failed to get session key: %s\n",
4124 nt_errstr(status));
4125 goto done;
4128 if (r->in.info->info32.info.fields_present == 0) {
4129 status = NT_STATUS_INVALID_PARAMETER;
4130 goto done;
4133 #define IFSET(bit) if (bit & r->in.info->info32.info.fields_present)
4134 IFSET(SAMR_FIELD_LAST_LOGON)
4136 SET_UINT64(msg, info32.info.last_logon, "lastLogon");
4138 IFSET(SAMR_FIELD_LAST_LOGOFF)
4140 SET_UINT64(msg, info32.info.last_logoff, "lastLogoff");
4142 IFSET(SAMR_FIELD_ACCT_EXPIRY)
4144 SET_UINT64(msg,
4145 info32.info.acct_expiry,
4146 "accountExpires");
4148 IFSET(SAMR_FIELD_ACCOUNT_NAME)
4150 SET_STRING(msg,
4151 info32.info.account_name,
4152 "samAccountName");
4154 IFSET(SAMR_FIELD_FULL_NAME)
4156 SET_STRING(msg, info32.info.full_name, "displayName");
4158 IFSET(SAMR_FIELD_HOME_DIRECTORY)
4160 SET_STRING(msg,
4161 info32.info.home_directory,
4162 "homeDirectory");
4164 IFSET(SAMR_FIELD_HOME_DRIVE)
4166 SET_STRING(msg, info32.info.home_drive, "homeDrive");
4168 IFSET(SAMR_FIELD_LOGON_SCRIPT)
4170 SET_STRING(msg, info32.info.logon_script, "scriptPath");
4172 IFSET(SAMR_FIELD_PROFILE_PATH)
4174 SET_STRING(msg,
4175 info32.info.profile_path,
4176 "profilePath");
4178 IFSET(SAMR_FIELD_DESCRIPTION)
4180 SET_STRING(msg, info32.info.description, "description");
4182 IFSET(SAMR_FIELD_WORKSTATIONS)
4184 SET_STRING(msg,
4185 info32.info.workstations,
4186 "userWorkstations");
4188 IFSET(SAMR_FIELD_COMMENT)
4190 SET_STRING(msg, info32.info.comment, "comment");
4192 IFSET(SAMR_FIELD_PARAMETERS)
4194 SET_PARAMETERS(msg,
4195 info32.info.parameters,
4196 "userParameters");
4198 IFSET(SAMR_FIELD_PRIMARY_GID)
4200 SET_UINT(msg,
4201 info32.info.primary_gid,
4202 "primaryGroupID");
4204 IFSET(SAMR_FIELD_ACCT_FLAGS)
4206 SET_AFLAGS(msg,
4207 info32.info.acct_flags,
4208 "userAccountControl");
4210 IFSET(SAMR_FIELD_LOGON_HOURS)
4212 SET_LHOURS(msg, info32.info.logon_hours, "logonHours");
4214 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
4216 SET_UINT(msg,
4217 info32.info.bad_password_count,
4218 "badPwdCount");
4220 IFSET(SAMR_FIELD_NUM_LOGONS)
4222 SET_UINT(msg, info32.info.logon_count, "logonCount");
4224 IFSET(SAMR_FIELD_COUNTRY_CODE)
4226 SET_UINT(msg, info32.info.country_code, "countryCode");
4228 IFSET(SAMR_FIELD_CODE_PAGE)
4230 SET_UINT(msg, info32.info.code_page, "codePage");
4233 /* password change fields */
4234 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
4236 status = NT_STATUS_ACCESS_DENIED;
4237 goto done;
4240 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT)
4242 status = samr_set_password_aes(
4243 dce_call,
4244 mem_ctx,
4245 &session_key,
4246 a_state->sam_ctx,
4247 a_state->account_dn,
4248 &r->in.info->info32.password,
4249 DSDB_PASSWORD_RESET);
4251 else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT)
4253 status = samr_set_password_aes(
4254 dce_call,
4255 mem_ctx,
4256 &session_key,
4257 a_state->sam_ctx,
4258 a_state->account_dn,
4259 &r->in.info->info32.password,
4260 DSDB_PASSWORD_RESET);
4262 if (!NT_STATUS_IS_OK(status)) {
4263 goto done;
4266 IFSET(SAMR_FIELD_EXPIRED_FLAG)
4268 const char *t = "0";
4269 struct ldb_message_element *set_el;
4270 if (r->in.info->info32.info.password_expired ==
4271 PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4272 t = "-1";
4274 if (ldb_msg_add_string(msg, "pwdLastSet", t) !=
4275 LDB_SUCCESS) {
4276 status = NT_STATUS_NO_MEMORY;
4277 goto done;
4279 set_el = ldb_msg_find_element(msg, "pwdLastSet");
4280 set_el->flags = LDB_FLAG_MOD_REPLACE;
4282 #undef IFSET
4284 break;
4285 default:
4286 /* many info classes are not valid for SetUserInfo */
4287 status = NT_STATUS_INVALID_INFO_CLASS;
4288 goto done;
4291 if (!NT_STATUS_IS_OK(status)) {
4292 goto done;
4295 /* modify the samdb record */
4296 if (msg->num_elements > 0) {
4297 ret = ldb_modify(sam_ctx, msg);
4298 if (ret != LDB_SUCCESS) {
4299 DEBUG(1,("Failed to modify record %s: %s\n",
4300 ldb_dn_get_linearized(a_state->account_dn),
4301 ldb_errstring(sam_ctx)));
4303 status = dsdb_ldb_err_to_ntstatus(ret);
4304 goto done;
4308 ret = ldb_transaction_commit(sam_ctx);
4309 if (ret != LDB_SUCCESS) {
4310 DBG_ERR("Failed to commit transaction modifying account record "
4311 "%s: %s\n",
4312 ldb_dn_get_linearized(msg->dn),
4313 ldb_errstring(sam_ctx));
4314 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4317 status = NT_STATUS_OK;
4318 done:
4319 if (!NT_STATUS_IS_OK(status)) {
4320 ldb_transaction_cancel(sam_ctx);
4323 return status;
4328 samr_GetGroupsForUser
4330 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4331 struct samr_GetGroupsForUser *r)
4333 struct dcesrv_handle *h;
4334 struct samr_account_state *a_state;
4335 struct samr_domain_state *d_state;
4336 struct ldb_result *res, *res_memberof;
4337 const char * const attrs[] = { "primaryGroupID",
4338 "memberOf",
4339 NULL };
4340 const char * const group_attrs[] = { "objectSid",
4341 NULL };
4343 struct samr_RidWithAttributeArray *array;
4344 struct ldb_message_element *memberof_el;
4345 int i, ret, count = 0;
4346 uint32_t primary_group_id;
4347 char *filter;
4349 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4351 a_state = h->data;
4352 d_state = a_state->domain_state;
4354 ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
4355 &res,
4356 a_state->account_dn,
4357 attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
4359 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4360 return NT_STATUS_NO_SUCH_USER;
4361 } else if (ret != LDB_SUCCESS) {
4362 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4363 } else if (res->count != 1) {
4364 return NT_STATUS_NO_SUCH_USER;
4367 primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
4370 filter = talloc_asprintf(mem_ctx,
4371 "(&(|(grouptype=%d)(grouptype=%d))"
4372 "(objectclass=group)(|",
4373 GTYPE_SECURITY_UNIVERSAL_GROUP,
4374 GTYPE_SECURITY_GLOBAL_GROUP);
4375 if (filter == NULL) {
4376 return NT_STATUS_NO_MEMORY;
4379 memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
4380 if (memberof_el != NULL) {
4381 for (i = 0; i < memberof_el->num_values; i++) {
4382 const struct ldb_val *memberof_sid_binary;
4383 char *memberof_sid_escaped;
4384 struct ldb_dn *memberof_dn
4385 = ldb_dn_from_ldb_val(mem_ctx,
4386 a_state->sam_ctx,
4387 &memberof_el->values[i]);
4388 if (memberof_dn == NULL) {
4389 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4392 memberof_sid_binary
4393 = ldb_dn_get_extended_component(memberof_dn,
4394 "SID");
4395 if (memberof_sid_binary == NULL) {
4396 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4399 memberof_sid_escaped = ldb_binary_encode(mem_ctx,
4400 *memberof_sid_binary);
4401 if (memberof_sid_escaped == NULL) {
4402 return NT_STATUS_NO_MEMORY;
4404 filter = talloc_asprintf_append(filter, "(objectSID=%s)",
4405 memberof_sid_escaped);
4406 if (filter == NULL) {
4407 return NT_STATUS_NO_MEMORY;
4411 ret = dsdb_search(a_state->sam_ctx, mem_ctx,
4412 &res_memberof,
4413 d_state->domain_dn,
4414 LDB_SCOPE_SUBTREE,
4415 group_attrs, 0,
4416 "%s))", filter);
4418 if (ret != LDB_SUCCESS) {
4419 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4421 count = res_memberof->count;
4424 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
4425 if (array == NULL)
4426 return NT_STATUS_NO_MEMORY;
4428 array->count = 0;
4429 array->rids = NULL;
4431 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
4432 count + 1);
4433 if (array->rids == NULL)
4434 return NT_STATUS_NO_MEMORY;
4436 /* Adds the primary group */
4438 array->rids[0].rid = primary_group_id;
4439 array->rids[0].attributes = SE_GROUP_DEFAULT_FLAGS;
4440 array->count += 1;
4442 /* Adds the additional groups */
4443 for (i = 0; i < count; i++) {
4444 struct dom_sid *group_sid;
4446 group_sid = samdb_result_dom_sid(mem_ctx,
4447 res_memberof->msgs[i],
4448 "objectSid");
4449 if (group_sid == NULL) {
4450 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4453 array->rids[i + 1].rid =
4454 group_sid->sub_auths[group_sid->num_auths-1];
4455 array->rids[i + 1].attributes = SE_GROUP_DEFAULT_FLAGS;
4456 array->count += 1;
4459 *r->out.rids = array;
4461 return NT_STATUS_OK;
4465 * samr_QueryDisplayInfo
4467 * A cache of the GUID's matching the last query is maintained
4468 * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
4469 * n the dcesrv_handle.
4471 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4472 struct samr_QueryDisplayInfo *r)
4474 struct dcesrv_handle *h;
4475 struct samr_domain_state *d_state;
4476 struct ldb_result *res;
4477 uint32_t i;
4478 uint32_t results = 0;
4479 uint32_t count = 0;
4480 const char *const cache_attrs[] = {"objectGUID", NULL};
4481 const char *const attrs[] = {
4482 "objectSID", "sAMAccountName", "displayName", "description", NULL};
4483 struct samr_DispEntryFull *entriesFull = NULL;
4484 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
4485 struct samr_DispEntryAscii *entriesAscii = NULL;
4486 struct samr_DispEntryGeneral *entriesGeneral = NULL;
4487 const char *filter;
4488 int ret;
4489 NTSTATUS status;
4490 struct samr_guid_cache *cache = NULL;
4492 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4494 d_state = h->data;
4496 cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
4498 * Can the cached results be used?
4499 * The cache is discarded if the start index is zero, or the requested
4500 * level is different from that in the cache.
4502 if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
4504 * The cached results can not be used, so will need to query
4505 * the database.
4509 * Get the search filter for the current level
4511 switch (r->in.level) {
4512 case 1:
4513 case 4:
4514 filter = talloc_asprintf(mem_ctx,
4515 "(&(objectclass=user)"
4516 "(sAMAccountType=%d))",
4517 ATYPE_NORMAL_ACCOUNT);
4518 break;
4519 case 2:
4520 filter = talloc_asprintf(mem_ctx,
4521 "(&(objectclass=user)"
4522 "(sAMAccountType=%d))",
4523 ATYPE_WORKSTATION_TRUST);
4524 break;
4525 case 3:
4526 case 5:
4527 filter =
4528 talloc_asprintf(mem_ctx,
4529 "(&(|(groupType=%d)(groupType=%d))"
4530 "(objectClass=group))",
4531 GTYPE_SECURITY_UNIVERSAL_GROUP,
4532 GTYPE_SECURITY_GLOBAL_GROUP);
4533 break;
4534 default:
4535 return NT_STATUS_INVALID_INFO_CLASS;
4537 clear_guid_cache(cache);
4540 * search for all requested objects in all domains.
4542 ret = dsdb_search(d_state->sam_ctx,
4543 mem_ctx,
4544 &res,
4545 ldb_get_default_basedn(d_state->sam_ctx),
4546 LDB_SCOPE_SUBTREE,
4547 cache_attrs,
4549 "%s",
4550 filter);
4551 if (ret != LDB_SUCCESS) {
4552 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4554 if ((res->count == 0) || (r->in.max_entries == 0)) {
4555 return NT_STATUS_OK;
4558 status = load_guid_cache(cache, d_state, res->count, res->msgs);
4559 TALLOC_FREE(res);
4560 if (!NT_STATUS_IS_OK(status)) {
4561 return status;
4563 cache->handle = r->in.level;
4565 *r->out.total_size = cache->size;
4568 * if there are no entries or the requested start index is greater
4569 * than the number of entries, we return an empty response.
4571 if (r->in.start_idx >= cache->size) {
4572 *r->out.returned_size = 0;
4573 switch(r->in.level) {
4574 case 1:
4575 r->out.info->info1.count = *r->out.returned_size;
4576 r->out.info->info1.entries = NULL;
4577 break;
4578 case 2:
4579 r->out.info->info2.count = *r->out.returned_size;
4580 r->out.info->info2.entries = NULL;
4581 break;
4582 case 3:
4583 r->out.info->info3.count = *r->out.returned_size;
4584 r->out.info->info3.entries = NULL;
4585 break;
4586 case 4:
4587 r->out.info->info4.count = *r->out.returned_size;
4588 r->out.info->info4.entries = NULL;
4589 break;
4590 case 5:
4591 r->out.info->info5.count = *r->out.returned_size;
4592 r->out.info->info5.entries = NULL;
4593 break;
4595 return NT_STATUS_OK;
4599 * Allocate an array of the appropriate result structures for the
4600 * current query level.
4602 * r->in.start_idx is always < cache->size due to the check above
4604 results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
4605 switch (r->in.level) {
4606 case 1:
4607 entriesGeneral = talloc_array(
4608 mem_ctx, struct samr_DispEntryGeneral, results);
4609 break;
4610 case 2:
4611 entriesFull =
4612 talloc_array(mem_ctx, struct samr_DispEntryFull, results);
4613 break;
4614 case 3:
4615 entriesFullGroup = talloc_array(
4616 mem_ctx, struct samr_DispEntryFullGroup, results);
4617 break;
4618 case 4:
4619 case 5:
4620 entriesAscii =
4621 talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
4622 break;
4625 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
4626 (entriesAscii == NULL) && (entriesFullGroup == NULL))
4627 return NT_STATUS_NO_MEMORY;
4630 * Process the list of result GUID's.
4631 * Read the details of each object and populate the result structure
4632 * for the current level.
4634 count = 0;
4635 for (i = 0; i < results; i++) {
4636 struct dom_sid *objectsid;
4637 struct ldb_result *rec;
4638 const uint32_t idx = r->in.start_idx + i;
4639 uint32_t rid;
4642 * Read an object from disk using the GUID as the key
4644 * If the object can not be read, or it does not have a SID
4645 * it is ignored. In this case the number of entries returned
4646 * will be less than the requested size, there will also be
4647 * a gap in the idx numbers in the returned elements e.g. if
4648 * there are 3 GUIDs a, b, c in the cache and b is deleted from
4649 * disk then details for a, and c will be returned with
4650 * idx values of 1 and 3 respectively.
4653 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
4654 mem_ctx,
4655 &rec,
4656 &cache->entries[idx],
4657 attrs,
4659 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4660 struct GUID_txt_buf guid_buf;
4661 char *guid_str =
4662 GUID_buf_string(&cache->entries[idx],
4663 &guid_buf);
4664 DBG_WARNING("GUID [%s] not found\n", guid_str);
4665 continue;
4666 } else if (ret != LDB_SUCCESS) {
4667 clear_guid_cache(cache);
4668 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4670 objectsid = samdb_result_dom_sid(mem_ctx,
4671 rec->msgs[0],
4672 "objectSID");
4673 if (objectsid == NULL) {
4674 struct GUID_txt_buf guid_buf;
4675 DBG_WARNING(
4676 "objectSID for GUID [%s] not found\n",
4677 GUID_buf_string(&cache->entries[idx], &guid_buf));
4678 continue;
4680 status = dom_sid_split_rid(NULL,
4681 objectsid,
4682 NULL,
4683 &rid);
4684 if (!NT_STATUS_IS_OK(status)) {
4685 struct dom_sid_buf sid_buf;
4686 struct GUID_txt_buf guid_buf;
4687 DBG_WARNING(
4688 "objectSID [%s] for GUID [%s] invalid\n",
4689 dom_sid_str_buf(objectsid, &sid_buf),
4690 GUID_buf_string(&cache->entries[idx], &guid_buf));
4691 continue;
4695 * Populate the result structure for the current object
4697 switch(r->in.level) {
4698 case 1:
4700 entriesGeneral[count].idx = idx + 1;
4701 entriesGeneral[count].rid = rid;
4703 entriesGeneral[count].acct_flags =
4704 samdb_result_acct_flags(rec->msgs[0], NULL);
4705 entriesGeneral[count].account_name.string =
4706 ldb_msg_find_attr_as_string(
4707 rec->msgs[0], "sAMAccountName", "");
4708 entriesGeneral[count].full_name.string =
4709 ldb_msg_find_attr_as_string(
4710 rec->msgs[0], "displayName", "");
4711 entriesGeneral[count].description.string =
4712 ldb_msg_find_attr_as_string(
4713 rec->msgs[0], "description", "");
4714 break;
4715 case 2:
4716 entriesFull[count].idx = idx + 1;
4717 entriesFull[count].rid = rid;
4720 * No idea why we need to or in ACB_NORMAL here,
4721 * but this is what Win2k3 seems to do...
4723 entriesFull[count].acct_flags =
4724 samdb_result_acct_flags(rec->msgs[0], NULL) |
4725 ACB_NORMAL;
4726 entriesFull[count].account_name.string =
4727 ldb_msg_find_attr_as_string(
4728 rec->msgs[0], "sAMAccountName", "");
4729 entriesFull[count].description.string =
4730 ldb_msg_find_attr_as_string(
4731 rec->msgs[0], "description", "");
4732 break;
4733 case 3:
4734 entriesFullGroup[count].idx = idx + 1;
4735 entriesFullGroup[count].rid = rid;
4738 * We get a "7" here for groups
4740 entriesFullGroup[count].acct_flags = SE_GROUP_DEFAULT_FLAGS;
4741 entriesFullGroup[count].account_name.string =
4742 ldb_msg_find_attr_as_string(
4743 rec->msgs[0], "sAMAccountName", "");
4744 entriesFullGroup[count].description.string =
4745 ldb_msg_find_attr_as_string(
4746 rec->msgs[0], "description", "");
4747 break;
4748 case 4:
4749 case 5:
4750 entriesAscii[count].idx = idx + 1;
4751 entriesAscii[count].account_name.string =
4752 ldb_msg_find_attr_as_string(
4753 rec->msgs[0], "sAMAccountName", "");
4754 break;
4756 count++;
4760 * Build the response based on the request level.
4762 *r->out.returned_size = count;
4763 switch(r->in.level) {
4764 case 1:
4765 r->out.info->info1.count = count;
4766 r->out.info->info1.entries = entriesGeneral;
4767 break;
4768 case 2:
4769 r->out.info->info2.count = count;
4770 r->out.info->info2.entries = entriesFull;
4771 break;
4772 case 3:
4773 r->out.info->info3.count = count;
4774 r->out.info->info3.entries = entriesFullGroup;
4775 break;
4776 case 4:
4777 r->out.info->info4.count = count;
4778 r->out.info->info4.entries = entriesAscii;
4779 break;
4780 case 5:
4781 r->out.info->info5.count = count;
4782 r->out.info->info5.entries = entriesAscii;
4783 break;
4786 return ((r->in.start_idx + results) < cache->size)
4787 ? STATUS_MORE_ENTRIES
4788 : NT_STATUS_OK;
4793 samr_GetDisplayEnumerationIndex
4795 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4796 struct samr_GetDisplayEnumerationIndex *r)
4798 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4803 samr_TestPrivateFunctionsDomain
4805 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4806 struct samr_TestPrivateFunctionsDomain *r)
4808 return NT_STATUS_NOT_IMPLEMENTED;
4813 samr_TestPrivateFunctionsUser
4815 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4816 struct samr_TestPrivateFunctionsUser *r)
4818 return NT_STATUS_NOT_IMPLEMENTED;
4823 samr_GetUserPwInfo
4825 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4826 struct samr_GetUserPwInfo *r)
4828 struct dcesrv_handle *h;
4829 struct samr_account_state *a_state;
4831 ZERO_STRUCTP(r->out.info);
4833 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4835 a_state = h->data;
4837 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
4838 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
4839 NULL);
4840 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
4841 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
4843 return NT_STATUS_OK;
4848 samr_RemoveMemberFromForeignDomain
4850 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
4851 TALLOC_CTX *mem_ctx,
4852 struct samr_RemoveMemberFromForeignDomain *r)
4854 struct dcesrv_handle *h;
4855 struct samr_domain_state *d_state;
4856 const char *memberdn;
4857 struct ldb_message **res;
4858 const char *no_attrs[] = { NULL };
4859 int i, count;
4861 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4863 d_state = h->data;
4865 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
4866 "distinguishedName", "(objectSid=%s)",
4867 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
4868 /* Nothing to do */
4869 if (memberdn == NULL) {
4870 return NT_STATUS_OK;
4873 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4874 d_state->domain_dn, &res, no_attrs,
4875 d_state->domain_sid,
4876 "(&(member=%s)(objectClass=group)"
4877 "(|(groupType=%d)(groupType=%d)))",
4878 memberdn,
4879 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4880 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4882 if (count < 0)
4883 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4885 for (i=0; i<count; i++) {
4886 struct ldb_message *mod;
4887 int ret;
4889 mod = ldb_msg_new(mem_ctx);
4890 if (mod == NULL) {
4891 return NT_STATUS_NO_MEMORY;
4894 mod->dn = res[i]->dn;
4896 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4897 "member", memberdn) != LDB_SUCCESS)
4898 return NT_STATUS_NO_MEMORY;
4900 ret = ldb_modify(d_state->sam_ctx, mod);
4901 talloc_free(mod);
4902 if (ret != LDB_SUCCESS) {
4903 return dsdb_ldb_err_to_ntstatus(ret);
4907 return NT_STATUS_OK;
4912 samr_QueryDomainInfo2
4914 just an alias for samr_QueryDomainInfo
4916 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4917 struct samr_QueryDomainInfo2 *r)
4919 struct samr_QueryDomainInfo r1;
4920 NTSTATUS status;
4922 r1 = (struct samr_QueryDomainInfo) {
4923 .in.domain_handle = r->in.domain_handle,
4924 .in.level = r->in.level,
4925 .out.info = r->out.info,
4928 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4930 return status;
4935 samr_QueryUserInfo2
4937 just an alias for samr_QueryUserInfo
4939 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4940 struct samr_QueryUserInfo2 *r)
4942 struct samr_QueryUserInfo r1;
4943 NTSTATUS status;
4945 r1 = (struct samr_QueryUserInfo) {
4946 .in.user_handle = r->in.user_handle,
4947 .in.level = r->in.level,
4948 .out.info = r->out.info
4951 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4953 return status;
4958 samr_QueryDisplayInfo2
4960 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4961 struct samr_QueryDisplayInfo2 *r)
4963 struct samr_QueryDisplayInfo q;
4964 NTSTATUS result;
4966 q = (struct samr_QueryDisplayInfo) {
4967 .in.domain_handle = r->in.domain_handle,
4968 .in.level = r->in.level,
4969 .in.start_idx = r->in.start_idx,
4970 .in.max_entries = r->in.max_entries,
4971 .in.buf_size = r->in.buf_size,
4972 .out.total_size = r->out.total_size,
4973 .out.returned_size = r->out.returned_size,
4974 .out.info = r->out.info,
4977 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4979 return result;
4984 samr_GetDisplayEnumerationIndex2
4986 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4987 struct samr_GetDisplayEnumerationIndex2 *r)
4989 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4994 samr_QueryDisplayInfo3
4996 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4997 struct samr_QueryDisplayInfo3 *r)
4999 struct samr_QueryDisplayInfo q;
5000 NTSTATUS result;
5002 q = (struct samr_QueryDisplayInfo) {
5003 .in.domain_handle = r->in.domain_handle,
5004 .in.level = r->in.level,
5005 .in.start_idx = r->in.start_idx,
5006 .in.max_entries = r->in.max_entries,
5007 .in.buf_size = r->in.buf_size,
5008 .out.total_size = r->out.total_size,
5009 .out.returned_size = r->out.returned_size,
5010 .out.info = r->out.info,
5013 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
5015 return result;
5020 samr_AddMultipleMembersToAlias
5022 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5023 struct samr_AddMultipleMembersToAlias *r)
5025 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5030 samr_RemoveMultipleMembersFromAlias
5032 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5033 struct samr_RemoveMultipleMembersFromAlias *r)
5035 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5040 samr_GetDomPwInfo
5042 this fetches the default password properties for a domain
5044 note that w2k3 completely ignores the domain name in this call, and
5045 always returns the information for the servers primary domain
5047 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5048 struct samr_GetDomPwInfo *r)
5050 struct ldb_message **msgs;
5051 int ret;
5052 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
5053 struct ldb_context *sam_ctx;
5055 ZERO_STRUCTP(r->out.info);
5057 sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
5058 if (sam_ctx == NULL) {
5059 return NT_STATUS_INVALID_SYSTEM_SERVICE;
5062 /* The domain name in this call is ignored */
5063 ret = gendb_search_dn(sam_ctx,
5064 mem_ctx, NULL, &msgs, attrs);
5065 if (ret <= 0) {
5066 talloc_free(sam_ctx);
5068 return NT_STATUS_NO_SUCH_DOMAIN;
5070 if (ret > 1) {
5071 talloc_free(msgs);
5072 talloc_free(sam_ctx);
5074 return NT_STATUS_INTERNAL_DB_CORRUPTION;
5077 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
5078 "minPwdLength", 0);
5079 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
5080 "pwdProperties", 1);
5082 talloc_free(msgs);
5083 talloc_unlink(mem_ctx, sam_ctx);
5085 return NT_STATUS_OK;
5090 samr_Connect2
5092 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5093 struct samr_Connect2 *r)
5095 struct samr_Connect c;
5097 c = (struct samr_Connect) {
5098 .in.system_name = NULL,
5099 .in.access_mask = r->in.access_mask,
5100 .out.connect_handle = r->out.connect_handle,
5103 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5108 samr_SetUserInfo2
5110 just an alias for samr_SetUserInfo
5112 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5113 struct samr_SetUserInfo2 *r)
5115 struct samr_SetUserInfo r2;
5117 r2 = (struct samr_SetUserInfo) {
5118 .in.user_handle = r->in.user_handle,
5119 .in.level = r->in.level,
5120 .in.info = r->in.info,
5123 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
5128 samr_SetBootKeyInformation
5130 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5131 struct samr_SetBootKeyInformation *r)
5133 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5138 samr_GetBootKeyInformation
5140 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5141 struct samr_GetBootKeyInformation *r)
5143 /* Windows Server 2008 returns this */
5144 return NT_STATUS_NOT_SUPPORTED;
5149 samr_Connect3
5151 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5152 struct samr_Connect3 *r)
5154 struct samr_Connect c;
5156 c = (struct samr_Connect) {
5157 .in.system_name = NULL,
5158 .in.access_mask = r->in.access_mask,
5159 .out.connect_handle = r->out.connect_handle,
5162 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5167 samr_Connect4
5169 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5170 struct samr_Connect4 *r)
5172 struct samr_Connect c;
5174 c = (struct samr_Connect) {
5175 .in.system_name = NULL,
5176 .in.access_mask = r->in.access_mask,
5177 .out.connect_handle = r->out.connect_handle,
5180 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5185 samr_Connect5
5187 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5188 struct samr_Connect5 *r)
5190 struct samr_Connect c;
5191 NTSTATUS status;
5193 c = (struct samr_Connect) {
5194 .in.system_name = NULL,
5195 .in.access_mask = r->in.access_mask,
5196 .out.connect_handle = r->out.connect_handle,
5199 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5201 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
5202 r->out.info_out->info1.supported_features = 0;
5203 *r->out.level_out = r->in.level_in;
5205 return status;
5210 samr_RidToSid
5212 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5213 struct samr_RidToSid *r)
5215 struct samr_domain_state *d_state;
5216 struct dcesrv_handle *h;
5218 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
5220 d_state = h->data;
5222 /* form the users SID */
5223 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
5224 if (!*r->out.sid) {
5225 return NT_STATUS_NO_MEMORY;
5228 return NT_STATUS_OK;
5233 samr_SetDsrmPassword
5235 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5236 struct samr_SetDsrmPassword *r)
5238 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5243 samr_ValidatePassword
5245 For now the call checks the password complexity (if active) and the minimum
5246 password length on level 2 and 3. Level 1 is ignored for now.
5248 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
5249 TALLOC_CTX *mem_ctx,
5250 struct samr_ValidatePassword *r)
5252 struct samr_GetDomPwInfo r2 = {};
5253 struct samr_PwInfo pwInfo = {};
5254 const char *account = NULL;
5255 DATA_BLOB password;
5256 enum samr_ValidationStatus res;
5257 NTSTATUS status;
5258 enum dcerpc_transport_t transport =
5259 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
5260 enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
5262 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
5263 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5266 dcesrv_call_auth_info(dce_call, NULL, &auth_level);
5267 if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
5268 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5271 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
5273 r2 = (struct samr_GetDomPwInfo) {
5274 .in.domain_name = NULL,
5275 .out.info = &pwInfo,
5278 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
5279 if (!NT_STATUS_IS_OK(status)) {
5280 return status;
5283 switch (r->in.level) {
5284 case NetValidateAuthentication:
5285 /* we don't support this yet */
5286 return NT_STATUS_NOT_SUPPORTED;
5287 break;
5288 case NetValidatePasswordChange:
5289 account = r->in.req->req2.account.string;
5290 password = data_blob_const(r->in.req->req2.password.string,
5291 r->in.req->req2.password.length);
5292 res = samdb_check_password(mem_ctx,
5293 dce_call->conn->dce_ctx->lp_ctx,
5294 account,
5295 NULL, /* userPrincipalName */
5296 NULL, /* displayName/full_name */
5297 &password,
5298 pwInfo.password_properties,
5299 pwInfo.min_password_length);
5300 (*r->out.rep)->ctr2.status = res;
5301 break;
5302 case NetValidatePasswordReset:
5303 account = r->in.req->req3.account.string;
5304 password = data_blob_const(r->in.req->req3.password.string,
5305 r->in.req->req3.password.length);
5306 res = samdb_check_password(mem_ctx,
5307 dce_call->conn->dce_ctx->lp_ctx,
5308 account,
5309 NULL, /* userPrincipalName */
5310 NULL, /* displayName/full_name */
5311 &password,
5312 pwInfo.password_properties,
5313 pwInfo.min_password_length);
5314 (*r->out.rep)->ctr3.status = res;
5315 break;
5316 default:
5317 return NT_STATUS_INVALID_INFO_CLASS;
5318 break;
5321 return NT_STATUS_OK;
5324 static void dcesrv_samr_Opnum68NotUsedOnWire(struct dcesrv_call_state *dce_call,
5325 TALLOC_CTX *mem_ctx,
5326 struct samr_Opnum68NotUsedOnWire *r)
5328 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5331 static void dcesrv_samr_Opnum69NotUsedOnWire(struct dcesrv_call_state *dce_call,
5332 TALLOC_CTX *mem_ctx,
5333 struct samr_Opnum69NotUsedOnWire *r)
5335 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5338 static void dcesrv_samr_Opnum70NotUsedOnWire(struct dcesrv_call_state *dce_call,
5339 TALLOC_CTX *mem_ctx,
5340 struct samr_Opnum70NotUsedOnWire *r)
5342 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5345 static void dcesrv_samr_Opnum71NotUsedOnWire(struct dcesrv_call_state *dce_call,
5346 TALLOC_CTX *mem_ctx,
5347 struct samr_Opnum71NotUsedOnWire *r)
5349 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5352 static void dcesrv_samr_Opnum72NotUsedOnWire(struct dcesrv_call_state *dce_call,
5353 TALLOC_CTX *mem_ctx,
5354 struct samr_Opnum72NotUsedOnWire *r)
5356 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5359 /* include the generated boilerplate */
5360 #include "librpc/gen_ndr/ndr_samr_s.c"