samba-tool/backup: set the right permissions on our root dir
[samba4-gss.git] / source4 / rpc_server / samr / dcesrv_samr.c
blobdd767d936a696e399b6af6dbecd9373656a999ad
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 NUMERIC_CMP(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 (because
1201 * there are no subauths) the msg goes to the end of the list, but
1202 * before the NULL SIDs.
1204 status = dom_sid_split_rid(NULL, sid1, NULL, &rid1);
1205 if (!NT_STATUS_IS_OK(status)) {
1206 res = 1;
1207 goto exit;
1210 status = dom_sid_split_rid(NULL, sid2, NULL, &rid2);
1211 if (!NT_STATUS_IS_OK(status)) {
1212 res = -1;
1213 goto exit;
1216 if (rid1 == rid2) {
1217 res = 0;
1219 else if (rid1 > rid2) {
1220 res = 1;
1222 else {
1223 res = -1;
1225 exit:
1226 TALLOC_FREE(frame);
1227 return res;
1231 samr_EnumDomainGroups
1233 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1234 struct samr_EnumDomainGroups *r)
1236 struct dcesrv_handle *h;
1237 struct samr_domain_state *d_state;
1238 struct ldb_message **res;
1239 uint32_t i;
1240 uint32_t count;
1241 uint32_t results;
1242 uint32_t max_entries;
1243 uint32_t remaining_entries;
1244 uint32_t resume_handle;
1245 struct samr_SamEntry *entries;
1246 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1247 const char * const cache_attrs[] = { "objectSid", "objectGUID", NULL };
1248 struct samr_SamArray *sam;
1249 struct samr_guid_cache *cache = NULL;
1251 *r->out.resume_handle = 0;
1252 *r->out.sam = NULL;
1253 *r->out.num_entries = 0;
1255 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1257 d_state = h->data;
1258 cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_GROUPS_CACHE];
1261 * If the resume_handle is zero, query the database and cache the
1262 * matching GUID's
1264 if (*r->in.resume_handle == 0) {
1265 NTSTATUS status;
1266 int ldb_cnt;
1267 clear_guid_cache(cache);
1269 * search for all domain groups in this domain.
1271 ldb_cnt = samdb_search_domain(
1272 d_state->sam_ctx,
1273 mem_ctx,
1274 d_state->domain_dn,
1275 &res,
1276 cache_attrs,
1277 d_state->domain_sid,
1278 "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1279 GTYPE_SECURITY_UNIVERSAL_GROUP,
1280 GTYPE_SECURITY_GLOBAL_GROUP);
1281 if (ldb_cnt < 0) {
1282 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1285 * Sort the results into RID order, while the spec states there
1286 * is no order, Windows appears to sort the results by RID and
1287 * so it is possible that there are clients that depend on
1288 * this ordering
1290 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1293 * cache the sorted GUID's
1295 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1296 TALLOC_FREE(res);
1297 if (!NT_STATUS_IS_OK(status)) {
1298 return status;
1300 cache->handle = 0;
1305 * If the resume handle is out of range we return an empty response
1306 * and invalidate the cache.
1308 * From the specification:
1309 * Servers SHOULD validate that EnumerationContext is an expected
1310 * value for the server's implementation. Windows does NOT validate
1311 * the input, though the result of malformed information merely results
1312 * in inconsistent output to the client.
1314 if (*r->in.resume_handle >= cache->size) {
1315 clear_guid_cache(cache);
1316 sam = talloc(mem_ctx, struct samr_SamArray);
1317 if (!sam) {
1318 return NT_STATUS_NO_MEMORY;
1320 sam->entries = NULL;
1321 sam->count = 0;
1323 *r->out.sam = sam;
1324 *r->out.resume_handle = 0;
1325 return NT_STATUS_OK;
1330 * Calculate the number of entries to return limit by max_size.
1331 * Note that we use the w2k3 element size value of 54
1333 max_entries = 1 + (r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER);
1334 remaining_entries = cache->size - *r->in.resume_handle;
1335 results = MIN(remaining_entries, max_entries);
1338 * Process the list of result GUID's.
1339 * Read the details of each object and populate the Entries
1340 * for the current level.
1342 count = 0;
1343 resume_handle = *r->in.resume_handle;
1344 entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1345 if (entries == NULL) {
1346 clear_guid_cache(cache);
1347 return NT_STATUS_NO_MEMORY;
1349 for (i = 0; i < results; i++) {
1350 struct dom_sid *objectsid;
1351 uint32_t rid;
1352 struct ldb_result *rec;
1353 const uint32_t idx = *r->in.resume_handle + i;
1354 int ret;
1355 NTSTATUS status;
1356 const char *name = NULL;
1357 resume_handle++;
1359 * Read an object from disk using the GUID as the key
1361 * If the object can not be read, or it does not have a SID
1362 * it is ignored.
1364 * As a consequence of this, if all the remaining GUID's
1365 * have been deleted an empty result will be returned.
1366 * i.e. even if the previous call returned a non zero
1367 * resume_handle it is possible for no results to be returned.
1370 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1371 mem_ctx,
1372 &rec,
1373 &cache->entries[idx],
1374 attrs,
1376 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1377 struct GUID_txt_buf guid_buf;
1378 DBG_WARNING(
1379 "GUID [%s] not found\n",
1380 GUID_buf_string(&cache->entries[idx], &guid_buf));
1381 continue;
1382 } else if (ret != LDB_SUCCESS) {
1383 clear_guid_cache(cache);
1384 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1387 objectsid = samdb_result_dom_sid(mem_ctx,
1388 rec->msgs[0],
1389 "objectSID");
1390 if (objectsid == NULL) {
1391 struct GUID_txt_buf guid_buf;
1392 DBG_WARNING(
1393 "objectSID for GUID [%s] not found\n",
1394 GUID_buf_string(&cache->entries[idx], &guid_buf));
1395 continue;
1397 status = dom_sid_split_rid(NULL,
1398 objectsid,
1399 NULL,
1400 &rid);
1401 if (!NT_STATUS_IS_OK(status)) {
1402 struct dom_sid_buf sid_buf;
1403 struct GUID_txt_buf guid_buf;
1404 DBG_WARNING(
1405 "objectSID [%s] for GUID [%s] invalid\n",
1406 dom_sid_str_buf(objectsid, &sid_buf),
1407 GUID_buf_string(&cache->entries[idx], &guid_buf));
1408 continue;
1411 entries[count].idx = rid;
1412 name = ldb_msg_find_attr_as_string(
1413 rec->msgs[0], "sAMAccountName", "");
1414 entries[count].name.string = talloc_strdup(entries, name);
1415 count++;
1418 sam = talloc(mem_ctx, struct samr_SamArray);
1419 if (!sam) {
1420 clear_guid_cache(cache);
1421 return NT_STATUS_NO_MEMORY;
1424 sam->entries = entries;
1425 sam->count = count;
1427 *r->out.sam = sam;
1428 *r->out.resume_handle = resume_handle;
1429 *r->out.num_entries = count;
1432 * Signal no more results by returning zero resume handle,
1433 * the cache is also cleared at this point
1435 if (*r->out.resume_handle >= cache->size) {
1436 *r->out.resume_handle = 0;
1437 clear_guid_cache(cache);
1438 return NT_STATUS_OK;
1441 * There are more results to be returned.
1443 return STATUS_MORE_ENTRIES;
1448 samr_CreateUser2
1450 This call uses transactions to ensure we don't get a new conflicting
1451 user while we are processing this, and to ensure the user either
1452 completely exists, or does not.
1454 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1455 struct samr_CreateUser2 *r)
1457 NTSTATUS status;
1458 struct samr_domain_state *d_state;
1459 struct samr_account_state *a_state;
1460 struct dcesrv_handle *h;
1461 struct ldb_dn *dn;
1462 struct dom_sid *sid;
1463 struct dcesrv_handle *u_handle;
1464 const char *account_name;
1466 ZERO_STRUCTP(r->out.user_handle);
1467 *r->out.access_granted = 0;
1468 *r->out.rid = 0;
1470 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1472 d_state = h->data;
1474 if (d_state->builtin) {
1475 DEBUG(5, ("Cannot create a user in the BUILTIN domain\n"));
1476 return NT_STATUS_ACCESS_DENIED;
1477 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1478 /* Domain trust accounts must be created by the LSA calls */
1479 return NT_STATUS_ACCESS_DENIED;
1481 account_name = r->in.account_name->string;
1483 if (account_name == NULL) {
1484 return NT_STATUS_INVALID_PARAMETER;
1487 status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1488 &sid, &dn);
1489 if (!NT_STATUS_IS_OK(status)) {
1490 return status;
1492 a_state = talloc(mem_ctx, struct samr_account_state);
1493 if (!a_state) {
1494 return NT_STATUS_NO_MEMORY;
1496 a_state->sam_ctx = d_state->sam_ctx;
1497 a_state->access_mask = r->in.access_mask;
1498 a_state->domain_state = talloc_reference(a_state, d_state);
1499 a_state->account_dn = talloc_steal(a_state, dn);
1501 a_state->account_name = talloc_steal(a_state, account_name);
1502 if (!a_state->account_name) {
1503 return NT_STATUS_NO_MEMORY;
1506 /* create the policy handle */
1507 u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
1508 if (!u_handle) {
1509 return NT_STATUS_NO_MEMORY;
1512 u_handle->data = talloc_steal(u_handle, a_state);
1514 *r->out.user_handle = u_handle->wire_handle;
1515 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1517 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1519 return NT_STATUS_OK;
1524 samr_CreateUser
1526 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1527 struct samr_CreateUser *r)
1529 struct samr_CreateUser2 r2;
1530 uint32_t access_granted = 0;
1533 /* a simple wrapper around samr_CreateUser2 works nicely */
1535 r2 = (struct samr_CreateUser2) {
1536 .in.domain_handle = r->in.domain_handle,
1537 .in.account_name = r->in.account_name,
1538 .in.acct_flags = ACB_NORMAL,
1539 .in.access_mask = r->in.access_mask,
1540 .out.user_handle = r->out.user_handle,
1541 .out.access_granted = &access_granted,
1542 .out.rid = r->out.rid
1545 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1548 struct enum_dom_users_ctx {
1549 struct samr_SamEntry *entries;
1550 uint32_t num_entries;
1551 uint32_t acct_flags;
1552 struct dom_sid *domain_sid;
1555 static int user_iterate_callback(struct ldb_request *req,
1556 struct ldb_reply *ares);
1559 * Iterate users and add all those that match a domain SID and pass an acct
1560 * flags check to an array of SamEntry objects.
1562 static int user_iterate_callback(struct ldb_request *req,
1563 struct ldb_reply *ares)
1565 struct enum_dom_users_ctx *ac =\
1566 talloc_get_type(req->context, struct enum_dom_users_ctx);
1567 int ret = LDB_ERR_OPERATIONS_ERROR;
1569 if (!ares) {
1570 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1572 if (ares->error != LDB_SUCCESS) {
1573 return ldb_request_done(req, ares->error);
1576 switch (ares->type) {
1577 case LDB_REPLY_ENTRY:
1579 struct ldb_message *msg = ares->message;
1580 const struct ldb_val *val;
1581 struct samr_SamEntry *ent;
1582 struct dom_sid objectsid;
1583 uint32_t rid;
1584 size_t entries_array_len = 0;
1585 NTSTATUS status;
1586 ssize_t sid_size;
1588 if (ac->acct_flags && ((samdb_result_acct_flags(msg, NULL) &
1589 ac->acct_flags) == 0)) {
1590 ret = LDB_SUCCESS;
1591 break;
1594 val = ldb_msg_find_ldb_val(msg, "objectSID");
1595 if (val == NULL) {
1596 DBG_WARNING("objectSID for DN %s not found\n",
1597 ldb_dn_get_linearized(msg->dn));
1598 ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1599 break;
1602 sid_size = sid_parse(val->data, val->length, &objectsid);
1603 if (sid_size == -1) {
1604 struct dom_sid_buf sid_buf;
1605 DBG_WARNING("objectsid [%s] for DN [%s] invalid\n",
1606 dom_sid_str_buf(&objectsid, &sid_buf),
1607 ldb_dn_get_linearized(msg->dn));
1608 ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1609 break;
1612 if (!dom_sid_in_domain(ac->domain_sid, &objectsid)) {
1613 /* Ignore if user isn't in the domain */
1614 ret = LDB_SUCCESS;
1615 break;
1618 status = dom_sid_split_rid(ares, &objectsid, NULL, &rid);
1619 if (!NT_STATUS_IS_OK(status)) {
1620 struct dom_sid_buf sid_buf;
1621 DBG_WARNING("Couldn't split RID from "
1622 "SID [%s] of DN [%s]\n",
1623 dom_sid_str_buf(&objectsid, &sid_buf),
1624 ldb_dn_get_linearized(msg->dn));
1625 ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1626 break;
1629 entries_array_len = talloc_array_length(ac->entries);
1630 if (ac->num_entries >= entries_array_len) {
1631 if (entries_array_len * 2 < entries_array_len) {
1632 ret = ldb_request_done(req,
1633 LDB_ERR_OPERATIONS_ERROR);
1634 break;
1636 ac->entries = talloc_realloc(ac,
1637 ac->entries,
1638 struct samr_SamEntry,
1639 entries_array_len * 2);
1640 if (ac->entries == NULL) {
1641 ret = ldb_request_done(req,
1642 LDB_ERR_OPERATIONS_ERROR);
1643 break;
1647 ent = &(ac->entries[ac->num_entries++]);
1648 val = ldb_msg_find_ldb_val(msg, "samaccountname");
1649 if (val == NULL) {
1650 DBG_WARNING("samaccountname attribute not found\n");
1651 ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1652 break;
1654 ent->name.string = talloc_steal(ac->entries,
1655 (char *)val->data);
1656 ent->idx = rid;
1657 ret = LDB_SUCCESS;
1658 break;
1660 case LDB_REPLY_DONE:
1662 if (ac->num_entries != 0 &&
1663 ac->num_entries != talloc_array_length(ac->entries)) {
1664 ac->entries = talloc_realloc(ac,
1665 ac->entries,
1666 struct samr_SamEntry,
1667 ac->num_entries);
1668 if (ac->entries == NULL) {
1669 ret = ldb_request_done(req,
1670 LDB_ERR_OPERATIONS_ERROR);
1671 break;
1674 ret = ldb_request_done(req, LDB_SUCCESS);
1675 break;
1677 case LDB_REPLY_REFERRAL:
1679 ret = LDB_SUCCESS;
1680 break;
1682 default:
1683 /* Doesn't happen */
1684 ret = LDB_ERR_OPERATIONS_ERROR;
1686 TALLOC_FREE(ares);
1688 return ret;
1692 * samr_EnumDomainUsers
1693 * The previous implementation did an initial search and stored a list of
1694 * matching GUIDs on the connection handle's domain state, then did direct
1695 * GUID lookups for each record in a page indexed by resume_handle. That
1696 * approach was memory efficient, requiring only 16 bytes per record, but
1697 * was too slow for winbind which needs this RPC call for getpwent.
1699 * Now we use an iterate pattern to populate a cached list of the rids and
1700 * names for each record. This improves runtime performance but requires
1701 * about 200 bytes per record which will mean for a 100k database we use
1702 * about 2MB, which is fine. The speedup achieved by this new approach is
1703 * around 50%.
1705 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call,
1706 TALLOC_CTX *mem_ctx,
1707 struct samr_EnumDomainUsers *r)
1709 struct dcesrv_handle *h;
1710 struct samr_domain_state *d_state;
1711 uint32_t results;
1712 uint32_t max_entries;
1713 uint32_t num_entries;
1714 uint32_t remaining_entries;
1715 struct samr_SamEntry *entries;
1716 const char * const attrs[] = { "objectSid", "sAMAccountName",
1717 "userAccountControl", NULL };
1718 struct samr_SamArray *sam;
1719 struct ldb_request *req;
1721 *r->out.resume_handle = 0;
1722 *r->out.sam = NULL;
1723 *r->out.num_entries = 0;
1725 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1727 d_state = h->data;
1728 entries = d_state->domain_users_cached;
1731 * If the resume_handle is zero, query the database and cache the
1732 * matching entries.
1734 if (*r->in.resume_handle == 0) {
1735 int ret;
1736 struct enum_dom_users_ctx *ac;
1737 if (entries != NULL) {
1738 talloc_free(entries);
1739 d_state->domain_users_cached = NULL;
1742 ac = talloc(mem_ctx, struct enum_dom_users_ctx);
1743 ac->num_entries = 0;
1744 ac->domain_sid = d_state->domain_sid;
1745 ac->entries = talloc_array(ac,
1746 struct samr_SamEntry,
1747 100);
1748 if (ac->entries == NULL) {
1749 talloc_free(ac);
1750 return NT_STATUS_NO_MEMORY;
1752 ac->acct_flags = r->in.acct_flags;
1754 ret = ldb_build_search_req(&req,
1755 d_state->sam_ctx,
1756 mem_ctx,
1757 d_state->domain_dn,
1758 LDB_SCOPE_SUBTREE,
1759 "(objectClass=user)",
1760 attrs,
1761 NULL,
1763 user_iterate_callback,
1764 NULL);
1765 if (ret != LDB_SUCCESS) {
1766 talloc_free(ac);
1767 return dsdb_ldb_err_to_ntstatus(ret);
1770 ret = ldb_request(d_state->sam_ctx, req);
1771 if (ret != LDB_SUCCESS) {
1772 talloc_free(ac);
1773 return dsdb_ldb_err_to_ntstatus(ret);
1776 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1777 if (ret != LDB_SUCCESS) {
1778 return dsdb_ldb_err_to_ntstatus(ret);
1781 if (ac->num_entries == 0) {
1782 DBG_WARNING("No users in domain %s\n",
1783 ldb_dn_get_linearized(d_state->domain_dn));
1784 talloc_free(ac);
1787 * test_EnumDomainUsers_all() expects that r.out.sam
1788 * should be non-NULL, even if we have no entries.
1790 sam = talloc_zero(mem_ctx, struct samr_SamArray);
1791 if (sam == NULL) {
1792 return NT_STATUS_NO_MEMORY;
1794 *r->out.sam = sam;
1796 return NT_STATUS_OK;
1799 entries = talloc_steal(d_state, ac->entries);
1800 d_state->domain_users_cached = entries;
1801 num_entries = ac->num_entries;
1802 talloc_free(ac);
1805 * Sort the entries into RID order, while the spec states there
1806 * is no order, Windows appears to sort the results by RID and
1807 * so it is possible that there are clients that depend on
1808 * this ordering
1810 TYPESAFE_QSORT(entries, num_entries, compare_SamEntry);
1811 } else {
1812 num_entries = talloc_array_length(entries);
1816 * If the resume handle is out of range we return an empty response
1817 * and invalidate the cache.
1819 * From the specification:
1820 * Servers SHOULD validate that EnumerationContext is an expected
1821 * value for the server's implementation. Windows does NOT validate
1822 * the input, though the result of malformed information merely results
1823 * in inconsistent output to the client.
1825 if (*r->in.resume_handle >= num_entries) {
1826 talloc_free(entries);
1827 d_state->domain_users_cached = NULL;
1828 sam = talloc(mem_ctx, struct samr_SamArray);
1829 if (!sam) {
1830 return NT_STATUS_NO_MEMORY;
1832 sam->entries = NULL;
1833 sam->count = 0;
1835 *r->out.sam = sam;
1836 *r->out.resume_handle = 0;
1837 return NT_STATUS_OK;
1841 * Calculate the number of entries to return limit by max_size.
1842 * Note that we use the w2k3 element size value of 54
1844 max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER);
1845 remaining_entries = num_entries - *r->in.resume_handle;
1846 results = MIN(remaining_entries, max_entries);
1848 sam = talloc(mem_ctx, struct samr_SamArray);
1849 if (!sam) {
1850 d_state->domain_users_cached = NULL;
1851 return NT_STATUS_NO_MEMORY;
1854 sam->entries = entries + *r->in.resume_handle;
1855 sam->count = results;
1857 *r->out.sam = sam;
1858 *r->out.resume_handle = *r->in.resume_handle + results;
1859 *r->out.num_entries = results;
1862 * Signal no more results by returning zero resume handle,
1863 * the cache is also cleared at this point
1865 if (*r->out.resume_handle >= num_entries) {
1866 *r->out.resume_handle = 0;
1867 return NT_STATUS_OK;
1870 * There are more results to be returned.
1872 return STATUS_MORE_ENTRIES;
1877 samr_CreateDomAlias
1879 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1880 struct samr_CreateDomAlias *r)
1882 struct samr_domain_state *d_state;
1883 struct samr_account_state *a_state;
1884 struct dcesrv_handle *h;
1885 const char *alias_name;
1886 struct dom_sid *sid;
1887 struct dcesrv_handle *a_handle;
1888 struct ldb_dn *dn;
1889 NTSTATUS status;
1891 ZERO_STRUCTP(r->out.alias_handle);
1892 *r->out.rid = 0;
1894 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1896 d_state = h->data;
1898 if (d_state->builtin) {
1899 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain\n"));
1900 return NT_STATUS_ACCESS_DENIED;
1903 alias_name = r->in.alias_name->string;
1905 if (alias_name == NULL) {
1906 return NT_STATUS_INVALID_PARAMETER;
1909 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1910 if (!NT_STATUS_IS_OK(status)) {
1911 return status;
1914 a_state = talloc(mem_ctx, struct samr_account_state);
1915 if (!a_state) {
1916 return NT_STATUS_NO_MEMORY;
1919 a_state->sam_ctx = d_state->sam_ctx;
1920 a_state->access_mask = r->in.access_mask;
1921 a_state->domain_state = talloc_reference(a_state, d_state);
1922 a_state->account_dn = talloc_steal(a_state, dn);
1924 a_state->account_name = talloc_steal(a_state, alias_name);
1926 /* create the policy handle */
1927 a_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
1928 if (a_handle == NULL)
1929 return NT_STATUS_NO_MEMORY;
1931 a_handle->data = talloc_steal(a_handle, a_state);
1933 *r->out.alias_handle = a_handle->wire_handle;
1935 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1937 return NT_STATUS_OK;
1942 samr_EnumDomainAliases
1944 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1945 struct samr_EnumDomainAliases *r)
1947 struct dcesrv_handle *h;
1948 struct samr_domain_state *d_state;
1949 struct ldb_message **res;
1950 int i, ldb_cnt;
1951 uint32_t first, count;
1952 struct samr_SamEntry *entries;
1953 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1954 struct samr_SamArray *sam;
1956 *r->out.resume_handle = 0;
1957 *r->out.sam = NULL;
1958 *r->out.num_entries = 0;
1960 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1962 d_state = h->data;
1964 /* search for all domain aliases in this domain. This could possibly be
1965 cached and resumed based on resume_key */
1966 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1967 &res, attrs,
1968 d_state->domain_sid,
1969 "(&(|(grouptype=%d)(grouptype=%d)))"
1970 "(objectclass=group))",
1971 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1972 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1973 if (ldb_cnt < 0) {
1974 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1977 /* convert to SamEntry format */
1978 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1979 if (!entries) {
1980 return NT_STATUS_NO_MEMORY;
1983 count = 0;
1985 for (i=0;i<ldb_cnt;i++) {
1986 struct dom_sid *alias_sid;
1988 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1989 "objectSid");
1991 if (alias_sid == NULL) {
1992 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1995 entries[count].idx =
1996 alias_sid->sub_auths[alias_sid->num_auths-1];
1997 entries[count].name.string =
1998 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1999 count += 1;
2002 /* sort the results by rid */
2003 TYPESAFE_QSORT(entries, count, compare_SamEntry);
2005 /* find the first entry to return */
2006 for (first=0;
2007 first<count && entries[first].idx <= *r->in.resume_handle;
2008 first++) ;
2010 /* return the rest, limit by max_size. Note that we
2011 use the w2k3 element size value of 54 */
2012 *r->out.num_entries = count - first;
2013 *r->out.num_entries = MIN(*r->out.num_entries,
2014 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
2016 sam = talloc(mem_ctx, struct samr_SamArray);
2017 if (!sam) {
2018 return NT_STATUS_NO_MEMORY;
2021 sam->entries = entries+first;
2022 sam->count = *r->out.num_entries;
2024 *r->out.sam = sam;
2026 if (first == count) {
2027 return NT_STATUS_OK;
2030 if (*r->out.num_entries < count - first) {
2031 *r->out.resume_handle =
2032 entries[first+*r->out.num_entries-1].idx;
2033 return STATUS_MORE_ENTRIES;
2036 return NT_STATUS_OK;
2041 samr_GetAliasMembership
2043 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2044 struct samr_GetAliasMembership *r)
2046 struct dcesrv_handle *h;
2047 struct samr_domain_state *d_state;
2048 char *filter;
2049 const char * const attrs[] = { "objectSid", NULL };
2050 struct ldb_message **res;
2051 uint32_t i;
2052 int count = 0;
2054 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2056 d_state = h->data;
2058 filter = talloc_asprintf(mem_ctx,
2059 "(&(|(grouptype=%d)(grouptype=%d))"
2060 "(objectclass=group)(|",
2061 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2062 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2063 if (filter == NULL) {
2064 return NT_STATUS_NO_MEMORY;
2067 for (i=0; i<r->in.sids->num_sids; i++) {
2068 struct dom_sid_buf buf;
2070 filter = talloc_asprintf_append(
2071 filter,
2072 "(member=<SID=%s>)",
2073 dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
2075 if (filter == NULL) {
2076 return NT_STATUS_NO_MEMORY;
2080 /* Find out if we had at least one valid member SID passed - otherwise
2081 * just skip the search. */
2082 if (strstr(filter, "member") != NULL) {
2083 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
2084 &res, attrs, d_state->domain_sid,
2085 "%s))", filter);
2086 if (count < 0) {
2087 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2091 r->out.rids->count = 0;
2092 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
2093 if (r->out.rids->ids == NULL)
2094 return NT_STATUS_NO_MEMORY;
2096 for (i=0; i<count; i++) {
2097 struct dom_sid *alias_sid;
2099 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
2100 if (alias_sid == NULL) {
2101 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2104 r->out.rids->ids[r->out.rids->count] =
2105 alias_sid->sub_auths[alias_sid->num_auths-1];
2106 r->out.rids->count += 1;
2109 return NT_STATUS_OK;
2114 samr_LookupNames
2116 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2117 struct samr_LookupNames *r)
2119 struct dcesrv_handle *h;
2120 struct samr_domain_state *d_state;
2121 uint32_t i, num_mapped;
2122 NTSTATUS status = NT_STATUS_OK;
2123 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
2124 int count;
2126 ZERO_STRUCTP(r->out.rids);
2127 ZERO_STRUCTP(r->out.types);
2129 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2131 d_state = h->data;
2133 if (r->in.num_names == 0) {
2134 return NT_STATUS_OK;
2137 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
2138 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
2139 if (!r->out.rids->ids || !r->out.types->ids) {
2140 return NT_STATUS_NO_MEMORY;
2142 r->out.rids->count = r->in.num_names;
2143 r->out.types->count = r->in.num_names;
2145 num_mapped = 0;
2147 for (i=0;i<r->in.num_names;i++) {
2148 struct ldb_message **res;
2149 struct dom_sid *sid;
2150 uint32_t atype, rtype;
2152 r->out.rids->ids[i] = 0;
2153 r->out.types->ids[i] = SID_NAME_UNKNOWN;
2155 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
2156 "sAMAccountName=%s",
2157 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
2158 if (count != 1) {
2159 status = STATUS_SOME_UNMAPPED;
2160 continue;
2163 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
2164 if (sid == NULL) {
2165 status = STATUS_SOME_UNMAPPED;
2166 continue;
2169 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
2170 if (atype == 0) {
2171 status = STATUS_SOME_UNMAPPED;
2172 continue;
2175 rtype = ds_atype_map(atype);
2177 if (rtype == SID_NAME_UNKNOWN) {
2178 status = STATUS_SOME_UNMAPPED;
2179 continue;
2182 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
2183 r->out.types->ids[i] = rtype;
2184 num_mapped++;
2187 if (num_mapped == 0) {
2188 return NT_STATUS_NONE_MAPPED;
2190 return status;
2195 samr_LookupRids
2197 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2198 struct samr_LookupRids *r)
2200 NTSTATUS status;
2201 struct dcesrv_handle *h;
2202 struct samr_domain_state *d_state;
2203 const char **names;
2204 struct lsa_String *lsa_names;
2205 enum lsa_SidType *ids;
2207 ZERO_STRUCTP(r->out.names);
2208 ZERO_STRUCTP(r->out.types);
2210 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2212 d_state = h->data;
2214 if (r->in.num_rids == 0)
2215 return NT_STATUS_OK;
2217 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
2218 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
2219 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
2221 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
2222 return NT_STATUS_NO_MEMORY;
2224 r->out.names->names = lsa_names;
2225 r->out.names->count = r->in.num_rids;
2227 r->out.types->ids = (uint32_t *) ids;
2228 r->out.types->count = r->in.num_rids;
2230 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
2231 r->in.num_rids, r->in.rids, names, ids);
2232 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
2233 uint32_t i;
2234 for (i = 0; i < r->in.num_rids; i++) {
2235 lsa_names[i].string = names[i];
2238 return status;
2243 samr_OpenGroup
2245 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2246 struct samr_OpenGroup *r)
2248 struct samr_domain_state *d_state;
2249 struct samr_account_state *a_state;
2250 struct dcesrv_handle *h;
2251 const char *groupname;
2252 struct dom_sid *sid;
2253 struct ldb_message **msgs;
2254 struct dcesrv_handle *g_handle;
2255 const char * const attrs[2] = { "sAMAccountName", NULL };
2256 int ret;
2258 ZERO_STRUCTP(r->out.group_handle);
2260 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2262 d_state = h->data;
2264 /* form the group SID */
2265 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2266 if (!sid) {
2267 return NT_STATUS_NO_MEMORY;
2270 /* search for the group record */
2271 if (d_state->builtin) {
2272 ret = gendb_search(d_state->sam_ctx,
2273 mem_ctx, d_state->domain_dn, &msgs, attrs,
2274 "(&(objectSid=%s)(objectClass=group)"
2275 "(groupType=%d))",
2276 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2277 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
2278 } else {
2279 ret = gendb_search(d_state->sam_ctx,
2280 mem_ctx, d_state->domain_dn, &msgs, attrs,
2281 "(&(objectSid=%s)(objectClass=group)"
2282 "(|(groupType=%d)(groupType=%d)))",
2283 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2284 GTYPE_SECURITY_UNIVERSAL_GROUP,
2285 GTYPE_SECURITY_GLOBAL_GROUP);
2287 if (ret == 0) {
2288 return NT_STATUS_NO_SUCH_GROUP;
2290 if (ret != 1) {
2291 DEBUG(0,("Found %d records matching sid %s\n",
2292 ret, dom_sid_string(mem_ctx, sid)));
2293 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2296 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2297 if (groupname == NULL) {
2298 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2299 dom_sid_string(mem_ctx, sid)));
2300 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2303 a_state = talloc(mem_ctx, struct samr_account_state);
2304 if (!a_state) {
2305 return NT_STATUS_NO_MEMORY;
2307 a_state->sam_ctx = d_state->sam_ctx;
2308 a_state->access_mask = r->in.access_mask;
2309 a_state->domain_state = talloc_reference(a_state, d_state);
2310 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2311 a_state->account_sid = talloc_steal(a_state, sid);
2312 a_state->account_name = talloc_strdup(a_state, groupname);
2313 if (!a_state->account_name) {
2314 return NT_STATUS_NO_MEMORY;
2317 /* create the policy handle */
2318 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
2319 if (!g_handle) {
2320 return NT_STATUS_NO_MEMORY;
2323 g_handle->data = talloc_steal(g_handle, a_state);
2325 *r->out.group_handle = g_handle->wire_handle;
2327 return NT_STATUS_OK;
2331 samr_QueryGroupInfo
2333 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2334 struct samr_QueryGroupInfo *r)
2336 struct dcesrv_handle *h;
2337 struct samr_account_state *a_state;
2338 struct ldb_message *msg, **res;
2339 const char * const attrs[4] = { "sAMAccountName", "description",
2340 "numMembers", NULL };
2341 int ret;
2342 union samr_GroupInfo *info;
2344 *r->out.info = NULL;
2346 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2348 a_state = h->data;
2350 /* pull all the group attributes */
2351 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2352 a_state->account_dn, &res, attrs);
2353 if (ret == 0) {
2354 return NT_STATUS_NO_SUCH_GROUP;
2356 if (ret != 1) {
2357 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2359 msg = res[0];
2361 /* allocate the info structure */
2362 info = talloc_zero(mem_ctx, union samr_GroupInfo);
2363 if (info == NULL) {
2364 return NT_STATUS_NO_MEMORY;
2367 /* Fill in the level */
2368 switch (r->in.level) {
2369 case GROUPINFOALL:
2370 QUERY_STRING(msg, all.name, "sAMAccountName");
2371 info->all.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2372 QUERY_UINT (msg, all.num_members, "numMembers")
2373 QUERY_STRING(msg, all.description, "description");
2374 break;
2375 case GROUPINFONAME:
2376 QUERY_STRING(msg, name, "sAMAccountName");
2377 break;
2378 case GROUPINFOATTRIBUTES:
2379 info->attributes.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2380 break;
2381 case GROUPINFODESCRIPTION:
2382 QUERY_STRING(msg, description, "description");
2383 break;
2384 case GROUPINFOALL2:
2385 QUERY_STRING(msg, all2.name, "sAMAccountName");
2386 info->all.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2387 QUERY_UINT (msg, all2.num_members, "numMembers")
2388 QUERY_STRING(msg, all2.description, "description");
2389 break;
2390 default:
2391 talloc_free(info);
2392 return NT_STATUS_INVALID_INFO_CLASS;
2395 *r->out.info = info;
2397 return NT_STATUS_OK;
2402 samr_SetGroupInfo
2404 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2405 struct samr_SetGroupInfo *r)
2407 struct dcesrv_handle *h;
2408 struct samr_account_state *g_state;
2409 struct ldb_message *msg;
2410 int ret;
2412 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2414 g_state = h->data;
2416 msg = ldb_msg_new(mem_ctx);
2417 if (msg == NULL) {
2418 return NT_STATUS_NO_MEMORY;
2421 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2422 if (!msg->dn) {
2423 return NT_STATUS_NO_MEMORY;
2426 switch (r->in.level) {
2427 case GROUPINFODESCRIPTION:
2428 SET_STRING(msg, description, "description");
2429 break;
2430 case GROUPINFONAME:
2431 /* On W2k3 this does not change the name, it changes the
2432 * sAMAccountName attribute */
2433 SET_STRING(msg, name, "sAMAccountName");
2434 break;
2435 case GROUPINFOATTRIBUTES:
2436 /* This does not do anything obviously visible in W2k3 LDAP */
2437 return NT_STATUS_OK;
2438 default:
2439 return NT_STATUS_INVALID_INFO_CLASS;
2442 /* modify the samdb record */
2443 ret = ldb_modify(g_state->sam_ctx, msg);
2444 if (ret != LDB_SUCCESS) {
2445 return dsdb_ldb_err_to_ntstatus(ret);
2448 return NT_STATUS_OK;
2453 samr_AddGroupMember
2455 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2456 struct samr_AddGroupMember *r)
2458 struct dcesrv_handle *h;
2459 struct samr_account_state *a_state;
2460 struct samr_domain_state *d_state;
2461 struct ldb_message *mod;
2462 struct dom_sid *membersid;
2463 const char *memberdn;
2464 struct ldb_result *res;
2465 const char * const attrs[] = { NULL };
2466 int ret;
2468 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2470 a_state = h->data;
2471 d_state = a_state->domain_state;
2473 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2474 if (membersid == NULL) {
2475 return NT_STATUS_NO_MEMORY;
2478 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2479 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2480 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2481 "(objectSid=%s)",
2482 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2484 if (ret != LDB_SUCCESS) {
2485 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2488 if (res->count == 0) {
2489 return NT_STATUS_NO_SUCH_USER;
2492 if (res->count > 1) {
2493 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2496 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2498 if (memberdn == NULL)
2499 return NT_STATUS_NO_MEMORY;
2501 mod = ldb_msg_new(mem_ctx);
2502 if (mod == NULL) {
2503 return NT_STATUS_NO_MEMORY;
2506 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2508 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2509 memberdn);
2510 if (ret != LDB_SUCCESS) {
2511 return dsdb_ldb_err_to_ntstatus(ret);
2514 ret = ldb_modify(a_state->sam_ctx, mod);
2515 switch (ret) {
2516 case LDB_SUCCESS:
2517 return NT_STATUS_OK;
2518 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2519 return NT_STATUS_MEMBER_IN_GROUP;
2520 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2521 return NT_STATUS_ACCESS_DENIED;
2522 default:
2523 return dsdb_ldb_err_to_ntstatus(ret);
2529 samr_DeleteDomainGroup
2531 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2532 struct samr_DeleteDomainGroup *r)
2534 struct dcesrv_handle *h;
2535 struct samr_account_state *a_state;
2536 int ret;
2538 *r->out.group_handle = *r->in.group_handle;
2540 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2542 a_state = h->data;
2544 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2545 if (ret != LDB_SUCCESS) {
2546 return dsdb_ldb_err_to_ntstatus(ret);
2549 talloc_free(h);
2550 ZERO_STRUCTP(r->out.group_handle);
2552 return NT_STATUS_OK;
2557 samr_DeleteGroupMember
2559 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2560 struct samr_DeleteGroupMember *r)
2562 struct dcesrv_handle *h;
2563 struct samr_account_state *a_state;
2564 struct samr_domain_state *d_state;
2565 struct ldb_message *mod;
2566 struct dom_sid *membersid;
2567 const char *memberdn;
2568 struct ldb_result *res;
2569 const char * const attrs[] = { NULL };
2570 int ret;
2572 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2574 a_state = h->data;
2575 d_state = a_state->domain_state;
2577 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2578 if (membersid == NULL) {
2579 return NT_STATUS_NO_MEMORY;
2582 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2583 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2584 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2585 "(objectSid=%s)",
2586 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2588 if (ret != LDB_SUCCESS) {
2589 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2592 if (res->count == 0) {
2593 return NT_STATUS_NO_SUCH_USER;
2596 if (res->count > 1) {
2597 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2600 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2602 if (memberdn == NULL)
2603 return NT_STATUS_NO_MEMORY;
2605 mod = ldb_msg_new(mem_ctx);
2606 if (mod == NULL) {
2607 return NT_STATUS_NO_MEMORY;
2610 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2612 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2613 memberdn);
2614 if (ret != LDB_SUCCESS) {
2615 return NT_STATUS_NO_MEMORY;
2618 ret = ldb_modify(a_state->sam_ctx, mod);
2619 switch (ret) {
2620 case LDB_SUCCESS:
2621 return NT_STATUS_OK;
2622 case LDB_ERR_UNWILLING_TO_PERFORM:
2623 case LDB_ERR_NO_SUCH_ATTRIBUTE:
2624 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2625 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2626 return NT_STATUS_ACCESS_DENIED;
2627 default:
2628 return dsdb_ldb_err_to_ntstatus(ret);
2634 samr_QueryGroupMember
2636 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2637 struct samr_QueryGroupMember *r)
2639 struct dcesrv_handle *h;
2640 struct samr_account_state *a_state;
2641 struct samr_domain_state *d_state;
2642 struct samr_RidAttrArray *array;
2643 unsigned int i, num_members;
2644 struct dom_sid *members;
2645 NTSTATUS status;
2647 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2649 a_state = h->data;
2650 d_state = a_state->domain_state;
2652 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2653 a_state->account_dn, &members,
2654 &num_members);
2655 if (!NT_STATUS_IS_OK(status)) {
2656 return status;
2659 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2660 if (array == NULL) {
2661 return NT_STATUS_NO_MEMORY;
2664 if (num_members == 0) {
2665 *r->out.rids = array;
2667 return NT_STATUS_OK;
2670 array->rids = talloc_array(array, uint32_t, num_members);
2671 if (array->rids == NULL) {
2672 return NT_STATUS_NO_MEMORY;
2675 array->attributes = talloc_array(array, uint32_t, num_members);
2676 if (array->attributes == NULL) {
2677 return NT_STATUS_NO_MEMORY;
2680 array->count = 0;
2681 for (i=0; i<num_members; i++) {
2682 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2683 continue;
2686 status = dom_sid_split_rid(NULL, &members[i], NULL,
2687 &array->rids[array->count]);
2688 if (!NT_STATUS_IS_OK(status)) {
2689 return status;
2692 array->attributes[array->count] = SE_GROUP_DEFAULT_FLAGS;
2693 array->count++;
2696 *r->out.rids = array;
2698 return NT_STATUS_OK;
2703 samr_SetMemberAttributesOfGroup
2705 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2706 struct samr_SetMemberAttributesOfGroup *r)
2708 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2713 samr_OpenAlias
2715 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2716 struct samr_OpenAlias *r)
2718 struct samr_domain_state *d_state;
2719 struct samr_account_state *a_state;
2720 struct dcesrv_handle *h;
2721 const char *alias_name;
2722 struct dom_sid *sid;
2723 struct ldb_message **msgs;
2724 struct dcesrv_handle *g_handle;
2725 const char * const attrs[2] = { "sAMAccountName", NULL };
2726 int ret;
2728 ZERO_STRUCTP(r->out.alias_handle);
2730 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2732 d_state = h->data;
2734 /* form the alias SID */
2735 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2736 if (sid == NULL)
2737 return NT_STATUS_NO_MEMORY;
2739 /* search for the group record */
2740 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2741 "(&(objectSid=%s)(objectclass=group)"
2742 "(|(grouptype=%d)(grouptype=%d)))",
2743 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2744 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2745 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2746 if (ret == 0) {
2747 return NT_STATUS_NO_SUCH_ALIAS;
2749 if (ret != 1) {
2750 DEBUG(0,("Found %d records matching sid %s\n",
2751 ret, dom_sid_string(mem_ctx, sid)));
2752 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2755 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2756 if (alias_name == NULL) {
2757 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2758 dom_sid_string(mem_ctx, sid)));
2759 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2762 a_state = talloc(mem_ctx, struct samr_account_state);
2763 if (!a_state) {
2764 return NT_STATUS_NO_MEMORY;
2766 a_state->sam_ctx = d_state->sam_ctx;
2767 a_state->access_mask = r->in.access_mask;
2768 a_state->domain_state = talloc_reference(a_state, d_state);
2769 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2770 a_state->account_sid = talloc_steal(a_state, sid);
2771 a_state->account_name = talloc_strdup(a_state, alias_name);
2772 if (!a_state->account_name) {
2773 return NT_STATUS_NO_MEMORY;
2776 /* create the policy handle */
2777 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
2778 if (!g_handle) {
2779 return NT_STATUS_NO_MEMORY;
2782 g_handle->data = talloc_steal(g_handle, a_state);
2784 *r->out.alias_handle = g_handle->wire_handle;
2786 return NT_STATUS_OK;
2791 samr_QueryAliasInfo
2793 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2794 struct samr_QueryAliasInfo *r)
2796 struct dcesrv_handle *h;
2797 struct samr_account_state *a_state;
2798 struct ldb_message *msg, **res;
2799 const char * const attrs[4] = { "sAMAccountName", "description",
2800 "numMembers", NULL };
2801 int ret;
2802 union samr_AliasInfo *info;
2804 *r->out.info = NULL;
2806 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2808 a_state = h->data;
2810 /* pull all the alias attributes */
2811 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2812 a_state->account_dn, &res, attrs);
2813 if (ret == 0) {
2814 return NT_STATUS_NO_SUCH_ALIAS;
2816 if (ret != 1) {
2817 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2819 msg = res[0];
2821 /* allocate the info structure */
2822 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2823 if (info == NULL) {
2824 return NT_STATUS_NO_MEMORY;
2827 switch(r->in.level) {
2828 case ALIASINFOALL:
2829 QUERY_STRING(msg, all.name, "sAMAccountName");
2830 QUERY_UINT (msg, all.num_members, "numMembers");
2831 QUERY_STRING(msg, all.description, "description");
2832 break;
2833 case ALIASINFONAME:
2834 QUERY_STRING(msg, name, "sAMAccountName");
2835 break;
2836 case ALIASINFODESCRIPTION:
2837 QUERY_STRING(msg, description, "description");
2838 break;
2839 default:
2840 talloc_free(info);
2841 return NT_STATUS_INVALID_INFO_CLASS;
2844 *r->out.info = info;
2846 return NT_STATUS_OK;
2851 samr_SetAliasInfo
2853 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2854 struct samr_SetAliasInfo *r)
2856 struct dcesrv_handle *h;
2857 struct samr_account_state *a_state;
2858 struct ldb_message *msg;
2859 int ret;
2861 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2863 a_state = h->data;
2865 msg = ldb_msg_new(mem_ctx);
2866 if (msg == NULL) {
2867 return NT_STATUS_NO_MEMORY;
2870 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2871 if (!msg->dn) {
2872 return NT_STATUS_NO_MEMORY;
2875 switch (r->in.level) {
2876 case ALIASINFODESCRIPTION:
2877 SET_STRING(msg, description, "description");
2878 break;
2879 case ALIASINFONAME:
2880 /* On W2k3 this does not change the name, it changes the
2881 * sAMAccountName attribute */
2882 SET_STRING(msg, name, "sAMAccountName");
2883 break;
2884 default:
2885 return NT_STATUS_INVALID_INFO_CLASS;
2888 /* modify the samdb record */
2889 ret = ldb_modify(a_state->sam_ctx, msg);
2890 if (ret != LDB_SUCCESS) {
2891 return dsdb_ldb_err_to_ntstatus(ret);
2894 return NT_STATUS_OK;
2899 samr_DeleteDomAlias
2901 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2902 struct samr_DeleteDomAlias *r)
2904 struct dcesrv_handle *h;
2905 struct samr_account_state *a_state;
2906 int ret;
2908 *r->out.alias_handle = *r->in.alias_handle;
2910 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2912 a_state = h->data;
2914 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2915 if (ret != LDB_SUCCESS) {
2916 return dsdb_ldb_err_to_ntstatus(ret);
2919 talloc_free(h);
2920 ZERO_STRUCTP(r->out.alias_handle);
2922 return NT_STATUS_OK;
2927 samr_AddAliasMember
2929 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2930 struct samr_AddAliasMember *r)
2932 struct dcesrv_handle *h;
2933 struct samr_account_state *a_state;
2934 struct samr_domain_state *d_state;
2935 struct ldb_message *mod;
2936 struct ldb_message **msgs;
2937 const char * const attrs[] = { NULL };
2938 struct ldb_dn *memberdn = NULL;
2939 int ret;
2940 NTSTATUS status;
2942 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2944 a_state = h->data;
2945 d_state = a_state->domain_state;
2947 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2948 &msgs, attrs, "(objectsid=%s)",
2949 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2951 if (ret == 1) {
2952 memberdn = msgs[0]->dn;
2953 } else if (ret == 0) {
2954 status = samdb_create_foreign_security_principal(
2955 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2956 if (!NT_STATUS_IS_OK(status)) {
2957 return status;
2959 } else {
2960 DEBUG(0,("Found %d records matching sid %s\n",
2961 ret, dom_sid_string(mem_ctx, r->in.sid)));
2962 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2965 if (memberdn == NULL) {
2966 DEBUG(0, ("Could not find memberdn\n"));
2967 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2970 mod = ldb_msg_new(mem_ctx);
2971 if (mod == NULL) {
2972 return NT_STATUS_NO_MEMORY;
2975 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2977 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2978 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2979 if (ret != LDB_SUCCESS) {
2980 return dsdb_ldb_err_to_ntstatus(ret);
2983 ret = ldb_modify(a_state->sam_ctx, mod);
2984 switch (ret) {
2985 case LDB_SUCCESS:
2986 return NT_STATUS_OK;
2987 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2988 return NT_STATUS_MEMBER_IN_GROUP;
2989 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2990 return NT_STATUS_ACCESS_DENIED;
2991 default:
2992 return dsdb_ldb_err_to_ntstatus(ret);
2998 samr_DeleteAliasMember
3000 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3001 struct samr_DeleteAliasMember *r)
3003 struct dcesrv_handle *h;
3004 struct samr_account_state *a_state;
3005 struct samr_domain_state *d_state;
3006 struct ldb_message *mod;
3007 const char *memberdn;
3008 int ret;
3010 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
3012 a_state = h->data;
3013 d_state = a_state->domain_state;
3015 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3016 "distinguishedName", "(objectSid=%s)",
3017 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3018 if (memberdn == NULL) {
3019 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3022 mod = ldb_msg_new(mem_ctx);
3023 if (mod == NULL) {
3024 return NT_STATUS_NO_MEMORY;
3027 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
3029 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
3030 memberdn);
3031 if (ret != LDB_SUCCESS) {
3032 return dsdb_ldb_err_to_ntstatus(ret);
3035 ret = ldb_modify(a_state->sam_ctx, mod);
3036 switch (ret) {
3037 case LDB_SUCCESS:
3038 return NT_STATUS_OK;
3039 case LDB_ERR_UNWILLING_TO_PERFORM:
3040 return NT_STATUS_MEMBER_NOT_IN_GROUP;
3041 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
3042 return NT_STATUS_ACCESS_DENIED;
3043 default:
3044 return dsdb_ldb_err_to_ntstatus(ret);
3050 samr_GetMembersInAlias
3052 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3053 struct samr_GetMembersInAlias *r)
3055 struct dcesrv_handle *h;
3056 struct samr_account_state *a_state;
3057 struct samr_domain_state *d_state;
3058 struct lsa_SidPtr *array;
3059 unsigned int i, num_members;
3060 struct dom_sid *members;
3061 NTSTATUS status;
3063 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
3065 a_state = h->data;
3066 d_state = a_state->domain_state;
3068 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
3069 a_state->account_dn, &members,
3070 &num_members);
3071 if (!NT_STATUS_IS_OK(status)) {
3072 return status;
3075 if (num_members == 0) {
3076 r->out.sids->sids = NULL;
3078 return NT_STATUS_OK;
3081 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
3082 if (array == NULL) {
3083 return NT_STATUS_NO_MEMORY;
3086 for (i=0; i<num_members; i++) {
3087 array[i].sid = &members[i];
3090 r->out.sids->num_sids = num_members;
3091 r->out.sids->sids = array;
3093 return NT_STATUS_OK;
3097 samr_OpenUser
3099 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3100 struct samr_OpenUser *r)
3102 struct samr_domain_state *d_state;
3103 struct samr_account_state *a_state;
3104 struct dcesrv_handle *h;
3105 const char *account_name;
3106 struct dom_sid *sid;
3107 struct ldb_message **msgs;
3108 struct dcesrv_handle *u_handle;
3109 const char * const attrs[2] = { "sAMAccountName", NULL };
3110 int ret;
3112 ZERO_STRUCTP(r->out.user_handle);
3114 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3116 d_state = h->data;
3118 /* form the users SID */
3119 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
3120 if (!sid) {
3121 return NT_STATUS_NO_MEMORY;
3124 /* search for the user record */
3125 ret = gendb_search(d_state->sam_ctx,
3126 mem_ctx, d_state->domain_dn, &msgs, attrs,
3127 "(&(objectSid=%s)(objectclass=user))",
3128 ldap_encode_ndr_dom_sid(mem_ctx, sid));
3129 if (ret == 0) {
3130 return NT_STATUS_NO_SUCH_USER;
3132 if (ret != 1) {
3133 DEBUG(0,("Found %d records matching sid %s\n", ret,
3134 dom_sid_string(mem_ctx, sid)));
3135 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3138 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
3139 if (account_name == NULL) {
3140 DEBUG(0,("sAMAccountName field missing for sid %s\n",
3141 dom_sid_string(mem_ctx, sid)));
3142 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3145 a_state = talloc(mem_ctx, struct samr_account_state);
3146 if (!a_state) {
3147 return NT_STATUS_NO_MEMORY;
3149 a_state->sam_ctx = d_state->sam_ctx;
3150 a_state->access_mask = r->in.access_mask;
3151 a_state->domain_state = talloc_reference(a_state, d_state);
3152 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
3153 a_state->account_sid = talloc_steal(a_state, sid);
3154 a_state->account_name = talloc_strdup(a_state, account_name);
3155 if (!a_state->account_name) {
3156 return NT_STATUS_NO_MEMORY;
3159 /* create the policy handle */
3160 u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
3161 if (!u_handle) {
3162 return NT_STATUS_NO_MEMORY;
3165 u_handle->data = talloc_steal(u_handle, a_state);
3167 *r->out.user_handle = u_handle->wire_handle;
3169 return NT_STATUS_OK;
3175 samr_DeleteUser
3177 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3178 struct samr_DeleteUser *r)
3180 struct dcesrv_handle *h;
3181 struct samr_account_state *a_state;
3182 int ret;
3184 *r->out.user_handle = *r->in.user_handle;
3186 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3188 a_state = h->data;
3190 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
3191 if (ret != LDB_SUCCESS) {
3192 DEBUG(1, ("Failed to delete user: %s: %s\n",
3193 ldb_dn_get_linearized(a_state->account_dn),
3194 ldb_errstring(a_state->sam_ctx)));
3195 return dsdb_ldb_err_to_ntstatus(ret);
3198 talloc_free(h);
3199 ZERO_STRUCTP(r->out.user_handle);
3201 return NT_STATUS_OK;
3206 samr_QueryUserInfo
3208 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3209 struct samr_QueryUserInfo *r)
3211 struct dcesrv_handle *h;
3212 struct samr_account_state *a_state;
3213 struct ldb_message *msg, **res;
3214 int ret;
3215 struct ldb_context *sam_ctx;
3217 const char * const *attrs = NULL;
3218 union samr_UserInfo *info;
3220 NTSTATUS status;
3222 *r->out.info = NULL;
3224 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3226 a_state = h->data;
3227 sam_ctx = a_state->sam_ctx;
3229 /* fill in the reply */
3230 switch (r->in.level) {
3231 case 1:
3233 static const char * const attrs2[] = {"sAMAccountName",
3234 "displayName",
3235 "primaryGroupID",
3236 "description",
3237 "comment",
3238 NULL};
3239 attrs = attrs2;
3240 break;
3242 case 2:
3244 static const char * const attrs2[] = {"comment",
3245 "countryCode",
3246 "codePage",
3247 NULL};
3248 attrs = attrs2;
3249 break;
3251 case 3:
3253 static const char * const attrs2[] = {"sAMAccountName",
3254 "displayName",
3255 "objectSid",
3256 "primaryGroupID",
3257 "homeDirectory",
3258 "homeDrive",
3259 "scriptPath",
3260 "profilePath",
3261 "userWorkstations",
3262 "lastLogon",
3263 "lastLogoff",
3264 "pwdLastSet",
3265 "msDS-UserPasswordExpiryTimeComputed",
3266 "logonHours",
3267 "badPwdCount",
3268 "badPasswordTime",
3269 "logonCount",
3270 "userAccountControl",
3271 "msDS-User-Account-Control-Computed",
3272 NULL};
3273 attrs = attrs2;
3274 break;
3276 case 4:
3278 static const char * const attrs2[] = {"logonHours",
3279 NULL};
3280 attrs = attrs2;
3281 break;
3283 case 5:
3285 static const char * const attrs2[] = {"sAMAccountName",
3286 "displayName",
3287 "objectSid",
3288 "primaryGroupID",
3289 "homeDirectory",
3290 "homeDrive",
3291 "scriptPath",
3292 "profilePath",
3293 "description",
3294 "userWorkstations",
3295 "lastLogon",
3296 "lastLogoff",
3297 "logonHours",
3298 "badPwdCount",
3299 "badPasswordTime",
3300 "logonCount",
3301 "pwdLastSet",
3302 "msDS-ResultantPSO",
3303 "msDS-UserPasswordExpiryTimeComputed",
3304 "accountExpires",
3305 "userAccountControl",
3306 "msDS-User-Account-Control-Computed",
3307 NULL};
3308 attrs = attrs2;
3309 break;
3311 case 6:
3313 static const char * const attrs2[] = {"sAMAccountName",
3314 "displayName",
3315 NULL};
3316 attrs = attrs2;
3317 break;
3319 case 7:
3321 static const char * const attrs2[] = {"sAMAccountName",
3322 NULL};
3323 attrs = attrs2;
3324 break;
3326 case 8:
3328 static const char * const attrs2[] = {"displayName",
3329 NULL};
3330 attrs = attrs2;
3331 break;
3333 case 9:
3335 static const char * const attrs2[] = {"primaryGroupID",
3336 NULL};
3337 attrs = attrs2;
3338 break;
3340 case 10:
3342 static const char * const attrs2[] = {"homeDirectory",
3343 "homeDrive",
3344 NULL};
3345 attrs = attrs2;
3346 break;
3348 case 11:
3350 static const char * const attrs2[] = {"scriptPath",
3351 NULL};
3352 attrs = attrs2;
3353 break;
3355 case 12:
3357 static const char * const attrs2[] = {"profilePath",
3358 NULL};
3359 attrs = attrs2;
3360 break;
3362 case 13:
3364 static const char * const attrs2[] = {"description",
3365 NULL};
3366 attrs = attrs2;
3367 break;
3369 case 14:
3371 static const char * const attrs2[] = {"userWorkstations",
3372 NULL};
3373 attrs = attrs2;
3374 break;
3376 case 16:
3378 static const char * const attrs2[] = {"userAccountControl",
3379 "msDS-User-Account-Control-Computed",
3380 "pwdLastSet",
3381 "msDS-UserPasswordExpiryTimeComputed",
3382 NULL};
3383 attrs = attrs2;
3384 break;
3386 case 17:
3388 static const char * const attrs2[] = {"accountExpires",
3389 NULL};
3390 attrs = attrs2;
3391 break;
3393 case 18:
3395 return NT_STATUS_NOT_SUPPORTED;
3397 case 20:
3399 static const char * const attrs2[] = {"userParameters",
3400 NULL};
3401 attrs = attrs2;
3402 break;
3404 case 21:
3406 static const char * const attrs2[] = {"lastLogon",
3407 "lastLogoff",
3408 "pwdLastSet",
3409 "msDS-ResultantPSO",
3410 "msDS-UserPasswordExpiryTimeComputed",
3411 "accountExpires",
3412 "sAMAccountName",
3413 "displayName",
3414 "homeDirectory",
3415 "homeDrive",
3416 "scriptPath",
3417 "profilePath",
3418 "description",
3419 "userWorkstations",
3420 "comment",
3421 "userParameters",
3422 "objectSid",
3423 "primaryGroupID",
3424 "userAccountControl",
3425 "msDS-User-Account-Control-Computed",
3426 "logonHours",
3427 "badPwdCount",
3428 "badPasswordTime",
3429 "logonCount",
3430 "countryCode",
3431 "codePage",
3432 NULL};
3433 attrs = attrs2;
3434 break;
3436 case 23:
3437 case 24:
3438 case 25:
3439 case 26:
3441 return NT_STATUS_NOT_SUPPORTED;
3443 default:
3445 return NT_STATUS_INVALID_INFO_CLASS;
3449 /* pull all the user attributes */
3450 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3451 a_state->account_dn, &res, attrs);
3452 if (ret == 0) {
3453 return NT_STATUS_NO_SUCH_USER;
3455 if (ret != 1) {
3456 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3458 msg = res[0];
3460 /* allocate the info structure */
3461 info = talloc_zero(mem_ctx, union samr_UserInfo);
3462 if (info == NULL) {
3463 return NT_STATUS_NO_MEMORY;
3466 /* fill in the reply */
3467 switch (r->in.level) {
3468 case 1:
3469 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
3470 QUERY_STRING(msg, info1.full_name, "displayName");
3471 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3472 QUERY_STRING(msg, info1.description, "description");
3473 QUERY_STRING(msg, info1.comment, "comment");
3474 break;
3476 case 2:
3477 QUERY_STRING(msg, info2.comment, "comment");
3478 QUERY_UINT (msg, info2.country_code, "countryCode");
3479 QUERY_UINT (msg, info2.code_page, "codePage");
3480 break;
3482 case 3:
3483 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
3484 QUERY_STRING(msg, info3.full_name, "displayName");
3485 QUERY_RID (msg, info3.rid, "objectSid");
3486 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3487 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
3488 QUERY_STRING(msg, info3.home_drive, "homeDrive");
3489 QUERY_STRING(msg, info3.logon_script, "scriptPath");
3490 QUERY_STRING(msg, info3.profile_path, "profilePath");
3491 QUERY_STRING(msg, info3.workstations, "userWorkstations");
3492 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
3493 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
3494 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
3495 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3496 QUERY_UINT64(msg, info3.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3497 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3498 /* level 3 gives the raw badPwdCount value */
3499 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3500 QUERY_UINT (msg, info3.logon_count, "logonCount");
3501 QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
3502 break;
3504 case 4:
3505 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3506 break;
3508 case 5:
3509 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
3510 QUERY_STRING(msg, info5.full_name, "displayName");
3511 QUERY_RID (msg, info5.rid, "objectSid");
3512 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3513 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
3514 QUERY_STRING(msg, info5.home_drive, "homeDrive");
3515 QUERY_STRING(msg, info5.logon_script, "scriptPath");
3516 QUERY_STRING(msg, info5.profile_path, "profilePath");
3517 QUERY_STRING(msg, info5.description, "description");
3518 QUERY_STRING(msg, info5.workstations, "userWorkstations");
3519 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
3520 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
3521 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3522 QUERY_BPWDCT(msg, info5.bad_password_count, "badPwdCount");
3523 QUERY_UINT (msg, info5.logon_count, "logonCount");
3524 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
3525 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
3526 QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
3527 break;
3529 case 6:
3530 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3531 QUERY_STRING(msg, info6.full_name, "displayName");
3532 break;
3534 case 7:
3535 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3536 break;
3538 case 8:
3539 QUERY_STRING(msg, info8.full_name, "displayName");
3540 break;
3542 case 9:
3543 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3544 break;
3546 case 10:
3547 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3548 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3549 break;
3551 case 11:
3552 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3553 break;
3555 case 12:
3556 QUERY_STRING(msg, info12.profile_path, "profilePath");
3557 break;
3559 case 13:
3560 QUERY_STRING(msg, info13.description, "description");
3561 break;
3563 case 14:
3564 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3565 break;
3567 case 16:
3568 QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3569 break;
3571 case 17:
3572 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3573 break;
3575 case 20:
3576 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
3577 if (!NT_STATUS_IS_OK(status)) {
3578 talloc_free(info);
3579 return status;
3581 break;
3583 case 21:
3584 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3585 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3586 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3587 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3588 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3589 QUERY_UINT64(msg, info21.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3590 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3591 QUERY_STRING(msg, info21.full_name, "displayName");
3592 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3593 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3594 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3595 QUERY_STRING(msg, info21.profile_path, "profilePath");
3596 QUERY_STRING(msg, info21.description, "description");
3597 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3598 QUERY_STRING(msg, info21.comment, "comment");
3599 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
3600 if (!NT_STATUS_IS_OK(status)) {
3601 talloc_free(info);
3602 return status;
3605 QUERY_RID (msg, info21.rid, "objectSid");
3606 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3607 QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3608 info->info21.fields_present = 0x08FFFFFF;
3609 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3610 QUERY_BPWDCT(msg, info21.bad_password_count, "badPwdCount");
3611 QUERY_UINT (msg, info21.logon_count, "logonCount");
3612 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3613 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3614 } else {
3615 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3617 QUERY_UINT (msg, info21.country_code, "countryCode");
3618 QUERY_UINT (msg, info21.code_page, "codePage");
3619 break;
3622 default:
3623 talloc_free(info);
3624 return NT_STATUS_INVALID_INFO_CLASS;
3627 *r->out.info = info;
3629 return NT_STATUS_OK;
3634 samr_SetUserInfo
3636 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3637 struct samr_SetUserInfo *r)
3639 struct dcesrv_handle *h;
3640 struct samr_account_state *a_state;
3641 struct ldb_message *msg;
3642 int ret;
3643 NTSTATUS status = NT_STATUS_OK;
3644 struct ldb_context *sam_ctx;
3645 DATA_BLOB session_key = data_blob_null;
3647 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3649 a_state = h->data;
3650 sam_ctx = a_state->sam_ctx;
3652 msg = ldb_msg_new(mem_ctx);
3653 if (msg == NULL) {
3654 return NT_STATUS_NO_MEMORY;
3657 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3658 if (!msg->dn) {
3659 return NT_STATUS_NO_MEMORY;
3662 ret = ldb_transaction_start(sam_ctx);
3663 if (ret != LDB_SUCCESS) {
3664 DBG_ERR("Failed to start a transaction: %s\n",
3665 ldb_errstring(sam_ctx));
3666 return NT_STATUS_LOCK_NOT_GRANTED;
3669 switch (r->in.level) {
3670 case 2:
3671 SET_STRING(msg, info2.comment, "comment");
3672 SET_UINT (msg, info2.country_code, "countryCode");
3673 SET_UINT (msg, info2.code_page, "codePage");
3674 break;
3676 case 4:
3677 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3678 break;
3680 case 6:
3681 SET_STRING(msg, info6.account_name, "samAccountName");
3682 SET_STRING(msg, info6.full_name, "displayName");
3683 break;
3685 case 7:
3686 SET_STRING(msg, info7.account_name, "samAccountName");
3687 break;
3689 case 8:
3690 SET_STRING(msg, info8.full_name, "displayName");
3691 break;
3693 case 9:
3694 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3695 break;
3697 case 10:
3698 SET_STRING(msg, info10.home_directory, "homeDirectory");
3699 SET_STRING(msg, info10.home_drive, "homeDrive");
3700 break;
3702 case 11:
3703 SET_STRING(msg, info11.logon_script, "scriptPath");
3704 break;
3706 case 12:
3707 SET_STRING(msg, info12.profile_path, "profilePath");
3708 break;
3710 case 13:
3711 SET_STRING(msg, info13.description, "description");
3712 break;
3714 case 14:
3715 SET_STRING(msg, info14.workstations, "userWorkstations");
3716 break;
3718 case 16:
3719 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3720 break;
3722 case 17:
3723 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3724 break;
3726 case 18:
3727 status = samr_set_password_buffers(dce_call,
3728 sam_ctx,
3729 a_state->account_dn,
3730 mem_ctx,
3731 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3732 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3733 if (!NT_STATUS_IS_OK(status)) {
3734 goto done;
3737 if (r->in.info->info18.password_expired > 0) {
3738 struct ldb_message_element *set_el;
3739 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3740 status = NT_STATUS_NO_MEMORY;
3741 goto done;
3743 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3744 set_el->flags = LDB_FLAG_MOD_REPLACE;
3746 break;
3748 case 20:
3749 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3750 break;
3752 case 21:
3753 if (r->in.info->info21.fields_present == 0) {
3754 status = NT_STATUS_INVALID_PARAMETER;
3755 goto done;
3758 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3759 IFSET(SAMR_FIELD_LAST_LOGON)
3760 SET_UINT64(msg, info21.last_logon, "lastLogon");
3761 IFSET(SAMR_FIELD_LAST_LOGOFF)
3762 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3763 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3764 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3765 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3766 SET_STRING(msg, info21.account_name, "samAccountName");
3767 IFSET(SAMR_FIELD_FULL_NAME)
3768 SET_STRING(msg, info21.full_name, "displayName");
3769 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3770 SET_STRING(msg, info21.home_directory, "homeDirectory");
3771 IFSET(SAMR_FIELD_HOME_DRIVE)
3772 SET_STRING(msg, info21.home_drive, "homeDrive");
3773 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3774 SET_STRING(msg, info21.logon_script, "scriptPath");
3775 IFSET(SAMR_FIELD_PROFILE_PATH)
3776 SET_STRING(msg, info21.profile_path, "profilePath");
3777 IFSET(SAMR_FIELD_DESCRIPTION)
3778 SET_STRING(msg, info21.description, "description");
3779 IFSET(SAMR_FIELD_WORKSTATIONS)
3780 SET_STRING(msg, info21.workstations, "userWorkstations");
3781 IFSET(SAMR_FIELD_COMMENT)
3782 SET_STRING(msg, info21.comment, "comment");
3783 IFSET(SAMR_FIELD_PARAMETERS)
3784 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3785 IFSET(SAMR_FIELD_PRIMARY_GID)
3786 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3787 IFSET(SAMR_FIELD_ACCT_FLAGS)
3788 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3789 IFSET(SAMR_FIELD_LOGON_HOURS)
3790 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3791 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3792 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3793 IFSET(SAMR_FIELD_NUM_LOGONS)
3794 SET_UINT (msg, info21.logon_count, "logonCount");
3795 IFSET(SAMR_FIELD_COUNTRY_CODE)
3796 SET_UINT (msg, info21.country_code, "countryCode");
3797 IFSET(SAMR_FIELD_CODE_PAGE)
3798 SET_UINT (msg, info21.code_page, "codePage");
3800 /* password change fields */
3801 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
3802 status = NT_STATUS_ACCESS_DENIED;
3803 goto done;
3806 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3807 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3808 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3810 if (r->in.info->info21.lm_password_set) {
3811 if ((r->in.info->info21.lm_owf_password.length != 16)
3812 || (r->in.info->info21.lm_owf_password.size != 16)) {
3813 status = NT_STATUS_INVALID_PARAMETER;
3814 goto done;
3817 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3819 if (r->in.info->info21.nt_password_set) {
3820 if ((r->in.info->info21.nt_owf_password.length != 16)
3821 || (r->in.info->info21.nt_owf_password.size != 16)) {
3822 status = NT_STATUS_INVALID_PARAMETER;
3823 goto done;
3826 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3828 status = samr_set_password_buffers(dce_call,
3829 sam_ctx,
3830 a_state->account_dn,
3831 mem_ctx,
3832 lm_pwd_hash,
3833 nt_pwd_hash);
3834 if (!NT_STATUS_IS_OK(status)) {
3835 goto done;
3840 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3841 const char *t = "0";
3842 struct ldb_message_element *set_el;
3843 if (r->in.info->info21.password_expired
3844 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3845 t = "-1";
3847 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3848 status = NT_STATUS_NO_MEMORY;
3849 goto done;
3851 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3852 set_el->flags = LDB_FLAG_MOD_REPLACE;
3854 #undef IFSET
3855 break;
3857 case 23:
3858 if (r->in.info->info23.info.fields_present == 0) {
3859 status = NT_STATUS_INVALID_PARAMETER;
3860 goto done;
3863 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3864 IFSET(SAMR_FIELD_LAST_LOGON)
3865 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3866 IFSET(SAMR_FIELD_LAST_LOGOFF)
3867 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3868 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3869 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3870 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3871 SET_STRING(msg, info23.info.account_name, "samAccountName");
3872 IFSET(SAMR_FIELD_FULL_NAME)
3873 SET_STRING(msg, info23.info.full_name, "displayName");
3874 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3875 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3876 IFSET(SAMR_FIELD_HOME_DRIVE)
3877 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3878 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3879 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3880 IFSET(SAMR_FIELD_PROFILE_PATH)
3881 SET_STRING(msg, info23.info.profile_path, "profilePath");
3882 IFSET(SAMR_FIELD_DESCRIPTION)
3883 SET_STRING(msg, info23.info.description, "description");
3884 IFSET(SAMR_FIELD_WORKSTATIONS)
3885 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3886 IFSET(SAMR_FIELD_COMMENT)
3887 SET_STRING(msg, info23.info.comment, "comment");
3888 IFSET(SAMR_FIELD_PARAMETERS)
3889 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3890 IFSET(SAMR_FIELD_PRIMARY_GID)
3891 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3892 IFSET(SAMR_FIELD_ACCT_FLAGS)
3893 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3894 IFSET(SAMR_FIELD_LOGON_HOURS)
3895 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3896 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3897 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3898 IFSET(SAMR_FIELD_NUM_LOGONS)
3899 SET_UINT (msg, info23.info.logon_count, "logonCount");
3901 IFSET(SAMR_FIELD_COUNTRY_CODE)
3902 SET_UINT (msg, info23.info.country_code, "countryCode");
3903 IFSET(SAMR_FIELD_CODE_PAGE)
3904 SET_UINT (msg, info23.info.code_page, "codePage");
3906 /* password change fields */
3907 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
3908 status = NT_STATUS_ACCESS_DENIED;
3909 goto done;
3912 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3913 status = samr_set_password(dce_call,
3914 sam_ctx,
3915 a_state->account_dn,
3916 mem_ctx,
3917 &r->in.info->info23.password);
3918 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3919 status = samr_set_password(dce_call,
3920 sam_ctx,
3921 a_state->account_dn,
3922 mem_ctx,
3923 &r->in.info->info23.password);
3925 if (!NT_STATUS_IS_OK(status)) {
3926 goto done;
3929 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3930 const char *t = "0";
3931 struct ldb_message_element *set_el;
3932 if (r->in.info->info23.info.password_expired
3933 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3934 t = "-1";
3936 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3937 status = NT_STATUS_NO_MEMORY;
3938 goto done;
3940 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3941 set_el->flags = LDB_FLAG_MOD_REPLACE;
3943 #undef IFSET
3944 break;
3946 /* the set password levels are handled separately */
3947 case 24:
3948 status = samr_set_password(dce_call,
3949 sam_ctx,
3950 a_state->account_dn,
3951 mem_ctx,
3952 &r->in.info->info24.password);
3953 if (!NT_STATUS_IS_OK(status)) {
3954 goto done;
3957 if (r->in.info->info24.password_expired > 0) {
3958 struct ldb_message_element *set_el;
3959 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3960 status = NT_STATUS_NO_MEMORY;
3961 goto done;
3963 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3964 set_el->flags = LDB_FLAG_MOD_REPLACE;
3966 break;
3968 case 25:
3969 if (r->in.info->info25.info.fields_present == 0) {
3970 status = NT_STATUS_INVALID_PARAMETER;
3971 goto done;
3974 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3975 IFSET(SAMR_FIELD_LAST_LOGON)
3976 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3977 IFSET(SAMR_FIELD_LAST_LOGOFF)
3978 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3979 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3980 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3981 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3982 SET_STRING(msg, info25.info.account_name, "samAccountName");
3983 IFSET(SAMR_FIELD_FULL_NAME)
3984 SET_STRING(msg, info25.info.full_name, "displayName");
3985 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3986 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3987 IFSET(SAMR_FIELD_HOME_DRIVE)
3988 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3989 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3990 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3991 IFSET(SAMR_FIELD_PROFILE_PATH)
3992 SET_STRING(msg, info25.info.profile_path, "profilePath");
3993 IFSET(SAMR_FIELD_DESCRIPTION)
3994 SET_STRING(msg, info25.info.description, "description");
3995 IFSET(SAMR_FIELD_WORKSTATIONS)
3996 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3997 IFSET(SAMR_FIELD_COMMENT)
3998 SET_STRING(msg, info25.info.comment, "comment");
3999 IFSET(SAMR_FIELD_PARAMETERS)
4000 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
4001 IFSET(SAMR_FIELD_PRIMARY_GID)
4002 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
4003 IFSET(SAMR_FIELD_ACCT_FLAGS)
4004 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
4005 IFSET(SAMR_FIELD_LOGON_HOURS)
4006 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
4007 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
4008 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
4009 IFSET(SAMR_FIELD_NUM_LOGONS)
4010 SET_UINT (msg, info25.info.logon_count, "logonCount");
4011 IFSET(SAMR_FIELD_COUNTRY_CODE)
4012 SET_UINT (msg, info25.info.country_code, "countryCode");
4013 IFSET(SAMR_FIELD_CODE_PAGE)
4014 SET_UINT (msg, info25.info.code_page, "codePage");
4016 /* password change fields */
4017 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
4018 status = NT_STATUS_ACCESS_DENIED;
4019 goto done;
4022 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
4023 status = samr_set_password_ex(dce_call,
4024 sam_ctx,
4025 a_state->account_dn,
4026 mem_ctx,
4027 &r->in.info->info25.password);
4028 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
4029 status = samr_set_password_ex(dce_call,
4030 sam_ctx,
4031 a_state->account_dn,
4032 mem_ctx,
4033 &r->in.info->info25.password);
4035 if (!NT_STATUS_IS_OK(status)) {
4036 goto done;
4039 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
4040 const char *t = "0";
4041 struct ldb_message_element *set_el;
4042 if (r->in.info->info25.info.password_expired
4043 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4044 t = "-1";
4046 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4047 status = NT_STATUS_NO_MEMORY;
4048 goto done;
4050 set_el = ldb_msg_find_element(msg, "pwdLastSet");
4051 set_el->flags = LDB_FLAG_MOD_REPLACE;
4053 #undef IFSET
4054 break;
4056 /* the set password levels are handled separately */
4057 case 26:
4058 status = samr_set_password_ex(dce_call,
4059 sam_ctx,
4060 a_state->account_dn,
4061 mem_ctx,
4062 &r->in.info->info26.password);
4063 if (!NT_STATUS_IS_OK(status)) {
4064 goto done;
4067 if (r->in.info->info26.password_expired > 0) {
4068 const char *t = "0";
4069 struct ldb_message_element *set_el;
4070 if (r->in.info->info26.password_expired
4071 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4072 t = "-1";
4074 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4075 status = NT_STATUS_NO_MEMORY;
4076 goto done;
4078 set_el = ldb_msg_find_element(msg, "pwdLastSet");
4079 set_el->flags = LDB_FLAG_MOD_REPLACE;
4081 break;
4083 case 31:
4084 status = dcesrv_transport_session_key(dce_call, &session_key);
4085 if (!NT_STATUS_IS_OK(status)) {
4086 DBG_NOTICE("samr: failed to get session key: %s\n",
4087 nt_errstr(status));
4088 goto done;
4091 status = samr_set_password_aes(dce_call,
4092 mem_ctx,
4093 &session_key,
4094 sam_ctx,
4095 a_state->account_dn,
4096 &r->in.info->info31.password,
4097 DSDB_PASSWORD_RESET);
4098 if (!NT_STATUS_IS_OK(status)) {
4099 goto done;
4102 if (r->in.info->info31.password_expired > 0) {
4103 const char *t = "0";
4104 struct ldb_message_element *set_el = NULL;
4106 if (r->in.info->info31.password_expired ==
4107 PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4108 t = "-1";
4111 ret = ldb_msg_add_string(msg, "pwdLastSet", t);
4112 if (ret != LDB_SUCCESS) {
4113 status = NT_STATUS_NO_MEMORY;
4114 goto done;
4116 set_el = ldb_msg_find_element(msg, "pwdLastSet");
4117 set_el->flags = LDB_FLAG_MOD_REPLACE;
4120 break;
4121 case 32:
4122 status = dcesrv_transport_session_key(dce_call, &session_key);
4123 if (!NT_STATUS_IS_OK(status)) {
4124 DBG_NOTICE("samr: failed to get session key: %s\n",
4125 nt_errstr(status));
4126 goto done;
4129 if (r->in.info->info32.info.fields_present == 0) {
4130 status = NT_STATUS_INVALID_PARAMETER;
4131 goto done;
4134 #define IFSET(bit) if (bit & r->in.info->info32.info.fields_present)
4135 IFSET(SAMR_FIELD_LAST_LOGON)
4137 SET_UINT64(msg, info32.info.last_logon, "lastLogon");
4139 IFSET(SAMR_FIELD_LAST_LOGOFF)
4141 SET_UINT64(msg, info32.info.last_logoff, "lastLogoff");
4143 IFSET(SAMR_FIELD_ACCT_EXPIRY)
4145 SET_UINT64(msg,
4146 info32.info.acct_expiry,
4147 "accountExpires");
4149 IFSET(SAMR_FIELD_ACCOUNT_NAME)
4151 SET_STRING(msg,
4152 info32.info.account_name,
4153 "samAccountName");
4155 IFSET(SAMR_FIELD_FULL_NAME)
4157 SET_STRING(msg, info32.info.full_name, "displayName");
4159 IFSET(SAMR_FIELD_HOME_DIRECTORY)
4161 SET_STRING(msg,
4162 info32.info.home_directory,
4163 "homeDirectory");
4165 IFSET(SAMR_FIELD_HOME_DRIVE)
4167 SET_STRING(msg, info32.info.home_drive, "homeDrive");
4169 IFSET(SAMR_FIELD_LOGON_SCRIPT)
4171 SET_STRING(msg, info32.info.logon_script, "scriptPath");
4173 IFSET(SAMR_FIELD_PROFILE_PATH)
4175 SET_STRING(msg,
4176 info32.info.profile_path,
4177 "profilePath");
4179 IFSET(SAMR_FIELD_DESCRIPTION)
4181 SET_STRING(msg, info32.info.description, "description");
4183 IFSET(SAMR_FIELD_WORKSTATIONS)
4185 SET_STRING(msg,
4186 info32.info.workstations,
4187 "userWorkstations");
4189 IFSET(SAMR_FIELD_COMMENT)
4191 SET_STRING(msg, info32.info.comment, "comment");
4193 IFSET(SAMR_FIELD_PARAMETERS)
4195 SET_PARAMETERS(msg,
4196 info32.info.parameters,
4197 "userParameters");
4199 IFSET(SAMR_FIELD_PRIMARY_GID)
4201 SET_UINT(msg,
4202 info32.info.primary_gid,
4203 "primaryGroupID");
4205 IFSET(SAMR_FIELD_ACCT_FLAGS)
4207 SET_AFLAGS(msg,
4208 info32.info.acct_flags,
4209 "userAccountControl");
4211 IFSET(SAMR_FIELD_LOGON_HOURS)
4213 SET_LHOURS(msg, info32.info.logon_hours, "logonHours");
4215 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
4217 SET_UINT(msg,
4218 info32.info.bad_password_count,
4219 "badPwdCount");
4221 IFSET(SAMR_FIELD_NUM_LOGONS)
4223 SET_UINT(msg, info32.info.logon_count, "logonCount");
4225 IFSET(SAMR_FIELD_COUNTRY_CODE)
4227 SET_UINT(msg, info32.info.country_code, "countryCode");
4229 IFSET(SAMR_FIELD_CODE_PAGE)
4231 SET_UINT(msg, info32.info.code_page, "codePage");
4234 /* password change fields */
4235 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
4237 status = NT_STATUS_ACCESS_DENIED;
4238 goto done;
4241 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT)
4243 status = samr_set_password_aes(
4244 dce_call,
4245 mem_ctx,
4246 &session_key,
4247 a_state->sam_ctx,
4248 a_state->account_dn,
4249 &r->in.info->info32.password,
4250 DSDB_PASSWORD_RESET);
4252 else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT)
4254 status = samr_set_password_aes(
4255 dce_call,
4256 mem_ctx,
4257 &session_key,
4258 a_state->sam_ctx,
4259 a_state->account_dn,
4260 &r->in.info->info32.password,
4261 DSDB_PASSWORD_RESET);
4263 if (!NT_STATUS_IS_OK(status)) {
4264 goto done;
4267 IFSET(SAMR_FIELD_EXPIRED_FLAG)
4269 const char *t = "0";
4270 struct ldb_message_element *set_el;
4271 if (r->in.info->info32.info.password_expired ==
4272 PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4273 t = "-1";
4275 if (ldb_msg_add_string(msg, "pwdLastSet", t) !=
4276 LDB_SUCCESS) {
4277 status = NT_STATUS_NO_MEMORY;
4278 goto done;
4280 set_el = ldb_msg_find_element(msg, "pwdLastSet");
4281 set_el->flags = LDB_FLAG_MOD_REPLACE;
4283 #undef IFSET
4285 break;
4286 default:
4287 /* many info classes are not valid for SetUserInfo */
4288 status = NT_STATUS_INVALID_INFO_CLASS;
4289 goto done;
4292 if (!NT_STATUS_IS_OK(status)) {
4293 goto done;
4296 /* modify the samdb record */
4297 if (msg->num_elements > 0) {
4298 ret = ldb_modify(sam_ctx, msg);
4299 if (ret != LDB_SUCCESS) {
4300 DEBUG(1,("Failed to modify record %s: %s\n",
4301 ldb_dn_get_linearized(a_state->account_dn),
4302 ldb_errstring(sam_ctx)));
4304 status = dsdb_ldb_err_to_ntstatus(ret);
4305 goto done;
4309 ret = ldb_transaction_commit(sam_ctx);
4310 if (ret != LDB_SUCCESS) {
4311 DBG_ERR("Failed to commit transaction modifying account record "
4312 "%s: %s\n",
4313 ldb_dn_get_linearized(msg->dn),
4314 ldb_errstring(sam_ctx));
4315 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4318 status = NT_STATUS_OK;
4319 done:
4320 if (!NT_STATUS_IS_OK(status)) {
4321 ldb_transaction_cancel(sam_ctx);
4324 return status;
4329 samr_GetGroupsForUser
4331 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4332 struct samr_GetGroupsForUser *r)
4334 struct dcesrv_handle *h;
4335 struct samr_account_state *a_state;
4336 struct samr_domain_state *d_state;
4337 struct ldb_result *res, *res_memberof;
4338 const char * const attrs[] = { "primaryGroupID",
4339 "memberOf",
4340 NULL };
4341 const char * const group_attrs[] = { "objectSid",
4342 NULL };
4344 struct samr_RidWithAttributeArray *array;
4345 struct ldb_message_element *memberof_el;
4346 int i, ret, count = 0;
4347 uint32_t primary_group_id;
4348 char *filter;
4350 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4352 a_state = h->data;
4353 d_state = a_state->domain_state;
4355 ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
4356 &res,
4357 a_state->account_dn,
4358 attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
4360 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4361 return NT_STATUS_NO_SUCH_USER;
4362 } else if (ret != LDB_SUCCESS) {
4363 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4364 } else if (res->count != 1) {
4365 return NT_STATUS_NO_SUCH_USER;
4368 primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
4371 filter = talloc_asprintf(mem_ctx,
4372 "(&(|(grouptype=%d)(grouptype=%d))"
4373 "(objectclass=group)(|",
4374 GTYPE_SECURITY_UNIVERSAL_GROUP,
4375 GTYPE_SECURITY_GLOBAL_GROUP);
4376 if (filter == NULL) {
4377 return NT_STATUS_NO_MEMORY;
4380 memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
4381 if (memberof_el != NULL) {
4382 for (i = 0; i < memberof_el->num_values; i++) {
4383 const struct ldb_val *memberof_sid_binary;
4384 char *memberof_sid_escaped;
4385 struct ldb_dn *memberof_dn
4386 = ldb_dn_from_ldb_val(mem_ctx,
4387 a_state->sam_ctx,
4388 &memberof_el->values[i]);
4389 if (memberof_dn == NULL) {
4390 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4393 memberof_sid_binary
4394 = ldb_dn_get_extended_component(memberof_dn,
4395 "SID");
4396 if (memberof_sid_binary == NULL) {
4397 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4400 memberof_sid_escaped = ldb_binary_encode(mem_ctx,
4401 *memberof_sid_binary);
4402 if (memberof_sid_escaped == NULL) {
4403 return NT_STATUS_NO_MEMORY;
4405 filter = talloc_asprintf_append(filter, "(objectSID=%s)",
4406 memberof_sid_escaped);
4407 if (filter == NULL) {
4408 return NT_STATUS_NO_MEMORY;
4412 ret = dsdb_search(a_state->sam_ctx, mem_ctx,
4413 &res_memberof,
4414 d_state->domain_dn,
4415 LDB_SCOPE_SUBTREE,
4416 group_attrs, 0,
4417 "%s))", filter);
4419 if (ret != LDB_SUCCESS) {
4420 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4422 count = res_memberof->count;
4425 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
4426 if (array == NULL)
4427 return NT_STATUS_NO_MEMORY;
4429 array->count = 0;
4430 array->rids = NULL;
4432 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
4433 count + 1);
4434 if (array->rids == NULL)
4435 return NT_STATUS_NO_MEMORY;
4437 /* Adds the primary group */
4439 array->rids[0].rid = primary_group_id;
4440 array->rids[0].attributes = SE_GROUP_DEFAULT_FLAGS;
4441 array->count += 1;
4443 /* Adds the additional groups */
4444 for (i = 0; i < count; i++) {
4445 struct dom_sid *group_sid;
4447 group_sid = samdb_result_dom_sid(mem_ctx,
4448 res_memberof->msgs[i],
4449 "objectSid");
4450 if (group_sid == NULL) {
4451 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4454 array->rids[i + 1].rid =
4455 group_sid->sub_auths[group_sid->num_auths-1];
4456 array->rids[i + 1].attributes = SE_GROUP_DEFAULT_FLAGS;
4457 array->count += 1;
4460 *r->out.rids = array;
4462 return NT_STATUS_OK;
4466 * samr_QueryDisplayInfo
4468 * A cache of the GUID's matching the last query is maintained
4469 * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
4470 * n the dcesrv_handle.
4472 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4473 struct samr_QueryDisplayInfo *r)
4475 struct dcesrv_handle *h;
4476 struct samr_domain_state *d_state;
4477 struct ldb_result *res;
4478 uint32_t i;
4479 uint32_t results = 0;
4480 uint32_t count = 0;
4481 const char *const cache_attrs[] = {"objectGUID", NULL};
4482 const char *const attrs[] = {
4483 "objectSID", "sAMAccountName", "displayName", "description", NULL};
4484 struct samr_DispEntryFull *entriesFull = NULL;
4485 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
4486 struct samr_DispEntryAscii *entriesAscii = NULL;
4487 struct samr_DispEntryGeneral *entriesGeneral = NULL;
4488 const char *filter;
4489 int ret;
4490 NTSTATUS status;
4491 struct samr_guid_cache *cache = NULL;
4493 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4495 d_state = h->data;
4497 cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
4499 * Can the cached results be used?
4500 * The cache is discarded if the start index is zero, or the requested
4501 * level is different from that in the cache.
4503 if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
4505 * The cached results can not be used, so will need to query
4506 * the database.
4510 * Get the search filter for the current level
4512 switch (r->in.level) {
4513 case 1:
4514 case 4:
4515 filter = talloc_asprintf(mem_ctx,
4516 "(&(objectclass=user)"
4517 "(sAMAccountType=%d))",
4518 ATYPE_NORMAL_ACCOUNT);
4519 break;
4520 case 2:
4521 filter = talloc_asprintf(mem_ctx,
4522 "(&(objectclass=user)"
4523 "(sAMAccountType=%d))",
4524 ATYPE_WORKSTATION_TRUST);
4525 break;
4526 case 3:
4527 case 5:
4528 filter =
4529 talloc_asprintf(mem_ctx,
4530 "(&(|(groupType=%d)(groupType=%d))"
4531 "(objectClass=group))",
4532 GTYPE_SECURITY_UNIVERSAL_GROUP,
4533 GTYPE_SECURITY_GLOBAL_GROUP);
4534 break;
4535 default:
4536 return NT_STATUS_INVALID_INFO_CLASS;
4538 clear_guid_cache(cache);
4541 * search for all requested objects in all domains.
4543 ret = dsdb_search(d_state->sam_ctx,
4544 mem_ctx,
4545 &res,
4546 ldb_get_default_basedn(d_state->sam_ctx),
4547 LDB_SCOPE_SUBTREE,
4548 cache_attrs,
4550 "%s",
4551 filter);
4552 if (ret != LDB_SUCCESS) {
4553 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4555 if ((res->count == 0) || (r->in.max_entries == 0)) {
4556 return NT_STATUS_OK;
4559 status = load_guid_cache(cache, d_state, res->count, res->msgs);
4560 TALLOC_FREE(res);
4561 if (!NT_STATUS_IS_OK(status)) {
4562 return status;
4564 cache->handle = r->in.level;
4566 *r->out.total_size = cache->size;
4569 * if there are no entries or the requested start index is greater
4570 * than the number of entries, we return an empty response.
4572 if (r->in.start_idx >= cache->size) {
4573 *r->out.returned_size = 0;
4574 switch(r->in.level) {
4575 case 1:
4576 r->out.info->info1.count = *r->out.returned_size;
4577 r->out.info->info1.entries = NULL;
4578 break;
4579 case 2:
4580 r->out.info->info2.count = *r->out.returned_size;
4581 r->out.info->info2.entries = NULL;
4582 break;
4583 case 3:
4584 r->out.info->info3.count = *r->out.returned_size;
4585 r->out.info->info3.entries = NULL;
4586 break;
4587 case 4:
4588 r->out.info->info4.count = *r->out.returned_size;
4589 r->out.info->info4.entries = NULL;
4590 break;
4591 case 5:
4592 r->out.info->info5.count = *r->out.returned_size;
4593 r->out.info->info5.entries = NULL;
4594 break;
4596 return NT_STATUS_OK;
4600 * Allocate an array of the appropriate result structures for the
4601 * current query level.
4603 * r->in.start_idx is always < cache->size due to the check above
4605 results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
4606 switch (r->in.level) {
4607 case 1:
4608 entriesGeneral = talloc_array(
4609 mem_ctx, struct samr_DispEntryGeneral, results);
4610 break;
4611 case 2:
4612 entriesFull =
4613 talloc_array(mem_ctx, struct samr_DispEntryFull, results);
4614 break;
4615 case 3:
4616 entriesFullGroup = talloc_array(
4617 mem_ctx, struct samr_DispEntryFullGroup, results);
4618 break;
4619 case 4:
4620 case 5:
4621 entriesAscii =
4622 talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
4623 break;
4626 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
4627 (entriesAscii == NULL) && (entriesFullGroup == NULL))
4628 return NT_STATUS_NO_MEMORY;
4631 * Process the list of result GUID's.
4632 * Read the details of each object and populate the result structure
4633 * for the current level.
4635 count = 0;
4636 for (i = 0; i < results; i++) {
4637 struct dom_sid *objectsid;
4638 struct ldb_result *rec;
4639 const uint32_t idx = r->in.start_idx + i;
4640 uint32_t rid;
4643 * Read an object from disk using the GUID as the key
4645 * If the object can not be read, or it does not have a SID
4646 * it is ignored. In this case the number of entries returned
4647 * will be less than the requested size, there will also be
4648 * a gap in the idx numbers in the returned elements e.g. if
4649 * there are 3 GUIDs a, b, c in the cache and b is deleted from
4650 * disk then details for a, and c will be returned with
4651 * idx values of 1 and 3 respectively.
4654 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
4655 mem_ctx,
4656 &rec,
4657 &cache->entries[idx],
4658 attrs,
4660 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4661 struct GUID_txt_buf guid_buf;
4662 char *guid_str =
4663 GUID_buf_string(&cache->entries[idx],
4664 &guid_buf);
4665 DBG_WARNING("GUID [%s] not found\n", guid_str);
4666 continue;
4667 } else if (ret != LDB_SUCCESS) {
4668 clear_guid_cache(cache);
4669 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4671 objectsid = samdb_result_dom_sid(mem_ctx,
4672 rec->msgs[0],
4673 "objectSID");
4674 if (objectsid == NULL) {
4675 struct GUID_txt_buf guid_buf;
4676 DBG_WARNING(
4677 "objectSID for GUID [%s] not found\n",
4678 GUID_buf_string(&cache->entries[idx], &guid_buf));
4679 continue;
4681 status = dom_sid_split_rid(NULL,
4682 objectsid,
4683 NULL,
4684 &rid);
4685 if (!NT_STATUS_IS_OK(status)) {
4686 struct dom_sid_buf sid_buf;
4687 struct GUID_txt_buf guid_buf;
4688 DBG_WARNING(
4689 "objectSID [%s] for GUID [%s] invalid\n",
4690 dom_sid_str_buf(objectsid, &sid_buf),
4691 GUID_buf_string(&cache->entries[idx], &guid_buf));
4692 continue;
4696 * Populate the result structure for the current object
4698 switch(r->in.level) {
4699 case 1:
4701 entriesGeneral[count].idx = idx + 1;
4702 entriesGeneral[count].rid = rid;
4704 entriesGeneral[count].acct_flags =
4705 samdb_result_acct_flags(rec->msgs[0], NULL);
4706 entriesGeneral[count].account_name.string =
4707 ldb_msg_find_attr_as_string(
4708 rec->msgs[0], "sAMAccountName", "");
4709 entriesGeneral[count].full_name.string =
4710 ldb_msg_find_attr_as_string(
4711 rec->msgs[0], "displayName", "");
4712 entriesGeneral[count].description.string =
4713 ldb_msg_find_attr_as_string(
4714 rec->msgs[0], "description", "");
4715 break;
4716 case 2:
4717 entriesFull[count].idx = idx + 1;
4718 entriesFull[count].rid = rid;
4721 * No idea why we need to or in ACB_NORMAL here,
4722 * but this is what Win2k3 seems to do...
4724 entriesFull[count].acct_flags =
4725 samdb_result_acct_flags(rec->msgs[0], NULL) |
4726 ACB_NORMAL;
4727 entriesFull[count].account_name.string =
4728 ldb_msg_find_attr_as_string(
4729 rec->msgs[0], "sAMAccountName", "");
4730 entriesFull[count].description.string =
4731 ldb_msg_find_attr_as_string(
4732 rec->msgs[0], "description", "");
4733 break;
4734 case 3:
4735 entriesFullGroup[count].idx = idx + 1;
4736 entriesFullGroup[count].rid = rid;
4739 * We get a "7" here for groups
4741 entriesFullGroup[count].acct_flags = SE_GROUP_DEFAULT_FLAGS;
4742 entriesFullGroup[count].account_name.string =
4743 ldb_msg_find_attr_as_string(
4744 rec->msgs[0], "sAMAccountName", "");
4745 entriesFullGroup[count].description.string =
4746 ldb_msg_find_attr_as_string(
4747 rec->msgs[0], "description", "");
4748 break;
4749 case 4:
4750 case 5:
4751 entriesAscii[count].idx = idx + 1;
4752 entriesAscii[count].account_name.string =
4753 ldb_msg_find_attr_as_string(
4754 rec->msgs[0], "sAMAccountName", "");
4755 break;
4757 count++;
4761 * Build the response based on the request level.
4763 *r->out.returned_size = count;
4764 switch(r->in.level) {
4765 case 1:
4766 r->out.info->info1.count = count;
4767 r->out.info->info1.entries = entriesGeneral;
4768 break;
4769 case 2:
4770 r->out.info->info2.count = count;
4771 r->out.info->info2.entries = entriesFull;
4772 break;
4773 case 3:
4774 r->out.info->info3.count = count;
4775 r->out.info->info3.entries = entriesFullGroup;
4776 break;
4777 case 4:
4778 r->out.info->info4.count = count;
4779 r->out.info->info4.entries = entriesAscii;
4780 break;
4781 case 5:
4782 r->out.info->info5.count = count;
4783 r->out.info->info5.entries = entriesAscii;
4784 break;
4787 return ((r->in.start_idx + results) < cache->size)
4788 ? STATUS_MORE_ENTRIES
4789 : NT_STATUS_OK;
4794 samr_GetDisplayEnumerationIndex
4796 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4797 struct samr_GetDisplayEnumerationIndex *r)
4799 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4804 samr_TestPrivateFunctionsDomain
4806 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4807 struct samr_TestPrivateFunctionsDomain *r)
4809 return NT_STATUS_NOT_IMPLEMENTED;
4814 samr_TestPrivateFunctionsUser
4816 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4817 struct samr_TestPrivateFunctionsUser *r)
4819 return NT_STATUS_NOT_IMPLEMENTED;
4824 samr_GetUserPwInfo
4826 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4827 struct samr_GetUserPwInfo *r)
4829 struct dcesrv_handle *h;
4830 struct samr_account_state *a_state;
4832 ZERO_STRUCTP(r->out.info);
4834 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4836 a_state = h->data;
4838 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
4839 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
4840 NULL);
4841 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
4842 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
4844 return NT_STATUS_OK;
4849 samr_RemoveMemberFromForeignDomain
4851 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
4852 TALLOC_CTX *mem_ctx,
4853 struct samr_RemoveMemberFromForeignDomain *r)
4855 struct dcesrv_handle *h;
4856 struct samr_domain_state *d_state;
4857 const char *memberdn;
4858 struct ldb_message **res;
4859 const char *no_attrs[] = { NULL };
4860 int i, count;
4862 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4864 d_state = h->data;
4866 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
4867 "distinguishedName", "(objectSid=%s)",
4868 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
4869 /* Nothing to do */
4870 if (memberdn == NULL) {
4871 return NT_STATUS_OK;
4874 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4875 d_state->domain_dn, &res, no_attrs,
4876 d_state->domain_sid,
4877 "(&(member=%s)(objectClass=group)"
4878 "(|(groupType=%d)(groupType=%d)))",
4879 memberdn,
4880 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4881 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4883 if (count < 0)
4884 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4886 for (i=0; i<count; i++) {
4887 struct ldb_message *mod;
4888 int ret;
4890 mod = ldb_msg_new(mem_ctx);
4891 if (mod == NULL) {
4892 return NT_STATUS_NO_MEMORY;
4895 mod->dn = res[i]->dn;
4897 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4898 "member", memberdn) != LDB_SUCCESS)
4899 return NT_STATUS_NO_MEMORY;
4901 ret = ldb_modify(d_state->sam_ctx, mod);
4902 talloc_free(mod);
4903 if (ret != LDB_SUCCESS) {
4904 return dsdb_ldb_err_to_ntstatus(ret);
4908 return NT_STATUS_OK;
4913 samr_QueryDomainInfo2
4915 just an alias for samr_QueryDomainInfo
4917 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4918 struct samr_QueryDomainInfo2 *r)
4920 struct samr_QueryDomainInfo r1;
4921 NTSTATUS status;
4923 r1 = (struct samr_QueryDomainInfo) {
4924 .in.domain_handle = r->in.domain_handle,
4925 .in.level = r->in.level,
4926 .out.info = r->out.info,
4929 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4931 return status;
4936 samr_QueryUserInfo2
4938 just an alias for samr_QueryUserInfo
4940 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4941 struct samr_QueryUserInfo2 *r)
4943 struct samr_QueryUserInfo r1;
4944 NTSTATUS status;
4946 r1 = (struct samr_QueryUserInfo) {
4947 .in.user_handle = r->in.user_handle,
4948 .in.level = r->in.level,
4949 .out.info = r->out.info
4952 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4954 return status;
4959 samr_QueryDisplayInfo2
4961 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4962 struct samr_QueryDisplayInfo2 *r)
4964 struct samr_QueryDisplayInfo q;
4965 NTSTATUS result;
4967 q = (struct samr_QueryDisplayInfo) {
4968 .in.domain_handle = r->in.domain_handle,
4969 .in.level = r->in.level,
4970 .in.start_idx = r->in.start_idx,
4971 .in.max_entries = r->in.max_entries,
4972 .in.buf_size = r->in.buf_size,
4973 .out.total_size = r->out.total_size,
4974 .out.returned_size = r->out.returned_size,
4975 .out.info = r->out.info,
4978 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4980 return result;
4985 samr_GetDisplayEnumerationIndex2
4987 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4988 struct samr_GetDisplayEnumerationIndex2 *r)
4990 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4995 samr_QueryDisplayInfo3
4997 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4998 struct samr_QueryDisplayInfo3 *r)
5000 struct samr_QueryDisplayInfo q;
5001 NTSTATUS result;
5003 q = (struct samr_QueryDisplayInfo) {
5004 .in.domain_handle = r->in.domain_handle,
5005 .in.level = r->in.level,
5006 .in.start_idx = r->in.start_idx,
5007 .in.max_entries = r->in.max_entries,
5008 .in.buf_size = r->in.buf_size,
5009 .out.total_size = r->out.total_size,
5010 .out.returned_size = r->out.returned_size,
5011 .out.info = r->out.info,
5014 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
5016 return result;
5021 samr_AddMultipleMembersToAlias
5023 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5024 struct samr_AddMultipleMembersToAlias *r)
5026 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5031 samr_RemoveMultipleMembersFromAlias
5033 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5034 struct samr_RemoveMultipleMembersFromAlias *r)
5036 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5041 samr_GetDomPwInfo
5043 this fetches the default password properties for a domain
5045 note that w2k3 completely ignores the domain name in this call, and
5046 always returns the information for the servers primary domain
5048 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5049 struct samr_GetDomPwInfo *r)
5051 struct ldb_message **msgs;
5052 int ret;
5053 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
5054 struct ldb_context *sam_ctx;
5056 ZERO_STRUCTP(r->out.info);
5058 sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
5059 if (sam_ctx == NULL) {
5060 return NT_STATUS_INVALID_SYSTEM_SERVICE;
5063 /* The domain name in this call is ignored */
5064 ret = gendb_search_dn(sam_ctx,
5065 mem_ctx, NULL, &msgs, attrs);
5066 if (ret <= 0) {
5067 talloc_free(sam_ctx);
5069 return NT_STATUS_NO_SUCH_DOMAIN;
5071 if (ret > 1) {
5072 talloc_free(msgs);
5073 talloc_free(sam_ctx);
5075 return NT_STATUS_INTERNAL_DB_CORRUPTION;
5078 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
5079 "minPwdLength", 0);
5080 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
5081 "pwdProperties", 1);
5083 talloc_free(msgs);
5084 talloc_unlink(mem_ctx, sam_ctx);
5086 return NT_STATUS_OK;
5091 samr_Connect2
5093 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5094 struct samr_Connect2 *r)
5096 struct samr_Connect c;
5098 c = (struct samr_Connect) {
5099 .in.system_name = NULL,
5100 .in.access_mask = r->in.access_mask,
5101 .out.connect_handle = r->out.connect_handle,
5104 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5109 samr_SetUserInfo2
5111 just an alias for samr_SetUserInfo
5113 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5114 struct samr_SetUserInfo2 *r)
5116 struct samr_SetUserInfo r2;
5118 r2 = (struct samr_SetUserInfo) {
5119 .in.user_handle = r->in.user_handle,
5120 .in.level = r->in.level,
5121 .in.info = r->in.info,
5124 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
5129 samr_SetBootKeyInformation
5131 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5132 struct samr_SetBootKeyInformation *r)
5134 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5139 samr_GetBootKeyInformation
5141 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5142 struct samr_GetBootKeyInformation *r)
5144 /* Windows Server 2008 returns this */
5145 return NT_STATUS_NOT_SUPPORTED;
5150 samr_Connect3
5152 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5153 struct samr_Connect3 *r)
5155 struct samr_Connect c;
5157 c = (struct samr_Connect) {
5158 .in.system_name = NULL,
5159 .in.access_mask = r->in.access_mask,
5160 .out.connect_handle = r->out.connect_handle,
5163 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5168 samr_Connect4
5170 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5171 struct samr_Connect4 *r)
5173 struct samr_Connect c;
5175 c = (struct samr_Connect) {
5176 .in.system_name = NULL,
5177 .in.access_mask = r->in.access_mask,
5178 .out.connect_handle = r->out.connect_handle,
5181 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5186 samr_Connect5
5188 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5189 struct samr_Connect5 *r)
5191 struct samr_Connect c;
5192 NTSTATUS status;
5194 c = (struct samr_Connect) {
5195 .in.system_name = NULL,
5196 .in.access_mask = r->in.access_mask,
5197 .out.connect_handle = r->out.connect_handle,
5200 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5202 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
5203 r->out.info_out->info1.supported_features = 0;
5204 *r->out.level_out = r->in.level_in;
5206 return status;
5211 samr_RidToSid
5213 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5214 struct samr_RidToSid *r)
5216 struct samr_domain_state *d_state;
5217 struct dcesrv_handle *h;
5219 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
5221 d_state = h->data;
5223 /* form the users SID */
5224 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
5225 if (!*r->out.sid) {
5226 return NT_STATUS_NO_MEMORY;
5229 return NT_STATUS_OK;
5234 samr_SetDsrmPassword
5236 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5237 struct samr_SetDsrmPassword *r)
5239 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5244 samr_ValidatePassword
5246 For now the call checks the password complexity (if active) and the minimum
5247 password length on level 2 and 3. Level 1 is ignored for now.
5249 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
5250 TALLOC_CTX *mem_ctx,
5251 struct samr_ValidatePassword *r)
5253 struct samr_GetDomPwInfo r2 = {};
5254 struct samr_PwInfo pwInfo = {};
5255 const char *account = NULL;
5256 DATA_BLOB password;
5257 enum samr_ValidationStatus res;
5258 NTSTATUS status;
5259 enum dcerpc_transport_t transport =
5260 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
5261 enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
5263 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
5264 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5267 dcesrv_call_auth_info(dce_call, NULL, &auth_level);
5268 if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
5269 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5272 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
5274 r2 = (struct samr_GetDomPwInfo) {
5275 .in.domain_name = NULL,
5276 .out.info = &pwInfo,
5279 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
5280 if (!NT_STATUS_IS_OK(status)) {
5281 return status;
5284 switch (r->in.level) {
5285 case NetValidateAuthentication:
5286 /* we don't support this yet */
5287 return NT_STATUS_NOT_SUPPORTED;
5288 break;
5289 case NetValidatePasswordChange:
5290 account = r->in.req->req2.account.string;
5291 password = data_blob_const(r->in.req->req2.password.string,
5292 r->in.req->req2.password.length);
5293 res = samdb_check_password(mem_ctx,
5294 dce_call->conn->dce_ctx->lp_ctx,
5295 account,
5296 NULL, /* userPrincipalName */
5297 NULL, /* displayName/full_name */
5298 &password,
5299 pwInfo.password_properties,
5300 pwInfo.min_password_length);
5301 (*r->out.rep)->ctr2.status = res;
5302 break;
5303 case NetValidatePasswordReset:
5304 account = r->in.req->req3.account.string;
5305 password = data_blob_const(r->in.req->req3.password.string,
5306 r->in.req->req3.password.length);
5307 res = samdb_check_password(mem_ctx,
5308 dce_call->conn->dce_ctx->lp_ctx,
5309 account,
5310 NULL, /* userPrincipalName */
5311 NULL, /* displayName/full_name */
5312 &password,
5313 pwInfo.password_properties,
5314 pwInfo.min_password_length);
5315 (*r->out.rep)->ctr3.status = res;
5316 break;
5317 default:
5318 return NT_STATUS_INVALID_INFO_CLASS;
5319 break;
5322 return NT_STATUS_OK;
5325 static void dcesrv_samr_Opnum68NotUsedOnWire(struct dcesrv_call_state *dce_call,
5326 TALLOC_CTX *mem_ctx,
5327 struct samr_Opnum68NotUsedOnWire *r)
5329 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5332 static void dcesrv_samr_Opnum69NotUsedOnWire(struct dcesrv_call_state *dce_call,
5333 TALLOC_CTX *mem_ctx,
5334 struct samr_Opnum69NotUsedOnWire *r)
5336 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5339 static void dcesrv_samr_Opnum70NotUsedOnWire(struct dcesrv_call_state *dce_call,
5340 TALLOC_CTX *mem_ctx,
5341 struct samr_Opnum70NotUsedOnWire *r)
5343 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5346 static void dcesrv_samr_Opnum71NotUsedOnWire(struct dcesrv_call_state *dce_call,
5347 TALLOC_CTX *mem_ctx,
5348 struct samr_Opnum71NotUsedOnWire *r)
5350 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5353 static void dcesrv_samr_Opnum72NotUsedOnWire(struct dcesrv_call_state *dce_call,
5354 TALLOC_CTX *mem_ctx,
5355 struct samr_Opnum72NotUsedOnWire *r)
5357 DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5360 /* include the generated boilerplate */
5361 #include "librpc/gen_ndr/ndr_samr_s.c"