s4:dsdb:util: export SAMBA_CPS_{ACCOUNT,USER_PRINCIPAL,FULL}_NAME for check password...
[Samba.git] / source4 / rpc_server / samr / dcesrv_samr.c
blob84400284f42c793b3c89f7c533a1dab15cebbfa6
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 #define DCESRV_INTERFACE_SAMR_BIND(context, iface) \
45 dcesrv_interface_samr_bind(context, iface)
46 static NTSTATUS dcesrv_interface_samr_bind(struct dcesrv_connection_context *context,
47 const struct dcesrv_interface *iface)
49 return dcesrv_interface_bind_reject_connect(context, iface);
52 /* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
54 #define QUERY_STRING(msg, field, attr) \
55 info->field.string = ldb_msg_find_attr_as_string(msg, attr, "");
56 #define QUERY_UINT(msg, field, attr) \
57 info->field = ldb_msg_find_attr_as_uint(msg, attr, 0);
58 #define QUERY_RID(msg, field, attr) \
59 info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
60 #define QUERY_UINT64(msg, field, attr) \
61 info->field = ldb_msg_find_attr_as_uint64(msg, attr, 0);
62 #define QUERY_APASSC(msg, field, attr) \
63 info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
64 a_state->domain_state->domain_dn, msg, attr);
65 #define QUERY_BPWDCT(msg, field, attr) \
66 info->field = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, \
67 a_state->domain_state->domain_dn, msg);
68 #define QUERY_LHOURS(msg, field, attr) \
69 info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
70 #define QUERY_AFLAGS(msg, field, attr) \
71 info->field = samdb_result_acct_flags(msg, attr);
74 /* these are used to make the Set[User|Group]Info code easier to follow */
76 #define SET_STRING(msg, field, attr) do { \
77 struct ldb_message_element *set_el; \
78 if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
79 if (r->in.info->field.string[0] == '\0') { \
80 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
81 return NT_STATUS_NO_MEMORY; \
82 } \
83 } \
84 if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
85 return NT_STATUS_NO_MEMORY; \
86 } \
87 set_el = ldb_msg_find_element(msg, attr); \
88 set_el->flags = LDB_FLAG_MOD_REPLACE; \
89 } while (0)
91 #define SET_UINT(msg, field, attr) do { \
92 struct ldb_message_element *set_el; \
93 if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
94 return NT_STATUS_NO_MEMORY; \
95 } \
96 set_el = ldb_msg_find_element(msg, attr); \
97 set_el->flags = LDB_FLAG_MOD_REPLACE; \
98 } while (0)
100 #define SET_INT64(msg, field, attr) do { \
101 struct ldb_message_element *set_el; \
102 if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
103 return NT_STATUS_NO_MEMORY; \
105 set_el = ldb_msg_find_element(msg, attr); \
106 set_el->flags = LDB_FLAG_MOD_REPLACE; \
107 } while (0)
109 #define SET_UINT64(msg, field, attr) do { \
110 struct ldb_message_element *set_el; \
111 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
112 return NT_STATUS_NO_MEMORY; \
114 set_el = ldb_msg_find_element(msg, attr); \
115 set_el->flags = LDB_FLAG_MOD_REPLACE; \
116 } while (0)
118 /* Set account flags, discarding flags that cannot be set with SAMR */
119 #define SET_AFLAGS(msg, field, attr) do { \
120 struct ldb_message_element *set_el; \
121 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
122 return NT_STATUS_NO_MEMORY; \
124 set_el = ldb_msg_find_element(msg, attr); \
125 set_el->flags = LDB_FLAG_MOD_REPLACE; \
126 } while (0)
128 #define SET_LHOURS(msg, field, attr) do { \
129 struct ldb_message_element *set_el; \
130 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
131 return NT_STATUS_NO_MEMORY; \
133 set_el = ldb_msg_find_element(msg, attr); \
134 set_el->flags = LDB_FLAG_MOD_REPLACE; \
135 } while (0)
137 #define SET_PARAMETERS(msg, field, attr) do { \
138 struct ldb_message_element *set_el; \
139 if (r->in.info->field.length != 0) { \
140 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
141 return NT_STATUS_NO_MEMORY; \
143 set_el = ldb_msg_find_element(msg, attr); \
144 set_el->flags = LDB_FLAG_MOD_REPLACE; \
146 } while (0)
149 * Clear a GUID cache
151 static void clear_guid_cache(struct samr_guid_cache *cache)
153 cache->handle = 0;
154 cache->size = 0;
155 TALLOC_FREE(cache->entries);
159 * initialize a GUID cache
161 static void initialize_guid_cache(struct samr_guid_cache *cache)
163 cache->handle = 0;
164 cache->size = 0;
165 cache->entries = NULL;
168 static NTSTATUS load_guid_cache(
169 struct samr_guid_cache *cache,
170 struct samr_domain_state *d_state,
171 unsigned int ldb_cnt,
172 struct ldb_message **res)
174 NTSTATUS status = NT_STATUS_OK;
175 unsigned int i;
176 TALLOC_CTX *frame = talloc_stackframe();
178 clear_guid_cache(cache);
181 * Store the GUID's in the cache.
183 cache->handle = 0;
184 cache->size = ldb_cnt;
185 cache->entries = talloc_array(d_state, struct GUID, ldb_cnt);
186 if (cache->entries == NULL) {
187 clear_guid_cache(cache);
188 status = NT_STATUS_NO_MEMORY;
189 goto exit;
193 * Extract a list of the GUIDs for all the matching objects
194 * we cache just the GUIDS to reduce the memory overhead of
195 * the result cache.
197 for (i = 0; i < ldb_cnt; i++) {
198 cache->entries[i] = samdb_result_guid(res[i], "objectGUID");
200 exit:
201 TALLOC_FREE(frame);
202 return status;
206 samr_Connect
208 create a connection to the SAM database
210 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
211 struct samr_Connect *r)
213 struct auth_session_info *session_info =
214 dcesrv_call_session_info(dce_call);
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 = samdb_connect(c_state,
227 dce_call->event_ctx,
228 dce_call->conn->dce_ctx->lp_ctx,
229 session_info,
230 dce_call->conn->remote_address,
232 if (c_state->sam_ctx == NULL) {
233 talloc_free(c_state);
234 return NT_STATUS_INVALID_SYSTEM_SERVICE;
238 handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_CONNECT);
239 if (!handle) {
240 talloc_free(c_state);
241 return NT_STATUS_NO_MEMORY;
244 handle->data = talloc_steal(handle, c_state);
246 c_state->access_mask = r->in.access_mask;
247 *r->out.connect_handle = handle->wire_handle;
249 return NT_STATUS_OK;
254 samr_Close
256 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
257 struct samr_Close *r)
259 struct dcesrv_handle *h;
261 *r->out.handle = *r->in.handle;
263 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
265 talloc_free(h);
267 ZERO_STRUCTP(r->out.handle);
269 return NT_STATUS_OK;
274 samr_SetSecurity
276 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
277 struct samr_SetSecurity *r)
279 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
284 samr_QuerySecurity
286 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
287 struct samr_QuerySecurity *r)
289 struct dcesrv_handle *h;
290 struct sec_desc_buf *sd;
292 *r->out.sdbuf = NULL;
294 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
296 sd = talloc(mem_ctx, struct sec_desc_buf);
297 if (sd == NULL) {
298 return NT_STATUS_NO_MEMORY;
301 sd->sd = samdb_default_security_descriptor(mem_ctx);
303 *r->out.sdbuf = sd;
305 return NT_STATUS_OK;
310 samr_Shutdown
312 we refuse this operation completely. If a admin wants to shutdown samr
313 in Samba then they should use the samba admin tools to disable the samr pipe
315 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
316 struct samr_Shutdown *r)
318 return NT_STATUS_ACCESS_DENIED;
323 samr_LookupDomain
325 this maps from a domain name to a SID
327 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
328 struct samr_LookupDomain *r)
330 struct samr_connect_state *c_state;
331 struct dcesrv_handle *h;
332 struct dom_sid *sid;
333 const char * const dom_attrs[] = { "objectSid", NULL};
334 struct ldb_message **dom_msgs;
335 int ret;
337 *r->out.sid = NULL;
339 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
341 c_state = h->data;
343 if (r->in.domain_name->string == NULL) {
344 return NT_STATUS_INVALID_PARAMETER;
347 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
348 ret = gendb_search(c_state->sam_ctx,
349 mem_ctx, NULL, &dom_msgs, dom_attrs,
350 "(objectClass=builtinDomain)");
351 } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
352 ret = gendb_search_dn(c_state->sam_ctx,
353 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
354 &dom_msgs, dom_attrs);
355 } else {
356 return NT_STATUS_NO_SUCH_DOMAIN;
358 if (ret != 1) {
359 return NT_STATUS_NO_SUCH_DOMAIN;
362 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
363 "objectSid");
365 if (sid == NULL) {
366 return NT_STATUS_NO_SUCH_DOMAIN;
369 *r->out.sid = sid;
371 return NT_STATUS_OK;
376 samr_EnumDomains
378 list the domains in the SAM
380 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
381 struct samr_EnumDomains *r)
383 struct dcesrv_handle *h;
384 struct samr_SamArray *array;
385 uint32_t i, start_i;
387 *r->out.resume_handle = 0;
388 *r->out.sam = NULL;
389 *r->out.num_entries = 0;
391 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
393 *r->out.resume_handle = 2;
395 start_i = *r->in.resume_handle;
397 if (start_i >= 2) {
398 /* search past end of list is not an error for this call */
399 return NT_STATUS_OK;
402 array = talloc(mem_ctx, struct samr_SamArray);
403 if (array == NULL) {
404 return NT_STATUS_NO_MEMORY;
407 array->count = 0;
408 array->entries = NULL;
410 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
411 if (array->entries == NULL) {
412 return NT_STATUS_NO_MEMORY;
415 for (i=0;i<2-start_i;i++) {
416 array->entries[i].idx = start_i + i;
417 if (i == 0) {
418 array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
419 } else {
420 array->entries[i].name.string = "BUILTIN";
424 *r->out.sam = array;
425 *r->out.num_entries = i;
426 array->count = *r->out.num_entries;
428 return NT_STATUS_OK;
433 samr_OpenDomain
435 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
436 struct samr_OpenDomain *r)
438 struct dcesrv_handle *h_conn, *h_domain;
439 struct samr_connect_state *c_state;
440 struct samr_domain_state *d_state;
441 const char * const dom_attrs[] = { "cn", NULL};
442 struct ldb_message **dom_msgs;
443 int ret;
444 unsigned int i;
446 ZERO_STRUCTP(r->out.domain_handle);
448 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
450 c_state = h_conn->data;
452 if (r->in.sid == NULL) {
453 return NT_STATUS_INVALID_PARAMETER;
456 d_state = talloc(mem_ctx, struct samr_domain_state);
457 if (!d_state) {
458 return NT_STATUS_NO_MEMORY;
461 d_state->domain_sid = talloc_steal(d_state, r->in.sid);
463 if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
464 d_state->builtin = true;
465 d_state->domain_name = "BUILTIN";
466 } else {
467 d_state->builtin = false;
468 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
471 ret = gendb_search(c_state->sam_ctx,
472 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
473 "(objectSid=%s)",
474 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
476 if (ret == 0) {
477 talloc_free(d_state);
478 return NT_STATUS_NO_SUCH_DOMAIN;
479 } else if (ret > 1) {
480 talloc_free(d_state);
481 return NT_STATUS_INTERNAL_DB_CORRUPTION;
482 } else if (ret == -1) {
483 talloc_free(d_state);
484 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
485 return NT_STATUS_INTERNAL_DB_CORRUPTION;
488 d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
489 d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
490 d_state->connect_state = talloc_reference(d_state, c_state);
491 d_state->sam_ctx = c_state->sam_ctx;
492 d_state->access_mask = r->in.access_mask;
494 d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
496 for (i = 0; i < SAMR_LAST_CACHE; i++) {
497 initialize_guid_cache(&d_state->guid_caches[i]);
500 h_domain = dcesrv_handle_create(dce_call, SAMR_HANDLE_DOMAIN);
501 if (!h_domain) {
502 talloc_free(d_state);
503 return NT_STATUS_NO_MEMORY;
506 h_domain->data = talloc_steal(h_domain, d_state);
508 *r->out.domain_handle = h_domain->wire_handle;
510 return NT_STATUS_OK;
514 return DomInfo1
516 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
517 TALLOC_CTX *mem_ctx,
518 struct ldb_message **dom_msgs,
519 struct samr_DomInfo1 *info)
521 info->min_password_length =
522 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
523 info->password_history_length =
524 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
525 info->password_properties =
526 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
527 info->max_password_age =
528 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
529 info->min_password_age =
530 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
532 return NT_STATUS_OK;
536 return DomInfo2
538 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
539 TALLOC_CTX *mem_ctx,
540 struct ldb_message **dom_msgs,
541 struct samr_DomGeneralInformation *info)
543 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
544 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
545 "domainReplica",
546 "");
548 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
549 0x8000000000000000LL);
551 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
552 "oEMInformation",
553 "");
554 info->domain_name.string = state->domain_name;
556 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
558 switch (state->role) {
559 case ROLE_ACTIVE_DIRECTORY_DC:
560 /* This pulls the NetBIOS name from the
561 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
562 string */
563 if (samdb_is_pdc(state->sam_ctx)) {
564 info->role = SAMR_ROLE_DOMAIN_PDC;
565 } else {
566 info->role = SAMR_ROLE_DOMAIN_BDC;
568 break;
569 case ROLE_DOMAIN_PDC:
570 case ROLE_DOMAIN_BDC:
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;
581 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx,
582 state->domain_dn,
583 "(objectClass=user)");
584 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx,
585 state->domain_dn,
586 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
587 GTYPE_SECURITY_UNIVERSAL_GROUP,
588 GTYPE_SECURITY_GLOBAL_GROUP);
589 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx,
590 state->domain_dn,
591 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
592 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
593 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
595 return NT_STATUS_OK;
599 return DomInfo3
601 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
602 TALLOC_CTX *mem_ctx,
603 struct ldb_message **dom_msgs,
604 struct samr_DomInfo3 *info)
606 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
607 0x8000000000000000LL);
609 return NT_STATUS_OK;
613 return DomInfo4
615 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
616 TALLOC_CTX *mem_ctx,
617 struct ldb_message **dom_msgs,
618 struct samr_DomOEMInformation *info)
620 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
621 "oEMInformation",
622 "");
624 return NT_STATUS_OK;
628 return DomInfo5
630 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
631 TALLOC_CTX *mem_ctx,
632 struct ldb_message **dom_msgs,
633 struct samr_DomInfo5 *info)
635 info->domain_name.string = state->domain_name;
637 return NT_STATUS_OK;
641 return DomInfo6
643 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
644 TALLOC_CTX *mem_ctx,
645 struct ldb_message **dom_msgs,
646 struct samr_DomInfo6 *info)
648 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
649 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
650 "domainReplica",
651 "");
653 return NT_STATUS_OK;
657 return DomInfo7
659 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
660 TALLOC_CTX *mem_ctx,
661 struct ldb_message **dom_msgs,
662 struct samr_DomInfo7 *info)
665 switch (state->role) {
666 case ROLE_ACTIVE_DIRECTORY_DC:
667 /* This pulls the NetBIOS name from the
668 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
669 string */
670 if (samdb_is_pdc(state->sam_ctx)) {
671 info->role = SAMR_ROLE_DOMAIN_PDC;
672 } else {
673 info->role = SAMR_ROLE_DOMAIN_BDC;
675 break;
676 case ROLE_DOMAIN_PDC:
677 case ROLE_DOMAIN_BDC:
678 case ROLE_AUTO:
679 return NT_STATUS_INTERNAL_ERROR;
680 case ROLE_DOMAIN_MEMBER:
681 info->role = SAMR_ROLE_DOMAIN_MEMBER;
682 break;
683 case ROLE_STANDALONE:
684 info->role = SAMR_ROLE_STANDALONE;
685 break;
688 return NT_STATUS_OK;
692 return DomInfo8
694 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
695 TALLOC_CTX *mem_ctx,
696 struct ldb_message **dom_msgs,
697 struct samr_DomInfo8 *info)
699 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
700 time(NULL));
702 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
703 0x0LL);
705 return NT_STATUS_OK;
709 return DomInfo9
711 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
712 TALLOC_CTX *mem_ctx,
713 struct ldb_message **dom_msgs,
714 struct samr_DomInfo9 *info)
716 info->domain_server_state = DOMAIN_SERVER_ENABLED;
718 return NT_STATUS_OK;
722 return DomInfo11
724 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
725 TALLOC_CTX *mem_ctx,
726 struct ldb_message **dom_msgs,
727 struct samr_DomGeneralInformation2 *info)
729 NTSTATUS status;
730 status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
731 if (!NT_STATUS_IS_OK(status)) {
732 return status;
735 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
736 -18000000000LL);
737 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
738 -18000000000LL);
739 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
741 return NT_STATUS_OK;
745 return DomInfo12
747 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
748 TALLOC_CTX *mem_ctx,
749 struct ldb_message **dom_msgs,
750 struct samr_DomInfo12 *info)
752 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
753 -18000000000LL);
754 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
755 -18000000000LL);
756 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
758 return NT_STATUS_OK;
762 return DomInfo13
764 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
765 TALLOC_CTX *mem_ctx,
766 struct ldb_message **dom_msgs,
767 struct samr_DomInfo13 *info)
769 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
770 time(NULL));
772 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
773 0x0LL);
775 info->modified_count_at_last_promotion = 0;
777 return NT_STATUS_OK;
781 samr_QueryDomainInfo
783 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
784 TALLOC_CTX *mem_ctx,
785 struct samr_QueryDomainInfo *r)
787 struct dcesrv_handle *h;
788 struct samr_domain_state *d_state;
789 union samr_DomainInfo *info;
791 struct ldb_message **dom_msgs;
792 const char * const *attrs = NULL;
794 *r->out.info = NULL;
796 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
798 d_state = h->data;
800 switch (r->in.level) {
801 case 1:
803 static const char * const attrs2[] = { "minPwdLength",
804 "pwdHistoryLength",
805 "pwdProperties",
806 "maxPwdAge",
807 "minPwdAge",
808 NULL };
809 attrs = attrs2;
810 break;
812 case 2:
814 static const char * const attrs2[] = {"forceLogoff",
815 "oEMInformation",
816 "modifiedCount",
817 "domainReplica",
818 NULL};
819 attrs = attrs2;
820 break;
822 case 3:
824 static const char * const attrs2[] = {"forceLogoff",
825 NULL};
826 attrs = attrs2;
827 break;
829 case 4:
831 static const char * const attrs2[] = {"oEMInformation",
832 NULL};
833 attrs = attrs2;
834 break;
836 case 5:
838 attrs = NULL;
839 break;
841 case 6:
843 static const char * const attrs2[] = { "domainReplica",
844 NULL };
845 attrs = attrs2;
846 break;
848 case 7:
850 attrs = NULL;
851 break;
853 case 8:
855 static const char * const attrs2[] = { "modifiedCount",
856 "creationTime",
857 NULL };
858 attrs = attrs2;
859 break;
861 case 9:
863 attrs = NULL;
864 break;
866 case 11:
868 static const char * const attrs2[] = { "oEMInformation",
869 "forceLogoff",
870 "modifiedCount",
871 "lockoutDuration",
872 "lockOutObservationWindow",
873 "lockoutThreshold",
874 NULL};
875 attrs = attrs2;
876 break;
878 case 12:
880 static const char * const attrs2[] = { "lockoutDuration",
881 "lockOutObservationWindow",
882 "lockoutThreshold",
883 NULL};
884 attrs = attrs2;
885 break;
887 case 13:
889 static const char * const attrs2[] = { "modifiedCount",
890 "creationTime",
891 NULL };
892 attrs = attrs2;
893 break;
895 default:
897 return NT_STATUS_INVALID_INFO_CLASS;
901 /* some levels don't need a search */
902 if (attrs) {
903 int ret;
904 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
905 d_state->domain_dn, &dom_msgs, attrs);
906 if (ret == 0) {
907 return NT_STATUS_NO_SUCH_DOMAIN;
909 if (ret != 1) {
910 return NT_STATUS_INTERNAL_DB_CORRUPTION;
914 /* allocate the info structure */
915 info = talloc_zero(mem_ctx, union samr_DomainInfo);
916 if (info == NULL) {
917 return NT_STATUS_NO_MEMORY;
920 *r->out.info = info;
922 switch (r->in.level) {
923 case 1:
924 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
925 &info->info1);
926 case 2:
927 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
928 &info->general);
929 case 3:
930 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
931 &info->info3);
932 case 4:
933 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
934 &info->oem);
935 case 5:
936 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
937 &info->info5);
938 case 6:
939 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
940 &info->info6);
941 case 7:
942 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
943 &info->info7);
944 case 8:
945 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
946 &info->info8);
947 case 9:
948 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
949 &info->info9);
950 case 11:
951 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
952 &info->general2);
953 case 12:
954 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
955 &info->info12);
956 case 13:
957 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
958 &info->info13);
959 default:
960 return NT_STATUS_INVALID_INFO_CLASS;
966 samr_SetDomainInfo
968 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
969 struct samr_SetDomainInfo *r)
971 struct dcesrv_handle *h;
972 struct samr_domain_state *d_state;
973 struct ldb_message *msg;
974 int ret;
975 struct ldb_context *sam_ctx;
977 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
979 d_state = h->data;
980 sam_ctx = d_state->sam_ctx;
982 msg = ldb_msg_new(mem_ctx);
983 if (msg == NULL) {
984 return NT_STATUS_NO_MEMORY;
987 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
988 if (!msg->dn) {
989 return NT_STATUS_NO_MEMORY;
992 switch (r->in.level) {
993 case 1:
994 SET_UINT (msg, info1.min_password_length, "minPwdLength");
995 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
996 SET_UINT (msg, info1.password_properties, "pwdProperties");
997 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
998 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
999 break;
1000 case 3:
1001 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
1002 break;
1003 case 4:
1004 SET_STRING(msg, oem.oem_information, "oEMInformation");
1005 break;
1007 case 6:
1008 case 7:
1009 case 9:
1010 /* No op, we don't know where to set these */
1011 return NT_STATUS_OK;
1013 case 12:
1015 * It is not possible to set lockout_duration < lockout_window.
1016 * (The test is the other way around since the negative numbers
1017 * are stored...)
1019 * TODO:
1020 * This check should be moved to the backend, i.e. to some
1021 * ldb module under dsdb/samdb/ldb_modules/ .
1023 * This constraint is documented here for the samr rpc service:
1024 * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
1025 * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
1027 * And here for the ldap backend:
1028 * MS-ADTS 3.1.1.5.3.2 Constraints
1029 * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
1031 if (r->in.info->info12.lockout_duration >
1032 r->in.info->info12.lockout_window)
1034 return NT_STATUS_INVALID_PARAMETER;
1036 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
1037 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
1038 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
1039 break;
1041 default:
1042 /* many info classes are not valid for SetDomainInfo */
1043 return NT_STATUS_INVALID_INFO_CLASS;
1046 /* modify the samdb record */
1047 ret = ldb_modify(sam_ctx, msg);
1048 if (ret != LDB_SUCCESS) {
1049 DEBUG(1,("Failed to modify record %s: %s\n",
1050 ldb_dn_get_linearized(d_state->domain_dn),
1051 ldb_errstring(sam_ctx)));
1052 return dsdb_ldb_err_to_ntstatus(ret);
1055 return NT_STATUS_OK;
1059 samr_CreateDomainGroup
1061 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1062 struct samr_CreateDomainGroup *r)
1064 NTSTATUS status;
1065 struct samr_domain_state *d_state;
1066 struct samr_account_state *a_state;
1067 struct dcesrv_handle *h;
1068 const char *groupname;
1069 struct dom_sid *group_sid;
1070 struct ldb_dn *group_dn;
1071 struct dcesrv_handle *g_handle;
1073 ZERO_STRUCTP(r->out.group_handle);
1074 *r->out.rid = 0;
1076 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1078 d_state = h->data;
1080 if (d_state->builtin) {
1081 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
1082 return NT_STATUS_ACCESS_DENIED;
1085 groupname = r->in.name->string;
1087 if (groupname == NULL) {
1088 return NT_STATUS_INVALID_PARAMETER;
1091 status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1092 if (!NT_STATUS_IS_OK(status)) {
1093 return status;
1096 a_state = talloc(mem_ctx, struct samr_account_state);
1097 if (!a_state) {
1098 return NT_STATUS_NO_MEMORY;
1100 a_state->sam_ctx = d_state->sam_ctx;
1101 a_state->access_mask = r->in.access_mask;
1102 a_state->domain_state = talloc_reference(a_state, d_state);
1103 a_state->account_dn = talloc_steal(a_state, group_dn);
1105 a_state->account_name = talloc_steal(a_state, groupname);
1107 /* create the policy handle */
1108 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
1109 if (!g_handle) {
1110 return NT_STATUS_NO_MEMORY;
1113 g_handle->data = talloc_steal(g_handle, a_state);
1115 *r->out.group_handle = g_handle->wire_handle;
1116 *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1118 return NT_STATUS_OK;
1123 comparison function for sorting SamEntry array
1125 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1127 return e1->idx - e2->idx;
1130 static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) {
1131 struct dom_sid *sid1 = NULL;
1132 struct dom_sid *sid2 = NULL;
1133 uint32_t rid1;
1134 uint32_t rid2;
1135 int res = 0;
1136 NTSTATUS status;
1137 TALLOC_CTX *frame = talloc_stackframe();
1139 sid1 = samdb_result_dom_sid(frame, *m1, "objectSid");
1140 sid2 = samdb_result_dom_sid(frame, *m2, "objectSid");
1143 * If entries don't have a SID we want to sort them to the end of
1144 * the list.
1146 if (sid1 == NULL && sid2 == NULL) {
1147 res = 0;
1148 goto exit;
1149 } else if (sid2 == NULL) {
1150 res = 1;
1151 goto exit;
1152 } else if (sid1 == NULL) {
1153 res = -1;
1154 goto exit;
1158 * Get and compare the rids, if we fail to extract a rid treat it as a
1159 * missing SID and sort to the end of the list
1161 status = dom_sid_split_rid(NULL, sid1, NULL, &rid1);
1162 if (!NT_STATUS_IS_OK(status)) {
1163 res = 1;
1164 goto exit;
1167 status = dom_sid_split_rid(NULL, sid2, NULL, &rid2);
1168 if (!NT_STATUS_IS_OK(status)) {
1169 res = -1;
1170 goto exit;
1173 if (rid1 == rid2) {
1174 res = 0;
1176 else if (rid1 > rid2) {
1177 res = 1;
1179 else {
1180 res = -1;
1182 exit:
1183 TALLOC_FREE(frame);
1184 return res;
1188 samr_EnumDomainGroups
1190 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1191 struct samr_EnumDomainGroups *r)
1193 struct dcesrv_handle *h;
1194 struct samr_domain_state *d_state;
1195 struct ldb_message **res;
1196 uint32_t i;
1197 uint32_t count;
1198 uint32_t results;
1199 uint32_t max_entries;
1200 uint32_t remaining_entries;
1201 uint32_t resume_handle;
1202 struct samr_SamEntry *entries;
1203 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1204 const char * const cache_attrs[] = { "objectSid", "objectGUID", NULL };
1205 struct samr_SamArray *sam;
1206 struct samr_guid_cache *cache = NULL;
1208 *r->out.resume_handle = 0;
1209 *r->out.sam = NULL;
1210 *r->out.num_entries = 0;
1212 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1214 d_state = h->data;
1215 cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_GROUPS_CACHE];
1218 * If the resume_handle is zero, query the database and cache the
1219 * matching GUID's
1221 if (*r->in.resume_handle == 0) {
1222 NTSTATUS status;
1223 int ldb_cnt;
1224 clear_guid_cache(cache);
1226 * search for all domain groups in this domain.
1228 ldb_cnt = samdb_search_domain(
1229 d_state->sam_ctx,
1230 mem_ctx,
1231 d_state->domain_dn,
1232 &res,
1233 cache_attrs,
1234 d_state->domain_sid,
1235 "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1236 GTYPE_SECURITY_UNIVERSAL_GROUP,
1237 GTYPE_SECURITY_GLOBAL_GROUP);
1238 if (ldb_cnt < 0) {
1239 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1242 * Sort the results into RID order, while the spec states there
1243 * is no order, Windows appears to sort the results by RID and
1244 * so it is possible that there are clients that depend on
1245 * this ordering
1247 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1250 * cache the sorted GUID's
1252 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1253 TALLOC_FREE(res);
1254 if (!NT_STATUS_IS_OK(status)) {
1255 return status;
1257 cache->handle = 0;
1262 * If the resume handle is out of range we return an empty response
1263 * and invalidate the cache.
1265 * From the specification:
1266 * Servers SHOULD validate that EnumerationContext is an expected
1267 * value for the server's implementation. Windows does NOT validate
1268 * the input, though the result of malformed information merely results
1269 * in inconsistent output to the client.
1271 if (*r->in.resume_handle >= cache->size) {
1272 clear_guid_cache(cache);
1273 sam = talloc(mem_ctx, struct samr_SamArray);
1274 if (!sam) {
1275 return NT_STATUS_NO_MEMORY;
1277 sam->entries = NULL;
1278 sam->count = 0;
1280 *r->out.sam = sam;
1281 *r->out.resume_handle = 0;
1282 return NT_STATUS_OK;
1287 * Calculate the number of entries to return limit by max_size.
1288 * Note that we use the w2k3 element size value of 54
1290 max_entries = 1 + (r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER);
1291 remaining_entries = cache->size - *r->in.resume_handle;
1292 results = MIN(remaining_entries, max_entries);
1295 * Process the list of result GUID's.
1296 * Read the details of each object and populate the Entries
1297 * for the current level.
1299 count = 0;
1300 resume_handle = *r->in.resume_handle;
1301 entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1302 if (entries == NULL) {
1303 clear_guid_cache(cache);
1304 return NT_STATUS_NO_MEMORY;
1306 for (i = 0; i < results; i++) {
1307 struct dom_sid *objectsid;
1308 uint32_t rid;
1309 struct ldb_result *rec;
1310 const uint32_t idx = *r->in.resume_handle + i;
1311 int ret;
1312 NTSTATUS status;
1313 const char *name = NULL;
1314 resume_handle++;
1316 * Read an object from disk using the GUID as the key
1318 * If the object can not be read, or it does not have a SID
1319 * it is ignored.
1321 * As a consequence of this, if all the remaining GUID's
1322 * have been deleted an empty result will be returned.
1323 * i.e. even if the previous call returned a non zero
1324 * resume_handle it is possible for no results to be returned.
1327 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1328 mem_ctx,
1329 &rec,
1330 &cache->entries[idx],
1331 attrs,
1333 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1334 struct GUID_txt_buf guid_buf;
1335 DBG_WARNING(
1336 "GUID [%s] not found\n",
1337 GUID_buf_string(&cache->entries[idx], &guid_buf));
1338 continue;
1339 } else if (ret != LDB_SUCCESS) {
1340 clear_guid_cache(cache);
1341 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1344 objectsid = samdb_result_dom_sid(mem_ctx,
1345 rec->msgs[0],
1346 "objectSID");
1347 if (objectsid == NULL) {
1348 struct GUID_txt_buf guid_buf;
1349 DBG_WARNING(
1350 "objectSID for GUID [%s] not found\n",
1351 GUID_buf_string(&cache->entries[idx], &guid_buf));
1352 continue;
1354 status = dom_sid_split_rid(NULL,
1355 objectsid,
1356 NULL,
1357 &rid);
1358 if (!NT_STATUS_IS_OK(status)) {
1359 struct dom_sid_buf sid_buf;
1360 struct GUID_txt_buf guid_buf;
1361 DBG_WARNING(
1362 "objectSID [%s] for GUID [%s] invalid\n",
1363 dom_sid_str_buf(objectsid, &sid_buf),
1364 GUID_buf_string(&cache->entries[idx], &guid_buf));
1365 continue;
1368 entries[count].idx = rid;
1369 name = ldb_msg_find_attr_as_string(
1370 rec->msgs[0], "sAMAccountName", "");
1371 entries[count].name.string = talloc_strdup(entries, name);
1372 count++;
1375 sam = talloc(mem_ctx, struct samr_SamArray);
1376 if (!sam) {
1377 clear_guid_cache(cache);
1378 return NT_STATUS_NO_MEMORY;
1381 sam->entries = entries;
1382 sam->count = count;
1384 *r->out.sam = sam;
1385 *r->out.resume_handle = resume_handle;
1386 *r->out.num_entries = count;
1389 * Signal no more results by returning zero resume handle,
1390 * the cache is also cleared at this point
1392 if (*r->out.resume_handle >= cache->size) {
1393 *r->out.resume_handle = 0;
1394 clear_guid_cache(cache);
1395 return NT_STATUS_OK;
1398 * There are more results to be returned.
1400 return STATUS_MORE_ENTRIES;
1405 samr_CreateUser2
1407 This call uses transactions to ensure we don't get a new conflicting
1408 user while we are processing this, and to ensure the user either
1409 completly exists, or does not.
1411 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1412 struct samr_CreateUser2 *r)
1414 NTSTATUS status;
1415 struct samr_domain_state *d_state;
1416 struct samr_account_state *a_state;
1417 struct dcesrv_handle *h;
1418 struct ldb_dn *dn;
1419 struct dom_sid *sid;
1420 struct dcesrv_handle *u_handle;
1421 const char *account_name;
1423 ZERO_STRUCTP(r->out.user_handle);
1424 *r->out.access_granted = 0;
1425 *r->out.rid = 0;
1427 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1429 d_state = h->data;
1431 if (d_state->builtin) {
1432 DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
1433 return NT_STATUS_ACCESS_DENIED;
1434 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1435 /* Domain trust accounts must be created by the LSA calls */
1436 return NT_STATUS_ACCESS_DENIED;
1438 account_name = r->in.account_name->string;
1440 if (account_name == NULL) {
1441 return NT_STATUS_INVALID_PARAMETER;
1444 status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1445 &sid, &dn);
1446 if (!NT_STATUS_IS_OK(status)) {
1447 return status;
1449 a_state = talloc(mem_ctx, struct samr_account_state);
1450 if (!a_state) {
1451 return NT_STATUS_NO_MEMORY;
1453 a_state->sam_ctx = d_state->sam_ctx;
1454 a_state->access_mask = r->in.access_mask;
1455 a_state->domain_state = talloc_reference(a_state, d_state);
1456 a_state->account_dn = talloc_steal(a_state, dn);
1458 a_state->account_name = talloc_steal(a_state, account_name);
1459 if (!a_state->account_name) {
1460 return NT_STATUS_NO_MEMORY;
1463 /* create the policy handle */
1464 u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
1465 if (!u_handle) {
1466 return NT_STATUS_NO_MEMORY;
1469 u_handle->data = talloc_steal(u_handle, a_state);
1471 *r->out.user_handle = u_handle->wire_handle;
1472 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1474 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1476 return NT_STATUS_OK;
1481 samr_CreateUser
1483 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1484 struct samr_CreateUser *r)
1486 struct samr_CreateUser2 r2;
1487 uint32_t access_granted = 0;
1490 /* a simple wrapper around samr_CreateUser2 works nicely */
1492 r2 = (struct samr_CreateUser2) {
1493 .in.domain_handle = r->in.domain_handle,
1494 .in.account_name = r->in.account_name,
1495 .in.acct_flags = ACB_NORMAL,
1496 .in.access_mask = r->in.access_mask,
1497 .out.user_handle = r->out.user_handle,
1498 .out.access_granted = &access_granted,
1499 .out.rid = r->out.rid
1502 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1506 samr_EnumDomainUsers
1508 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1509 struct samr_EnumDomainUsers *r)
1511 struct dcesrv_handle *h;
1512 struct samr_domain_state *d_state;
1513 struct ldb_message **res;
1514 uint32_t i;
1515 uint32_t count;
1516 uint32_t results;
1517 uint32_t max_entries;
1518 uint32_t remaining_entries;
1519 uint32_t resume_handle;
1520 struct samr_SamEntry *entries;
1521 const char * const attrs[] = { "objectSid", "sAMAccountName",
1522 "userAccountControl", NULL };
1523 const char *const cache_attrs[] = {"objectSid", "objectGUID", NULL};
1524 struct samr_SamArray *sam;
1525 struct samr_guid_cache *cache = NULL;
1527 *r->out.resume_handle = 0;
1528 *r->out.sam = NULL;
1529 *r->out.num_entries = 0;
1531 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1533 d_state = h->data;
1534 cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_USERS_CACHE];
1537 * If the resume_handle is zero, query the database and cache the
1538 * matching GUID's
1540 if (*r->in.resume_handle == 0) {
1541 NTSTATUS status;
1542 int ldb_cnt;
1543 clear_guid_cache(cache);
1545 * search for all domain users in this domain.
1547 ldb_cnt = samdb_search_domain(d_state->sam_ctx,
1548 mem_ctx,
1549 d_state->domain_dn,
1550 &res,
1551 cache_attrs,
1552 d_state->domain_sid,
1553 "(objectClass=user)");
1554 if (ldb_cnt < 0) {
1555 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1558 * Sort the results into RID order, while the spec states there
1559 * is no order, Windows appears to sort the results by RID and
1560 * so it is possible that there are clients that depend on
1561 * this ordering
1563 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1566 * cache the sorted GUID's
1568 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1569 TALLOC_FREE(res);
1570 if (!NT_STATUS_IS_OK(status)) {
1571 return status;
1573 cache->handle = 0;
1577 * If the resume handle is out of range we return an empty response
1578 * and invalidate the cache.
1580 * From the specification:
1581 * Servers SHOULD validate that EnumerationContext is an expected
1582 * value for the server's implementation. Windows does NOT validate
1583 * the input, though the result of malformed information merely results
1584 * in inconsistent output to the client.
1586 if (*r->in.resume_handle >= cache->size) {
1587 clear_guid_cache(cache);
1588 sam = talloc(mem_ctx, struct samr_SamArray);
1589 if (!sam) {
1590 return NT_STATUS_NO_MEMORY;
1592 sam->entries = NULL;
1593 sam->count = 0;
1595 *r->out.sam = sam;
1596 *r->out.resume_handle = 0;
1597 return NT_STATUS_OK;
1601 * Calculate the number of entries to return limit by max_size.
1602 * Note that we use the w2k3 element size value of 54
1604 max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER);
1605 remaining_entries = cache->size - *r->in.resume_handle;
1606 results = MIN(remaining_entries, max_entries);
1609 * Process the list of result GUID's.
1610 * Read the details of each object and populate the Entries
1611 * for the current level.
1613 count = 0;
1614 resume_handle = *r->in.resume_handle;
1615 entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1616 if (entries == NULL) {
1617 clear_guid_cache(cache);
1618 return NT_STATUS_NO_MEMORY;
1620 for (i = 0; i < results; i++) {
1621 struct dom_sid *objectsid;
1622 uint32_t rid;
1623 struct ldb_result *rec;
1624 const uint32_t idx = *r->in.resume_handle + i;
1625 int ret;
1626 NTSTATUS status;
1627 const char *name = NULL;
1629 resume_handle++;
1631 * Read an object from disk using the GUID as the key
1633 * If the object can not be read, or it does not have a SID
1634 * it is ignored.
1636 * As a consequence of this, if all the remaining GUID's
1637 * have been deleted an empty result will be returned.
1638 * i.e. even if the previous call returned a non zero
1639 * resume_handle it is possible for no results to be returned.
1642 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1643 mem_ctx,
1644 &rec,
1645 &cache->entries[idx],
1646 attrs,
1648 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1649 struct GUID_txt_buf guid_buf;
1650 DBG_WARNING(
1651 "GUID [%s] not found\n",
1652 GUID_buf_string(&cache->entries[idx], &guid_buf));
1653 continue;
1654 } else if (ret != LDB_SUCCESS) {
1655 clear_guid_cache(cache);
1656 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1658 objectsid = samdb_result_dom_sid(mem_ctx,
1659 rec->msgs[0],
1660 "objectSID");
1661 if (objectsid == NULL) {
1662 struct GUID_txt_buf guid_buf;
1663 DBG_WARNING(
1664 "objectSID for GUID [%s] not found\n",
1665 GUID_buf_string(&cache->entries[idx], &guid_buf));
1666 continue;
1668 if (r->in.acct_flags &&
1669 ((samdb_result_acct_flags(rec->msgs[0], NULL) &
1670 r->in.acct_flags) == 0)) {
1671 continue;
1673 status = dom_sid_split_rid(NULL,
1674 objectsid,
1675 NULL,
1676 &rid);
1677 if (!NT_STATUS_IS_OK(status)) {
1678 struct dom_sid_buf sid_buf;
1679 struct GUID_txt_buf guid_buf;
1680 DBG_WARNING(
1681 "objectSID [%s] for GUID [%s] invalid\n",
1682 dom_sid_str_buf(objectsid, &sid_buf),
1683 GUID_buf_string(&cache->entries[idx], &guid_buf));
1684 continue;
1687 entries[count].idx = rid;
1688 name = ldb_msg_find_attr_as_string(
1689 rec->msgs[0], "sAMAccountName", "");
1690 entries[count].name.string = talloc_strdup(entries, name);
1691 count++;
1694 sam = talloc(mem_ctx, struct samr_SamArray);
1695 if (!sam) {
1696 clear_guid_cache(cache);
1697 return NT_STATUS_NO_MEMORY;
1700 sam->entries = entries;
1701 sam->count = count;
1703 *r->out.sam = sam;
1704 *r->out.resume_handle = resume_handle;
1705 *r->out.num_entries = count;
1708 * Signal no more results by returning zero resume handle,
1709 * the cache is also cleared at this point
1711 if (*r->out.resume_handle >= cache->size) {
1712 *r->out.resume_handle = 0;
1713 clear_guid_cache(cache);
1714 return NT_STATUS_OK;
1717 * There are more results to be returned.
1719 return STATUS_MORE_ENTRIES;
1724 samr_CreateDomAlias
1726 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1727 struct samr_CreateDomAlias *r)
1729 struct samr_domain_state *d_state;
1730 struct samr_account_state *a_state;
1731 struct dcesrv_handle *h;
1732 const char *alias_name;
1733 struct dom_sid *sid;
1734 struct dcesrv_handle *a_handle;
1735 struct ldb_dn *dn;
1736 NTSTATUS status;
1738 ZERO_STRUCTP(r->out.alias_handle);
1739 *r->out.rid = 0;
1741 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1743 d_state = h->data;
1745 if (d_state->builtin) {
1746 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1747 return NT_STATUS_ACCESS_DENIED;
1750 alias_name = r->in.alias_name->string;
1752 if (alias_name == NULL) {
1753 return NT_STATUS_INVALID_PARAMETER;
1756 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1757 if (!NT_STATUS_IS_OK(status)) {
1758 return status;
1761 a_state = talloc(mem_ctx, struct samr_account_state);
1762 if (!a_state) {
1763 return NT_STATUS_NO_MEMORY;
1766 a_state->sam_ctx = d_state->sam_ctx;
1767 a_state->access_mask = r->in.access_mask;
1768 a_state->domain_state = talloc_reference(a_state, d_state);
1769 a_state->account_dn = talloc_steal(a_state, dn);
1771 a_state->account_name = talloc_steal(a_state, alias_name);
1773 /* create the policy handle */
1774 a_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
1775 if (a_handle == NULL)
1776 return NT_STATUS_NO_MEMORY;
1778 a_handle->data = talloc_steal(a_handle, a_state);
1780 *r->out.alias_handle = a_handle->wire_handle;
1782 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1784 return NT_STATUS_OK;
1789 samr_EnumDomainAliases
1791 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1792 struct samr_EnumDomainAliases *r)
1794 struct dcesrv_handle *h;
1795 struct samr_domain_state *d_state;
1796 struct ldb_message **res;
1797 int i, ldb_cnt;
1798 uint32_t first, count;
1799 struct samr_SamEntry *entries;
1800 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1801 struct samr_SamArray *sam;
1803 *r->out.resume_handle = 0;
1804 *r->out.sam = NULL;
1805 *r->out.num_entries = 0;
1807 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1809 d_state = h->data;
1811 /* search for all domain aliases in this domain. This could possibly be
1812 cached and resumed based on resume_key */
1813 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1814 &res, attrs,
1815 d_state->domain_sid,
1816 "(&(|(grouptype=%d)(grouptype=%d)))"
1817 "(objectclass=group))",
1818 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1819 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1820 if (ldb_cnt < 0) {
1821 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1824 /* convert to SamEntry format */
1825 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1826 if (!entries) {
1827 return NT_STATUS_NO_MEMORY;
1830 count = 0;
1832 for (i=0;i<ldb_cnt;i++) {
1833 struct dom_sid *alias_sid;
1835 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1836 "objectSid");
1838 if (alias_sid == NULL) {
1839 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1842 entries[count].idx =
1843 alias_sid->sub_auths[alias_sid->num_auths-1];
1844 entries[count].name.string =
1845 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1846 count += 1;
1849 /* sort the results by rid */
1850 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1852 /* find the first entry to return */
1853 for (first=0;
1854 first<count && entries[first].idx <= *r->in.resume_handle;
1855 first++) ;
1857 /* return the rest, limit by max_size. Note that we
1858 use the w2k3 element size value of 54 */
1859 *r->out.num_entries = count - first;
1860 *r->out.num_entries = MIN(*r->out.num_entries,
1861 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1863 sam = talloc(mem_ctx, struct samr_SamArray);
1864 if (!sam) {
1865 return NT_STATUS_NO_MEMORY;
1868 sam->entries = entries+first;
1869 sam->count = *r->out.num_entries;
1871 *r->out.sam = sam;
1873 if (first == count) {
1874 return NT_STATUS_OK;
1877 if (*r->out.num_entries < count - first) {
1878 *r->out.resume_handle =
1879 entries[first+*r->out.num_entries-1].idx;
1880 return STATUS_MORE_ENTRIES;
1883 return NT_STATUS_OK;
1888 samr_GetAliasMembership
1890 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1891 struct samr_GetAliasMembership *r)
1893 struct dcesrv_handle *h;
1894 struct samr_domain_state *d_state;
1895 char *filter;
1896 const char * const attrs[] = { "objectSid", NULL };
1897 struct ldb_message **res;
1898 uint32_t i;
1899 int count = 0;
1901 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1903 d_state = h->data;
1905 filter = talloc_asprintf(mem_ctx,
1906 "(&(|(grouptype=%d)(grouptype=%d))"
1907 "(objectclass=group)(|",
1908 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1909 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1910 if (filter == NULL) {
1911 return NT_STATUS_NO_MEMORY;
1914 for (i=0; i<r->in.sids->num_sids; i++) {
1915 struct dom_sid_buf buf;
1917 filter = talloc_asprintf_append(
1918 filter,
1919 "(member=<SID=%s>)",
1920 dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
1922 if (filter == NULL) {
1923 return NT_STATUS_NO_MEMORY;
1927 /* Find out if we had at least one valid member SID passed - otherwise
1928 * just skip the search. */
1929 if (strstr(filter, "member") != NULL) {
1930 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1931 &res, attrs, d_state->domain_sid,
1932 "%s))", filter);
1933 if (count < 0) {
1934 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1938 r->out.rids->count = 0;
1939 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1940 if (r->out.rids->ids == NULL)
1941 return NT_STATUS_NO_MEMORY;
1943 for (i=0; i<count; i++) {
1944 struct dom_sid *alias_sid;
1946 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1947 if (alias_sid == NULL) {
1948 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1951 r->out.rids->ids[r->out.rids->count] =
1952 alias_sid->sub_auths[alias_sid->num_auths-1];
1953 r->out.rids->count += 1;
1956 return NT_STATUS_OK;
1961 samr_LookupNames
1963 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1964 struct samr_LookupNames *r)
1966 struct dcesrv_handle *h;
1967 struct samr_domain_state *d_state;
1968 uint32_t i, num_mapped;
1969 NTSTATUS status = NT_STATUS_OK;
1970 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1971 int count;
1973 ZERO_STRUCTP(r->out.rids);
1974 ZERO_STRUCTP(r->out.types);
1976 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1978 d_state = h->data;
1980 if (r->in.num_names == 0) {
1981 return NT_STATUS_OK;
1984 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1985 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1986 if (!r->out.rids->ids || !r->out.types->ids) {
1987 return NT_STATUS_NO_MEMORY;
1989 r->out.rids->count = r->in.num_names;
1990 r->out.types->count = r->in.num_names;
1992 num_mapped = 0;
1994 for (i=0;i<r->in.num_names;i++) {
1995 struct ldb_message **res;
1996 struct dom_sid *sid;
1997 uint32_t atype, rtype;
1999 r->out.rids->ids[i] = 0;
2000 r->out.types->ids[i] = SID_NAME_UNKNOWN;
2002 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
2003 "sAMAccountName=%s",
2004 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
2005 if (count != 1) {
2006 status = STATUS_SOME_UNMAPPED;
2007 continue;
2010 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
2011 if (sid == NULL) {
2012 status = STATUS_SOME_UNMAPPED;
2013 continue;
2016 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
2017 if (atype == 0) {
2018 status = STATUS_SOME_UNMAPPED;
2019 continue;
2022 rtype = ds_atype_map(atype);
2024 if (rtype == SID_NAME_UNKNOWN) {
2025 status = STATUS_SOME_UNMAPPED;
2026 continue;
2029 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
2030 r->out.types->ids[i] = rtype;
2031 num_mapped++;
2034 if (num_mapped == 0) {
2035 return NT_STATUS_NONE_MAPPED;
2037 return status;
2042 samr_LookupRids
2044 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2045 struct samr_LookupRids *r)
2047 NTSTATUS status;
2048 struct dcesrv_handle *h;
2049 struct samr_domain_state *d_state;
2050 const char **names;
2051 struct lsa_String *lsa_names;
2052 enum lsa_SidType *ids;
2054 ZERO_STRUCTP(r->out.names);
2055 ZERO_STRUCTP(r->out.types);
2057 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2059 d_state = h->data;
2061 if (r->in.num_rids == 0)
2062 return NT_STATUS_OK;
2064 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
2065 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
2066 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
2068 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
2069 return NT_STATUS_NO_MEMORY;
2071 r->out.names->names = lsa_names;
2072 r->out.names->count = r->in.num_rids;
2074 r->out.types->ids = (uint32_t *) ids;
2075 r->out.types->count = r->in.num_rids;
2077 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
2078 r->in.num_rids, r->in.rids, names, ids);
2079 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
2080 uint32_t i;
2081 for (i = 0; i < r->in.num_rids; i++) {
2082 lsa_names[i].string = names[i];
2085 return status;
2090 samr_OpenGroup
2092 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2093 struct samr_OpenGroup *r)
2095 struct samr_domain_state *d_state;
2096 struct samr_account_state *a_state;
2097 struct dcesrv_handle *h;
2098 const char *groupname;
2099 struct dom_sid *sid;
2100 struct ldb_message **msgs;
2101 struct dcesrv_handle *g_handle;
2102 const char * const attrs[2] = { "sAMAccountName", NULL };
2103 int ret;
2105 ZERO_STRUCTP(r->out.group_handle);
2107 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2109 d_state = h->data;
2111 /* form the group SID */
2112 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2113 if (!sid) {
2114 return NT_STATUS_NO_MEMORY;
2117 /* search for the group record */
2118 if (d_state->builtin) {
2119 ret = gendb_search(d_state->sam_ctx,
2120 mem_ctx, d_state->domain_dn, &msgs, attrs,
2121 "(&(objectSid=%s)(objectClass=group)"
2122 "(groupType=%d))",
2123 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2124 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
2125 } else {
2126 ret = gendb_search(d_state->sam_ctx,
2127 mem_ctx, d_state->domain_dn, &msgs, attrs,
2128 "(&(objectSid=%s)(objectClass=group)"
2129 "(|(groupType=%d)(groupType=%d)))",
2130 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2131 GTYPE_SECURITY_UNIVERSAL_GROUP,
2132 GTYPE_SECURITY_GLOBAL_GROUP);
2134 if (ret == 0) {
2135 return NT_STATUS_NO_SUCH_GROUP;
2137 if (ret != 1) {
2138 DEBUG(0,("Found %d records matching sid %s\n",
2139 ret, dom_sid_string(mem_ctx, sid)));
2140 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2143 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2144 if (groupname == NULL) {
2145 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2146 dom_sid_string(mem_ctx, sid)));
2147 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2150 a_state = talloc(mem_ctx, struct samr_account_state);
2151 if (!a_state) {
2152 return NT_STATUS_NO_MEMORY;
2154 a_state->sam_ctx = d_state->sam_ctx;
2155 a_state->access_mask = r->in.access_mask;
2156 a_state->domain_state = talloc_reference(a_state, d_state);
2157 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2158 a_state->account_sid = talloc_steal(a_state, sid);
2159 a_state->account_name = talloc_strdup(a_state, groupname);
2160 if (!a_state->account_name) {
2161 return NT_STATUS_NO_MEMORY;
2164 /* create the policy handle */
2165 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
2166 if (!g_handle) {
2167 return NT_STATUS_NO_MEMORY;
2170 g_handle->data = talloc_steal(g_handle, a_state);
2172 *r->out.group_handle = g_handle->wire_handle;
2174 return NT_STATUS_OK;
2178 samr_QueryGroupInfo
2180 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2181 struct samr_QueryGroupInfo *r)
2183 struct dcesrv_handle *h;
2184 struct samr_account_state *a_state;
2185 struct ldb_message *msg, **res;
2186 const char * const attrs[4] = { "sAMAccountName", "description",
2187 "numMembers", NULL };
2188 int ret;
2189 union samr_GroupInfo *info;
2191 *r->out.info = NULL;
2193 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2195 a_state = h->data;
2197 /* pull all the group attributes */
2198 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2199 a_state->account_dn, &res, attrs);
2200 if (ret == 0) {
2201 return NT_STATUS_NO_SUCH_GROUP;
2203 if (ret != 1) {
2204 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2206 msg = res[0];
2208 /* allocate the info structure */
2209 info = talloc_zero(mem_ctx, union samr_GroupInfo);
2210 if (info == NULL) {
2211 return NT_STATUS_NO_MEMORY;
2214 /* Fill in the level */
2215 switch (r->in.level) {
2216 case GROUPINFOALL:
2217 QUERY_STRING(msg, all.name, "sAMAccountName");
2218 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2219 QUERY_UINT (msg, all.num_members, "numMembers")
2220 QUERY_STRING(msg, all.description, "description");
2221 break;
2222 case GROUPINFONAME:
2223 QUERY_STRING(msg, name, "sAMAccountName");
2224 break;
2225 case GROUPINFOATTRIBUTES:
2226 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2227 break;
2228 case GROUPINFODESCRIPTION:
2229 QUERY_STRING(msg, description, "description");
2230 break;
2231 case GROUPINFOALL2:
2232 QUERY_STRING(msg, all2.name, "sAMAccountName");
2233 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2234 QUERY_UINT (msg, all2.num_members, "numMembers")
2235 QUERY_STRING(msg, all2.description, "description");
2236 break;
2237 default:
2238 talloc_free(info);
2239 return NT_STATUS_INVALID_INFO_CLASS;
2242 *r->out.info = info;
2244 return NT_STATUS_OK;
2249 samr_SetGroupInfo
2251 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2252 struct samr_SetGroupInfo *r)
2254 struct dcesrv_handle *h;
2255 struct samr_account_state *g_state;
2256 struct ldb_message *msg;
2257 int ret;
2259 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2261 g_state = h->data;
2263 msg = ldb_msg_new(mem_ctx);
2264 if (msg == NULL) {
2265 return NT_STATUS_NO_MEMORY;
2268 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2269 if (!msg->dn) {
2270 return NT_STATUS_NO_MEMORY;
2273 switch (r->in.level) {
2274 case GROUPINFODESCRIPTION:
2275 SET_STRING(msg, description, "description");
2276 break;
2277 case GROUPINFONAME:
2278 /* On W2k3 this does not change the name, it changes the
2279 * sAMAccountName attribute */
2280 SET_STRING(msg, name, "sAMAccountName");
2281 break;
2282 case GROUPINFOATTRIBUTES:
2283 /* This does not do anything obviously visible in W2k3 LDAP */
2284 return NT_STATUS_OK;
2285 default:
2286 return NT_STATUS_INVALID_INFO_CLASS;
2289 /* modify the samdb record */
2290 ret = ldb_modify(g_state->sam_ctx, msg);
2291 if (ret != LDB_SUCCESS) {
2292 return dsdb_ldb_err_to_ntstatus(ret);
2295 return NT_STATUS_OK;
2300 samr_AddGroupMember
2302 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2303 struct samr_AddGroupMember *r)
2305 struct dcesrv_handle *h;
2306 struct samr_account_state *a_state;
2307 struct samr_domain_state *d_state;
2308 struct ldb_message *mod;
2309 struct dom_sid *membersid;
2310 const char *memberdn;
2311 struct ldb_result *res;
2312 const char * const attrs[] = { NULL };
2313 int ret;
2315 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2317 a_state = h->data;
2318 d_state = a_state->domain_state;
2320 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2321 if (membersid == NULL) {
2322 return NT_STATUS_NO_MEMORY;
2325 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2326 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2327 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2328 "(objectSid=%s)",
2329 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2331 if (ret != LDB_SUCCESS) {
2332 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2335 if (res->count == 0) {
2336 return NT_STATUS_NO_SUCH_USER;
2339 if (res->count > 1) {
2340 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2343 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2345 if (memberdn == NULL)
2346 return NT_STATUS_NO_MEMORY;
2348 mod = ldb_msg_new(mem_ctx);
2349 if (mod == NULL) {
2350 return NT_STATUS_NO_MEMORY;
2353 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2355 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2356 memberdn);
2357 if (ret != LDB_SUCCESS) {
2358 return dsdb_ldb_err_to_ntstatus(ret);
2361 ret = ldb_modify(a_state->sam_ctx, mod);
2362 switch (ret) {
2363 case LDB_SUCCESS:
2364 return NT_STATUS_OK;
2365 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2366 return NT_STATUS_MEMBER_IN_GROUP;
2367 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2368 return NT_STATUS_ACCESS_DENIED;
2369 default:
2370 return dsdb_ldb_err_to_ntstatus(ret);
2376 samr_DeleteDomainGroup
2378 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2379 struct samr_DeleteDomainGroup *r)
2381 struct dcesrv_handle *h;
2382 struct samr_account_state *a_state;
2383 int ret;
2385 *r->out.group_handle = *r->in.group_handle;
2387 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2389 a_state = h->data;
2391 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2392 if (ret != LDB_SUCCESS) {
2393 return dsdb_ldb_err_to_ntstatus(ret);
2396 talloc_free(h);
2397 ZERO_STRUCTP(r->out.group_handle);
2399 return NT_STATUS_OK;
2404 samr_DeleteGroupMember
2406 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2407 struct samr_DeleteGroupMember *r)
2409 struct dcesrv_handle *h;
2410 struct samr_account_state *a_state;
2411 struct samr_domain_state *d_state;
2412 struct ldb_message *mod;
2413 struct dom_sid *membersid;
2414 const char *memberdn;
2415 struct ldb_result *res;
2416 const char * const attrs[] = { NULL };
2417 int ret;
2419 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2421 a_state = h->data;
2422 d_state = a_state->domain_state;
2424 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2425 if (membersid == NULL) {
2426 return NT_STATUS_NO_MEMORY;
2429 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2430 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2431 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2432 "(objectSid=%s)",
2433 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2435 if (ret != LDB_SUCCESS) {
2436 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2439 if (res->count == 0) {
2440 return NT_STATUS_NO_SUCH_USER;
2443 if (res->count > 1) {
2444 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2447 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2449 if (memberdn == NULL)
2450 return NT_STATUS_NO_MEMORY;
2452 mod = ldb_msg_new(mem_ctx);
2453 if (mod == NULL) {
2454 return NT_STATUS_NO_MEMORY;
2457 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2459 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2460 memberdn);
2461 if (ret != LDB_SUCCESS) {
2462 return NT_STATUS_NO_MEMORY;
2465 ret = ldb_modify(a_state->sam_ctx, mod);
2466 switch (ret) {
2467 case LDB_SUCCESS:
2468 return NT_STATUS_OK;
2469 case LDB_ERR_UNWILLING_TO_PERFORM:
2470 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2471 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2472 return NT_STATUS_ACCESS_DENIED;
2473 default:
2474 return dsdb_ldb_err_to_ntstatus(ret);
2480 samr_QueryGroupMember
2482 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2483 struct samr_QueryGroupMember *r)
2485 struct dcesrv_handle *h;
2486 struct samr_account_state *a_state;
2487 struct samr_domain_state *d_state;
2488 struct samr_RidAttrArray *array;
2489 unsigned int i, num_members;
2490 struct dom_sid *members;
2491 NTSTATUS status;
2493 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2495 a_state = h->data;
2496 d_state = a_state->domain_state;
2498 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2499 a_state->account_dn, &members,
2500 &num_members);
2501 if (!NT_STATUS_IS_OK(status)) {
2502 return status;
2505 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2506 if (array == NULL) {
2507 return NT_STATUS_NO_MEMORY;
2510 if (num_members == 0) {
2511 *r->out.rids = array;
2513 return NT_STATUS_OK;
2516 array->rids = talloc_array(array, uint32_t, num_members);
2517 if (array->rids == NULL) {
2518 return NT_STATUS_NO_MEMORY;
2521 array->attributes = talloc_array(array, uint32_t, num_members);
2522 if (array->attributes == NULL) {
2523 return NT_STATUS_NO_MEMORY;
2526 array->count = 0;
2527 for (i=0; i<num_members; i++) {
2528 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2529 continue;
2532 status = dom_sid_split_rid(NULL, &members[i], NULL,
2533 &array->rids[array->count]);
2534 if (!NT_STATUS_IS_OK(status)) {
2535 return status;
2538 array->attributes[array->count] = SE_GROUP_MANDATORY |
2539 SE_GROUP_ENABLED_BY_DEFAULT |
2540 SE_GROUP_ENABLED;
2541 array->count++;
2544 *r->out.rids = array;
2546 return NT_STATUS_OK;
2551 samr_SetMemberAttributesOfGroup
2553 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2554 struct samr_SetMemberAttributesOfGroup *r)
2556 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2561 samr_OpenAlias
2563 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2564 struct samr_OpenAlias *r)
2566 struct samr_domain_state *d_state;
2567 struct samr_account_state *a_state;
2568 struct dcesrv_handle *h;
2569 const char *alias_name;
2570 struct dom_sid *sid;
2571 struct ldb_message **msgs;
2572 struct dcesrv_handle *g_handle;
2573 const char * const attrs[2] = { "sAMAccountName", NULL };
2574 int ret;
2576 ZERO_STRUCTP(r->out.alias_handle);
2578 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2580 d_state = h->data;
2582 /* form the alias SID */
2583 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2584 if (sid == NULL)
2585 return NT_STATUS_NO_MEMORY;
2587 /* search for the group record */
2588 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2589 "(&(objectSid=%s)(objectclass=group)"
2590 "(|(grouptype=%d)(grouptype=%d)))",
2591 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2592 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2593 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2594 if (ret == 0) {
2595 return NT_STATUS_NO_SUCH_ALIAS;
2597 if (ret != 1) {
2598 DEBUG(0,("Found %d records matching sid %s\n",
2599 ret, dom_sid_string(mem_ctx, sid)));
2600 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2603 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2604 if (alias_name == NULL) {
2605 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2606 dom_sid_string(mem_ctx, sid)));
2607 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2610 a_state = talloc(mem_ctx, struct samr_account_state);
2611 if (!a_state) {
2612 return NT_STATUS_NO_MEMORY;
2614 a_state->sam_ctx = d_state->sam_ctx;
2615 a_state->access_mask = r->in.access_mask;
2616 a_state->domain_state = talloc_reference(a_state, d_state);
2617 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2618 a_state->account_sid = talloc_steal(a_state, sid);
2619 a_state->account_name = talloc_strdup(a_state, alias_name);
2620 if (!a_state->account_name) {
2621 return NT_STATUS_NO_MEMORY;
2624 /* create the policy handle */
2625 g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
2626 if (!g_handle) {
2627 return NT_STATUS_NO_MEMORY;
2630 g_handle->data = talloc_steal(g_handle, a_state);
2632 *r->out.alias_handle = g_handle->wire_handle;
2634 return NT_STATUS_OK;
2639 samr_QueryAliasInfo
2641 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2642 struct samr_QueryAliasInfo *r)
2644 struct dcesrv_handle *h;
2645 struct samr_account_state *a_state;
2646 struct ldb_message *msg, **res;
2647 const char * const attrs[4] = { "sAMAccountName", "description",
2648 "numMembers", NULL };
2649 int ret;
2650 union samr_AliasInfo *info;
2652 *r->out.info = NULL;
2654 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2656 a_state = h->data;
2658 /* pull all the alias attributes */
2659 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2660 a_state->account_dn, &res, attrs);
2661 if (ret == 0) {
2662 return NT_STATUS_NO_SUCH_ALIAS;
2664 if (ret != 1) {
2665 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2667 msg = res[0];
2669 /* allocate the info structure */
2670 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2671 if (info == NULL) {
2672 return NT_STATUS_NO_MEMORY;
2675 switch(r->in.level) {
2676 case ALIASINFOALL:
2677 QUERY_STRING(msg, all.name, "sAMAccountName");
2678 QUERY_UINT (msg, all.num_members, "numMembers");
2679 QUERY_STRING(msg, all.description, "description");
2680 break;
2681 case ALIASINFONAME:
2682 QUERY_STRING(msg, name, "sAMAccountName");
2683 break;
2684 case ALIASINFODESCRIPTION:
2685 QUERY_STRING(msg, description, "description");
2686 break;
2687 default:
2688 talloc_free(info);
2689 return NT_STATUS_INVALID_INFO_CLASS;
2692 *r->out.info = info;
2694 return NT_STATUS_OK;
2699 samr_SetAliasInfo
2701 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2702 struct samr_SetAliasInfo *r)
2704 struct dcesrv_handle *h;
2705 struct samr_account_state *a_state;
2706 struct ldb_message *msg;
2707 int ret;
2709 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2711 a_state = h->data;
2713 msg = ldb_msg_new(mem_ctx);
2714 if (msg == NULL) {
2715 return NT_STATUS_NO_MEMORY;
2718 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2719 if (!msg->dn) {
2720 return NT_STATUS_NO_MEMORY;
2723 switch (r->in.level) {
2724 case ALIASINFODESCRIPTION:
2725 SET_STRING(msg, description, "description");
2726 break;
2727 case ALIASINFONAME:
2728 /* On W2k3 this does not change the name, it changes the
2729 * sAMAccountName attribute */
2730 SET_STRING(msg, name, "sAMAccountName");
2731 break;
2732 default:
2733 return NT_STATUS_INVALID_INFO_CLASS;
2736 /* modify the samdb record */
2737 ret = ldb_modify(a_state->sam_ctx, msg);
2738 if (ret != LDB_SUCCESS) {
2739 return dsdb_ldb_err_to_ntstatus(ret);
2742 return NT_STATUS_OK;
2747 samr_DeleteDomAlias
2749 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2750 struct samr_DeleteDomAlias *r)
2752 struct dcesrv_handle *h;
2753 struct samr_account_state *a_state;
2754 int ret;
2756 *r->out.alias_handle = *r->in.alias_handle;
2758 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2760 a_state = h->data;
2762 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2763 if (ret != LDB_SUCCESS) {
2764 return dsdb_ldb_err_to_ntstatus(ret);
2767 talloc_free(h);
2768 ZERO_STRUCTP(r->out.alias_handle);
2770 return NT_STATUS_OK;
2775 samr_AddAliasMember
2777 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2778 struct samr_AddAliasMember *r)
2780 struct dcesrv_handle *h;
2781 struct samr_account_state *a_state;
2782 struct samr_domain_state *d_state;
2783 struct ldb_message *mod;
2784 struct ldb_message **msgs;
2785 const char * const attrs[] = { NULL };
2786 struct ldb_dn *memberdn = NULL;
2787 int ret;
2788 NTSTATUS status;
2790 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2792 a_state = h->data;
2793 d_state = a_state->domain_state;
2795 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2796 &msgs, attrs, "(objectsid=%s)",
2797 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2799 if (ret == 1) {
2800 memberdn = msgs[0]->dn;
2801 } else if (ret == 0) {
2802 status = samdb_create_foreign_security_principal(
2803 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2804 if (!NT_STATUS_IS_OK(status)) {
2805 return status;
2807 } else {
2808 DEBUG(0,("Found %d records matching sid %s\n",
2809 ret, dom_sid_string(mem_ctx, r->in.sid)));
2810 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2813 if (memberdn == NULL) {
2814 DEBUG(0, ("Could not find memberdn\n"));
2815 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2818 mod = ldb_msg_new(mem_ctx);
2819 if (mod == NULL) {
2820 return NT_STATUS_NO_MEMORY;
2823 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2825 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2826 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2827 if (ret != LDB_SUCCESS) {
2828 return dsdb_ldb_err_to_ntstatus(ret);
2831 ret = ldb_modify(a_state->sam_ctx, mod);
2832 switch (ret) {
2833 case LDB_SUCCESS:
2834 return NT_STATUS_OK;
2835 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2836 return NT_STATUS_MEMBER_IN_GROUP;
2837 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2838 return NT_STATUS_ACCESS_DENIED;
2839 default:
2840 return dsdb_ldb_err_to_ntstatus(ret);
2846 samr_DeleteAliasMember
2848 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2849 struct samr_DeleteAliasMember *r)
2851 struct dcesrv_handle *h;
2852 struct samr_account_state *a_state;
2853 struct samr_domain_state *d_state;
2854 struct ldb_message *mod;
2855 const char *memberdn;
2856 int ret;
2858 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2860 a_state = h->data;
2861 d_state = a_state->domain_state;
2863 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2864 "distinguishedName", "(objectSid=%s)",
2865 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2866 if (memberdn == NULL) {
2867 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2870 mod = ldb_msg_new(mem_ctx);
2871 if (mod == NULL) {
2872 return NT_STATUS_NO_MEMORY;
2875 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2877 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2878 memberdn);
2879 if (ret != LDB_SUCCESS) {
2880 return dsdb_ldb_err_to_ntstatus(ret);
2883 ret = ldb_modify(a_state->sam_ctx, mod);
2884 switch (ret) {
2885 case LDB_SUCCESS:
2886 return NT_STATUS_OK;
2887 case LDB_ERR_UNWILLING_TO_PERFORM:
2888 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2889 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2890 return NT_STATUS_ACCESS_DENIED;
2891 default:
2892 return dsdb_ldb_err_to_ntstatus(ret);
2898 samr_GetMembersInAlias
2900 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2901 struct samr_GetMembersInAlias *r)
2903 struct dcesrv_handle *h;
2904 struct samr_account_state *a_state;
2905 struct samr_domain_state *d_state;
2906 struct lsa_SidPtr *array;
2907 unsigned int i, num_members;
2908 struct dom_sid *members;
2909 NTSTATUS status;
2911 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2913 a_state = h->data;
2914 d_state = a_state->domain_state;
2916 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2917 a_state->account_dn, &members,
2918 &num_members);
2919 if (!NT_STATUS_IS_OK(status)) {
2920 return status;
2923 if (num_members == 0) {
2924 r->out.sids->sids = NULL;
2926 return NT_STATUS_OK;
2929 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
2930 if (array == NULL) {
2931 return NT_STATUS_NO_MEMORY;
2934 for (i=0; i<num_members; i++) {
2935 array[i].sid = &members[i];
2938 r->out.sids->num_sids = num_members;
2939 r->out.sids->sids = array;
2941 return NT_STATUS_OK;
2945 samr_OpenUser
2947 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2948 struct samr_OpenUser *r)
2950 struct samr_domain_state *d_state;
2951 struct samr_account_state *a_state;
2952 struct dcesrv_handle *h;
2953 const char *account_name;
2954 struct dom_sid *sid;
2955 struct ldb_message **msgs;
2956 struct dcesrv_handle *u_handle;
2957 const char * const attrs[2] = { "sAMAccountName", NULL };
2958 int ret;
2960 ZERO_STRUCTP(r->out.user_handle);
2962 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2964 d_state = h->data;
2966 /* form the users SID */
2967 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2968 if (!sid) {
2969 return NT_STATUS_NO_MEMORY;
2972 /* search for the user record */
2973 ret = gendb_search(d_state->sam_ctx,
2974 mem_ctx, d_state->domain_dn, &msgs, attrs,
2975 "(&(objectSid=%s)(objectclass=user))",
2976 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2977 if (ret == 0) {
2978 return NT_STATUS_NO_SUCH_USER;
2980 if (ret != 1) {
2981 DEBUG(0,("Found %d records matching sid %s\n", ret,
2982 dom_sid_string(mem_ctx, sid)));
2983 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2986 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2987 if (account_name == NULL) {
2988 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2989 dom_sid_string(mem_ctx, sid)));
2990 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2993 a_state = talloc(mem_ctx, struct samr_account_state);
2994 if (!a_state) {
2995 return NT_STATUS_NO_MEMORY;
2997 a_state->sam_ctx = d_state->sam_ctx;
2998 a_state->access_mask = r->in.access_mask;
2999 a_state->domain_state = talloc_reference(a_state, d_state);
3000 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
3001 a_state->account_sid = talloc_steal(a_state, sid);
3002 a_state->account_name = talloc_strdup(a_state, account_name);
3003 if (!a_state->account_name) {
3004 return NT_STATUS_NO_MEMORY;
3007 /* create the policy handle */
3008 u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
3009 if (!u_handle) {
3010 return NT_STATUS_NO_MEMORY;
3013 u_handle->data = talloc_steal(u_handle, a_state);
3015 *r->out.user_handle = u_handle->wire_handle;
3017 return NT_STATUS_OK;
3023 samr_DeleteUser
3025 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3026 struct samr_DeleteUser *r)
3028 struct dcesrv_handle *h;
3029 struct samr_account_state *a_state;
3030 int ret;
3032 *r->out.user_handle = *r->in.user_handle;
3034 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3036 a_state = h->data;
3038 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
3039 if (ret != LDB_SUCCESS) {
3040 DEBUG(1, ("Failed to delete user: %s: %s\n",
3041 ldb_dn_get_linearized(a_state->account_dn),
3042 ldb_errstring(a_state->sam_ctx)));
3043 return dsdb_ldb_err_to_ntstatus(ret);
3046 talloc_free(h);
3047 ZERO_STRUCTP(r->out.user_handle);
3049 return NT_STATUS_OK;
3054 samr_QueryUserInfo
3056 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3057 struct samr_QueryUserInfo *r)
3059 struct dcesrv_handle *h;
3060 struct samr_account_state *a_state;
3061 struct ldb_message *msg, **res;
3062 int ret;
3063 struct ldb_context *sam_ctx;
3065 const char * const *attrs = NULL;
3066 union samr_UserInfo *info;
3068 NTSTATUS status;
3070 *r->out.info = NULL;
3072 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3074 a_state = h->data;
3075 sam_ctx = a_state->sam_ctx;
3077 /* fill in the reply */
3078 switch (r->in.level) {
3079 case 1:
3081 static const char * const attrs2[] = {"sAMAccountName",
3082 "displayName",
3083 "primaryGroupID",
3084 "description",
3085 "comment",
3086 NULL};
3087 attrs = attrs2;
3088 break;
3090 case 2:
3092 static const char * const attrs2[] = {"comment",
3093 "countryCode",
3094 "codePage",
3095 NULL};
3096 attrs = attrs2;
3097 break;
3099 case 3:
3101 static const char * const attrs2[] = {"sAMAccountName",
3102 "displayName",
3103 "objectSid",
3104 "primaryGroupID",
3105 "homeDirectory",
3106 "homeDrive",
3107 "scriptPath",
3108 "profilePath",
3109 "userWorkstations",
3110 "lastLogon",
3111 "lastLogoff",
3112 "pwdLastSet",
3113 "msDS-UserPasswordExpiryTimeComputed",
3114 "logonHours",
3115 "badPwdCount",
3116 "badPasswordTime",
3117 "logonCount",
3118 "userAccountControl",
3119 "msDS-User-Account-Control-Computed",
3120 NULL};
3121 attrs = attrs2;
3122 break;
3124 case 4:
3126 static const char * const attrs2[] = {"logonHours",
3127 NULL};
3128 attrs = attrs2;
3129 break;
3131 case 5:
3133 static const char * const attrs2[] = {"sAMAccountName",
3134 "displayName",
3135 "objectSid",
3136 "primaryGroupID",
3137 "homeDirectory",
3138 "homeDrive",
3139 "scriptPath",
3140 "profilePath",
3141 "description",
3142 "userWorkstations",
3143 "lastLogon",
3144 "lastLogoff",
3145 "logonHours",
3146 "badPwdCount",
3147 "badPasswordTime",
3148 "logonCount",
3149 "pwdLastSet",
3150 "msDS-ResultantPSO",
3151 "msDS-UserPasswordExpiryTimeComputed",
3152 "accountExpires",
3153 "userAccountControl",
3154 "msDS-User-Account-Control-Computed",
3155 NULL};
3156 attrs = attrs2;
3157 break;
3159 case 6:
3161 static const char * const attrs2[] = {"sAMAccountName",
3162 "displayName",
3163 NULL};
3164 attrs = attrs2;
3165 break;
3167 case 7:
3169 static const char * const attrs2[] = {"sAMAccountName",
3170 NULL};
3171 attrs = attrs2;
3172 break;
3174 case 8:
3176 static const char * const attrs2[] = {"displayName",
3177 NULL};
3178 attrs = attrs2;
3179 break;
3181 case 9:
3183 static const char * const attrs2[] = {"primaryGroupID",
3184 NULL};
3185 attrs = attrs2;
3186 break;
3188 case 10:
3190 static const char * const attrs2[] = {"homeDirectory",
3191 "homeDrive",
3192 NULL};
3193 attrs = attrs2;
3194 break;
3196 case 11:
3198 static const char * const attrs2[] = {"scriptPath",
3199 NULL};
3200 attrs = attrs2;
3201 break;
3203 case 12:
3205 static const char * const attrs2[] = {"profilePath",
3206 NULL};
3207 attrs = attrs2;
3208 break;
3210 case 13:
3212 static const char * const attrs2[] = {"description",
3213 NULL};
3214 attrs = attrs2;
3215 break;
3217 case 14:
3219 static const char * const attrs2[] = {"userWorkstations",
3220 NULL};
3221 attrs = attrs2;
3222 break;
3224 case 16:
3226 static const char * const attrs2[] = {"userAccountControl",
3227 "msDS-User-Account-Control-Computed",
3228 "pwdLastSet",
3229 "msDS-UserPasswordExpiryTimeComputed",
3230 NULL};
3231 attrs = attrs2;
3232 break;
3234 case 17:
3236 static const char * const attrs2[] = {"accountExpires",
3237 NULL};
3238 attrs = attrs2;
3239 break;
3241 case 18:
3243 return NT_STATUS_NOT_SUPPORTED;
3245 case 20:
3247 static const char * const attrs2[] = {"userParameters",
3248 NULL};
3249 attrs = attrs2;
3250 break;
3252 case 21:
3254 static const char * const attrs2[] = {"lastLogon",
3255 "lastLogoff",
3256 "pwdLastSet",
3257 "msDS-ResultantPSO",
3258 "msDS-UserPasswordExpiryTimeComputed",
3259 "accountExpires",
3260 "sAMAccountName",
3261 "displayName",
3262 "homeDirectory",
3263 "homeDrive",
3264 "scriptPath",
3265 "profilePath",
3266 "description",
3267 "userWorkstations",
3268 "comment",
3269 "userParameters",
3270 "objectSid",
3271 "primaryGroupID",
3272 "userAccountControl",
3273 "msDS-User-Account-Control-Computed",
3274 "logonHours",
3275 "badPwdCount",
3276 "badPasswordTime",
3277 "logonCount",
3278 "countryCode",
3279 "codePage",
3280 NULL};
3281 attrs = attrs2;
3282 break;
3284 case 23:
3285 case 24:
3286 case 25:
3287 case 26:
3289 return NT_STATUS_NOT_SUPPORTED;
3291 default:
3293 return NT_STATUS_INVALID_INFO_CLASS;
3297 /* pull all the user attributes */
3298 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3299 a_state->account_dn, &res, attrs);
3300 if (ret == 0) {
3301 return NT_STATUS_NO_SUCH_USER;
3303 if (ret != 1) {
3304 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3306 msg = res[0];
3308 /* allocate the info structure */
3309 info = talloc_zero(mem_ctx, union samr_UserInfo);
3310 if (info == NULL) {
3311 return NT_STATUS_NO_MEMORY;
3314 /* fill in the reply */
3315 switch (r->in.level) {
3316 case 1:
3317 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
3318 QUERY_STRING(msg, info1.full_name, "displayName");
3319 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3320 QUERY_STRING(msg, info1.description, "description");
3321 QUERY_STRING(msg, info1.comment, "comment");
3322 break;
3324 case 2:
3325 QUERY_STRING(msg, info2.comment, "comment");
3326 QUERY_UINT (msg, info2.country_code, "countryCode");
3327 QUERY_UINT (msg, info2.code_page, "codePage");
3328 break;
3330 case 3:
3331 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
3332 QUERY_STRING(msg, info3.full_name, "displayName");
3333 QUERY_RID (msg, info3.rid, "objectSid");
3334 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3335 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
3336 QUERY_STRING(msg, info3.home_drive, "homeDrive");
3337 QUERY_STRING(msg, info3.logon_script, "scriptPath");
3338 QUERY_STRING(msg, info3.profile_path, "profilePath");
3339 QUERY_STRING(msg, info3.workstations, "userWorkstations");
3340 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
3341 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
3342 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
3343 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3344 QUERY_UINT64(msg, info3.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3345 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3346 /* level 3 gives the raw badPwdCount value */
3347 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3348 QUERY_UINT (msg, info3.logon_count, "logonCount");
3349 QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
3350 break;
3352 case 4:
3353 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3354 break;
3356 case 5:
3357 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
3358 QUERY_STRING(msg, info5.full_name, "displayName");
3359 QUERY_RID (msg, info5.rid, "objectSid");
3360 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3361 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
3362 QUERY_STRING(msg, info5.home_drive, "homeDrive");
3363 QUERY_STRING(msg, info5.logon_script, "scriptPath");
3364 QUERY_STRING(msg, info5.profile_path, "profilePath");
3365 QUERY_STRING(msg, info5.description, "description");
3366 QUERY_STRING(msg, info5.workstations, "userWorkstations");
3367 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
3368 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
3369 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3370 QUERY_BPWDCT(msg, info5.bad_password_count, "badPwdCount");
3371 QUERY_UINT (msg, info5.logon_count, "logonCount");
3372 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
3373 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
3374 QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
3375 break;
3377 case 6:
3378 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3379 QUERY_STRING(msg, info6.full_name, "displayName");
3380 break;
3382 case 7:
3383 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3384 break;
3386 case 8:
3387 QUERY_STRING(msg, info8.full_name, "displayName");
3388 break;
3390 case 9:
3391 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3392 break;
3394 case 10:
3395 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3396 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3397 break;
3399 case 11:
3400 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3401 break;
3403 case 12:
3404 QUERY_STRING(msg, info12.profile_path, "profilePath");
3405 break;
3407 case 13:
3408 QUERY_STRING(msg, info13.description, "description");
3409 break;
3411 case 14:
3412 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3413 break;
3415 case 16:
3416 QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3417 break;
3419 case 17:
3420 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3421 break;
3423 case 20:
3424 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
3425 if (!NT_STATUS_IS_OK(status)) {
3426 talloc_free(info);
3427 return status;
3429 break;
3431 case 21:
3432 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3433 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3434 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3435 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3436 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3437 QUERY_UINT64(msg, info21.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3438 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3439 QUERY_STRING(msg, info21.full_name, "displayName");
3440 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3441 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3442 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3443 QUERY_STRING(msg, info21.profile_path, "profilePath");
3444 QUERY_STRING(msg, info21.description, "description");
3445 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3446 QUERY_STRING(msg, info21.comment, "comment");
3447 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
3448 if (!NT_STATUS_IS_OK(status)) {
3449 talloc_free(info);
3450 return status;
3453 QUERY_RID (msg, info21.rid, "objectSid");
3454 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3455 QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3456 info->info21.fields_present = 0x08FFFFFF;
3457 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3458 QUERY_BPWDCT(msg, info21.bad_password_count, "badPwdCount");
3459 QUERY_UINT (msg, info21.logon_count, "logonCount");
3460 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3461 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3462 } else {
3463 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3465 QUERY_UINT (msg, info21.country_code, "countryCode");
3466 QUERY_UINT (msg, info21.code_page, "codePage");
3467 break;
3470 default:
3471 talloc_free(info);
3472 return NT_STATUS_INVALID_INFO_CLASS;
3475 *r->out.info = info;
3477 return NT_STATUS_OK;
3482 samr_SetUserInfo
3484 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3485 struct samr_SetUserInfo *r)
3487 struct dcesrv_handle *h;
3488 struct samr_account_state *a_state;
3489 struct ldb_message *msg;
3490 int ret;
3491 NTSTATUS status = NT_STATUS_OK;
3492 struct ldb_context *sam_ctx;
3494 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3496 a_state = h->data;
3497 sam_ctx = a_state->sam_ctx;
3499 msg = ldb_msg_new(mem_ctx);
3500 if (msg == NULL) {
3501 return NT_STATUS_NO_MEMORY;
3504 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3505 if (!msg->dn) {
3506 return NT_STATUS_NO_MEMORY;
3509 switch (r->in.level) {
3510 case 2:
3511 SET_STRING(msg, info2.comment, "comment");
3512 SET_UINT (msg, info2.country_code, "countryCode");
3513 SET_UINT (msg, info2.code_page, "codePage");
3514 break;
3516 case 4:
3517 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3518 break;
3520 case 6:
3521 SET_STRING(msg, info6.account_name, "samAccountName");
3522 SET_STRING(msg, info6.full_name, "displayName");
3523 break;
3525 case 7:
3526 SET_STRING(msg, info7.account_name, "samAccountName");
3527 break;
3529 case 8:
3530 SET_STRING(msg, info8.full_name, "displayName");
3531 break;
3533 case 9:
3534 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3535 break;
3537 case 10:
3538 SET_STRING(msg, info10.home_directory, "homeDirectory");
3539 SET_STRING(msg, info10.home_drive, "homeDrive");
3540 break;
3542 case 11:
3543 SET_STRING(msg, info11.logon_script, "scriptPath");
3544 break;
3546 case 12:
3547 SET_STRING(msg, info12.profile_path, "profilePath");
3548 break;
3550 case 13:
3551 SET_STRING(msg, info13.description, "description");
3552 break;
3554 case 14:
3555 SET_STRING(msg, info14.workstations, "userWorkstations");
3556 break;
3558 case 16:
3559 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3560 break;
3562 case 17:
3563 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3564 break;
3566 case 18:
3567 status = samr_set_password_buffers(dce_call,
3568 a_state->sam_ctx,
3569 a_state->account_dn,
3570 a_state->domain_state->domain_dn,
3571 mem_ctx,
3572 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3573 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3574 if (!NT_STATUS_IS_OK(status)) {
3575 return status;
3578 if (r->in.info->info18.password_expired > 0) {
3579 struct ldb_message_element *set_el;
3580 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3581 return NT_STATUS_NO_MEMORY;
3583 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3584 set_el->flags = LDB_FLAG_MOD_REPLACE;
3586 break;
3588 case 20:
3589 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3590 break;
3592 case 21:
3593 if (r->in.info->info21.fields_present == 0)
3594 return NT_STATUS_INVALID_PARAMETER;
3596 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3597 IFSET(SAMR_FIELD_LAST_LOGON)
3598 SET_UINT64(msg, info21.last_logon, "lastLogon");
3599 IFSET(SAMR_FIELD_LAST_LOGOFF)
3600 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3601 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3602 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3603 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3604 SET_STRING(msg, info21.account_name, "samAccountName");
3605 IFSET(SAMR_FIELD_FULL_NAME)
3606 SET_STRING(msg, info21.full_name, "displayName");
3607 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3608 SET_STRING(msg, info21.home_directory, "homeDirectory");
3609 IFSET(SAMR_FIELD_HOME_DRIVE)
3610 SET_STRING(msg, info21.home_drive, "homeDrive");
3611 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3612 SET_STRING(msg, info21.logon_script, "scriptPath");
3613 IFSET(SAMR_FIELD_PROFILE_PATH)
3614 SET_STRING(msg, info21.profile_path, "profilePath");
3615 IFSET(SAMR_FIELD_DESCRIPTION)
3616 SET_STRING(msg, info21.description, "description");
3617 IFSET(SAMR_FIELD_WORKSTATIONS)
3618 SET_STRING(msg, info21.workstations, "userWorkstations");
3619 IFSET(SAMR_FIELD_COMMENT)
3620 SET_STRING(msg, info21.comment, "comment");
3621 IFSET(SAMR_FIELD_PARAMETERS)
3622 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3623 IFSET(SAMR_FIELD_PRIMARY_GID)
3624 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3625 IFSET(SAMR_FIELD_ACCT_FLAGS)
3626 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3627 IFSET(SAMR_FIELD_LOGON_HOURS)
3628 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3629 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3630 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3631 IFSET(SAMR_FIELD_NUM_LOGONS)
3632 SET_UINT (msg, info21.logon_count, "logonCount");
3633 IFSET(SAMR_FIELD_COUNTRY_CODE)
3634 SET_UINT (msg, info21.country_code, "countryCode");
3635 IFSET(SAMR_FIELD_CODE_PAGE)
3636 SET_UINT (msg, info21.code_page, "codePage");
3638 /* password change fields */
3639 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3640 return NT_STATUS_ACCESS_DENIED;
3642 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3643 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3644 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3646 if (r->in.info->info21.lm_password_set) {
3647 if ((r->in.info->info21.lm_owf_password.length != 16)
3648 || (r->in.info->info21.lm_owf_password.size != 16)) {
3649 return NT_STATUS_INVALID_PARAMETER;
3652 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3654 if (r->in.info->info21.nt_password_set) {
3655 if ((r->in.info->info21.nt_owf_password.length != 16)
3656 || (r->in.info->info21.nt_owf_password.size != 16)) {
3657 return NT_STATUS_INVALID_PARAMETER;
3660 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3662 status = samr_set_password_buffers(dce_call,
3663 a_state->sam_ctx,
3664 a_state->account_dn,
3665 a_state->domain_state->domain_dn,
3666 mem_ctx,
3667 lm_pwd_hash,
3668 nt_pwd_hash);
3669 if (!NT_STATUS_IS_OK(status)) {
3670 return status;
3675 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3676 const char *t = "0";
3677 struct ldb_message_element *set_el;
3678 if (r->in.info->info21.password_expired
3679 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3680 t = "-1";
3682 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3683 return NT_STATUS_NO_MEMORY;
3685 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3686 set_el->flags = LDB_FLAG_MOD_REPLACE;
3688 #undef IFSET
3689 break;
3691 case 23:
3692 if (r->in.info->info23.info.fields_present == 0)
3693 return NT_STATUS_INVALID_PARAMETER;
3695 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3696 IFSET(SAMR_FIELD_LAST_LOGON)
3697 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3698 IFSET(SAMR_FIELD_LAST_LOGOFF)
3699 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3700 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3701 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3702 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3703 SET_STRING(msg, info23.info.account_name, "samAccountName");
3704 IFSET(SAMR_FIELD_FULL_NAME)
3705 SET_STRING(msg, info23.info.full_name, "displayName");
3706 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3707 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3708 IFSET(SAMR_FIELD_HOME_DRIVE)
3709 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3710 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3711 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3712 IFSET(SAMR_FIELD_PROFILE_PATH)
3713 SET_STRING(msg, info23.info.profile_path, "profilePath");
3714 IFSET(SAMR_FIELD_DESCRIPTION)
3715 SET_STRING(msg, info23.info.description, "description");
3716 IFSET(SAMR_FIELD_WORKSTATIONS)
3717 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3718 IFSET(SAMR_FIELD_COMMENT)
3719 SET_STRING(msg, info23.info.comment, "comment");
3720 IFSET(SAMR_FIELD_PARAMETERS)
3721 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3722 IFSET(SAMR_FIELD_PRIMARY_GID)
3723 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3724 IFSET(SAMR_FIELD_ACCT_FLAGS)
3725 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3726 IFSET(SAMR_FIELD_LOGON_HOURS)
3727 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3728 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3729 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3730 IFSET(SAMR_FIELD_NUM_LOGONS)
3731 SET_UINT (msg, info23.info.logon_count, "logonCount");
3733 IFSET(SAMR_FIELD_COUNTRY_CODE)
3734 SET_UINT (msg, info23.info.country_code, "countryCode");
3735 IFSET(SAMR_FIELD_CODE_PAGE)
3736 SET_UINT (msg, info23.info.code_page, "codePage");
3738 /* password change fields */
3739 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3740 return NT_STATUS_ACCESS_DENIED;
3742 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3743 status = samr_set_password(dce_call,
3744 a_state->sam_ctx,
3745 a_state->account_dn,
3746 a_state->domain_state->domain_dn,
3747 mem_ctx,
3748 &r->in.info->info23.password);
3749 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3750 status = samr_set_password(dce_call,
3751 a_state->sam_ctx,
3752 a_state->account_dn,
3753 a_state->domain_state->domain_dn,
3754 mem_ctx,
3755 &r->in.info->info23.password);
3757 if (!NT_STATUS_IS_OK(status)) {
3758 return status;
3761 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3762 const char *t = "0";
3763 struct ldb_message_element *set_el;
3764 if (r->in.info->info23.info.password_expired
3765 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3766 t = "-1";
3768 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3769 return NT_STATUS_NO_MEMORY;
3771 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3772 set_el->flags = LDB_FLAG_MOD_REPLACE;
3774 #undef IFSET
3775 break;
3777 /* the set password levels are handled separately */
3778 case 24:
3779 status = samr_set_password(dce_call,
3780 a_state->sam_ctx,
3781 a_state->account_dn,
3782 a_state->domain_state->domain_dn,
3783 mem_ctx,
3784 &r->in.info->info24.password);
3785 if (!NT_STATUS_IS_OK(status)) {
3786 return status;
3789 if (r->in.info->info24.password_expired > 0) {
3790 struct ldb_message_element *set_el;
3791 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3792 return NT_STATUS_NO_MEMORY;
3794 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3795 set_el->flags = LDB_FLAG_MOD_REPLACE;
3797 break;
3799 case 25:
3800 if (r->in.info->info25.info.fields_present == 0)
3801 return NT_STATUS_INVALID_PARAMETER;
3803 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3804 IFSET(SAMR_FIELD_LAST_LOGON)
3805 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3806 IFSET(SAMR_FIELD_LAST_LOGOFF)
3807 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3808 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3809 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3810 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3811 SET_STRING(msg, info25.info.account_name, "samAccountName");
3812 IFSET(SAMR_FIELD_FULL_NAME)
3813 SET_STRING(msg, info25.info.full_name, "displayName");
3814 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3815 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3816 IFSET(SAMR_FIELD_HOME_DRIVE)
3817 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3818 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3819 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3820 IFSET(SAMR_FIELD_PROFILE_PATH)
3821 SET_STRING(msg, info25.info.profile_path, "profilePath");
3822 IFSET(SAMR_FIELD_DESCRIPTION)
3823 SET_STRING(msg, info25.info.description, "description");
3824 IFSET(SAMR_FIELD_WORKSTATIONS)
3825 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3826 IFSET(SAMR_FIELD_COMMENT)
3827 SET_STRING(msg, info25.info.comment, "comment");
3828 IFSET(SAMR_FIELD_PARAMETERS)
3829 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3830 IFSET(SAMR_FIELD_PRIMARY_GID)
3831 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3832 IFSET(SAMR_FIELD_ACCT_FLAGS)
3833 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3834 IFSET(SAMR_FIELD_LOGON_HOURS)
3835 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3836 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3837 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
3838 IFSET(SAMR_FIELD_NUM_LOGONS)
3839 SET_UINT (msg, info25.info.logon_count, "logonCount");
3840 IFSET(SAMR_FIELD_COUNTRY_CODE)
3841 SET_UINT (msg, info25.info.country_code, "countryCode");
3842 IFSET(SAMR_FIELD_CODE_PAGE)
3843 SET_UINT (msg, info25.info.code_page, "codePage");
3845 /* password change fields */
3846 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3847 return NT_STATUS_ACCESS_DENIED;
3849 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3850 status = samr_set_password_ex(dce_call,
3851 a_state->sam_ctx,
3852 a_state->account_dn,
3853 a_state->domain_state->domain_dn,
3854 mem_ctx,
3855 &r->in.info->info25.password);
3856 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3857 status = samr_set_password_ex(dce_call,
3858 a_state->sam_ctx,
3859 a_state->account_dn,
3860 a_state->domain_state->domain_dn,
3861 mem_ctx,
3862 &r->in.info->info25.password);
3864 if (!NT_STATUS_IS_OK(status)) {
3865 return status;
3868 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3869 const char *t = "0";
3870 struct ldb_message_element *set_el;
3871 if (r->in.info->info25.info.password_expired
3872 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3873 t = "-1";
3875 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3876 return NT_STATUS_NO_MEMORY;
3878 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3879 set_el->flags = LDB_FLAG_MOD_REPLACE;
3881 #undef IFSET
3882 break;
3884 /* the set password levels are handled separately */
3885 case 26:
3886 status = samr_set_password_ex(dce_call,
3887 a_state->sam_ctx,
3888 a_state->account_dn,
3889 a_state->domain_state->domain_dn,
3890 mem_ctx,
3891 &r->in.info->info26.password);
3892 if (!NT_STATUS_IS_OK(status)) {
3893 return status;
3896 if (r->in.info->info26.password_expired > 0) {
3897 const char *t = "0";
3898 struct ldb_message_element *set_el;
3899 if (r->in.info->info26.password_expired
3900 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3901 t = "-1";
3903 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3904 return NT_STATUS_NO_MEMORY;
3906 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3907 set_el->flags = LDB_FLAG_MOD_REPLACE;
3909 break;
3911 default:
3912 /* many info classes are not valid for SetUserInfo */
3913 return NT_STATUS_INVALID_INFO_CLASS;
3916 if (!NT_STATUS_IS_OK(status)) {
3917 return status;
3920 /* modify the samdb record */
3921 if (msg->num_elements > 0) {
3922 ret = ldb_modify(a_state->sam_ctx, msg);
3923 if (ret != LDB_SUCCESS) {
3924 DEBUG(1,("Failed to modify record %s: %s\n",
3925 ldb_dn_get_linearized(a_state->account_dn),
3926 ldb_errstring(a_state->sam_ctx)));
3928 return dsdb_ldb_err_to_ntstatus(ret);
3932 return NT_STATUS_OK;
3937 samr_GetGroupsForUser
3939 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3940 struct samr_GetGroupsForUser *r)
3942 struct dcesrv_handle *h;
3943 struct samr_account_state *a_state;
3944 struct samr_domain_state *d_state;
3945 struct ldb_result *res, *res_memberof;
3946 const char * const attrs[] = { "primaryGroupID",
3947 "memberOf",
3948 NULL };
3949 const char * const group_attrs[] = { "objectSid",
3950 NULL };
3952 struct samr_RidWithAttributeArray *array;
3953 struct ldb_message_element *memberof_el;
3954 int i, ret, count = 0;
3955 uint32_t primary_group_id;
3956 char *filter;
3958 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3960 a_state = h->data;
3961 d_state = a_state->domain_state;
3963 ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
3964 &res,
3965 a_state->account_dn,
3966 attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
3968 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3969 return NT_STATUS_NO_SUCH_USER;
3970 } else if (ret != LDB_SUCCESS) {
3971 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3972 } else if (res->count != 1) {
3973 return NT_STATUS_NO_SUCH_USER;
3976 primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
3979 filter = talloc_asprintf(mem_ctx,
3980 "(&(|(grouptype=%d)(grouptype=%d))"
3981 "(objectclass=group)(|",
3982 GTYPE_SECURITY_UNIVERSAL_GROUP,
3983 GTYPE_SECURITY_GLOBAL_GROUP);
3984 if (filter == NULL) {
3985 return NT_STATUS_NO_MEMORY;
3988 memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
3989 if (memberof_el != NULL) {
3990 for (i = 0; i < memberof_el->num_values; i++) {
3991 const struct ldb_val *memberof_sid_binary;
3992 char *memberof_sid_escaped;
3993 struct ldb_dn *memberof_dn
3994 = ldb_dn_from_ldb_val(mem_ctx,
3995 a_state->sam_ctx,
3996 &memberof_el->values[i]);
3997 if (memberof_dn == NULL) {
3998 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4001 memberof_sid_binary
4002 = ldb_dn_get_extended_component(memberof_dn,
4003 "SID");
4004 if (memberof_sid_binary == NULL) {
4005 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4008 memberof_sid_escaped = ldb_binary_encode(mem_ctx,
4009 *memberof_sid_binary);
4010 if (memberof_sid_escaped == NULL) {
4011 return NT_STATUS_NO_MEMORY;
4013 filter = talloc_asprintf_append(filter, "(objectSID=%s)",
4014 memberof_sid_escaped);
4015 if (filter == NULL) {
4016 return NT_STATUS_NO_MEMORY;
4020 ret = dsdb_search(a_state->sam_ctx, mem_ctx,
4021 &res_memberof,
4022 d_state->domain_dn,
4023 LDB_SCOPE_SUBTREE,
4024 group_attrs, 0,
4025 "%s))", filter);
4027 if (ret != LDB_SUCCESS) {
4028 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4030 count = res_memberof->count;
4033 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
4034 if (array == NULL)
4035 return NT_STATUS_NO_MEMORY;
4037 array->count = 0;
4038 array->rids = NULL;
4040 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
4041 count + 1);
4042 if (array->rids == NULL)
4043 return NT_STATUS_NO_MEMORY;
4045 /* Adds the primary group */
4047 array->rids[0].rid = primary_group_id;
4048 array->rids[0].attributes = SE_GROUP_MANDATORY
4049 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
4050 array->count += 1;
4052 /* Adds the additional groups */
4053 for (i = 0; i < count; i++) {
4054 struct dom_sid *group_sid;
4056 group_sid = samdb_result_dom_sid(mem_ctx,
4057 res_memberof->msgs[i],
4058 "objectSid");
4059 if (group_sid == NULL) {
4060 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4063 array->rids[i + 1].rid =
4064 group_sid->sub_auths[group_sid->num_auths-1];
4065 array->rids[i + 1].attributes = SE_GROUP_MANDATORY
4066 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
4067 array->count += 1;
4070 *r->out.rids = array;
4072 return NT_STATUS_OK;
4076 * samr_QueryDisplayInfo
4078 * A cache of the GUID's matching the last query is maintained
4079 * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
4080 * n the dcesrv_handle.
4082 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4083 struct samr_QueryDisplayInfo *r)
4085 struct dcesrv_handle *h;
4086 struct samr_domain_state *d_state;
4087 struct ldb_result *res;
4088 uint32_t i;
4089 uint32_t results = 0;
4090 uint32_t count = 0;
4091 const char *const cache_attrs[] = {"objectGUID", NULL};
4092 const char *const attrs[] = {
4093 "objectSID", "sAMAccountName", "displayName", "description", NULL};
4094 struct samr_DispEntryFull *entriesFull = NULL;
4095 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
4096 struct samr_DispEntryAscii *entriesAscii = NULL;
4097 struct samr_DispEntryGeneral *entriesGeneral = NULL;
4098 const char *filter;
4099 int ret;
4100 NTSTATUS status;
4101 struct samr_guid_cache *cache = NULL;
4103 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4105 d_state = h->data;
4107 cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
4109 * Can the cached results be used?
4110 * The cache is discarded if the start index is zero, or the requested
4111 * level is different from that in the cache.
4113 if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
4115 * The cached results can not be used, so will need to query
4116 * the database.
4120 * Get the search filter for the current level
4122 switch (r->in.level) {
4123 case 1:
4124 case 4:
4125 filter = talloc_asprintf(mem_ctx,
4126 "(&(objectclass=user)"
4127 "(sAMAccountType=%d))",
4128 ATYPE_NORMAL_ACCOUNT);
4129 break;
4130 case 2:
4131 filter = talloc_asprintf(mem_ctx,
4132 "(&(objectclass=user)"
4133 "(sAMAccountType=%d))",
4134 ATYPE_WORKSTATION_TRUST);
4135 break;
4136 case 3:
4137 case 5:
4138 filter =
4139 talloc_asprintf(mem_ctx,
4140 "(&(|(groupType=%d)(groupType=%d))"
4141 "(objectClass=group))",
4142 GTYPE_SECURITY_UNIVERSAL_GROUP,
4143 GTYPE_SECURITY_GLOBAL_GROUP);
4144 break;
4145 default:
4146 return NT_STATUS_INVALID_INFO_CLASS;
4148 clear_guid_cache(cache);
4151 * search for all requested objects in all domains.
4153 ret = dsdb_search(d_state->sam_ctx,
4154 mem_ctx,
4155 &res,
4156 ldb_get_default_basedn(d_state->sam_ctx),
4157 LDB_SCOPE_SUBTREE,
4158 cache_attrs,
4160 "%s",
4161 filter);
4162 if (ret != LDB_SUCCESS) {
4163 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4165 if ((res->count == 0) || (r->in.max_entries == 0)) {
4166 return NT_STATUS_OK;
4169 status = load_guid_cache(cache, d_state, res->count, res->msgs);
4170 TALLOC_FREE(res);
4171 if (!NT_STATUS_IS_OK(status)) {
4172 return status;
4174 cache->handle = r->in.level;
4176 *r->out.total_size = cache->size;
4179 * if there are no entries or the requested start index is greater
4180 * than the number of entries, we return an empty response.
4182 if (r->in.start_idx >= cache->size) {
4183 *r->out.returned_size = 0;
4184 switch(r->in.level) {
4185 case 1:
4186 r->out.info->info1.count = *r->out.returned_size;
4187 r->out.info->info1.entries = NULL;
4188 break;
4189 case 2:
4190 r->out.info->info2.count = *r->out.returned_size;
4191 r->out.info->info2.entries = NULL;
4192 break;
4193 case 3:
4194 r->out.info->info3.count = *r->out.returned_size;
4195 r->out.info->info3.entries = NULL;
4196 break;
4197 case 4:
4198 r->out.info->info4.count = *r->out.returned_size;
4199 r->out.info->info4.entries = NULL;
4200 break;
4201 case 5:
4202 r->out.info->info5.count = *r->out.returned_size;
4203 r->out.info->info5.entries = NULL;
4204 break;
4206 return NT_STATUS_OK;
4210 * Allocate an array of the appropriate result structures for the
4211 * current query level.
4213 * r->in.start_idx is always < cache->size due to the check above
4215 results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
4216 switch (r->in.level) {
4217 case 1:
4218 entriesGeneral = talloc_array(
4219 mem_ctx, struct samr_DispEntryGeneral, results);
4220 break;
4221 case 2:
4222 entriesFull =
4223 talloc_array(mem_ctx, struct samr_DispEntryFull, results);
4224 break;
4225 case 3:
4226 entriesFullGroup = talloc_array(
4227 mem_ctx, struct samr_DispEntryFullGroup, results);
4228 break;
4229 case 4:
4230 case 5:
4231 entriesAscii =
4232 talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
4233 break;
4236 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
4237 (entriesAscii == NULL) && (entriesFullGroup == NULL))
4238 return NT_STATUS_NO_MEMORY;
4241 * Process the list of result GUID's.
4242 * Read the details of each object and populate the result structure
4243 * for the current level.
4245 count = 0;
4246 for (i = 0; i < results; i++) {
4247 struct dom_sid *objectsid;
4248 struct ldb_result *rec;
4249 const uint32_t idx = r->in.start_idx + i;
4250 uint32_t rid;
4253 * Read an object from disk using the GUID as the key
4255 * If the object can not be read, or it does not have a SID
4256 * it is ignored. In this case the number of entries returned
4257 * will be less than the requested size, there will also be
4258 * a gap in the idx numbers in the returned elements e.g. if
4259 * there are 3 GUIDs a, b, c in the cache and b is deleted from
4260 * disk then details for a, and c will be returned with
4261 * idx values of 1 and 3 respectively.
4264 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
4265 mem_ctx,
4266 &rec,
4267 &cache->entries[idx],
4268 attrs,
4270 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4271 struct GUID_txt_buf guid_buf;
4272 char *guid_str =
4273 GUID_buf_string(&cache->entries[idx],
4274 &guid_buf);
4275 DBG_WARNING("GUID [%s] not found\n", guid_str);
4276 continue;
4277 } else if (ret != LDB_SUCCESS) {
4278 clear_guid_cache(cache);
4279 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4281 objectsid = samdb_result_dom_sid(mem_ctx,
4282 rec->msgs[0],
4283 "objectSID");
4284 if (objectsid == NULL) {
4285 struct GUID_txt_buf guid_buf;
4286 DBG_WARNING(
4287 "objectSID for GUID [%s] not found\n",
4288 GUID_buf_string(&cache->entries[idx], &guid_buf));
4289 continue;
4291 status = dom_sid_split_rid(NULL,
4292 objectsid,
4293 NULL,
4294 &rid);
4295 if (!NT_STATUS_IS_OK(status)) {
4296 struct dom_sid_buf sid_buf;
4297 struct GUID_txt_buf guid_buf;
4298 DBG_WARNING(
4299 "objectSID [%s] for GUID [%s] invalid\n",
4300 dom_sid_str_buf(objectsid, &sid_buf),
4301 GUID_buf_string(&cache->entries[idx], &guid_buf));
4302 continue;
4306 * Populate the result structure for the current object
4308 switch(r->in.level) {
4309 case 1:
4311 entriesGeneral[count].idx = idx + 1;
4312 entriesGeneral[count].rid = rid;
4314 entriesGeneral[count].acct_flags =
4315 samdb_result_acct_flags(rec->msgs[0], NULL);
4316 entriesGeneral[count].account_name.string =
4317 ldb_msg_find_attr_as_string(
4318 rec->msgs[0], "sAMAccountName", "");
4319 entriesGeneral[count].full_name.string =
4320 ldb_msg_find_attr_as_string(
4321 rec->msgs[0], "displayName", "");
4322 entriesGeneral[count].description.string =
4323 ldb_msg_find_attr_as_string(
4324 rec->msgs[0], "description", "");
4325 break;
4326 case 2:
4327 entriesFull[count].idx = idx + 1;
4328 entriesFull[count].rid = rid;
4331 * No idea why we need to or in ACB_NORMAL here,
4332 * but this is what Win2k3 seems to do...
4334 entriesFull[count].acct_flags =
4335 samdb_result_acct_flags(rec->msgs[0], NULL) |
4336 ACB_NORMAL;
4337 entriesFull[count].account_name.string =
4338 ldb_msg_find_attr_as_string(
4339 rec->msgs[0], "sAMAccountName", "");
4340 entriesFull[count].description.string =
4341 ldb_msg_find_attr_as_string(
4342 rec->msgs[0], "description", "");
4343 break;
4344 case 3:
4345 entriesFullGroup[count].idx = idx + 1;
4346 entriesFullGroup[count].rid = rid;
4349 * We get a "7" here for groups
4351 entriesFullGroup[count].acct_flags =
4352 SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
4353 SE_GROUP_ENABLED;
4354 entriesFullGroup[count].account_name.string =
4355 ldb_msg_find_attr_as_string(
4356 rec->msgs[0], "sAMAccountName", "");
4357 entriesFullGroup[count].description.string =
4358 ldb_msg_find_attr_as_string(
4359 rec->msgs[0], "description", "");
4360 break;
4361 case 4:
4362 case 5:
4363 entriesAscii[count].idx = idx + 1;
4364 entriesAscii[count].account_name.string =
4365 ldb_msg_find_attr_as_string(
4366 rec->msgs[0], "sAMAccountName", "");
4367 break;
4369 count++;
4373 * Build the response based on the request level.
4375 *r->out.returned_size = count;
4376 switch(r->in.level) {
4377 case 1:
4378 r->out.info->info1.count = count;
4379 r->out.info->info1.entries = entriesGeneral;
4380 break;
4381 case 2:
4382 r->out.info->info2.count = count;
4383 r->out.info->info2.entries = entriesFull;
4384 break;
4385 case 3:
4386 r->out.info->info3.count = count;
4387 r->out.info->info3.entries = entriesFullGroup;
4388 break;
4389 case 4:
4390 r->out.info->info4.count = count;
4391 r->out.info->info4.entries = entriesAscii;
4392 break;
4393 case 5:
4394 r->out.info->info5.count = count;
4395 r->out.info->info5.entries = entriesAscii;
4396 break;
4399 return ((r->in.start_idx + results) < cache->size)
4400 ? STATUS_MORE_ENTRIES
4401 : NT_STATUS_OK;
4406 samr_GetDisplayEnumerationIndex
4408 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4409 struct samr_GetDisplayEnumerationIndex *r)
4411 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4416 samr_TestPrivateFunctionsDomain
4418 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4419 struct samr_TestPrivateFunctionsDomain *r)
4421 return NT_STATUS_NOT_IMPLEMENTED;
4426 samr_TestPrivateFunctionsUser
4428 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4429 struct samr_TestPrivateFunctionsUser *r)
4431 return NT_STATUS_NOT_IMPLEMENTED;
4436 samr_GetUserPwInfo
4438 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4439 struct samr_GetUserPwInfo *r)
4441 struct dcesrv_handle *h;
4442 struct samr_account_state *a_state;
4444 ZERO_STRUCTP(r->out.info);
4446 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4448 a_state = h->data;
4450 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
4451 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
4452 NULL);
4453 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
4454 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
4456 return NT_STATUS_OK;
4461 samr_RemoveMemberFromForeignDomain
4463 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
4464 TALLOC_CTX *mem_ctx,
4465 struct samr_RemoveMemberFromForeignDomain *r)
4467 struct dcesrv_handle *h;
4468 struct samr_domain_state *d_state;
4469 const char *memberdn;
4470 struct ldb_message **res;
4471 const char *no_attrs[] = { NULL };
4472 int i, count;
4474 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4476 d_state = h->data;
4478 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
4479 "distinguishedName", "(objectSid=%s)",
4480 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
4481 /* Nothing to do */
4482 if (memberdn == NULL) {
4483 return NT_STATUS_OK;
4486 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4487 d_state->domain_dn, &res, no_attrs,
4488 d_state->domain_sid,
4489 "(&(member=%s)(objectClass=group)"
4490 "(|(groupType=%d)(groupType=%d)))",
4491 memberdn,
4492 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4493 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4495 if (count < 0)
4496 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4498 for (i=0; i<count; i++) {
4499 struct ldb_message *mod;
4500 int ret;
4502 mod = ldb_msg_new(mem_ctx);
4503 if (mod == NULL) {
4504 return NT_STATUS_NO_MEMORY;
4507 mod->dn = res[i]->dn;
4509 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4510 "member", memberdn) != LDB_SUCCESS)
4511 return NT_STATUS_NO_MEMORY;
4513 ret = ldb_modify(d_state->sam_ctx, mod);
4514 talloc_free(mod);
4515 if (ret != LDB_SUCCESS) {
4516 return dsdb_ldb_err_to_ntstatus(ret);
4520 return NT_STATUS_OK;
4525 samr_QueryDomainInfo2
4527 just an alias for samr_QueryDomainInfo
4529 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4530 struct samr_QueryDomainInfo2 *r)
4532 struct samr_QueryDomainInfo r1;
4533 NTSTATUS status;
4535 r1 = (struct samr_QueryDomainInfo) {
4536 .in.domain_handle = r->in.domain_handle,
4537 .in.level = r->in.level,
4538 .out.info = r->out.info,
4541 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4543 return status;
4548 samr_QueryUserInfo2
4550 just an alias for samr_QueryUserInfo
4552 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4553 struct samr_QueryUserInfo2 *r)
4555 struct samr_QueryUserInfo r1;
4556 NTSTATUS status;
4558 r1 = (struct samr_QueryUserInfo) {
4559 .in.user_handle = r->in.user_handle,
4560 .in.level = r->in.level,
4561 .out.info = r->out.info
4564 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4566 return status;
4571 samr_QueryDisplayInfo2
4573 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4574 struct samr_QueryDisplayInfo2 *r)
4576 struct samr_QueryDisplayInfo q;
4577 NTSTATUS result;
4579 q = (struct samr_QueryDisplayInfo) {
4580 .in.domain_handle = r->in.domain_handle,
4581 .in.level = r->in.level,
4582 .in.start_idx = r->in.start_idx,
4583 .in.max_entries = r->in.max_entries,
4584 .in.buf_size = r->in.buf_size,
4585 .out.total_size = r->out.total_size,
4586 .out.returned_size = r->out.returned_size,
4587 .out.info = r->out.info,
4590 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4592 return result;
4597 samr_GetDisplayEnumerationIndex2
4599 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4600 struct samr_GetDisplayEnumerationIndex2 *r)
4602 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4607 samr_QueryDisplayInfo3
4609 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4610 struct samr_QueryDisplayInfo3 *r)
4612 struct samr_QueryDisplayInfo q;
4613 NTSTATUS result;
4615 q = (struct samr_QueryDisplayInfo) {
4616 .in.domain_handle = r->in.domain_handle,
4617 .in.level = r->in.level,
4618 .in.start_idx = r->in.start_idx,
4619 .in.max_entries = r->in.max_entries,
4620 .in.buf_size = r->in.buf_size,
4621 .out.total_size = r->out.total_size,
4622 .out.returned_size = r->out.returned_size,
4623 .out.info = r->out.info,
4626 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4628 return result;
4633 samr_AddMultipleMembersToAlias
4635 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4636 struct samr_AddMultipleMembersToAlias *r)
4638 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4643 samr_RemoveMultipleMembersFromAlias
4645 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4646 struct samr_RemoveMultipleMembersFromAlias *r)
4648 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4653 samr_GetDomPwInfo
4655 this fetches the default password properties for a domain
4657 note that w2k3 completely ignores the domain name in this call, and
4658 always returns the information for the servers primary domain
4660 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4661 struct samr_GetDomPwInfo *r)
4663 struct auth_session_info *session_info =
4664 dcesrv_call_session_info(dce_call);
4665 struct ldb_message **msgs;
4666 int ret;
4667 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4668 struct ldb_context *sam_ctx;
4670 ZERO_STRUCTP(r->out.info);
4672 sam_ctx = samdb_connect(mem_ctx,
4673 dce_call->event_ctx,
4674 dce_call->conn->dce_ctx->lp_ctx,
4675 session_info,
4676 dce_call->conn->remote_address,
4678 if (sam_ctx == NULL) {
4679 return NT_STATUS_INVALID_SYSTEM_SERVICE;
4682 /* The domain name in this call is ignored */
4683 ret = gendb_search_dn(sam_ctx,
4684 mem_ctx, NULL, &msgs, attrs);
4685 if (ret <= 0) {
4686 talloc_free(sam_ctx);
4688 return NT_STATUS_NO_SUCH_DOMAIN;
4690 if (ret > 1) {
4691 talloc_free(msgs);
4692 talloc_free(sam_ctx);
4694 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4697 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
4698 "minPwdLength", 0);
4699 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
4700 "pwdProperties", 1);
4702 talloc_free(msgs);
4703 talloc_unlink(mem_ctx, sam_ctx);
4705 return NT_STATUS_OK;
4710 samr_Connect2
4712 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4713 struct samr_Connect2 *r)
4715 struct samr_Connect c;
4717 c = (struct samr_Connect) {
4718 .in.system_name = NULL,
4719 .in.access_mask = r->in.access_mask,
4720 .out.connect_handle = r->out.connect_handle,
4723 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4728 samr_SetUserInfo2
4730 just an alias for samr_SetUserInfo
4732 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4733 struct samr_SetUserInfo2 *r)
4735 struct samr_SetUserInfo r2;
4737 r2 = (struct samr_SetUserInfo) {
4738 .in.user_handle = r->in.user_handle,
4739 .in.level = r->in.level,
4740 .in.info = r->in.info,
4743 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4748 samr_SetBootKeyInformation
4750 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4751 struct samr_SetBootKeyInformation *r)
4753 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4758 samr_GetBootKeyInformation
4760 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4761 struct samr_GetBootKeyInformation *r)
4763 /* Windows Server 2008 returns this */
4764 return NT_STATUS_NOT_SUPPORTED;
4769 samr_Connect3
4771 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4772 struct samr_Connect3 *r)
4774 struct samr_Connect c;
4776 c = (struct samr_Connect) {
4777 .in.system_name = NULL,
4778 .in.access_mask = r->in.access_mask,
4779 .out.connect_handle = r->out.connect_handle,
4782 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4787 samr_Connect4
4789 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4790 struct samr_Connect4 *r)
4792 struct samr_Connect c;
4794 c = (struct samr_Connect) {
4795 .in.system_name = NULL,
4796 .in.access_mask = r->in.access_mask,
4797 .out.connect_handle = r->out.connect_handle,
4800 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4805 samr_Connect5
4807 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4808 struct samr_Connect5 *r)
4810 struct samr_Connect c;
4811 NTSTATUS status;
4813 c = (struct samr_Connect) {
4814 .in.system_name = NULL,
4815 .in.access_mask = r->in.access_mask,
4816 .out.connect_handle = r->out.connect_handle,
4819 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4821 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4822 r->out.info_out->info1.unknown2 = 0;
4823 *r->out.level_out = r->in.level_in;
4825 return status;
4830 samr_RidToSid
4832 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4833 struct samr_RidToSid *r)
4835 struct samr_domain_state *d_state;
4836 struct dcesrv_handle *h;
4838 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4840 d_state = h->data;
4842 /* form the users SID */
4843 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4844 if (!*r->out.sid) {
4845 return NT_STATUS_NO_MEMORY;
4848 return NT_STATUS_OK;
4853 samr_SetDsrmPassword
4855 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4856 struct samr_SetDsrmPassword *r)
4858 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4863 samr_ValidatePassword
4865 For now the call checks the password complexity (if active) and the minimum
4866 password length on level 2 and 3. Level 1 is ignored for now.
4868 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
4869 TALLOC_CTX *mem_ctx,
4870 struct samr_ValidatePassword *r)
4872 struct samr_GetDomPwInfo r2;
4873 struct samr_PwInfo pwInfo;
4874 const char *account = NULL;
4875 DATA_BLOB password;
4876 enum samr_ValidationStatus res;
4877 NTSTATUS status;
4878 enum dcerpc_transport_t transport =
4879 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
4880 enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
4882 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
4883 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4886 dcesrv_call_auth_info(dce_call, NULL, &auth_level);
4887 if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
4888 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4891 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
4893 r2 = (struct samr_GetDomPwInfo) {
4894 .in.domain_name = NULL,
4895 .out.info = &pwInfo,
4898 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
4899 if (!NT_STATUS_IS_OK(status)) {
4900 return status;
4903 switch (r->in.level) {
4904 case NetValidateAuthentication:
4905 /* we don't support this yet */
4906 return NT_STATUS_NOT_SUPPORTED;
4907 break;
4908 case NetValidatePasswordChange:
4909 account = r->in.req->req2.account.string;
4910 password = data_blob_const(r->in.req->req2.password.string,
4911 r->in.req->req2.password.length);
4912 res = samdb_check_password(mem_ctx,
4913 dce_call->conn->dce_ctx->lp_ctx,
4914 account,
4915 NULL, /* userPrincipalName */
4916 NULL, /* displayName/full_name */
4917 &password,
4918 pwInfo.password_properties,
4919 pwInfo.min_password_length);
4920 (*r->out.rep)->ctr2.status = res;
4921 break;
4922 case NetValidatePasswordReset:
4923 account = r->in.req->req3.account.string;
4924 password = data_blob_const(r->in.req->req3.password.string,
4925 r->in.req->req3.password.length);
4926 res = samdb_check_password(mem_ctx,
4927 dce_call->conn->dce_ctx->lp_ctx,
4928 account,
4929 NULL, /* userPrincipalName */
4930 NULL, /* displayName/full_name */
4931 &password,
4932 pwInfo.password_properties,
4933 pwInfo.min_password_length);
4934 (*r->out.rep)->ctr3.status = res;
4935 break;
4936 default:
4937 return NT_STATUS_INVALID_INFO_CLASS;
4938 break;
4941 return NT_STATUS_OK;
4945 /* include the generated boilerplate */
4946 #include "librpc/gen_ndr/ndr_samr_s.c"