s4:rpc_server/samr: Fix the O3 developer build
[Samba.git] / source4 / rpc_server / samr / dcesrv_samr.c
blobd7d2fa46684f7a902afefb535f2e0fe0b1b5c954
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(call, iface) \
45 dcesrv_interface_samr_bind(call, iface)
46 static NTSTATUS dcesrv_interface_samr_bind(struct dcesrv_call_state *dce_call,
47 const struct dcesrv_interface *iface)
49 return dcesrv_interface_bind_reject_connect(dce_call, 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 samr_connect_state *c_state;
214 struct dcesrv_handle *handle;
216 ZERO_STRUCTP(r->out.connect_handle);
218 c_state = talloc(mem_ctx, struct samr_connect_state);
219 if (!c_state) {
220 return NT_STATUS_NO_MEMORY;
223 /* make sure the sam database is accessible */
224 c_state->sam_ctx = samdb_connect(c_state,
225 dce_call->event_ctx,
226 dce_call->conn->dce_ctx->lp_ctx,
227 dce_call->conn->auth_state.session_info,
228 dce_call->conn->remote_address,
230 if (c_state->sam_ctx == NULL) {
231 talloc_free(c_state);
232 return NT_STATUS_INVALID_SYSTEM_SERVICE;
236 handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
237 if (!handle) {
238 talloc_free(c_state);
239 return NT_STATUS_NO_MEMORY;
242 handle->data = talloc_steal(handle, c_state);
244 c_state->access_mask = r->in.access_mask;
245 *r->out.connect_handle = handle->wire_handle;
247 return NT_STATUS_OK;
252 samr_Close
254 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
255 struct samr_Close *r)
257 struct dcesrv_handle *h;
259 *r->out.handle = *r->in.handle;
261 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
263 talloc_free(h);
265 ZERO_STRUCTP(r->out.handle);
267 return NT_STATUS_OK;
272 samr_SetSecurity
274 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
275 struct samr_SetSecurity *r)
277 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
282 samr_QuerySecurity
284 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
285 struct samr_QuerySecurity *r)
287 struct dcesrv_handle *h;
288 struct sec_desc_buf *sd;
290 *r->out.sdbuf = NULL;
292 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
294 sd = talloc(mem_ctx, struct sec_desc_buf);
295 if (sd == NULL) {
296 return NT_STATUS_NO_MEMORY;
299 sd->sd = samdb_default_security_descriptor(mem_ctx);
301 *r->out.sdbuf = sd;
303 return NT_STATUS_OK;
308 samr_Shutdown
310 we refuse this operation completely. If a admin wants to shutdown samr
311 in Samba then they should use the samba admin tools to disable the samr pipe
313 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
314 struct samr_Shutdown *r)
316 return NT_STATUS_ACCESS_DENIED;
321 samr_LookupDomain
323 this maps from a domain name to a SID
325 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
326 struct samr_LookupDomain *r)
328 struct samr_connect_state *c_state;
329 struct dcesrv_handle *h;
330 struct dom_sid *sid;
331 const char * const dom_attrs[] = { "objectSid", NULL};
332 struct ldb_message **dom_msgs;
333 int ret;
335 *r->out.sid = NULL;
337 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
339 c_state = h->data;
341 if (r->in.domain_name->string == NULL) {
342 return NT_STATUS_INVALID_PARAMETER;
345 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
346 ret = gendb_search(c_state->sam_ctx,
347 mem_ctx, NULL, &dom_msgs, dom_attrs,
348 "(objectClass=builtinDomain)");
349 } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
350 ret = gendb_search_dn(c_state->sam_ctx,
351 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
352 &dom_msgs, dom_attrs);
353 } else {
354 return NT_STATUS_NO_SUCH_DOMAIN;
356 if (ret != 1) {
357 return NT_STATUS_NO_SUCH_DOMAIN;
360 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
361 "objectSid");
363 if (sid == NULL) {
364 return NT_STATUS_NO_SUCH_DOMAIN;
367 *r->out.sid = sid;
369 return NT_STATUS_OK;
374 samr_EnumDomains
376 list the domains in the SAM
378 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
379 struct samr_EnumDomains *r)
381 struct dcesrv_handle *h;
382 struct samr_SamArray *array;
383 uint32_t i, start_i;
385 *r->out.resume_handle = 0;
386 *r->out.sam = NULL;
387 *r->out.num_entries = 0;
389 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
391 *r->out.resume_handle = 2;
393 start_i = *r->in.resume_handle;
395 if (start_i >= 2) {
396 /* search past end of list is not an error for this call */
397 return NT_STATUS_OK;
400 array = talloc(mem_ctx, struct samr_SamArray);
401 if (array == NULL) {
402 return NT_STATUS_NO_MEMORY;
405 array->count = 0;
406 array->entries = NULL;
408 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
409 if (array->entries == NULL) {
410 return NT_STATUS_NO_MEMORY;
413 for (i=0;i<2-start_i;i++) {
414 array->entries[i].idx = start_i + i;
415 if (i == 0) {
416 array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
417 } else {
418 array->entries[i].name.string = "BUILTIN";
422 *r->out.sam = array;
423 *r->out.num_entries = i;
424 array->count = *r->out.num_entries;
426 return NT_STATUS_OK;
431 samr_OpenDomain
433 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
434 struct samr_OpenDomain *r)
436 struct dcesrv_handle *h_conn, *h_domain;
437 struct samr_connect_state *c_state;
438 struct samr_domain_state *d_state;
439 const char * const dom_attrs[] = { "cn", NULL};
440 struct ldb_message **dom_msgs;
441 int ret;
442 unsigned int i;
444 ZERO_STRUCTP(r->out.domain_handle);
446 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
448 c_state = h_conn->data;
450 if (r->in.sid == NULL) {
451 return NT_STATUS_INVALID_PARAMETER;
454 d_state = talloc(mem_ctx, struct samr_domain_state);
455 if (!d_state) {
456 return NT_STATUS_NO_MEMORY;
459 d_state->domain_sid = talloc_steal(d_state, r->in.sid);
461 if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
462 d_state->builtin = true;
463 d_state->domain_name = "BUILTIN";
464 } else {
465 d_state->builtin = false;
466 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
469 ret = gendb_search(c_state->sam_ctx,
470 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
471 "(objectSid=%s)",
472 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
474 if (ret == 0) {
475 talloc_free(d_state);
476 return NT_STATUS_NO_SUCH_DOMAIN;
477 } else if (ret > 1) {
478 talloc_free(d_state);
479 return NT_STATUS_INTERNAL_DB_CORRUPTION;
480 } else if (ret == -1) {
481 talloc_free(d_state);
482 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
483 return NT_STATUS_INTERNAL_DB_CORRUPTION;
486 d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
487 d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
488 d_state->connect_state = talloc_reference(d_state, c_state);
489 d_state->sam_ctx = c_state->sam_ctx;
490 d_state->access_mask = r->in.access_mask;
492 d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
494 for (i = 0; i < SAMR_LAST_CACHE; i++) {
495 initialize_guid_cache(&d_state->guid_caches[i]);
498 h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
499 if (!h_domain) {
500 talloc_free(d_state);
501 return NT_STATUS_NO_MEMORY;
504 h_domain->data = talloc_steal(h_domain, d_state);
506 *r->out.domain_handle = h_domain->wire_handle;
508 return NT_STATUS_OK;
512 return DomInfo1
514 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
515 TALLOC_CTX *mem_ctx,
516 struct ldb_message **dom_msgs,
517 struct samr_DomInfo1 *info)
519 info->min_password_length =
520 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
521 info->password_history_length =
522 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
523 info->password_properties =
524 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
525 info->max_password_age =
526 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
527 info->min_password_age =
528 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
530 return NT_STATUS_OK;
534 return DomInfo2
536 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
537 TALLOC_CTX *mem_ctx,
538 struct ldb_message **dom_msgs,
539 struct samr_DomGeneralInformation *info)
541 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
542 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
543 "domainReplica",
544 "");
546 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
547 0x8000000000000000LL);
549 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
550 "oEMInformation",
551 "");
552 info->domain_name.string = state->domain_name;
554 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
556 switch (state->role) {
557 case ROLE_ACTIVE_DIRECTORY_DC:
558 /* This pulls the NetBIOS name from the
559 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
560 string */
561 if (samdb_is_pdc(state->sam_ctx)) {
562 info->role = SAMR_ROLE_DOMAIN_PDC;
563 } else {
564 info->role = SAMR_ROLE_DOMAIN_BDC;
566 break;
567 case ROLE_DOMAIN_PDC:
568 case ROLE_DOMAIN_BDC:
569 case ROLE_AUTO:
570 return NT_STATUS_INTERNAL_ERROR;
571 case ROLE_DOMAIN_MEMBER:
572 info->role = SAMR_ROLE_DOMAIN_MEMBER;
573 break;
574 case ROLE_STANDALONE:
575 info->role = SAMR_ROLE_STANDALONE;
576 break;
579 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx,
580 state->domain_dn,
581 "(objectClass=user)");
582 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx,
583 state->domain_dn,
584 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
585 GTYPE_SECURITY_UNIVERSAL_GROUP,
586 GTYPE_SECURITY_GLOBAL_GROUP);
587 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx,
588 state->domain_dn,
589 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
590 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
591 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
593 return NT_STATUS_OK;
597 return DomInfo3
599 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
600 TALLOC_CTX *mem_ctx,
601 struct ldb_message **dom_msgs,
602 struct samr_DomInfo3 *info)
604 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
605 0x8000000000000000LL);
607 return NT_STATUS_OK;
611 return DomInfo4
613 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
614 TALLOC_CTX *mem_ctx,
615 struct ldb_message **dom_msgs,
616 struct samr_DomOEMInformation *info)
618 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
619 "oEMInformation",
620 "");
622 return NT_STATUS_OK;
626 return DomInfo5
628 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
629 TALLOC_CTX *mem_ctx,
630 struct ldb_message **dom_msgs,
631 struct samr_DomInfo5 *info)
633 info->domain_name.string = state->domain_name;
635 return NT_STATUS_OK;
639 return DomInfo6
641 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
642 TALLOC_CTX *mem_ctx,
643 struct ldb_message **dom_msgs,
644 struct samr_DomInfo6 *info)
646 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
647 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
648 "domainReplica",
649 "");
651 return NT_STATUS_OK;
655 return DomInfo7
657 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
658 TALLOC_CTX *mem_ctx,
659 struct ldb_message **dom_msgs,
660 struct samr_DomInfo7 *info)
663 switch (state->role) {
664 case ROLE_ACTIVE_DIRECTORY_DC:
665 /* This pulls the NetBIOS name from the
666 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
667 string */
668 if (samdb_is_pdc(state->sam_ctx)) {
669 info->role = SAMR_ROLE_DOMAIN_PDC;
670 } else {
671 info->role = SAMR_ROLE_DOMAIN_BDC;
673 break;
674 case ROLE_DOMAIN_PDC:
675 case ROLE_DOMAIN_BDC:
676 case ROLE_AUTO:
677 return NT_STATUS_INTERNAL_ERROR;
678 case ROLE_DOMAIN_MEMBER:
679 info->role = SAMR_ROLE_DOMAIN_MEMBER;
680 break;
681 case ROLE_STANDALONE:
682 info->role = SAMR_ROLE_STANDALONE;
683 break;
686 return NT_STATUS_OK;
690 return DomInfo8
692 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
693 TALLOC_CTX *mem_ctx,
694 struct ldb_message **dom_msgs,
695 struct samr_DomInfo8 *info)
697 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
698 time(NULL));
700 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
701 0x0LL);
703 return NT_STATUS_OK;
707 return DomInfo9
709 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
710 TALLOC_CTX *mem_ctx,
711 struct ldb_message **dom_msgs,
712 struct samr_DomInfo9 *info)
714 info->domain_server_state = DOMAIN_SERVER_ENABLED;
716 return NT_STATUS_OK;
720 return DomInfo11
722 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
723 TALLOC_CTX *mem_ctx,
724 struct ldb_message **dom_msgs,
725 struct samr_DomGeneralInformation2 *info)
727 NTSTATUS status;
728 status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
729 if (!NT_STATUS_IS_OK(status)) {
730 return status;
733 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
734 -18000000000LL);
735 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
736 -18000000000LL);
737 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
739 return NT_STATUS_OK;
743 return DomInfo12
745 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
746 TALLOC_CTX *mem_ctx,
747 struct ldb_message **dom_msgs,
748 struct samr_DomInfo12 *info)
750 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
751 -18000000000LL);
752 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
753 -18000000000LL);
754 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
756 return NT_STATUS_OK;
760 return DomInfo13
762 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
763 TALLOC_CTX *mem_ctx,
764 struct ldb_message **dom_msgs,
765 struct samr_DomInfo13 *info)
767 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
768 time(NULL));
770 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
771 0x0LL);
773 info->modified_count_at_last_promotion = 0;
775 return NT_STATUS_OK;
779 samr_QueryDomainInfo
781 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
782 TALLOC_CTX *mem_ctx,
783 struct samr_QueryDomainInfo *r)
785 struct dcesrv_handle *h;
786 struct samr_domain_state *d_state;
787 union samr_DomainInfo *info;
789 struct ldb_message **dom_msgs;
790 const char * const *attrs = NULL;
792 *r->out.info = NULL;
794 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
796 d_state = h->data;
798 switch (r->in.level) {
799 case 1:
801 static const char * const attrs2[] = { "minPwdLength",
802 "pwdHistoryLength",
803 "pwdProperties",
804 "maxPwdAge",
805 "minPwdAge",
806 NULL };
807 attrs = attrs2;
808 break;
810 case 2:
812 static const char * const attrs2[] = {"forceLogoff",
813 "oEMInformation",
814 "modifiedCount",
815 "domainReplica",
816 NULL};
817 attrs = attrs2;
818 break;
820 case 3:
822 static const char * const attrs2[] = {"forceLogoff",
823 NULL};
824 attrs = attrs2;
825 break;
827 case 4:
829 static const char * const attrs2[] = {"oEMInformation",
830 NULL};
831 attrs = attrs2;
832 break;
834 case 5:
836 attrs = NULL;
837 break;
839 case 6:
841 static const char * const attrs2[] = { "domainReplica",
842 NULL };
843 attrs = attrs2;
844 break;
846 case 7:
848 attrs = NULL;
849 break;
851 case 8:
853 static const char * const attrs2[] = { "modifiedCount",
854 "creationTime",
855 NULL };
856 attrs = attrs2;
857 break;
859 case 9:
861 attrs = NULL;
862 break;
864 case 11:
866 static const char * const attrs2[] = { "oEMInformation",
867 "forceLogoff",
868 "modifiedCount",
869 "lockoutDuration",
870 "lockOutObservationWindow",
871 "lockoutThreshold",
872 NULL};
873 attrs = attrs2;
874 break;
876 case 12:
878 static const char * const attrs2[] = { "lockoutDuration",
879 "lockOutObservationWindow",
880 "lockoutThreshold",
881 NULL};
882 attrs = attrs2;
883 break;
885 case 13:
887 static const char * const attrs2[] = { "modifiedCount",
888 "creationTime",
889 NULL };
890 attrs = attrs2;
891 break;
893 default:
895 return NT_STATUS_INVALID_INFO_CLASS;
899 /* some levels don't need a search */
900 if (attrs) {
901 int ret;
902 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
903 d_state->domain_dn, &dom_msgs, attrs);
904 if (ret == 0) {
905 return NT_STATUS_NO_SUCH_DOMAIN;
907 if (ret != 1) {
908 return NT_STATUS_INTERNAL_DB_CORRUPTION;
912 /* allocate the info structure */
913 info = talloc_zero(mem_ctx, union samr_DomainInfo);
914 if (info == NULL) {
915 return NT_STATUS_NO_MEMORY;
918 *r->out.info = info;
920 switch (r->in.level) {
921 case 1:
922 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
923 &info->info1);
924 case 2:
925 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
926 &info->general);
927 case 3:
928 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
929 &info->info3);
930 case 4:
931 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
932 &info->oem);
933 case 5:
934 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
935 &info->info5);
936 case 6:
937 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
938 &info->info6);
939 case 7:
940 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
941 &info->info7);
942 case 8:
943 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
944 &info->info8);
945 case 9:
946 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
947 &info->info9);
948 case 11:
949 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
950 &info->general2);
951 case 12:
952 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
953 &info->info12);
954 case 13:
955 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
956 &info->info13);
957 default:
958 return NT_STATUS_INVALID_INFO_CLASS;
964 samr_SetDomainInfo
966 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
967 struct samr_SetDomainInfo *r)
969 struct dcesrv_handle *h;
970 struct samr_domain_state *d_state;
971 struct ldb_message *msg;
972 int ret;
973 struct ldb_context *sam_ctx;
975 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
977 d_state = h->data;
978 sam_ctx = d_state->sam_ctx;
980 msg = ldb_msg_new(mem_ctx);
981 if (msg == NULL) {
982 return NT_STATUS_NO_MEMORY;
985 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
986 if (!msg->dn) {
987 return NT_STATUS_NO_MEMORY;
990 switch (r->in.level) {
991 case 1:
992 SET_UINT (msg, info1.min_password_length, "minPwdLength");
993 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
994 SET_UINT (msg, info1.password_properties, "pwdProperties");
995 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
996 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
997 break;
998 case 3:
999 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
1000 break;
1001 case 4:
1002 SET_STRING(msg, oem.oem_information, "oEMInformation");
1003 break;
1005 case 6:
1006 case 7:
1007 case 9:
1008 /* No op, we don't know where to set these */
1009 return NT_STATUS_OK;
1011 case 12:
1013 * It is not possible to set lockout_duration < lockout_window.
1014 * (The test is the other way around since the negative numbers
1015 * are stored...)
1017 * TODO:
1018 * This check should be moved to the backend, i.e. to some
1019 * ldb module under dsdb/samdb/ldb_modules/ .
1021 * This constraint is documented here for the samr rpc service:
1022 * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
1023 * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
1025 * And here for the ldap backend:
1026 * MS-ADTS 3.1.1.5.3.2 Constraints
1027 * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
1029 if (r->in.info->info12.lockout_duration >
1030 r->in.info->info12.lockout_window)
1032 return NT_STATUS_INVALID_PARAMETER;
1034 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
1035 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
1036 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
1037 break;
1039 default:
1040 /* many info classes are not valid for SetDomainInfo */
1041 return NT_STATUS_INVALID_INFO_CLASS;
1044 /* modify the samdb record */
1045 ret = ldb_modify(sam_ctx, msg);
1046 if (ret != LDB_SUCCESS) {
1047 DEBUG(1,("Failed to modify record %s: %s\n",
1048 ldb_dn_get_linearized(d_state->domain_dn),
1049 ldb_errstring(sam_ctx)));
1050 return dsdb_ldb_err_to_ntstatus(ret);
1053 return NT_STATUS_OK;
1057 samr_CreateDomainGroup
1059 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1060 struct samr_CreateDomainGroup *r)
1062 NTSTATUS status;
1063 struct samr_domain_state *d_state;
1064 struct samr_account_state *a_state;
1065 struct dcesrv_handle *h;
1066 const char *groupname;
1067 struct dom_sid *group_sid;
1068 struct ldb_dn *group_dn;
1069 struct dcesrv_handle *g_handle;
1071 ZERO_STRUCTP(r->out.group_handle);
1072 *r->out.rid = 0;
1074 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1076 d_state = h->data;
1078 if (d_state->builtin) {
1079 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
1080 return NT_STATUS_ACCESS_DENIED;
1083 groupname = r->in.name->string;
1085 if (groupname == NULL) {
1086 return NT_STATUS_INVALID_PARAMETER;
1089 status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1090 if (!NT_STATUS_IS_OK(status)) {
1091 return status;
1094 a_state = talloc(mem_ctx, struct samr_account_state);
1095 if (!a_state) {
1096 return NT_STATUS_NO_MEMORY;
1098 a_state->sam_ctx = d_state->sam_ctx;
1099 a_state->access_mask = r->in.access_mask;
1100 a_state->domain_state = talloc_reference(a_state, d_state);
1101 a_state->account_dn = talloc_steal(a_state, group_dn);
1103 a_state->account_name = talloc_steal(a_state, groupname);
1105 /* create the policy handle */
1106 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1107 if (!g_handle) {
1108 return NT_STATUS_NO_MEMORY;
1111 g_handle->data = talloc_steal(g_handle, a_state);
1113 *r->out.group_handle = g_handle->wire_handle;
1114 *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1116 return NT_STATUS_OK;
1121 comparison function for sorting SamEntry array
1123 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1125 return e1->idx - e2->idx;
1128 static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) {
1129 struct dom_sid *sid1 = NULL;
1130 struct dom_sid *sid2 = NULL;
1131 uint32_t rid1;
1132 uint32_t rid2;
1133 int res = 0;
1134 NTSTATUS status;
1135 TALLOC_CTX *frame = talloc_stackframe();
1137 sid1 = samdb_result_dom_sid(frame, *m1, "objectSid");
1138 sid2 = samdb_result_dom_sid(frame, *m2, "objectSid");
1141 * If entries don't have a SID we want to sort them to the end of
1142 * the list.
1144 if (sid1 == NULL && sid2 == NULL) {
1145 res = 0;
1146 goto exit;
1147 } else if (sid2 == NULL) {
1148 res = 1;
1149 goto exit;
1150 } else if (sid1 == NULL) {
1151 res = -1;
1152 goto exit;
1156 * Get and compare the rids, if we fail to extract a rid treat it as a
1157 * missing SID and sort to the end of the list
1159 status = dom_sid_split_rid(NULL, sid1, NULL, &rid1);
1160 if (!NT_STATUS_IS_OK(status)) {
1161 res = 1;
1162 goto exit;
1165 status = dom_sid_split_rid(NULL, sid2, NULL, &rid2);
1166 if (!NT_STATUS_IS_OK(status)) {
1167 res = -1;
1168 goto exit;
1171 if (rid1 == rid2) {
1172 res = 0;
1174 else if (rid1 > rid2) {
1175 res = 1;
1177 else {
1178 res = -1;
1180 exit:
1181 TALLOC_FREE(frame);
1182 return res;
1186 samr_EnumDomainGroups
1188 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1189 struct samr_EnumDomainGroups *r)
1191 struct dcesrv_handle *h;
1192 struct samr_domain_state *d_state;
1193 struct ldb_message **res;
1194 uint32_t i;
1195 uint32_t count;
1196 uint32_t results;
1197 uint32_t max_entries;
1198 uint32_t remaining_entries;
1199 uint32_t resume_handle;
1200 struct samr_SamEntry *entries;
1201 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1202 const char * const cache_attrs[] = { "objectSid", "objectGUID", NULL };
1203 struct samr_SamArray *sam;
1204 struct samr_guid_cache *cache = NULL;
1206 *r->out.resume_handle = 0;
1207 *r->out.sam = NULL;
1208 *r->out.num_entries = 0;
1210 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1212 d_state = h->data;
1213 cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_GROUPS_CACHE];
1216 * If the resume_handle is zero, query the database and cache the
1217 * matching GUID's
1219 if (*r->in.resume_handle == 0) {
1220 NTSTATUS status;
1221 int ldb_cnt;
1222 clear_guid_cache(cache);
1224 * search for all domain groups in this domain.
1226 ldb_cnt = samdb_search_domain(
1227 d_state->sam_ctx,
1228 mem_ctx,
1229 d_state->domain_dn,
1230 &res,
1231 cache_attrs,
1232 d_state->domain_sid,
1233 "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1234 GTYPE_SECURITY_UNIVERSAL_GROUP,
1235 GTYPE_SECURITY_GLOBAL_GROUP);
1236 if (ldb_cnt < 0) {
1237 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1240 * Sort the results into RID order, while the spec states there
1241 * is no order, Windows appears to sort the results by RID and
1242 * so it is possible that there are clients that depend on
1243 * this ordering
1245 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1248 * cache the sorted GUID's
1250 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1251 TALLOC_FREE(res);
1252 if (!NT_STATUS_IS_OK(status)) {
1253 return status;
1255 cache->handle = 0;
1260 * If the resume handle is out of range we return an empty response
1261 * and invalidate the cache.
1263 * From the specification:
1264 * Servers SHOULD validate that EnumerationContext is an expected
1265 * value for the server's implementation. Windows does NOT validate
1266 * the input, though the result of malformed information merely results
1267 * in inconsistent output to the client.
1269 if (*r->in.resume_handle >= cache->size) {
1270 clear_guid_cache(cache);
1271 sam = talloc(mem_ctx, struct samr_SamArray);
1272 if (!sam) {
1273 return NT_STATUS_NO_MEMORY;
1275 sam->entries = NULL;
1276 sam->count = 0;
1278 *r->out.sam = sam;
1279 *r->out.resume_handle = 0;
1280 return NT_STATUS_OK;
1285 * Calculate the number of entries to return limit by max_size.
1286 * Note that we use the w2k3 element size value of 54
1288 max_entries = 1 + (r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER);
1289 remaining_entries = cache->size - *r->in.resume_handle;
1290 results = MIN(remaining_entries, max_entries);
1293 * Process the list of result GUID's.
1294 * Read the details of each object and populate the Entries
1295 * for the current level.
1297 count = 0;
1298 resume_handle = *r->in.resume_handle;
1299 entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1300 if (entries == NULL) {
1301 clear_guid_cache(cache);
1302 return NT_STATUS_NO_MEMORY;
1304 for (i = 0; i < results; i++) {
1305 struct dom_sid *objectsid;
1306 uint32_t rid;
1307 struct ldb_result *rec;
1308 const uint32_t idx = *r->in.resume_handle + i;
1309 int ret;
1310 NTSTATUS status;
1311 const char *name = NULL;
1312 resume_handle++;
1314 * Read an object from disk using the GUID as the key
1316 * If the object can not be read, or it does not have a SID
1317 * it is ignored.
1319 * As a consequence of this, if all the remaining GUID's
1320 * have been deleted an empty result will be returned.
1321 * i.e. even if the previous call returned a non zero
1322 * resume_handle it is possible for no results to be returned.
1325 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1326 mem_ctx,
1327 &rec,
1328 &cache->entries[idx],
1329 attrs,
1331 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1332 struct GUID_txt_buf guid_buf;
1333 DBG_WARNING(
1334 "GUID [%s] not found\n",
1335 GUID_buf_string(&cache->entries[idx], &guid_buf));
1336 continue;
1337 } else if (ret != LDB_SUCCESS) {
1338 clear_guid_cache(cache);
1339 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1342 objectsid = samdb_result_dom_sid(mem_ctx,
1343 rec->msgs[0],
1344 "objectSID");
1345 if (objectsid == NULL) {
1346 struct GUID_txt_buf guid_buf;
1347 DBG_WARNING(
1348 "objectSID for GUID [%s] not found\n",
1349 GUID_buf_string(&cache->entries[idx], &guid_buf));
1350 continue;
1352 status = dom_sid_split_rid(NULL,
1353 objectsid,
1354 NULL,
1355 &rid);
1356 if (!NT_STATUS_IS_OK(status)) {
1357 struct dom_sid_buf sid_buf;
1358 struct GUID_txt_buf guid_buf;
1359 DBG_WARNING(
1360 "objectSID [%s] for GUID [%s] invalid\n",
1361 dom_sid_str_buf(objectsid, &sid_buf),
1362 GUID_buf_string(&cache->entries[idx], &guid_buf));
1363 continue;
1366 entries[count].idx = rid;
1367 name = ldb_msg_find_attr_as_string(
1368 rec->msgs[0], "sAMAccountName", "");
1369 entries[count].name.string = talloc_strdup(entries, name);
1370 count++;
1373 sam = talloc(mem_ctx, struct samr_SamArray);
1374 if (!sam) {
1375 clear_guid_cache(cache);
1376 return NT_STATUS_NO_MEMORY;
1379 sam->entries = entries;
1380 sam->count = count;
1382 *r->out.sam = sam;
1383 *r->out.resume_handle = resume_handle;
1384 *r->out.num_entries = count;
1387 * Signal no more results by returning zero resume handle,
1388 * the cache is also cleared at this point
1390 if (*r->out.resume_handle >= cache->size) {
1391 *r->out.resume_handle = 0;
1392 clear_guid_cache(cache);
1393 return NT_STATUS_OK;
1396 * There are more results to be returned.
1398 return STATUS_MORE_ENTRIES;
1403 samr_CreateUser2
1405 This call uses transactions to ensure we don't get a new conflicting
1406 user while we are processing this, and to ensure the user either
1407 completly exists, or does not.
1409 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1410 struct samr_CreateUser2 *r)
1412 NTSTATUS status;
1413 struct samr_domain_state *d_state;
1414 struct samr_account_state *a_state;
1415 struct dcesrv_handle *h;
1416 struct ldb_dn *dn;
1417 struct dom_sid *sid;
1418 struct dcesrv_handle *u_handle;
1419 const char *account_name;
1421 ZERO_STRUCTP(r->out.user_handle);
1422 *r->out.access_granted = 0;
1423 *r->out.rid = 0;
1425 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1427 d_state = h->data;
1429 if (d_state->builtin) {
1430 DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
1431 return NT_STATUS_ACCESS_DENIED;
1432 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1433 /* Domain trust accounts must be created by the LSA calls */
1434 return NT_STATUS_ACCESS_DENIED;
1436 account_name = r->in.account_name->string;
1438 if (account_name == NULL) {
1439 return NT_STATUS_INVALID_PARAMETER;
1442 status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1443 &sid, &dn);
1444 if (!NT_STATUS_IS_OK(status)) {
1445 return status;
1447 a_state = talloc(mem_ctx, struct samr_account_state);
1448 if (!a_state) {
1449 return NT_STATUS_NO_MEMORY;
1451 a_state->sam_ctx = d_state->sam_ctx;
1452 a_state->access_mask = r->in.access_mask;
1453 a_state->domain_state = talloc_reference(a_state, d_state);
1454 a_state->account_dn = talloc_steal(a_state, dn);
1456 a_state->account_name = talloc_steal(a_state, account_name);
1457 if (!a_state->account_name) {
1458 return NT_STATUS_NO_MEMORY;
1461 /* create the policy handle */
1462 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
1463 if (!u_handle) {
1464 return NT_STATUS_NO_MEMORY;
1467 u_handle->data = talloc_steal(u_handle, a_state);
1469 *r->out.user_handle = u_handle->wire_handle;
1470 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1472 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1474 return NT_STATUS_OK;
1479 samr_CreateUser
1481 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1482 struct samr_CreateUser *r)
1484 struct samr_CreateUser2 r2;
1485 uint32_t access_granted = 0;
1488 /* a simple wrapper around samr_CreateUser2 works nicely */
1490 r2 = (struct samr_CreateUser2) {
1491 .in.domain_handle = r->in.domain_handle,
1492 .in.account_name = r->in.account_name,
1493 .in.acct_flags = ACB_NORMAL,
1494 .in.access_mask = r->in.access_mask,
1495 .out.user_handle = r->out.user_handle,
1496 .out.access_granted = &access_granted,
1497 .out.rid = r->out.rid
1500 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1504 samr_EnumDomainUsers
1506 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1507 struct samr_EnumDomainUsers *r)
1509 struct dcesrv_handle *h;
1510 struct samr_domain_state *d_state;
1511 struct ldb_message **res;
1512 uint32_t i;
1513 uint32_t count;
1514 uint32_t results;
1515 uint32_t max_entries;
1516 uint32_t remaining_entries;
1517 uint32_t resume_handle;
1518 struct samr_SamEntry *entries;
1519 const char * const attrs[] = { "objectSid", "sAMAccountName",
1520 "userAccountControl", NULL };
1521 const char *const cache_attrs[] = {"objectSid", "objectGUID", NULL};
1522 struct samr_SamArray *sam;
1523 struct samr_guid_cache *cache = NULL;
1525 *r->out.resume_handle = 0;
1526 *r->out.sam = NULL;
1527 *r->out.num_entries = 0;
1529 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1531 d_state = h->data;
1532 cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_USERS_CACHE];
1535 * If the resume_handle is zero, query the database and cache the
1536 * matching GUID's
1538 if (*r->in.resume_handle == 0) {
1539 NTSTATUS status;
1540 int ldb_cnt;
1541 clear_guid_cache(cache);
1543 * search for all domain users in this domain.
1545 ldb_cnt = samdb_search_domain(d_state->sam_ctx,
1546 mem_ctx,
1547 d_state->domain_dn,
1548 &res,
1549 cache_attrs,
1550 d_state->domain_sid,
1551 "(objectClass=user)");
1552 if (ldb_cnt < 0) {
1553 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1556 * Sort the results into RID order, while the spec states there
1557 * is no order, Windows appears to sort the results by RID and
1558 * so it is possible that there are clients that depend on
1559 * this ordering
1561 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1564 * cache the sorted GUID's
1566 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1567 TALLOC_FREE(res);
1568 if (!NT_STATUS_IS_OK(status)) {
1569 return status;
1571 cache->handle = 0;
1575 * If the resume handle is out of range we return an empty response
1576 * and invalidate the cache.
1578 * From the specification:
1579 * Servers SHOULD validate that EnumerationContext is an expected
1580 * value for the server's implementation. Windows does NOT validate
1581 * the input, though the result of malformed information merely results
1582 * in inconsistent output to the client.
1584 if (*r->in.resume_handle >= cache->size) {
1585 clear_guid_cache(cache);
1586 sam = talloc(mem_ctx, struct samr_SamArray);
1587 if (!sam) {
1588 return NT_STATUS_NO_MEMORY;
1590 sam->entries = NULL;
1591 sam->count = 0;
1593 *r->out.sam = sam;
1594 *r->out.resume_handle = 0;
1595 return NT_STATUS_OK;
1599 * Calculate the number of entries to return limit by max_size.
1600 * Note that we use the w2k3 element size value of 54
1602 max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER);
1603 remaining_entries = cache->size - *r->in.resume_handle;
1604 results = MIN(remaining_entries, max_entries);
1607 * Process the list of result GUID's.
1608 * Read the details of each object and populate the Entries
1609 * for the current level.
1611 count = 0;
1612 resume_handle = *r->in.resume_handle;
1613 entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1614 if (entries == NULL) {
1615 clear_guid_cache(cache);
1616 return NT_STATUS_NO_MEMORY;
1618 for (i = 0; i < results; i++) {
1619 struct dom_sid *objectsid;
1620 uint32_t rid;
1621 struct ldb_result *rec;
1622 const uint32_t idx = *r->in.resume_handle + i;
1623 int ret;
1624 NTSTATUS status;
1625 const char *name = NULL;
1627 resume_handle++;
1629 * Read an object from disk using the GUID as the key
1631 * If the object can not be read, or it does not have a SID
1632 * it is ignored.
1634 * As a consequence of this, if all the remaining GUID's
1635 * have been deleted an empty result will be returned.
1636 * i.e. even if the previous call returned a non zero
1637 * resume_handle it is possible for no results to be returned.
1640 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1641 mem_ctx,
1642 &rec,
1643 &cache->entries[idx],
1644 attrs,
1646 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1647 struct GUID_txt_buf guid_buf;
1648 DBG_WARNING(
1649 "GUID [%s] not found\n",
1650 GUID_buf_string(&cache->entries[idx], &guid_buf));
1651 continue;
1652 } else if (ret != LDB_SUCCESS) {
1653 clear_guid_cache(cache);
1654 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1656 objectsid = samdb_result_dom_sid(mem_ctx,
1657 rec->msgs[0],
1658 "objectSID");
1659 if (objectsid == NULL) {
1660 struct GUID_txt_buf guid_buf;
1661 DBG_WARNING(
1662 "objectSID for GUID [%s] not found\n",
1663 GUID_buf_string(&cache->entries[idx], &guid_buf));
1664 continue;
1666 if (r->in.acct_flags &&
1667 ((samdb_result_acct_flags(rec->msgs[0], NULL) &
1668 r->in.acct_flags) == 0)) {
1669 continue;
1671 status = dom_sid_split_rid(NULL,
1672 objectsid,
1673 NULL,
1674 &rid);
1675 if (!NT_STATUS_IS_OK(status)) {
1676 struct dom_sid_buf sid_buf;
1677 struct GUID_txt_buf guid_buf;
1678 DBG_WARNING(
1679 "objectSID [%s] for GUID [%s] invalid\n",
1680 dom_sid_str_buf(objectsid, &sid_buf),
1681 GUID_buf_string(&cache->entries[idx], &guid_buf));
1682 continue;
1685 entries[count].idx = rid;
1686 name = ldb_msg_find_attr_as_string(
1687 rec->msgs[0], "sAMAccountName", "");
1688 entries[count].name.string = talloc_strdup(entries, name);
1689 count++;
1692 sam = talloc(mem_ctx, struct samr_SamArray);
1693 if (!sam) {
1694 clear_guid_cache(cache);
1695 return NT_STATUS_NO_MEMORY;
1698 sam->entries = entries;
1699 sam->count = count;
1701 *r->out.sam = sam;
1702 *r->out.resume_handle = resume_handle;
1703 *r->out.num_entries = count;
1706 * Signal no more results by returning zero resume handle,
1707 * the cache is also cleared at this point
1709 if (*r->out.resume_handle >= cache->size) {
1710 *r->out.resume_handle = 0;
1711 clear_guid_cache(cache);
1712 return NT_STATUS_OK;
1715 * There are more results to be returned.
1717 return STATUS_MORE_ENTRIES;
1722 samr_CreateDomAlias
1724 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1725 struct samr_CreateDomAlias *r)
1727 struct samr_domain_state *d_state;
1728 struct samr_account_state *a_state;
1729 struct dcesrv_handle *h;
1730 const char *alias_name;
1731 struct dom_sid *sid;
1732 struct dcesrv_handle *a_handle;
1733 struct ldb_dn *dn;
1734 NTSTATUS status;
1736 ZERO_STRUCTP(r->out.alias_handle);
1737 *r->out.rid = 0;
1739 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1741 d_state = h->data;
1743 if (d_state->builtin) {
1744 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1745 return NT_STATUS_ACCESS_DENIED;
1748 alias_name = r->in.alias_name->string;
1750 if (alias_name == NULL) {
1751 return NT_STATUS_INVALID_PARAMETER;
1754 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1755 if (!NT_STATUS_IS_OK(status)) {
1756 return status;
1759 a_state = talloc(mem_ctx, struct samr_account_state);
1760 if (!a_state) {
1761 return NT_STATUS_NO_MEMORY;
1764 a_state->sam_ctx = d_state->sam_ctx;
1765 a_state->access_mask = r->in.access_mask;
1766 a_state->domain_state = talloc_reference(a_state, d_state);
1767 a_state->account_dn = talloc_steal(a_state, dn);
1769 a_state->account_name = talloc_steal(a_state, alias_name);
1771 /* create the policy handle */
1772 a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1773 if (a_handle == NULL)
1774 return NT_STATUS_NO_MEMORY;
1776 a_handle->data = talloc_steal(a_handle, a_state);
1778 *r->out.alias_handle = a_handle->wire_handle;
1780 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1782 return NT_STATUS_OK;
1787 samr_EnumDomainAliases
1789 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1790 struct samr_EnumDomainAliases *r)
1792 struct dcesrv_handle *h;
1793 struct samr_domain_state *d_state;
1794 struct ldb_message **res;
1795 int i, ldb_cnt;
1796 uint32_t first, count;
1797 struct samr_SamEntry *entries;
1798 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1799 struct samr_SamArray *sam;
1801 *r->out.resume_handle = 0;
1802 *r->out.sam = NULL;
1803 *r->out.num_entries = 0;
1805 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1807 d_state = h->data;
1809 /* search for all domain aliases in this domain. This could possibly be
1810 cached and resumed based on resume_key */
1811 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1812 &res, attrs,
1813 d_state->domain_sid,
1814 "(&(|(grouptype=%d)(grouptype=%d)))"
1815 "(objectclass=group))",
1816 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1817 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1818 if (ldb_cnt < 0) {
1819 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1822 /* convert to SamEntry format */
1823 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1824 if (!entries) {
1825 return NT_STATUS_NO_MEMORY;
1828 count = 0;
1830 for (i=0;i<ldb_cnt;i++) {
1831 struct dom_sid *alias_sid;
1833 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1834 "objectSid");
1836 if (alias_sid == NULL) {
1837 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1840 entries[count].idx =
1841 alias_sid->sub_auths[alias_sid->num_auths-1];
1842 entries[count].name.string =
1843 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1844 count += 1;
1847 /* sort the results by rid */
1848 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1850 /* find the first entry to return */
1851 for (first=0;
1852 first<count && entries[first].idx <= *r->in.resume_handle;
1853 first++) ;
1855 /* return the rest, limit by max_size. Note that we
1856 use the w2k3 element size value of 54 */
1857 *r->out.num_entries = count - first;
1858 *r->out.num_entries = MIN(*r->out.num_entries,
1859 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1861 sam = talloc(mem_ctx, struct samr_SamArray);
1862 if (!sam) {
1863 return NT_STATUS_NO_MEMORY;
1866 sam->entries = entries+first;
1867 sam->count = *r->out.num_entries;
1869 *r->out.sam = sam;
1871 if (first == count) {
1872 return NT_STATUS_OK;
1875 if (*r->out.num_entries < count - first) {
1876 *r->out.resume_handle =
1877 entries[first+*r->out.num_entries-1].idx;
1878 return STATUS_MORE_ENTRIES;
1881 return NT_STATUS_OK;
1886 samr_GetAliasMembership
1888 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1889 struct samr_GetAliasMembership *r)
1891 struct dcesrv_handle *h;
1892 struct samr_domain_state *d_state;
1893 char *filter;
1894 const char * const attrs[] = { "objectSid", NULL };
1895 struct ldb_message **res;
1896 uint32_t i;
1897 int count = 0;
1899 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1901 d_state = h->data;
1903 filter = talloc_asprintf(mem_ctx,
1904 "(&(|(grouptype=%d)(grouptype=%d))"
1905 "(objectclass=group)(|",
1906 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1907 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1908 if (filter == NULL) {
1909 return NT_STATUS_NO_MEMORY;
1912 for (i=0; i<r->in.sids->num_sids; i++) {
1913 struct dom_sid_buf buf;
1915 filter = talloc_asprintf_append(
1916 filter,
1917 "(member=<SID=%s>)",
1918 dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
1920 if (filter == NULL) {
1921 return NT_STATUS_NO_MEMORY;
1925 /* Find out if we had at least one valid member SID passed - otherwise
1926 * just skip the search. */
1927 if (strstr(filter, "member") != NULL) {
1928 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1929 &res, attrs, d_state->domain_sid,
1930 "%s))", filter);
1931 if (count < 0) {
1932 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1936 r->out.rids->count = 0;
1937 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1938 if (r->out.rids->ids == NULL)
1939 return NT_STATUS_NO_MEMORY;
1941 for (i=0; i<count; i++) {
1942 struct dom_sid *alias_sid;
1944 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1945 if (alias_sid == NULL) {
1946 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1949 r->out.rids->ids[r->out.rids->count] =
1950 alias_sid->sub_auths[alias_sid->num_auths-1];
1951 r->out.rids->count += 1;
1954 return NT_STATUS_OK;
1959 samr_LookupNames
1961 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1962 struct samr_LookupNames *r)
1964 struct dcesrv_handle *h;
1965 struct samr_domain_state *d_state;
1966 uint32_t i, num_mapped;
1967 NTSTATUS status = NT_STATUS_OK;
1968 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1969 int count;
1971 ZERO_STRUCTP(r->out.rids);
1972 ZERO_STRUCTP(r->out.types);
1974 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1976 d_state = h->data;
1978 if (r->in.num_names == 0) {
1979 return NT_STATUS_OK;
1982 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1983 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1984 if (!r->out.rids->ids || !r->out.types->ids) {
1985 return NT_STATUS_NO_MEMORY;
1987 r->out.rids->count = r->in.num_names;
1988 r->out.types->count = r->in.num_names;
1990 num_mapped = 0;
1992 for (i=0;i<r->in.num_names;i++) {
1993 struct ldb_message **res;
1994 struct dom_sid *sid;
1995 uint32_t atype, rtype;
1997 r->out.rids->ids[i] = 0;
1998 r->out.types->ids[i] = SID_NAME_UNKNOWN;
2000 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
2001 "sAMAccountName=%s",
2002 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
2003 if (count != 1) {
2004 status = STATUS_SOME_UNMAPPED;
2005 continue;
2008 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
2009 if (sid == NULL) {
2010 status = STATUS_SOME_UNMAPPED;
2011 continue;
2014 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
2015 if (atype == 0) {
2016 status = STATUS_SOME_UNMAPPED;
2017 continue;
2020 rtype = ds_atype_map(atype);
2022 if (rtype == SID_NAME_UNKNOWN) {
2023 status = STATUS_SOME_UNMAPPED;
2024 continue;
2027 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
2028 r->out.types->ids[i] = rtype;
2029 num_mapped++;
2032 if (num_mapped == 0) {
2033 return NT_STATUS_NONE_MAPPED;
2035 return status;
2040 samr_LookupRids
2042 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2043 struct samr_LookupRids *r)
2045 NTSTATUS status;
2046 struct dcesrv_handle *h;
2047 struct samr_domain_state *d_state;
2048 const char **names;
2049 struct lsa_String *lsa_names;
2050 enum lsa_SidType *ids;
2052 ZERO_STRUCTP(r->out.names);
2053 ZERO_STRUCTP(r->out.types);
2055 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2057 d_state = h->data;
2059 if (r->in.num_rids == 0)
2060 return NT_STATUS_OK;
2062 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
2063 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
2064 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
2066 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
2067 return NT_STATUS_NO_MEMORY;
2069 r->out.names->names = lsa_names;
2070 r->out.names->count = r->in.num_rids;
2072 r->out.types->ids = (uint32_t *) ids;
2073 r->out.types->count = r->in.num_rids;
2075 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
2076 r->in.num_rids, r->in.rids, names, ids);
2077 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
2078 uint32_t i;
2079 for (i = 0; i < r->in.num_rids; i++) {
2080 lsa_names[i].string = names[i];
2083 return status;
2088 samr_OpenGroup
2090 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2091 struct samr_OpenGroup *r)
2093 struct samr_domain_state *d_state;
2094 struct samr_account_state *a_state;
2095 struct dcesrv_handle *h;
2096 const char *groupname;
2097 struct dom_sid *sid;
2098 struct ldb_message **msgs;
2099 struct dcesrv_handle *g_handle;
2100 const char * const attrs[2] = { "sAMAccountName", NULL };
2101 int ret;
2103 ZERO_STRUCTP(r->out.group_handle);
2105 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2107 d_state = h->data;
2109 /* form the group SID */
2110 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2111 if (!sid) {
2112 return NT_STATUS_NO_MEMORY;
2115 /* search for the group record */
2116 if (d_state->builtin) {
2117 ret = gendb_search(d_state->sam_ctx,
2118 mem_ctx, d_state->domain_dn, &msgs, attrs,
2119 "(&(objectSid=%s)(objectClass=group)"
2120 "(groupType=%d))",
2121 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2122 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
2123 } else {
2124 ret = gendb_search(d_state->sam_ctx,
2125 mem_ctx, d_state->domain_dn, &msgs, attrs,
2126 "(&(objectSid=%s)(objectClass=group)"
2127 "(|(groupType=%d)(groupType=%d)))",
2128 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2129 GTYPE_SECURITY_UNIVERSAL_GROUP,
2130 GTYPE_SECURITY_GLOBAL_GROUP);
2132 if (ret == 0) {
2133 return NT_STATUS_NO_SUCH_GROUP;
2135 if (ret != 1) {
2136 DEBUG(0,("Found %d records matching sid %s\n",
2137 ret, dom_sid_string(mem_ctx, sid)));
2138 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2141 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2142 if (groupname == NULL) {
2143 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2144 dom_sid_string(mem_ctx, sid)));
2145 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2148 a_state = talloc(mem_ctx, struct samr_account_state);
2149 if (!a_state) {
2150 return NT_STATUS_NO_MEMORY;
2152 a_state->sam_ctx = d_state->sam_ctx;
2153 a_state->access_mask = r->in.access_mask;
2154 a_state->domain_state = talloc_reference(a_state, d_state);
2155 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2156 a_state->account_sid = talloc_steal(a_state, sid);
2157 a_state->account_name = talloc_strdup(a_state, groupname);
2158 if (!a_state->account_name) {
2159 return NT_STATUS_NO_MEMORY;
2162 /* create the policy handle */
2163 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
2164 if (!g_handle) {
2165 return NT_STATUS_NO_MEMORY;
2168 g_handle->data = talloc_steal(g_handle, a_state);
2170 *r->out.group_handle = g_handle->wire_handle;
2172 return NT_STATUS_OK;
2176 samr_QueryGroupInfo
2178 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2179 struct samr_QueryGroupInfo *r)
2181 struct dcesrv_handle *h;
2182 struct samr_account_state *a_state;
2183 struct ldb_message *msg, **res;
2184 const char * const attrs[4] = { "sAMAccountName", "description",
2185 "numMembers", NULL };
2186 int ret;
2187 union samr_GroupInfo *info;
2189 *r->out.info = NULL;
2191 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2193 a_state = h->data;
2195 /* pull all the group attributes */
2196 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2197 a_state->account_dn, &res, attrs);
2198 if (ret == 0) {
2199 return NT_STATUS_NO_SUCH_GROUP;
2201 if (ret != 1) {
2202 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2204 msg = res[0];
2206 /* allocate the info structure */
2207 info = talloc_zero(mem_ctx, union samr_GroupInfo);
2208 if (info == NULL) {
2209 return NT_STATUS_NO_MEMORY;
2212 /* Fill in the level */
2213 switch (r->in.level) {
2214 case GROUPINFOALL:
2215 QUERY_STRING(msg, all.name, "sAMAccountName");
2216 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2217 QUERY_UINT (msg, all.num_members, "numMembers")
2218 QUERY_STRING(msg, all.description, "description");
2219 break;
2220 case GROUPINFONAME:
2221 QUERY_STRING(msg, name, "sAMAccountName");
2222 break;
2223 case GROUPINFOATTRIBUTES:
2224 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2225 break;
2226 case GROUPINFODESCRIPTION:
2227 QUERY_STRING(msg, description, "description");
2228 break;
2229 case GROUPINFOALL2:
2230 QUERY_STRING(msg, all2.name, "sAMAccountName");
2231 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
2232 QUERY_UINT (msg, all2.num_members, "numMembers")
2233 QUERY_STRING(msg, all2.description, "description");
2234 break;
2235 default:
2236 talloc_free(info);
2237 return NT_STATUS_INVALID_INFO_CLASS;
2240 *r->out.info = info;
2242 return NT_STATUS_OK;
2247 samr_SetGroupInfo
2249 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2250 struct samr_SetGroupInfo *r)
2252 struct dcesrv_handle *h;
2253 struct samr_account_state *g_state;
2254 struct ldb_message *msg;
2255 int ret;
2257 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2259 g_state = h->data;
2261 msg = ldb_msg_new(mem_ctx);
2262 if (msg == NULL) {
2263 return NT_STATUS_NO_MEMORY;
2266 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2267 if (!msg->dn) {
2268 return NT_STATUS_NO_MEMORY;
2271 switch (r->in.level) {
2272 case GROUPINFODESCRIPTION:
2273 SET_STRING(msg, description, "description");
2274 break;
2275 case GROUPINFONAME:
2276 /* On W2k3 this does not change the name, it changes the
2277 * sAMAccountName attribute */
2278 SET_STRING(msg, name, "sAMAccountName");
2279 break;
2280 case GROUPINFOATTRIBUTES:
2281 /* This does not do anything obviously visible in W2k3 LDAP */
2282 return NT_STATUS_OK;
2283 default:
2284 return NT_STATUS_INVALID_INFO_CLASS;
2287 /* modify the samdb record */
2288 ret = ldb_modify(g_state->sam_ctx, msg);
2289 if (ret != LDB_SUCCESS) {
2290 return dsdb_ldb_err_to_ntstatus(ret);
2293 return NT_STATUS_OK;
2298 samr_AddGroupMember
2300 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2301 struct samr_AddGroupMember *r)
2303 struct dcesrv_handle *h;
2304 struct samr_account_state *a_state;
2305 struct samr_domain_state *d_state;
2306 struct ldb_message *mod;
2307 struct dom_sid *membersid;
2308 const char *memberdn;
2309 struct ldb_result *res;
2310 const char * const attrs[] = { NULL };
2311 int ret;
2313 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2315 a_state = h->data;
2316 d_state = a_state->domain_state;
2318 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2319 if (membersid == NULL) {
2320 return NT_STATUS_NO_MEMORY;
2323 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2324 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2325 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2326 "(objectSid=%s)",
2327 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2329 if (ret != LDB_SUCCESS) {
2330 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2333 if (res->count == 0) {
2334 return NT_STATUS_NO_SUCH_USER;
2337 if (res->count > 1) {
2338 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2341 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2343 if (memberdn == NULL)
2344 return NT_STATUS_NO_MEMORY;
2346 mod = ldb_msg_new(mem_ctx);
2347 if (mod == NULL) {
2348 return NT_STATUS_NO_MEMORY;
2351 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2353 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2354 memberdn);
2355 if (ret != LDB_SUCCESS) {
2356 return dsdb_ldb_err_to_ntstatus(ret);
2359 ret = ldb_modify(a_state->sam_ctx, mod);
2360 switch (ret) {
2361 case LDB_SUCCESS:
2362 return NT_STATUS_OK;
2363 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2364 return NT_STATUS_MEMBER_IN_GROUP;
2365 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2366 return NT_STATUS_ACCESS_DENIED;
2367 default:
2368 return dsdb_ldb_err_to_ntstatus(ret);
2374 samr_DeleteDomainGroup
2376 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2377 struct samr_DeleteDomainGroup *r)
2379 struct dcesrv_handle *h;
2380 struct samr_account_state *a_state;
2381 int ret;
2383 *r->out.group_handle = *r->in.group_handle;
2385 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2387 a_state = h->data;
2389 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2390 if (ret != LDB_SUCCESS) {
2391 return dsdb_ldb_err_to_ntstatus(ret);
2394 talloc_free(h);
2395 ZERO_STRUCTP(r->out.group_handle);
2397 return NT_STATUS_OK;
2402 samr_DeleteGroupMember
2404 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2405 struct samr_DeleteGroupMember *r)
2407 struct dcesrv_handle *h;
2408 struct samr_account_state *a_state;
2409 struct samr_domain_state *d_state;
2410 struct ldb_message *mod;
2411 struct dom_sid *membersid;
2412 const char *memberdn;
2413 struct ldb_result *res;
2414 const char * const attrs[] = { NULL };
2415 int ret;
2417 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2419 a_state = h->data;
2420 d_state = a_state->domain_state;
2422 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2423 if (membersid == NULL) {
2424 return NT_STATUS_NO_MEMORY;
2427 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2428 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2429 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2430 "(objectSid=%s)",
2431 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2433 if (ret != LDB_SUCCESS) {
2434 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2437 if (res->count == 0) {
2438 return NT_STATUS_NO_SUCH_USER;
2441 if (res->count > 1) {
2442 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2445 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2447 if (memberdn == NULL)
2448 return NT_STATUS_NO_MEMORY;
2450 mod = ldb_msg_new(mem_ctx);
2451 if (mod == NULL) {
2452 return NT_STATUS_NO_MEMORY;
2455 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2457 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2458 memberdn);
2459 if (ret != LDB_SUCCESS) {
2460 return NT_STATUS_NO_MEMORY;
2463 ret = ldb_modify(a_state->sam_ctx, mod);
2464 switch (ret) {
2465 case LDB_SUCCESS:
2466 return NT_STATUS_OK;
2467 case LDB_ERR_UNWILLING_TO_PERFORM:
2468 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2469 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2470 return NT_STATUS_ACCESS_DENIED;
2471 default:
2472 return dsdb_ldb_err_to_ntstatus(ret);
2478 samr_QueryGroupMember
2480 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2481 struct samr_QueryGroupMember *r)
2483 struct dcesrv_handle *h;
2484 struct samr_account_state *a_state;
2485 struct samr_domain_state *d_state;
2486 struct samr_RidAttrArray *array;
2487 unsigned int i, num_members;
2488 struct dom_sid *members;
2489 NTSTATUS status;
2491 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2493 a_state = h->data;
2494 d_state = a_state->domain_state;
2496 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2497 a_state->account_dn, &members,
2498 &num_members);
2499 if (!NT_STATUS_IS_OK(status)) {
2500 return status;
2503 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2504 if (array == NULL) {
2505 return NT_STATUS_NO_MEMORY;
2508 if (num_members == 0) {
2509 *r->out.rids = array;
2511 return NT_STATUS_OK;
2514 array->rids = talloc_array(array, uint32_t, num_members);
2515 if (array->rids == NULL) {
2516 return NT_STATUS_NO_MEMORY;
2519 array->attributes = talloc_array(array, uint32_t, num_members);
2520 if (array->attributes == NULL) {
2521 return NT_STATUS_NO_MEMORY;
2524 array->count = 0;
2525 for (i=0; i<num_members; i++) {
2526 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2527 continue;
2530 status = dom_sid_split_rid(NULL, &members[i], NULL,
2531 &array->rids[array->count]);
2532 if (!NT_STATUS_IS_OK(status)) {
2533 return status;
2536 array->attributes[array->count] = SE_GROUP_MANDATORY |
2537 SE_GROUP_ENABLED_BY_DEFAULT |
2538 SE_GROUP_ENABLED;
2539 array->count++;
2542 *r->out.rids = array;
2544 return NT_STATUS_OK;
2549 samr_SetMemberAttributesOfGroup
2551 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2552 struct samr_SetMemberAttributesOfGroup *r)
2554 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2559 samr_OpenAlias
2561 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2562 struct samr_OpenAlias *r)
2564 struct samr_domain_state *d_state;
2565 struct samr_account_state *a_state;
2566 struct dcesrv_handle *h;
2567 const char *alias_name;
2568 struct dom_sid *sid;
2569 struct ldb_message **msgs;
2570 struct dcesrv_handle *g_handle;
2571 const char * const attrs[2] = { "sAMAccountName", NULL };
2572 int ret;
2574 ZERO_STRUCTP(r->out.alias_handle);
2576 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2578 d_state = h->data;
2580 /* form the alias SID */
2581 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2582 if (sid == NULL)
2583 return NT_STATUS_NO_MEMORY;
2585 /* search for the group record */
2586 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2587 "(&(objectSid=%s)(objectclass=group)"
2588 "(|(grouptype=%d)(grouptype=%d)))",
2589 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2590 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2591 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2592 if (ret == 0) {
2593 return NT_STATUS_NO_SUCH_ALIAS;
2595 if (ret != 1) {
2596 DEBUG(0,("Found %d records matching sid %s\n",
2597 ret, dom_sid_string(mem_ctx, sid)));
2598 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2601 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2602 if (alias_name == NULL) {
2603 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2604 dom_sid_string(mem_ctx, sid)));
2605 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2608 a_state = talloc(mem_ctx, struct samr_account_state);
2609 if (!a_state) {
2610 return NT_STATUS_NO_MEMORY;
2612 a_state->sam_ctx = d_state->sam_ctx;
2613 a_state->access_mask = r->in.access_mask;
2614 a_state->domain_state = talloc_reference(a_state, d_state);
2615 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2616 a_state->account_sid = talloc_steal(a_state, sid);
2617 a_state->account_name = talloc_strdup(a_state, alias_name);
2618 if (!a_state->account_name) {
2619 return NT_STATUS_NO_MEMORY;
2622 /* create the policy handle */
2623 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2624 if (!g_handle) {
2625 return NT_STATUS_NO_MEMORY;
2628 g_handle->data = talloc_steal(g_handle, a_state);
2630 *r->out.alias_handle = g_handle->wire_handle;
2632 return NT_STATUS_OK;
2637 samr_QueryAliasInfo
2639 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2640 struct samr_QueryAliasInfo *r)
2642 struct dcesrv_handle *h;
2643 struct samr_account_state *a_state;
2644 struct ldb_message *msg, **res;
2645 const char * const attrs[4] = { "sAMAccountName", "description",
2646 "numMembers", NULL };
2647 int ret;
2648 union samr_AliasInfo *info;
2650 *r->out.info = NULL;
2652 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2654 a_state = h->data;
2656 /* pull all the alias attributes */
2657 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2658 a_state->account_dn, &res, attrs);
2659 if (ret == 0) {
2660 return NT_STATUS_NO_SUCH_ALIAS;
2662 if (ret != 1) {
2663 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2665 msg = res[0];
2667 /* allocate the info structure */
2668 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2669 if (info == NULL) {
2670 return NT_STATUS_NO_MEMORY;
2673 switch(r->in.level) {
2674 case ALIASINFOALL:
2675 QUERY_STRING(msg, all.name, "sAMAccountName");
2676 QUERY_UINT (msg, all.num_members, "numMembers");
2677 QUERY_STRING(msg, all.description, "description");
2678 break;
2679 case ALIASINFONAME:
2680 QUERY_STRING(msg, name, "sAMAccountName");
2681 break;
2682 case ALIASINFODESCRIPTION:
2683 QUERY_STRING(msg, description, "description");
2684 break;
2685 default:
2686 talloc_free(info);
2687 return NT_STATUS_INVALID_INFO_CLASS;
2690 *r->out.info = info;
2692 return NT_STATUS_OK;
2697 samr_SetAliasInfo
2699 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2700 struct samr_SetAliasInfo *r)
2702 struct dcesrv_handle *h;
2703 struct samr_account_state *a_state;
2704 struct ldb_message *msg;
2705 int ret;
2707 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2709 a_state = h->data;
2711 msg = ldb_msg_new(mem_ctx);
2712 if (msg == NULL) {
2713 return NT_STATUS_NO_MEMORY;
2716 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2717 if (!msg->dn) {
2718 return NT_STATUS_NO_MEMORY;
2721 switch (r->in.level) {
2722 case ALIASINFODESCRIPTION:
2723 SET_STRING(msg, description, "description");
2724 break;
2725 case ALIASINFONAME:
2726 /* On W2k3 this does not change the name, it changes the
2727 * sAMAccountName attribute */
2728 SET_STRING(msg, name, "sAMAccountName");
2729 break;
2730 default:
2731 return NT_STATUS_INVALID_INFO_CLASS;
2734 /* modify the samdb record */
2735 ret = ldb_modify(a_state->sam_ctx, msg);
2736 if (ret != LDB_SUCCESS) {
2737 return dsdb_ldb_err_to_ntstatus(ret);
2740 return NT_STATUS_OK;
2745 samr_DeleteDomAlias
2747 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2748 struct samr_DeleteDomAlias *r)
2750 struct dcesrv_handle *h;
2751 struct samr_account_state *a_state;
2752 int ret;
2754 *r->out.alias_handle = *r->in.alias_handle;
2756 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2758 a_state = h->data;
2760 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2761 if (ret != LDB_SUCCESS) {
2762 return dsdb_ldb_err_to_ntstatus(ret);
2765 talloc_free(h);
2766 ZERO_STRUCTP(r->out.alias_handle);
2768 return NT_STATUS_OK;
2773 samr_AddAliasMember
2775 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2776 struct samr_AddAliasMember *r)
2778 struct dcesrv_handle *h;
2779 struct samr_account_state *a_state;
2780 struct samr_domain_state *d_state;
2781 struct ldb_message *mod;
2782 struct ldb_message **msgs;
2783 const char * const attrs[] = { NULL };
2784 struct ldb_dn *memberdn = NULL;
2785 int ret;
2786 NTSTATUS status;
2788 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2790 a_state = h->data;
2791 d_state = a_state->domain_state;
2793 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2794 &msgs, attrs, "(objectsid=%s)",
2795 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2797 if (ret == 1) {
2798 memberdn = msgs[0]->dn;
2799 } else if (ret == 0) {
2800 status = samdb_create_foreign_security_principal(
2801 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2802 if (!NT_STATUS_IS_OK(status)) {
2803 return status;
2805 } else {
2806 DEBUG(0,("Found %d records matching sid %s\n",
2807 ret, dom_sid_string(mem_ctx, r->in.sid)));
2808 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2811 if (memberdn == NULL) {
2812 DEBUG(0, ("Could not find memberdn\n"));
2813 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2816 mod = ldb_msg_new(mem_ctx);
2817 if (mod == NULL) {
2818 return NT_STATUS_NO_MEMORY;
2821 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2823 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2824 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2825 if (ret != LDB_SUCCESS) {
2826 return dsdb_ldb_err_to_ntstatus(ret);
2829 ret = ldb_modify(a_state->sam_ctx, mod);
2830 switch (ret) {
2831 case LDB_SUCCESS:
2832 return NT_STATUS_OK;
2833 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2834 return NT_STATUS_MEMBER_IN_GROUP;
2835 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2836 return NT_STATUS_ACCESS_DENIED;
2837 default:
2838 return dsdb_ldb_err_to_ntstatus(ret);
2844 samr_DeleteAliasMember
2846 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2847 struct samr_DeleteAliasMember *r)
2849 struct dcesrv_handle *h;
2850 struct samr_account_state *a_state;
2851 struct samr_domain_state *d_state;
2852 struct ldb_message *mod;
2853 const char *memberdn;
2854 int ret;
2856 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2858 a_state = h->data;
2859 d_state = a_state->domain_state;
2861 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2862 "distinguishedName", "(objectSid=%s)",
2863 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2864 if (memberdn == NULL) {
2865 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2868 mod = ldb_msg_new(mem_ctx);
2869 if (mod == NULL) {
2870 return NT_STATUS_NO_MEMORY;
2873 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2875 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2876 memberdn);
2877 if (ret != LDB_SUCCESS) {
2878 return dsdb_ldb_err_to_ntstatus(ret);
2881 ret = ldb_modify(a_state->sam_ctx, mod);
2882 switch (ret) {
2883 case LDB_SUCCESS:
2884 return NT_STATUS_OK;
2885 case LDB_ERR_UNWILLING_TO_PERFORM:
2886 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2887 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2888 return NT_STATUS_ACCESS_DENIED;
2889 default:
2890 return dsdb_ldb_err_to_ntstatus(ret);
2896 samr_GetMembersInAlias
2898 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2899 struct samr_GetMembersInAlias *r)
2901 struct dcesrv_handle *h;
2902 struct samr_account_state *a_state;
2903 struct samr_domain_state *d_state;
2904 struct lsa_SidPtr *array;
2905 unsigned int i, num_members;
2906 struct dom_sid *members;
2907 NTSTATUS status;
2909 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2911 a_state = h->data;
2912 d_state = a_state->domain_state;
2914 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2915 a_state->account_dn, &members,
2916 &num_members);
2917 if (!NT_STATUS_IS_OK(status)) {
2918 return status;
2921 if (num_members == 0) {
2922 r->out.sids->sids = NULL;
2924 return NT_STATUS_OK;
2927 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
2928 if (array == NULL) {
2929 return NT_STATUS_NO_MEMORY;
2932 for (i=0; i<num_members; i++) {
2933 array[i].sid = &members[i];
2936 r->out.sids->num_sids = num_members;
2937 r->out.sids->sids = array;
2939 return NT_STATUS_OK;
2943 samr_OpenUser
2945 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2946 struct samr_OpenUser *r)
2948 struct samr_domain_state *d_state;
2949 struct samr_account_state *a_state;
2950 struct dcesrv_handle *h;
2951 const char *account_name;
2952 struct dom_sid *sid;
2953 struct ldb_message **msgs;
2954 struct dcesrv_handle *u_handle;
2955 const char * const attrs[2] = { "sAMAccountName", NULL };
2956 int ret;
2958 ZERO_STRUCTP(r->out.user_handle);
2960 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2962 d_state = h->data;
2964 /* form the users SID */
2965 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2966 if (!sid) {
2967 return NT_STATUS_NO_MEMORY;
2970 /* search for the user record */
2971 ret = gendb_search(d_state->sam_ctx,
2972 mem_ctx, d_state->domain_dn, &msgs, attrs,
2973 "(&(objectSid=%s)(objectclass=user))",
2974 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2975 if (ret == 0) {
2976 return NT_STATUS_NO_SUCH_USER;
2978 if (ret != 1) {
2979 DEBUG(0,("Found %d records matching sid %s\n", ret,
2980 dom_sid_string(mem_ctx, sid)));
2981 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2984 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2985 if (account_name == NULL) {
2986 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2987 dom_sid_string(mem_ctx, sid)));
2988 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2991 a_state = talloc(mem_ctx, struct samr_account_state);
2992 if (!a_state) {
2993 return NT_STATUS_NO_MEMORY;
2995 a_state->sam_ctx = d_state->sam_ctx;
2996 a_state->access_mask = r->in.access_mask;
2997 a_state->domain_state = talloc_reference(a_state, d_state);
2998 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2999 a_state->account_sid = talloc_steal(a_state, sid);
3000 a_state->account_name = talloc_strdup(a_state, account_name);
3001 if (!a_state->account_name) {
3002 return NT_STATUS_NO_MEMORY;
3005 /* create the policy handle */
3006 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
3007 if (!u_handle) {
3008 return NT_STATUS_NO_MEMORY;
3011 u_handle->data = talloc_steal(u_handle, a_state);
3013 *r->out.user_handle = u_handle->wire_handle;
3015 return NT_STATUS_OK;
3021 samr_DeleteUser
3023 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3024 struct samr_DeleteUser *r)
3026 struct dcesrv_handle *h;
3027 struct samr_account_state *a_state;
3028 int ret;
3030 *r->out.user_handle = *r->in.user_handle;
3032 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3034 a_state = h->data;
3036 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
3037 if (ret != LDB_SUCCESS) {
3038 DEBUG(1, ("Failed to delete user: %s: %s\n",
3039 ldb_dn_get_linearized(a_state->account_dn),
3040 ldb_errstring(a_state->sam_ctx)));
3041 return dsdb_ldb_err_to_ntstatus(ret);
3044 talloc_free(h);
3045 ZERO_STRUCTP(r->out.user_handle);
3047 return NT_STATUS_OK;
3052 samr_QueryUserInfo
3054 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3055 struct samr_QueryUserInfo *r)
3057 struct dcesrv_handle *h;
3058 struct samr_account_state *a_state;
3059 struct ldb_message *msg, **res;
3060 int ret;
3061 struct ldb_context *sam_ctx;
3063 const char * const *attrs = NULL;
3064 union samr_UserInfo *info;
3066 NTSTATUS status;
3068 *r->out.info = NULL;
3070 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3072 a_state = h->data;
3073 sam_ctx = a_state->sam_ctx;
3075 /* fill in the reply */
3076 switch (r->in.level) {
3077 case 1:
3079 static const char * const attrs2[] = {"sAMAccountName",
3080 "displayName",
3081 "primaryGroupID",
3082 "description",
3083 "comment",
3084 NULL};
3085 attrs = attrs2;
3086 break;
3088 case 2:
3090 static const char * const attrs2[] = {"comment",
3091 "countryCode",
3092 "codePage",
3093 NULL};
3094 attrs = attrs2;
3095 break;
3097 case 3:
3099 static const char * const attrs2[] = {"sAMAccountName",
3100 "displayName",
3101 "objectSid",
3102 "primaryGroupID",
3103 "homeDirectory",
3104 "homeDrive",
3105 "scriptPath",
3106 "profilePath",
3107 "userWorkstations",
3108 "lastLogon",
3109 "lastLogoff",
3110 "pwdLastSet",
3111 "msDS-UserPasswordExpiryTimeComputed",
3112 "logonHours",
3113 "badPwdCount",
3114 "badPasswordTime",
3115 "logonCount",
3116 "userAccountControl",
3117 "msDS-User-Account-Control-Computed",
3118 NULL};
3119 attrs = attrs2;
3120 break;
3122 case 4:
3124 static const char * const attrs2[] = {"logonHours",
3125 NULL};
3126 attrs = attrs2;
3127 break;
3129 case 5:
3131 static const char * const attrs2[] = {"sAMAccountName",
3132 "displayName",
3133 "objectSid",
3134 "primaryGroupID",
3135 "homeDirectory",
3136 "homeDrive",
3137 "scriptPath",
3138 "profilePath",
3139 "description",
3140 "userWorkstations",
3141 "lastLogon",
3142 "lastLogoff",
3143 "logonHours",
3144 "badPwdCount",
3145 "badPasswordTime",
3146 "logonCount",
3147 "pwdLastSet",
3148 "msDS-ResultantPSO",
3149 "msDS-UserPasswordExpiryTimeComputed",
3150 "accountExpires",
3151 "userAccountControl",
3152 "msDS-User-Account-Control-Computed",
3153 NULL};
3154 attrs = attrs2;
3155 break;
3157 case 6:
3159 static const char * const attrs2[] = {"sAMAccountName",
3160 "displayName",
3161 NULL};
3162 attrs = attrs2;
3163 break;
3165 case 7:
3167 static const char * const attrs2[] = {"sAMAccountName",
3168 NULL};
3169 attrs = attrs2;
3170 break;
3172 case 8:
3174 static const char * const attrs2[] = {"displayName",
3175 NULL};
3176 attrs = attrs2;
3177 break;
3179 case 9:
3181 static const char * const attrs2[] = {"primaryGroupID",
3182 NULL};
3183 attrs = attrs2;
3184 break;
3186 case 10:
3188 static const char * const attrs2[] = {"homeDirectory",
3189 "homeDrive",
3190 NULL};
3191 attrs = attrs2;
3192 break;
3194 case 11:
3196 static const char * const attrs2[] = {"scriptPath",
3197 NULL};
3198 attrs = attrs2;
3199 break;
3201 case 12:
3203 static const char * const attrs2[] = {"profilePath",
3204 NULL};
3205 attrs = attrs2;
3206 break;
3208 case 13:
3210 static const char * const attrs2[] = {"description",
3211 NULL};
3212 attrs = attrs2;
3213 break;
3215 case 14:
3217 static const char * const attrs2[] = {"userWorkstations",
3218 NULL};
3219 attrs = attrs2;
3220 break;
3222 case 16:
3224 static const char * const attrs2[] = {"userAccountControl",
3225 "msDS-User-Account-Control-Computed",
3226 "pwdLastSet",
3227 "msDS-UserPasswordExpiryTimeComputed",
3228 NULL};
3229 attrs = attrs2;
3230 break;
3232 case 17:
3234 static const char * const attrs2[] = {"accountExpires",
3235 NULL};
3236 attrs = attrs2;
3237 break;
3239 case 18:
3241 return NT_STATUS_NOT_SUPPORTED;
3243 case 20:
3245 static const char * const attrs2[] = {"userParameters",
3246 NULL};
3247 attrs = attrs2;
3248 break;
3250 case 21:
3252 static const char * const attrs2[] = {"lastLogon",
3253 "lastLogoff",
3254 "pwdLastSet",
3255 "msDS-ResultantPSO",
3256 "msDS-UserPasswordExpiryTimeComputed",
3257 "accountExpires",
3258 "sAMAccountName",
3259 "displayName",
3260 "homeDirectory",
3261 "homeDrive",
3262 "scriptPath",
3263 "profilePath",
3264 "description",
3265 "userWorkstations",
3266 "comment",
3267 "userParameters",
3268 "objectSid",
3269 "primaryGroupID",
3270 "userAccountControl",
3271 "msDS-User-Account-Control-Computed",
3272 "logonHours",
3273 "badPwdCount",
3274 "badPasswordTime",
3275 "logonCount",
3276 "countryCode",
3277 "codePage",
3278 NULL};
3279 attrs = attrs2;
3280 break;
3282 case 23:
3283 case 24:
3284 case 25:
3285 case 26:
3287 return NT_STATUS_NOT_SUPPORTED;
3289 default:
3291 return NT_STATUS_INVALID_INFO_CLASS;
3295 /* pull all the user attributes */
3296 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3297 a_state->account_dn, &res, attrs);
3298 if (ret == 0) {
3299 return NT_STATUS_NO_SUCH_USER;
3301 if (ret != 1) {
3302 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3304 msg = res[0];
3306 /* allocate the info structure */
3307 info = talloc_zero(mem_ctx, union samr_UserInfo);
3308 if (info == NULL) {
3309 return NT_STATUS_NO_MEMORY;
3312 /* fill in the reply */
3313 switch (r->in.level) {
3314 case 1:
3315 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
3316 QUERY_STRING(msg, info1.full_name, "displayName");
3317 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
3318 QUERY_STRING(msg, info1.description, "description");
3319 QUERY_STRING(msg, info1.comment, "comment");
3320 break;
3322 case 2:
3323 QUERY_STRING(msg, info2.comment, "comment");
3324 QUERY_UINT (msg, info2.country_code, "countryCode");
3325 QUERY_UINT (msg, info2.code_page, "codePage");
3326 break;
3328 case 3:
3329 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
3330 QUERY_STRING(msg, info3.full_name, "displayName");
3331 QUERY_RID (msg, info3.rid, "objectSid");
3332 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
3333 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
3334 QUERY_STRING(msg, info3.home_drive, "homeDrive");
3335 QUERY_STRING(msg, info3.logon_script, "scriptPath");
3336 QUERY_STRING(msg, info3.profile_path, "profilePath");
3337 QUERY_STRING(msg, info3.workstations, "userWorkstations");
3338 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
3339 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
3340 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
3341 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3342 QUERY_UINT64(msg, info3.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3343 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
3344 /* level 3 gives the raw badPwdCount value */
3345 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
3346 QUERY_UINT (msg, info3.logon_count, "logonCount");
3347 QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
3348 break;
3350 case 4:
3351 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
3352 break;
3354 case 5:
3355 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
3356 QUERY_STRING(msg, info5.full_name, "displayName");
3357 QUERY_RID (msg, info5.rid, "objectSid");
3358 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
3359 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
3360 QUERY_STRING(msg, info5.home_drive, "homeDrive");
3361 QUERY_STRING(msg, info5.logon_script, "scriptPath");
3362 QUERY_STRING(msg, info5.profile_path, "profilePath");
3363 QUERY_STRING(msg, info5.description, "description");
3364 QUERY_STRING(msg, info5.workstations, "userWorkstations");
3365 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
3366 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
3367 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
3368 QUERY_BPWDCT(msg, info5.bad_password_count, "badPwdCount");
3369 QUERY_UINT (msg, info5.logon_count, "logonCount");
3370 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
3371 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
3372 QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
3373 break;
3375 case 6:
3376 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3377 QUERY_STRING(msg, info6.full_name, "displayName");
3378 break;
3380 case 7:
3381 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3382 break;
3384 case 8:
3385 QUERY_STRING(msg, info8.full_name, "displayName");
3386 break;
3388 case 9:
3389 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3390 break;
3392 case 10:
3393 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3394 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3395 break;
3397 case 11:
3398 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3399 break;
3401 case 12:
3402 QUERY_STRING(msg, info12.profile_path, "profilePath");
3403 break;
3405 case 13:
3406 QUERY_STRING(msg, info13.description, "description");
3407 break;
3409 case 14:
3410 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3411 break;
3413 case 16:
3414 QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3415 break;
3417 case 17:
3418 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3419 break;
3421 case 20:
3422 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
3423 if (!NT_STATUS_IS_OK(status)) {
3424 talloc_free(info);
3425 return status;
3427 break;
3429 case 21:
3430 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3431 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3432 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3433 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3434 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3435 QUERY_UINT64(msg, info21.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3436 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3437 QUERY_STRING(msg, info21.full_name, "displayName");
3438 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3439 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3440 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3441 QUERY_STRING(msg, info21.profile_path, "profilePath");
3442 QUERY_STRING(msg, info21.description, "description");
3443 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3444 QUERY_STRING(msg, info21.comment, "comment");
3445 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
3446 if (!NT_STATUS_IS_OK(status)) {
3447 talloc_free(info);
3448 return status;
3451 QUERY_RID (msg, info21.rid, "objectSid");
3452 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3453 QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3454 info->info21.fields_present = 0x08FFFFFF;
3455 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3456 QUERY_BPWDCT(msg, info21.bad_password_count, "badPwdCount");
3457 QUERY_UINT (msg, info21.logon_count, "logonCount");
3458 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3459 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3460 } else {
3461 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3463 QUERY_UINT (msg, info21.country_code, "countryCode");
3464 QUERY_UINT (msg, info21.code_page, "codePage");
3465 break;
3468 default:
3469 talloc_free(info);
3470 return NT_STATUS_INVALID_INFO_CLASS;
3473 *r->out.info = info;
3475 return NT_STATUS_OK;
3480 samr_SetUserInfo
3482 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3483 struct samr_SetUserInfo *r)
3485 struct dcesrv_handle *h;
3486 struct samr_account_state *a_state;
3487 struct ldb_message *msg;
3488 int ret;
3489 NTSTATUS status = NT_STATUS_OK;
3490 struct ldb_context *sam_ctx;
3492 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3494 a_state = h->data;
3495 sam_ctx = a_state->sam_ctx;
3497 msg = ldb_msg_new(mem_ctx);
3498 if (msg == NULL) {
3499 return NT_STATUS_NO_MEMORY;
3502 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3503 if (!msg->dn) {
3504 return NT_STATUS_NO_MEMORY;
3507 switch (r->in.level) {
3508 case 2:
3509 SET_STRING(msg, info2.comment, "comment");
3510 SET_UINT (msg, info2.country_code, "countryCode");
3511 SET_UINT (msg, info2.code_page, "codePage");
3512 break;
3514 case 4:
3515 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3516 break;
3518 case 6:
3519 SET_STRING(msg, info6.account_name, "samAccountName");
3520 SET_STRING(msg, info6.full_name, "displayName");
3521 break;
3523 case 7:
3524 SET_STRING(msg, info7.account_name, "samAccountName");
3525 break;
3527 case 8:
3528 SET_STRING(msg, info8.full_name, "displayName");
3529 break;
3531 case 9:
3532 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3533 break;
3535 case 10:
3536 SET_STRING(msg, info10.home_directory, "homeDirectory");
3537 SET_STRING(msg, info10.home_drive, "homeDrive");
3538 break;
3540 case 11:
3541 SET_STRING(msg, info11.logon_script, "scriptPath");
3542 break;
3544 case 12:
3545 SET_STRING(msg, info12.profile_path, "profilePath");
3546 break;
3548 case 13:
3549 SET_STRING(msg, info13.description, "description");
3550 break;
3552 case 14:
3553 SET_STRING(msg, info14.workstations, "userWorkstations");
3554 break;
3556 case 16:
3557 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3558 break;
3560 case 17:
3561 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3562 break;
3564 case 18:
3565 status = samr_set_password_buffers(dce_call,
3566 a_state->sam_ctx,
3567 a_state->account_dn,
3568 a_state->domain_state->domain_dn,
3569 mem_ctx,
3570 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3571 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3572 if (!NT_STATUS_IS_OK(status)) {
3573 return status;
3576 if (r->in.info->info18.password_expired > 0) {
3577 struct ldb_message_element *set_el;
3578 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3579 return NT_STATUS_NO_MEMORY;
3581 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3582 set_el->flags = LDB_FLAG_MOD_REPLACE;
3584 break;
3586 case 20:
3587 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3588 break;
3590 case 21:
3591 if (r->in.info->info21.fields_present == 0)
3592 return NT_STATUS_INVALID_PARAMETER;
3594 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3595 IFSET(SAMR_FIELD_LAST_LOGON)
3596 SET_UINT64(msg, info21.last_logon, "lastLogon");
3597 IFSET(SAMR_FIELD_LAST_LOGOFF)
3598 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3599 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3600 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3601 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3602 SET_STRING(msg, info21.account_name, "samAccountName");
3603 IFSET(SAMR_FIELD_FULL_NAME)
3604 SET_STRING(msg, info21.full_name, "displayName");
3605 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3606 SET_STRING(msg, info21.home_directory, "homeDirectory");
3607 IFSET(SAMR_FIELD_HOME_DRIVE)
3608 SET_STRING(msg, info21.home_drive, "homeDrive");
3609 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3610 SET_STRING(msg, info21.logon_script, "scriptPath");
3611 IFSET(SAMR_FIELD_PROFILE_PATH)
3612 SET_STRING(msg, info21.profile_path, "profilePath");
3613 IFSET(SAMR_FIELD_DESCRIPTION)
3614 SET_STRING(msg, info21.description, "description");
3615 IFSET(SAMR_FIELD_WORKSTATIONS)
3616 SET_STRING(msg, info21.workstations, "userWorkstations");
3617 IFSET(SAMR_FIELD_COMMENT)
3618 SET_STRING(msg, info21.comment, "comment");
3619 IFSET(SAMR_FIELD_PARAMETERS)
3620 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3621 IFSET(SAMR_FIELD_PRIMARY_GID)
3622 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3623 IFSET(SAMR_FIELD_ACCT_FLAGS)
3624 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3625 IFSET(SAMR_FIELD_LOGON_HOURS)
3626 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3627 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3628 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3629 IFSET(SAMR_FIELD_NUM_LOGONS)
3630 SET_UINT (msg, info21.logon_count, "logonCount");
3631 IFSET(SAMR_FIELD_COUNTRY_CODE)
3632 SET_UINT (msg, info21.country_code, "countryCode");
3633 IFSET(SAMR_FIELD_CODE_PAGE)
3634 SET_UINT (msg, info21.code_page, "codePage");
3636 /* password change fields */
3637 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3638 return NT_STATUS_ACCESS_DENIED;
3640 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3641 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3642 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3644 if (r->in.info->info21.lm_password_set) {
3645 if ((r->in.info->info21.lm_owf_password.length != 16)
3646 || (r->in.info->info21.lm_owf_password.size != 16)) {
3647 return NT_STATUS_INVALID_PARAMETER;
3650 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3652 if (r->in.info->info21.nt_password_set) {
3653 if ((r->in.info->info21.nt_owf_password.length != 16)
3654 || (r->in.info->info21.nt_owf_password.size != 16)) {
3655 return NT_STATUS_INVALID_PARAMETER;
3658 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3660 status = samr_set_password_buffers(dce_call,
3661 a_state->sam_ctx,
3662 a_state->account_dn,
3663 a_state->domain_state->domain_dn,
3664 mem_ctx,
3665 lm_pwd_hash,
3666 nt_pwd_hash);
3667 if (!NT_STATUS_IS_OK(status)) {
3668 return status;
3673 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3674 const char *t = "0";
3675 struct ldb_message_element *set_el;
3676 if (r->in.info->info21.password_expired
3677 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3678 t = "-1";
3680 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3681 return NT_STATUS_NO_MEMORY;
3683 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3684 set_el->flags = LDB_FLAG_MOD_REPLACE;
3686 #undef IFSET
3687 break;
3689 case 23:
3690 if (r->in.info->info23.info.fields_present == 0)
3691 return NT_STATUS_INVALID_PARAMETER;
3693 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3694 IFSET(SAMR_FIELD_LAST_LOGON)
3695 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3696 IFSET(SAMR_FIELD_LAST_LOGOFF)
3697 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3698 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3699 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3700 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3701 SET_STRING(msg, info23.info.account_name, "samAccountName");
3702 IFSET(SAMR_FIELD_FULL_NAME)
3703 SET_STRING(msg, info23.info.full_name, "displayName");
3704 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3705 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3706 IFSET(SAMR_FIELD_HOME_DRIVE)
3707 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3708 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3709 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3710 IFSET(SAMR_FIELD_PROFILE_PATH)
3711 SET_STRING(msg, info23.info.profile_path, "profilePath");
3712 IFSET(SAMR_FIELD_DESCRIPTION)
3713 SET_STRING(msg, info23.info.description, "description");
3714 IFSET(SAMR_FIELD_WORKSTATIONS)
3715 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3716 IFSET(SAMR_FIELD_COMMENT)
3717 SET_STRING(msg, info23.info.comment, "comment");
3718 IFSET(SAMR_FIELD_PARAMETERS)
3719 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3720 IFSET(SAMR_FIELD_PRIMARY_GID)
3721 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3722 IFSET(SAMR_FIELD_ACCT_FLAGS)
3723 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3724 IFSET(SAMR_FIELD_LOGON_HOURS)
3725 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3726 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3727 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3728 IFSET(SAMR_FIELD_NUM_LOGONS)
3729 SET_UINT (msg, info23.info.logon_count, "logonCount");
3731 IFSET(SAMR_FIELD_COUNTRY_CODE)
3732 SET_UINT (msg, info23.info.country_code, "countryCode");
3733 IFSET(SAMR_FIELD_CODE_PAGE)
3734 SET_UINT (msg, info23.info.code_page, "codePage");
3736 /* password change fields */
3737 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3738 return NT_STATUS_ACCESS_DENIED;
3740 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3741 status = samr_set_password(dce_call,
3742 a_state->sam_ctx,
3743 a_state->account_dn,
3744 a_state->domain_state->domain_dn,
3745 mem_ctx,
3746 &r->in.info->info23.password);
3747 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3748 status = samr_set_password(dce_call,
3749 a_state->sam_ctx,
3750 a_state->account_dn,
3751 a_state->domain_state->domain_dn,
3752 mem_ctx,
3753 &r->in.info->info23.password);
3755 if (!NT_STATUS_IS_OK(status)) {
3756 return status;
3759 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3760 const char *t = "0";
3761 struct ldb_message_element *set_el;
3762 if (r->in.info->info23.info.password_expired
3763 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3764 t = "-1";
3766 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3767 return NT_STATUS_NO_MEMORY;
3769 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3770 set_el->flags = LDB_FLAG_MOD_REPLACE;
3772 #undef IFSET
3773 break;
3775 /* the set password levels are handled separately */
3776 case 24:
3777 status = samr_set_password(dce_call,
3778 a_state->sam_ctx,
3779 a_state->account_dn,
3780 a_state->domain_state->domain_dn,
3781 mem_ctx,
3782 &r->in.info->info24.password);
3783 if (!NT_STATUS_IS_OK(status)) {
3784 return status;
3787 if (r->in.info->info24.password_expired > 0) {
3788 struct ldb_message_element *set_el;
3789 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3790 return NT_STATUS_NO_MEMORY;
3792 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3793 set_el->flags = LDB_FLAG_MOD_REPLACE;
3795 break;
3797 case 25:
3798 if (r->in.info->info25.info.fields_present == 0)
3799 return NT_STATUS_INVALID_PARAMETER;
3801 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3802 IFSET(SAMR_FIELD_LAST_LOGON)
3803 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3804 IFSET(SAMR_FIELD_LAST_LOGOFF)
3805 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3806 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3807 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3808 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3809 SET_STRING(msg, info25.info.account_name, "samAccountName");
3810 IFSET(SAMR_FIELD_FULL_NAME)
3811 SET_STRING(msg, info25.info.full_name, "displayName");
3812 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3813 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3814 IFSET(SAMR_FIELD_HOME_DRIVE)
3815 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3816 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3817 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3818 IFSET(SAMR_FIELD_PROFILE_PATH)
3819 SET_STRING(msg, info25.info.profile_path, "profilePath");
3820 IFSET(SAMR_FIELD_DESCRIPTION)
3821 SET_STRING(msg, info25.info.description, "description");
3822 IFSET(SAMR_FIELD_WORKSTATIONS)
3823 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3824 IFSET(SAMR_FIELD_COMMENT)
3825 SET_STRING(msg, info25.info.comment, "comment");
3826 IFSET(SAMR_FIELD_PARAMETERS)
3827 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3828 IFSET(SAMR_FIELD_PRIMARY_GID)
3829 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3830 IFSET(SAMR_FIELD_ACCT_FLAGS)
3831 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3832 IFSET(SAMR_FIELD_LOGON_HOURS)
3833 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3834 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3835 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
3836 IFSET(SAMR_FIELD_NUM_LOGONS)
3837 SET_UINT (msg, info25.info.logon_count, "logonCount");
3838 IFSET(SAMR_FIELD_COUNTRY_CODE)
3839 SET_UINT (msg, info25.info.country_code, "countryCode");
3840 IFSET(SAMR_FIELD_CODE_PAGE)
3841 SET_UINT (msg, info25.info.code_page, "codePage");
3843 /* password change fields */
3844 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3845 return NT_STATUS_ACCESS_DENIED;
3847 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3848 status = samr_set_password_ex(dce_call,
3849 a_state->sam_ctx,
3850 a_state->account_dn,
3851 a_state->domain_state->domain_dn,
3852 mem_ctx,
3853 &r->in.info->info25.password);
3854 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3855 status = samr_set_password_ex(dce_call,
3856 a_state->sam_ctx,
3857 a_state->account_dn,
3858 a_state->domain_state->domain_dn,
3859 mem_ctx,
3860 &r->in.info->info25.password);
3862 if (!NT_STATUS_IS_OK(status)) {
3863 return status;
3866 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3867 const char *t = "0";
3868 struct ldb_message_element *set_el;
3869 if (r->in.info->info25.info.password_expired
3870 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3871 t = "-1";
3873 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3874 return NT_STATUS_NO_MEMORY;
3876 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3877 set_el->flags = LDB_FLAG_MOD_REPLACE;
3879 #undef IFSET
3880 break;
3882 /* the set password levels are handled separately */
3883 case 26:
3884 status = samr_set_password_ex(dce_call,
3885 a_state->sam_ctx,
3886 a_state->account_dn,
3887 a_state->domain_state->domain_dn,
3888 mem_ctx,
3889 &r->in.info->info26.password);
3890 if (!NT_STATUS_IS_OK(status)) {
3891 return status;
3894 if (r->in.info->info26.password_expired > 0) {
3895 const char *t = "0";
3896 struct ldb_message_element *set_el;
3897 if (r->in.info->info26.password_expired
3898 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3899 t = "-1";
3901 if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3902 return NT_STATUS_NO_MEMORY;
3904 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3905 set_el->flags = LDB_FLAG_MOD_REPLACE;
3907 break;
3909 default:
3910 /* many info classes are not valid for SetUserInfo */
3911 return NT_STATUS_INVALID_INFO_CLASS;
3914 if (!NT_STATUS_IS_OK(status)) {
3915 return status;
3918 /* modify the samdb record */
3919 if (msg->num_elements > 0) {
3920 ret = ldb_modify(a_state->sam_ctx, msg);
3921 if (ret != LDB_SUCCESS) {
3922 DEBUG(1,("Failed to modify record %s: %s\n",
3923 ldb_dn_get_linearized(a_state->account_dn),
3924 ldb_errstring(a_state->sam_ctx)));
3926 return dsdb_ldb_err_to_ntstatus(ret);
3930 return NT_STATUS_OK;
3935 samr_GetGroupsForUser
3937 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3938 struct samr_GetGroupsForUser *r)
3940 struct dcesrv_handle *h;
3941 struct samr_account_state *a_state;
3942 struct samr_domain_state *d_state;
3943 struct ldb_result *res, *res_memberof;
3944 const char * const attrs[] = { "primaryGroupID",
3945 "memberOf",
3946 NULL };
3947 const char * const group_attrs[] = { "objectSid",
3948 NULL };
3950 struct samr_RidWithAttributeArray *array;
3951 struct ldb_message_element *memberof_el;
3952 int i, ret, count = 0;
3953 uint32_t primary_group_id;
3954 char *filter;
3956 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3958 a_state = h->data;
3959 d_state = a_state->domain_state;
3961 ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
3962 &res,
3963 a_state->account_dn,
3964 attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
3966 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
3967 return NT_STATUS_NO_SUCH_USER;
3968 } else if (ret != LDB_SUCCESS) {
3969 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3970 } else if (res->count != 1) {
3971 return NT_STATUS_NO_SUCH_USER;
3974 primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
3977 filter = talloc_asprintf(mem_ctx,
3978 "(&(|(grouptype=%d)(grouptype=%d))"
3979 "(objectclass=group)(|",
3980 GTYPE_SECURITY_UNIVERSAL_GROUP,
3981 GTYPE_SECURITY_GLOBAL_GROUP);
3982 if (filter == NULL) {
3983 return NT_STATUS_NO_MEMORY;
3986 memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
3987 if (memberof_el != NULL) {
3988 for (i = 0; i < memberof_el->num_values; i++) {
3989 const struct ldb_val *memberof_sid_binary;
3990 char *memberof_sid_escaped;
3991 struct ldb_dn *memberof_dn
3992 = ldb_dn_from_ldb_val(mem_ctx,
3993 a_state->sam_ctx,
3994 &memberof_el->values[i]);
3995 if (memberof_dn == NULL) {
3996 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3999 memberof_sid_binary
4000 = ldb_dn_get_extended_component(memberof_dn,
4001 "SID");
4002 if (memberof_sid_binary == NULL) {
4003 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4006 memberof_sid_escaped = ldb_binary_encode(mem_ctx,
4007 *memberof_sid_binary);
4008 if (memberof_sid_escaped == NULL) {
4009 return NT_STATUS_NO_MEMORY;
4011 filter = talloc_asprintf_append(filter, "(objectSID=%s)",
4012 memberof_sid_escaped);
4013 if (filter == NULL) {
4014 return NT_STATUS_NO_MEMORY;
4018 ret = dsdb_search(a_state->sam_ctx, mem_ctx,
4019 &res_memberof,
4020 d_state->domain_dn,
4021 LDB_SCOPE_SUBTREE,
4022 group_attrs, 0,
4023 "%s))", filter);
4025 if (ret != LDB_SUCCESS) {
4026 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4028 count = res_memberof->count;
4031 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
4032 if (array == NULL)
4033 return NT_STATUS_NO_MEMORY;
4035 array->count = 0;
4036 array->rids = NULL;
4038 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
4039 count + 1);
4040 if (array->rids == NULL)
4041 return NT_STATUS_NO_MEMORY;
4043 /* Adds the primary group */
4045 array->rids[0].rid = primary_group_id;
4046 array->rids[0].attributes = SE_GROUP_MANDATORY
4047 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
4048 array->count += 1;
4050 /* Adds the additional groups */
4051 for (i = 0; i < count; i++) {
4052 struct dom_sid *group_sid;
4054 group_sid = samdb_result_dom_sid(mem_ctx,
4055 res_memberof->msgs[i],
4056 "objectSid");
4057 if (group_sid == NULL) {
4058 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4061 array->rids[i + 1].rid =
4062 group_sid->sub_auths[group_sid->num_auths-1];
4063 array->rids[i + 1].attributes = SE_GROUP_MANDATORY
4064 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
4065 array->count += 1;
4068 *r->out.rids = array;
4070 return NT_STATUS_OK;
4074 * samr_QueryDisplayInfo
4076 * A cache of the GUID's matching the last query is maintained
4077 * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
4078 * n the dcesrv_handle.
4080 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4081 struct samr_QueryDisplayInfo *r)
4083 struct dcesrv_handle *h;
4084 struct samr_domain_state *d_state;
4085 struct ldb_result *res;
4086 uint32_t i;
4087 uint32_t results = 0;
4088 uint32_t count = 0;
4089 const char *const cache_attrs[] = {"objectGUID", NULL};
4090 const char *const attrs[] = {
4091 "objectSID", "sAMAccountName", "displayName", "description", NULL};
4092 struct samr_DispEntryFull *entriesFull = NULL;
4093 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
4094 struct samr_DispEntryAscii *entriesAscii = NULL;
4095 struct samr_DispEntryGeneral *entriesGeneral = NULL;
4096 const char *filter;
4097 int ret;
4098 NTSTATUS status;
4099 struct samr_guid_cache *cache = NULL;
4101 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4103 d_state = h->data;
4105 cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
4107 * Can the cached results be used?
4108 * The cache is discarded if the start index is zero, or the requested
4109 * level is different from that in the cache.
4111 if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
4113 * The cached results can not be used, so will need to query
4114 * the database.
4118 * Get the search filter for the current level
4120 switch (r->in.level) {
4121 case 1:
4122 case 4:
4123 filter = talloc_asprintf(mem_ctx,
4124 "(&(objectclass=user)"
4125 "(sAMAccountType=%d))",
4126 ATYPE_NORMAL_ACCOUNT);
4127 break;
4128 case 2:
4129 filter = talloc_asprintf(mem_ctx,
4130 "(&(objectclass=user)"
4131 "(sAMAccountType=%d))",
4132 ATYPE_WORKSTATION_TRUST);
4133 break;
4134 case 3:
4135 case 5:
4136 filter =
4137 talloc_asprintf(mem_ctx,
4138 "(&(|(groupType=%d)(groupType=%d))"
4139 "(objectClass=group))",
4140 GTYPE_SECURITY_UNIVERSAL_GROUP,
4141 GTYPE_SECURITY_GLOBAL_GROUP);
4142 break;
4143 default:
4144 return NT_STATUS_INVALID_INFO_CLASS;
4146 clear_guid_cache(cache);
4149 * search for all requested objects in all domains.
4151 ret = dsdb_search(d_state->sam_ctx,
4152 mem_ctx,
4153 &res,
4154 ldb_get_default_basedn(d_state->sam_ctx),
4155 LDB_SCOPE_SUBTREE,
4156 cache_attrs,
4158 "%s",
4159 filter);
4160 if (ret != LDB_SUCCESS) {
4161 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4163 if ((res->count == 0) || (r->in.max_entries == 0)) {
4164 return NT_STATUS_OK;
4167 status = load_guid_cache(cache, d_state, res->count, res->msgs);
4168 TALLOC_FREE(res);
4169 if (!NT_STATUS_IS_OK(status)) {
4170 return status;
4172 cache->handle = r->in.level;
4174 *r->out.total_size = cache->size;
4177 * if there are no entries or the requested start index is greater
4178 * than the number of entries, we return an empty response.
4180 if (r->in.start_idx >= cache->size) {
4181 *r->out.returned_size = 0;
4182 switch(r->in.level) {
4183 case 1:
4184 r->out.info->info1.count = *r->out.returned_size;
4185 r->out.info->info1.entries = NULL;
4186 break;
4187 case 2:
4188 r->out.info->info2.count = *r->out.returned_size;
4189 r->out.info->info2.entries = NULL;
4190 break;
4191 case 3:
4192 r->out.info->info3.count = *r->out.returned_size;
4193 r->out.info->info3.entries = NULL;
4194 break;
4195 case 4:
4196 r->out.info->info4.count = *r->out.returned_size;
4197 r->out.info->info4.entries = NULL;
4198 break;
4199 case 5:
4200 r->out.info->info5.count = *r->out.returned_size;
4201 r->out.info->info5.entries = NULL;
4202 break;
4204 return NT_STATUS_OK;
4208 * Allocate an array of the appropriate result structures for the
4209 * current query level.
4211 * r->in.start_idx is always < cache->size due to the check above
4213 results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
4214 switch (r->in.level) {
4215 case 1:
4216 entriesGeneral = talloc_array(
4217 mem_ctx, struct samr_DispEntryGeneral, results);
4218 break;
4219 case 2:
4220 entriesFull =
4221 talloc_array(mem_ctx, struct samr_DispEntryFull, results);
4222 break;
4223 case 3:
4224 entriesFullGroup = talloc_array(
4225 mem_ctx, struct samr_DispEntryFullGroup, results);
4226 break;
4227 case 4:
4228 case 5:
4229 entriesAscii =
4230 talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
4231 break;
4234 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
4235 (entriesAscii == NULL) && (entriesFullGroup == NULL))
4236 return NT_STATUS_NO_MEMORY;
4239 * Process the list of result GUID's.
4240 * Read the details of each object and populate the result structure
4241 * for the current level.
4243 count = 0;
4244 for (i = 0; i < results; i++) {
4245 struct dom_sid *objectsid;
4246 struct ldb_result *rec;
4247 const uint32_t idx = r->in.start_idx + i;
4248 uint32_t rid;
4251 * Read an object from disk using the GUID as the key
4253 * If the object can not be read, or it does not have a SID
4254 * it is ignored. In this case the number of entries returned
4255 * will be less than the requested size, there will also be
4256 * a gap in the idx numbers in the returned elements e.g. if
4257 * there are 3 GUIDs a, b, c in the cache and b is deleted from
4258 * disk then details for a, and c will be returned with
4259 * idx values of 1 and 3 respectively.
4262 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
4263 mem_ctx,
4264 &rec,
4265 &cache->entries[idx],
4266 attrs,
4268 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4269 struct GUID_txt_buf guid_buf;
4270 char *guid_str =
4271 GUID_buf_string(&cache->entries[idx],
4272 &guid_buf);
4273 DBG_WARNING("GUID [%s] not found\n", guid_str);
4274 continue;
4275 } else if (ret != LDB_SUCCESS) {
4276 clear_guid_cache(cache);
4277 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4279 objectsid = samdb_result_dom_sid(mem_ctx,
4280 rec->msgs[0],
4281 "objectSID");
4282 if (objectsid == NULL) {
4283 struct GUID_txt_buf guid_buf;
4284 DBG_WARNING(
4285 "objectSID for GUID [%s] not found\n",
4286 GUID_buf_string(&cache->entries[idx], &guid_buf));
4287 continue;
4289 status = dom_sid_split_rid(NULL,
4290 objectsid,
4291 NULL,
4292 &rid);
4293 if (!NT_STATUS_IS_OK(status)) {
4294 struct dom_sid_buf sid_buf;
4295 struct GUID_txt_buf guid_buf;
4296 DBG_WARNING(
4297 "objectSID [%s] for GUID [%s] invalid\n",
4298 dom_sid_str_buf(objectsid, &sid_buf),
4299 GUID_buf_string(&cache->entries[idx], &guid_buf));
4300 continue;
4304 * Populate the result structure for the current object
4306 switch(r->in.level) {
4307 case 1:
4309 entriesGeneral[count].idx = idx + 1;
4310 entriesGeneral[count].rid = rid;
4312 entriesGeneral[count].acct_flags =
4313 samdb_result_acct_flags(rec->msgs[0], NULL);
4314 entriesGeneral[count].account_name.string =
4315 ldb_msg_find_attr_as_string(
4316 rec->msgs[0], "sAMAccountName", "");
4317 entriesGeneral[count].full_name.string =
4318 ldb_msg_find_attr_as_string(
4319 rec->msgs[0], "displayName", "");
4320 entriesGeneral[count].description.string =
4321 ldb_msg_find_attr_as_string(
4322 rec->msgs[0], "description", "");
4323 break;
4324 case 2:
4325 entriesFull[count].idx = idx + 1;
4326 entriesFull[count].rid = rid;
4329 * No idea why we need to or in ACB_NORMAL here,
4330 * but this is what Win2k3 seems to do...
4332 entriesFull[count].acct_flags =
4333 samdb_result_acct_flags(rec->msgs[0], NULL) |
4334 ACB_NORMAL;
4335 entriesFull[count].account_name.string =
4336 ldb_msg_find_attr_as_string(
4337 rec->msgs[0], "sAMAccountName", "");
4338 entriesFull[count].description.string =
4339 ldb_msg_find_attr_as_string(
4340 rec->msgs[0], "description", "");
4341 break;
4342 case 3:
4343 entriesFullGroup[count].idx = idx + 1;
4344 entriesFullGroup[count].rid = rid;
4347 * We get a "7" here for groups
4349 entriesFullGroup[count].acct_flags =
4350 SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
4351 SE_GROUP_ENABLED;
4352 entriesFullGroup[count].account_name.string =
4353 ldb_msg_find_attr_as_string(
4354 rec->msgs[0], "sAMAccountName", "");
4355 entriesFullGroup[count].description.string =
4356 ldb_msg_find_attr_as_string(
4357 rec->msgs[0], "description", "");
4358 break;
4359 case 4:
4360 case 5:
4361 entriesAscii[count].idx = idx + 1;
4362 entriesAscii[count].account_name.string =
4363 ldb_msg_find_attr_as_string(
4364 rec->msgs[0], "sAMAccountName", "");
4365 break;
4367 count++;
4371 * Build the response based on the request level.
4373 *r->out.returned_size = count;
4374 switch(r->in.level) {
4375 case 1:
4376 r->out.info->info1.count = count;
4377 r->out.info->info1.entries = entriesGeneral;
4378 break;
4379 case 2:
4380 r->out.info->info2.count = count;
4381 r->out.info->info2.entries = entriesFull;
4382 break;
4383 case 3:
4384 r->out.info->info3.count = count;
4385 r->out.info->info3.entries = entriesFullGroup;
4386 break;
4387 case 4:
4388 r->out.info->info4.count = count;
4389 r->out.info->info4.entries = entriesAscii;
4390 break;
4391 case 5:
4392 r->out.info->info5.count = count;
4393 r->out.info->info5.entries = entriesAscii;
4394 break;
4397 return ((r->in.start_idx + results) < cache->size)
4398 ? STATUS_MORE_ENTRIES
4399 : NT_STATUS_OK;
4404 samr_GetDisplayEnumerationIndex
4406 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4407 struct samr_GetDisplayEnumerationIndex *r)
4409 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4414 samr_TestPrivateFunctionsDomain
4416 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4417 struct samr_TestPrivateFunctionsDomain *r)
4419 return NT_STATUS_NOT_IMPLEMENTED;
4424 samr_TestPrivateFunctionsUser
4426 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4427 struct samr_TestPrivateFunctionsUser *r)
4429 return NT_STATUS_NOT_IMPLEMENTED;
4434 samr_GetUserPwInfo
4436 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4437 struct samr_GetUserPwInfo *r)
4439 struct dcesrv_handle *h;
4440 struct samr_account_state *a_state;
4442 ZERO_STRUCTP(r->out.info);
4444 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4446 a_state = h->data;
4448 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
4449 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
4450 NULL);
4451 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
4452 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
4454 return NT_STATUS_OK;
4459 samr_RemoveMemberFromForeignDomain
4461 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
4462 TALLOC_CTX *mem_ctx,
4463 struct samr_RemoveMemberFromForeignDomain *r)
4465 struct dcesrv_handle *h;
4466 struct samr_domain_state *d_state;
4467 const char *memberdn;
4468 struct ldb_message **res;
4469 const char *no_attrs[] = { NULL };
4470 int i, count;
4472 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4474 d_state = h->data;
4476 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
4477 "distinguishedName", "(objectSid=%s)",
4478 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
4479 /* Nothing to do */
4480 if (memberdn == NULL) {
4481 return NT_STATUS_OK;
4484 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4485 d_state->domain_dn, &res, no_attrs,
4486 d_state->domain_sid,
4487 "(&(member=%s)(objectClass=group)"
4488 "(|(groupType=%d)(groupType=%d)))",
4489 memberdn,
4490 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4491 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4493 if (count < 0)
4494 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4496 for (i=0; i<count; i++) {
4497 struct ldb_message *mod;
4498 int ret;
4500 mod = ldb_msg_new(mem_ctx);
4501 if (mod == NULL) {
4502 return NT_STATUS_NO_MEMORY;
4505 mod->dn = res[i]->dn;
4507 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4508 "member", memberdn) != LDB_SUCCESS)
4509 return NT_STATUS_NO_MEMORY;
4511 ret = ldb_modify(d_state->sam_ctx, mod);
4512 talloc_free(mod);
4513 if (ret != LDB_SUCCESS) {
4514 return dsdb_ldb_err_to_ntstatus(ret);
4518 return NT_STATUS_OK;
4523 samr_QueryDomainInfo2
4525 just an alias for samr_QueryDomainInfo
4527 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4528 struct samr_QueryDomainInfo2 *r)
4530 struct samr_QueryDomainInfo r1;
4531 NTSTATUS status;
4533 r1 = (struct samr_QueryDomainInfo) {
4534 .in.domain_handle = r->in.domain_handle,
4535 .in.level = r->in.level,
4536 .out.info = r->out.info,
4539 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4541 return status;
4546 samr_QueryUserInfo2
4548 just an alias for samr_QueryUserInfo
4550 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4551 struct samr_QueryUserInfo2 *r)
4553 struct samr_QueryUserInfo r1;
4554 NTSTATUS status;
4556 r1 = (struct samr_QueryUserInfo) {
4557 .in.user_handle = r->in.user_handle,
4558 .in.level = r->in.level,
4559 .out.info = r->out.info
4562 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4564 return status;
4569 samr_QueryDisplayInfo2
4571 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4572 struct samr_QueryDisplayInfo2 *r)
4574 struct samr_QueryDisplayInfo q;
4575 NTSTATUS result;
4577 q = (struct samr_QueryDisplayInfo) {
4578 .in.domain_handle = r->in.domain_handle,
4579 .in.level = r->in.level,
4580 .in.start_idx = r->in.start_idx,
4581 .in.max_entries = r->in.max_entries,
4582 .in.buf_size = r->in.buf_size,
4583 .out.total_size = r->out.total_size,
4584 .out.returned_size = r->out.returned_size,
4585 .out.info = r->out.info,
4588 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4590 return result;
4595 samr_GetDisplayEnumerationIndex2
4597 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4598 struct samr_GetDisplayEnumerationIndex2 *r)
4600 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4605 samr_QueryDisplayInfo3
4607 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4608 struct samr_QueryDisplayInfo3 *r)
4610 struct samr_QueryDisplayInfo q;
4611 NTSTATUS result;
4613 q = (struct samr_QueryDisplayInfo) {
4614 .in.domain_handle = r->in.domain_handle,
4615 .in.level = r->in.level,
4616 .in.start_idx = r->in.start_idx,
4617 .in.max_entries = r->in.max_entries,
4618 .in.buf_size = r->in.buf_size,
4619 .out.total_size = r->out.total_size,
4620 .out.returned_size = r->out.returned_size,
4621 .out.info = r->out.info,
4624 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4626 return result;
4631 samr_AddMultipleMembersToAlias
4633 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4634 struct samr_AddMultipleMembersToAlias *r)
4636 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4641 samr_RemoveMultipleMembersFromAlias
4643 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4644 struct samr_RemoveMultipleMembersFromAlias *r)
4646 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4651 samr_GetDomPwInfo
4653 this fetches the default password properties for a domain
4655 note that w2k3 completely ignores the domain name in this call, and
4656 always returns the information for the servers primary domain
4658 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4659 struct samr_GetDomPwInfo *r)
4661 struct ldb_message **msgs;
4662 int ret;
4663 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4664 struct ldb_context *sam_ctx;
4666 ZERO_STRUCTP(r->out.info);
4668 sam_ctx = samdb_connect(mem_ctx,
4669 dce_call->event_ctx,
4670 dce_call->conn->dce_ctx->lp_ctx,
4671 dce_call->conn->auth_state.session_info,
4672 dce_call->conn->remote_address,
4674 if (sam_ctx == NULL) {
4675 return NT_STATUS_INVALID_SYSTEM_SERVICE;
4678 /* The domain name in this call is ignored */
4679 ret = gendb_search_dn(sam_ctx,
4680 mem_ctx, NULL, &msgs, attrs);
4681 if (ret <= 0) {
4682 talloc_free(sam_ctx);
4684 return NT_STATUS_NO_SUCH_DOMAIN;
4686 if (ret > 1) {
4687 talloc_free(msgs);
4688 talloc_free(sam_ctx);
4690 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4693 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
4694 "minPwdLength", 0);
4695 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
4696 "pwdProperties", 1);
4698 talloc_free(msgs);
4699 talloc_unlink(mem_ctx, sam_ctx);
4701 return NT_STATUS_OK;
4706 samr_Connect2
4708 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4709 struct samr_Connect2 *r)
4711 struct samr_Connect c;
4713 c = (struct samr_Connect) {
4714 .in.system_name = NULL,
4715 .in.access_mask = r->in.access_mask,
4716 .out.connect_handle = r->out.connect_handle,
4719 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4724 samr_SetUserInfo2
4726 just an alias for samr_SetUserInfo
4728 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4729 struct samr_SetUserInfo2 *r)
4731 struct samr_SetUserInfo r2;
4733 r2 = (struct samr_SetUserInfo) {
4734 .in.user_handle = r->in.user_handle,
4735 .in.level = r->in.level,
4736 .in.info = r->in.info,
4739 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4744 samr_SetBootKeyInformation
4746 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4747 struct samr_SetBootKeyInformation *r)
4749 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4754 samr_GetBootKeyInformation
4756 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4757 struct samr_GetBootKeyInformation *r)
4759 /* Windows Server 2008 returns this */
4760 return NT_STATUS_NOT_SUPPORTED;
4765 samr_Connect3
4767 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4768 struct samr_Connect3 *r)
4770 struct samr_Connect c;
4772 c = (struct samr_Connect) {
4773 .in.system_name = NULL,
4774 .in.access_mask = r->in.access_mask,
4775 .out.connect_handle = r->out.connect_handle,
4778 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4783 samr_Connect4
4785 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4786 struct samr_Connect4 *r)
4788 struct samr_Connect c;
4790 c = (struct samr_Connect) {
4791 .in.system_name = NULL,
4792 .in.access_mask = r->in.access_mask,
4793 .out.connect_handle = r->out.connect_handle,
4796 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4801 samr_Connect5
4803 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4804 struct samr_Connect5 *r)
4806 struct samr_Connect c;
4807 NTSTATUS status;
4809 c = (struct samr_Connect) {
4810 .in.system_name = NULL,
4811 .in.access_mask = r->in.access_mask,
4812 .out.connect_handle = r->out.connect_handle,
4815 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4817 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4818 r->out.info_out->info1.unknown2 = 0;
4819 *r->out.level_out = r->in.level_in;
4821 return status;
4826 samr_RidToSid
4828 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4829 struct samr_RidToSid *r)
4831 struct samr_domain_state *d_state;
4832 struct dcesrv_handle *h;
4834 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4836 d_state = h->data;
4838 /* form the users SID */
4839 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4840 if (!*r->out.sid) {
4841 return NT_STATUS_NO_MEMORY;
4844 return NT_STATUS_OK;
4849 samr_SetDsrmPassword
4851 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4852 struct samr_SetDsrmPassword *r)
4854 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4859 samr_ValidatePassword
4861 For now the call checks the password complexity (if active) and the minimum
4862 password length on level 2 and 3. Level 1 is ignored for now.
4864 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
4865 TALLOC_CTX *mem_ctx,
4866 struct samr_ValidatePassword *r)
4868 struct samr_GetDomPwInfo r2;
4869 struct samr_PwInfo pwInfo;
4870 DATA_BLOB password;
4871 enum samr_ValidationStatus res;
4872 NTSTATUS status;
4873 enum dcerpc_transport_t transport =
4874 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
4876 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
4877 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4880 if (dce_call->conn->auth_state.auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
4881 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4884 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
4886 r2 = (struct samr_GetDomPwInfo) {
4887 .in.domain_name = NULL,
4888 .out.info = &pwInfo,
4891 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
4892 if (!NT_STATUS_IS_OK(status)) {
4893 return status;
4896 switch (r->in.level) {
4897 case NetValidateAuthentication:
4898 /* we don't support this yet */
4899 return NT_STATUS_NOT_SUPPORTED;
4900 break;
4901 case NetValidatePasswordChange:
4902 password = data_blob_const(r->in.req->req2.password.string,
4903 r->in.req->req2.password.length);
4904 res = samdb_check_password(mem_ctx,
4905 dce_call->conn->dce_ctx->lp_ctx,
4906 &password,
4907 pwInfo.password_properties,
4908 pwInfo.min_password_length);
4909 (*r->out.rep)->ctr2.status = res;
4910 break;
4911 case NetValidatePasswordReset:
4912 password = data_blob_const(r->in.req->req3.password.string,
4913 r->in.req->req3.password.length);
4914 res = samdb_check_password(mem_ctx,
4915 dce_call->conn->dce_ctx->lp_ctx,
4916 &password,
4917 pwInfo.password_properties,
4918 pwInfo.min_password_length);
4919 (*r->out.rep)->ctr3.status = res;
4920 break;
4921 default:
4922 return NT_STATUS_INVALID_INFO_CLASS;
4923 break;
4926 return NT_STATUS_OK;
4930 /* include the generated boilerplate */
4931 #include "librpc/gen_ndr/ndr_samr_s.c"