s4:rpc_server/samr: use the same logic in *info_DomInfo7() as in info_DomGeneralInfor...
[Samba.git] / source4 / rpc_server / samr / dcesrv_samr.c
blobd0c748e184839e86cfe206c778ee24c8de327029
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 /* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
46 #define QUERY_STRING(msg, field, attr) \
47 info->field.string = ldb_msg_find_attr_as_string(msg, attr, "");
48 #define QUERY_UINT(msg, field, attr) \
49 info->field = ldb_msg_find_attr_as_uint(msg, attr, 0);
50 #define QUERY_RID(msg, field, attr) \
51 info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
52 #define QUERY_UINT64(msg, field, attr) \
53 info->field = ldb_msg_find_attr_as_uint64(msg, attr, 0);
54 #define QUERY_APASSC(msg, field, attr) \
55 info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
56 a_state->domain_state->domain_dn, msg, attr);
57 #define QUERY_FPASSC(msg, field, attr) \
58 info->field = samdb_result_force_password_change(sam_ctx, mem_ctx, \
59 a_state->domain_state->domain_dn, msg);
60 #define QUERY_BPWDCT(msg, field, attr) \
61 info->field = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, \
62 a_state->domain_state->domain_dn, msg);
63 #define QUERY_LHOURS(msg, field, attr) \
64 info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
65 #define QUERY_AFLAGS(msg, field, attr) \
66 info->field = samdb_result_acct_flags(msg, attr);
69 /* these are used to make the Set[User|Group]Info code easier to follow */
71 #define SET_STRING(msg, field, attr) do { \
72 struct ldb_message_element *set_el; \
73 if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
74 if (r->in.info->field.string[0] == '\0') { \
75 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
76 return NT_STATUS_NO_MEMORY; \
77 } \
78 } \
79 if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
80 return NT_STATUS_NO_MEMORY; \
81 } \
82 set_el = ldb_msg_find_element(msg, attr); \
83 set_el->flags = LDB_FLAG_MOD_REPLACE; \
84 } while (0)
86 #define SET_UINT(msg, field, attr) do { \
87 struct ldb_message_element *set_el; \
88 if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
89 return NT_STATUS_NO_MEMORY; \
90 } \
91 set_el = ldb_msg_find_element(msg, attr); \
92 set_el->flags = LDB_FLAG_MOD_REPLACE; \
93 } while (0)
95 #define SET_INT64(msg, field, attr) do { \
96 struct ldb_message_element *set_el; \
97 if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
98 return NT_STATUS_NO_MEMORY; \
99 } \
100 set_el = ldb_msg_find_element(msg, attr); \
101 set_el->flags = LDB_FLAG_MOD_REPLACE; \
102 } while (0)
104 #define SET_UINT64(msg, field, attr) do { \
105 struct ldb_message_element *set_el; \
106 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
107 return NT_STATUS_NO_MEMORY; \
109 set_el = ldb_msg_find_element(msg, attr); \
110 set_el->flags = LDB_FLAG_MOD_REPLACE; \
111 } while (0)
113 /* Set account flags, discarding flags that cannot be set with SAMR */
114 #define SET_AFLAGS(msg, field, attr) do { \
115 struct ldb_message_element *set_el; \
116 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
117 return NT_STATUS_NO_MEMORY; \
119 set_el = ldb_msg_find_element(msg, attr); \
120 set_el->flags = LDB_FLAG_MOD_REPLACE; \
121 } while (0)
123 #define SET_LHOURS(msg, field, attr) do { \
124 struct ldb_message_element *set_el; \
125 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
126 return NT_STATUS_NO_MEMORY; \
128 set_el = ldb_msg_find_element(msg, attr); \
129 set_el->flags = LDB_FLAG_MOD_REPLACE; \
130 } while (0)
132 #define SET_PARAMETERS(msg, field, attr) do { \
133 struct ldb_message_element *set_el; \
134 if (r->in.info->field.length != 0) { \
135 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
136 return NT_STATUS_NO_MEMORY; \
138 set_el = ldb_msg_find_element(msg, attr); \
139 set_el->flags = LDB_FLAG_MOD_REPLACE; \
141 } while (0)
146 samr_Connect
148 create a connection to the SAM database
150 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
151 struct samr_Connect *r)
153 struct samr_connect_state *c_state;
154 struct dcesrv_handle *handle;
156 ZERO_STRUCTP(r->out.connect_handle);
158 c_state = talloc(mem_ctx, struct samr_connect_state);
159 if (!c_state) {
160 return NT_STATUS_NO_MEMORY;
163 /* make sure the sam database is accessible */
164 c_state->sam_ctx = samdb_connect(c_state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info, 0);
165 if (c_state->sam_ctx == NULL) {
166 talloc_free(c_state);
167 return NT_STATUS_INVALID_SYSTEM_SERVICE;
171 handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
172 if (!handle) {
173 talloc_free(c_state);
174 return NT_STATUS_NO_MEMORY;
177 handle->data = talloc_steal(handle, c_state);
179 c_state->access_mask = r->in.access_mask;
180 *r->out.connect_handle = handle->wire_handle;
182 return NT_STATUS_OK;
187 samr_Close
189 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
190 struct samr_Close *r)
192 struct dcesrv_handle *h;
194 *r->out.handle = *r->in.handle;
196 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
198 talloc_free(h);
200 ZERO_STRUCTP(r->out.handle);
202 return NT_STATUS_OK;
207 samr_SetSecurity
209 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
210 struct samr_SetSecurity *r)
212 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
217 samr_QuerySecurity
219 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
220 struct samr_QuerySecurity *r)
222 struct dcesrv_handle *h;
223 struct sec_desc_buf *sd;
225 *r->out.sdbuf = NULL;
227 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
229 sd = talloc(mem_ctx, struct sec_desc_buf);
230 if (sd == NULL) {
231 return NT_STATUS_NO_MEMORY;
234 sd->sd = samdb_default_security_descriptor(mem_ctx);
236 *r->out.sdbuf = sd;
238 return NT_STATUS_OK;
243 samr_Shutdown
245 we refuse this operation completely. If a admin wants to shutdown samr
246 in Samba then they should use the samba admin tools to disable the samr pipe
248 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
249 struct samr_Shutdown *r)
251 return NT_STATUS_ACCESS_DENIED;
256 samr_LookupDomain
258 this maps from a domain name to a SID
260 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
261 struct samr_LookupDomain *r)
263 struct samr_connect_state *c_state;
264 struct dcesrv_handle *h;
265 struct dom_sid *sid;
266 const char * const dom_attrs[] = { "objectSid", NULL};
267 struct ldb_message **dom_msgs;
268 int ret;
270 *r->out.sid = NULL;
272 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
274 c_state = h->data;
276 if (r->in.domain_name->string == NULL) {
277 return NT_STATUS_INVALID_PARAMETER;
280 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
281 ret = gendb_search(c_state->sam_ctx,
282 mem_ctx, NULL, &dom_msgs, dom_attrs,
283 "(objectClass=builtinDomain)");
284 } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
285 ret = gendb_search_dn(c_state->sam_ctx,
286 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
287 &dom_msgs, dom_attrs);
288 } else {
289 return NT_STATUS_NO_SUCH_DOMAIN;
291 if (ret != 1) {
292 return NT_STATUS_NO_SUCH_DOMAIN;
295 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
296 "objectSid");
298 if (sid == NULL) {
299 return NT_STATUS_NO_SUCH_DOMAIN;
302 *r->out.sid = sid;
304 return NT_STATUS_OK;
309 samr_EnumDomains
311 list the domains in the SAM
313 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
314 struct samr_EnumDomains *r)
316 struct dcesrv_handle *h;
317 struct samr_SamArray *array;
318 uint32_t i, start_i;
320 *r->out.resume_handle = 0;
321 *r->out.sam = NULL;
322 *r->out.num_entries = 0;
324 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
326 *r->out.resume_handle = 2;
328 start_i = *r->in.resume_handle;
330 if (start_i >= 2) {
331 /* search past end of list is not an error for this call */
332 return NT_STATUS_OK;
335 array = talloc(mem_ctx, struct samr_SamArray);
336 if (array == NULL) {
337 return NT_STATUS_NO_MEMORY;
340 array->count = 0;
341 array->entries = NULL;
343 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
344 if (array->entries == NULL) {
345 return NT_STATUS_NO_MEMORY;
348 for (i=0;i<2-start_i;i++) {
349 array->entries[i].idx = start_i + i;
350 if (i == 0) {
351 array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
352 } else {
353 array->entries[i].name.string = "BUILTIN";
357 *r->out.sam = array;
358 *r->out.num_entries = i;
359 array->count = *r->out.num_entries;
361 return NT_STATUS_OK;
366 samr_OpenDomain
368 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
369 struct samr_OpenDomain *r)
371 struct dcesrv_handle *h_conn, *h_domain;
372 struct samr_connect_state *c_state;
373 struct samr_domain_state *d_state;
374 const char * const dom_attrs[] = { "cn", NULL};
375 struct ldb_message **dom_msgs;
376 int ret;
378 ZERO_STRUCTP(r->out.domain_handle);
380 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
382 c_state = h_conn->data;
384 if (r->in.sid == NULL) {
385 return NT_STATUS_INVALID_PARAMETER;
388 d_state = talloc(mem_ctx, struct samr_domain_state);
389 if (!d_state) {
390 return NT_STATUS_NO_MEMORY;
393 d_state->domain_sid = talloc_steal(d_state, r->in.sid);
395 if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
396 d_state->builtin = true;
397 d_state->domain_name = "BUILTIN";
398 } else {
399 d_state->builtin = false;
400 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
403 ret = gendb_search(c_state->sam_ctx,
404 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
405 "(objectSid=%s)",
406 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
408 if (ret == 0) {
409 talloc_free(d_state);
410 return NT_STATUS_NO_SUCH_DOMAIN;
411 } else if (ret > 1) {
412 talloc_free(d_state);
413 return NT_STATUS_INTERNAL_DB_CORRUPTION;
414 } else if (ret == -1) {
415 talloc_free(d_state);
416 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
417 return NT_STATUS_INTERNAL_DB_CORRUPTION;
420 d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
421 d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
422 d_state->connect_state = talloc_reference(d_state, c_state);
423 d_state->sam_ctx = c_state->sam_ctx;
424 d_state->access_mask = r->in.access_mask;
426 d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
428 h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
429 if (!h_domain) {
430 talloc_free(d_state);
431 return NT_STATUS_NO_MEMORY;
434 h_domain->data = talloc_steal(h_domain, d_state);
436 *r->out.domain_handle = h_domain->wire_handle;
438 return NT_STATUS_OK;
442 return DomInfo1
444 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
445 TALLOC_CTX *mem_ctx,
446 struct ldb_message **dom_msgs,
447 struct samr_DomInfo1 *info)
449 info->min_password_length =
450 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
451 info->password_history_length =
452 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
453 info->password_properties =
454 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
455 info->max_password_age =
456 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
457 info->min_password_age =
458 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
460 return NT_STATUS_OK;
464 return DomInfo2
466 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
467 TALLOC_CTX *mem_ctx,
468 struct ldb_message **dom_msgs,
469 struct samr_DomGeneralInformation *info)
471 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
472 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
473 "domainReplica",
474 "");
476 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
477 0x8000000000000000LL);
479 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
480 "oEMInformation",
481 "");
482 info->domain_name.string = state->domain_name;
484 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
486 switch (state->role) {
487 case ROLE_ACTIVE_DIRECTORY_DC:
488 /* This pulls the NetBIOS name from the
489 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
490 string */
491 if (samdb_is_pdc(state->sam_ctx)) {
492 info->role = SAMR_ROLE_DOMAIN_PDC;
493 } else {
494 info->role = SAMR_ROLE_DOMAIN_BDC;
496 break;
497 case ROLE_DOMAIN_PDC:
498 case ROLE_DOMAIN_BDC:
499 case ROLE_AUTO:
500 return NT_STATUS_INTERNAL_ERROR;
501 case ROLE_DOMAIN_MEMBER:
502 info->role = SAMR_ROLE_DOMAIN_MEMBER;
503 break;
504 case ROLE_STANDALONE:
505 info->role = SAMR_ROLE_STANDALONE;
506 break;
509 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx,
510 state->domain_dn,
511 "(objectClass=user)");
512 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx,
513 state->domain_dn,
514 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
515 GTYPE_SECURITY_UNIVERSAL_GROUP,
516 GTYPE_SECURITY_GLOBAL_GROUP);
517 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx,
518 state->domain_dn,
519 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
520 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
521 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
523 return NT_STATUS_OK;
527 return DomInfo3
529 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
530 TALLOC_CTX *mem_ctx,
531 struct ldb_message **dom_msgs,
532 struct samr_DomInfo3 *info)
534 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
535 0x8000000000000000LL);
537 return NT_STATUS_OK;
541 return DomInfo4
543 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
544 TALLOC_CTX *mem_ctx,
545 struct ldb_message **dom_msgs,
546 struct samr_DomOEMInformation *info)
548 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
549 "oEMInformation",
550 "");
552 return NT_STATUS_OK;
556 return DomInfo5
558 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
559 TALLOC_CTX *mem_ctx,
560 struct ldb_message **dom_msgs,
561 struct samr_DomInfo5 *info)
563 info->domain_name.string = state->domain_name;
565 return NT_STATUS_OK;
569 return DomInfo6
571 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
572 TALLOC_CTX *mem_ctx,
573 struct ldb_message **dom_msgs,
574 struct samr_DomInfo6 *info)
576 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
577 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
578 "domainReplica",
579 "");
581 return NT_STATUS_OK;
585 return DomInfo7
587 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
588 TALLOC_CTX *mem_ctx,
589 struct ldb_message **dom_msgs,
590 struct samr_DomInfo7 *info)
593 switch (state->role) {
594 case ROLE_ACTIVE_DIRECTORY_DC:
595 /* This pulls the NetBIOS name from the
596 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
597 string */
598 if (samdb_is_pdc(state->sam_ctx)) {
599 info->role = SAMR_ROLE_DOMAIN_PDC;
600 } else {
601 info->role = SAMR_ROLE_DOMAIN_BDC;
603 break;
604 case ROLE_DOMAIN_PDC:
605 case ROLE_DOMAIN_BDC:
606 case ROLE_AUTO:
607 return NT_STATUS_INTERNAL_ERROR;
608 case ROLE_DOMAIN_MEMBER:
609 info->role = SAMR_ROLE_DOMAIN_MEMBER;
610 break;
611 case ROLE_STANDALONE:
612 info->role = SAMR_ROLE_STANDALONE;
613 break;
616 return NT_STATUS_OK;
620 return DomInfo8
622 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
623 TALLOC_CTX *mem_ctx,
624 struct ldb_message **dom_msgs,
625 struct samr_DomInfo8 *info)
627 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
628 time(NULL));
630 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
631 0x0LL);
633 return NT_STATUS_OK;
637 return DomInfo9
639 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
640 TALLOC_CTX *mem_ctx,
641 struct ldb_message **dom_msgs,
642 struct samr_DomInfo9 *info)
644 info->domain_server_state = DOMAIN_SERVER_ENABLED;
646 return NT_STATUS_OK;
650 return DomInfo11
652 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
653 TALLOC_CTX *mem_ctx,
654 struct ldb_message **dom_msgs,
655 struct samr_DomGeneralInformation2 *info)
657 NTSTATUS status;
658 status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
659 if (!NT_STATUS_IS_OK(status)) {
660 return status;
663 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
664 -18000000000LL);
665 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
666 -18000000000LL);
667 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
669 return NT_STATUS_OK;
673 return DomInfo12
675 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
676 TALLOC_CTX *mem_ctx,
677 struct ldb_message **dom_msgs,
678 struct samr_DomInfo12 *info)
680 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
681 -18000000000LL);
682 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
683 -18000000000LL);
684 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
686 return NT_STATUS_OK;
690 return DomInfo13
692 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
693 TALLOC_CTX *mem_ctx,
694 struct ldb_message **dom_msgs,
695 struct samr_DomInfo13 *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 info->modified_count_at_last_promotion = 0;
705 return NT_STATUS_OK;
709 samr_QueryDomainInfo
711 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
712 TALLOC_CTX *mem_ctx,
713 struct samr_QueryDomainInfo *r)
715 struct dcesrv_handle *h;
716 struct samr_domain_state *d_state;
717 union samr_DomainInfo *info;
719 struct ldb_message **dom_msgs;
720 const char * const *attrs = NULL;
722 *r->out.info = NULL;
724 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
726 d_state = h->data;
728 switch (r->in.level) {
729 case 1:
731 static const char * const attrs2[] = { "minPwdLength",
732 "pwdHistoryLength",
733 "pwdProperties",
734 "maxPwdAge",
735 "minPwdAge",
736 NULL };
737 attrs = attrs2;
738 break;
740 case 2:
742 static const char * const attrs2[] = {"forceLogoff",
743 "oEMInformation",
744 "modifiedCount",
745 "domainReplica",
746 NULL};
747 attrs = attrs2;
748 break;
750 case 3:
752 static const char * const attrs2[] = {"forceLogoff",
753 NULL};
754 attrs = attrs2;
755 break;
757 case 4:
759 static const char * const attrs2[] = {"oEMInformation",
760 NULL};
761 attrs = attrs2;
762 break;
764 case 5:
766 attrs = NULL;
767 break;
769 case 6:
771 static const char * const attrs2[] = { "domainReplica",
772 NULL };
773 attrs = attrs2;
774 break;
776 case 7:
778 attrs = NULL;
779 break;
781 case 8:
783 static const char * const attrs2[] = { "modifiedCount",
784 "creationTime",
785 NULL };
786 attrs = attrs2;
787 break;
789 case 9:
791 attrs = NULL;
792 break;
794 case 11:
796 static const char * const attrs2[] = { "oEMInformation",
797 "forceLogoff",
798 "modifiedCount",
799 "lockoutDuration",
800 "lockOutObservationWindow",
801 "lockoutThreshold",
802 NULL};
803 attrs = attrs2;
804 break;
806 case 12:
808 static const char * const attrs2[] = { "lockoutDuration",
809 "lockOutObservationWindow",
810 "lockoutThreshold",
811 NULL};
812 attrs = attrs2;
813 break;
815 case 13:
817 static const char * const attrs2[] = { "modifiedCount",
818 "creationTime",
819 NULL };
820 attrs = attrs2;
821 break;
823 default:
825 return NT_STATUS_INVALID_INFO_CLASS;
829 /* some levels don't need a search */
830 if (attrs) {
831 int ret;
832 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
833 d_state->domain_dn, &dom_msgs, attrs);
834 if (ret == 0) {
835 return NT_STATUS_NO_SUCH_DOMAIN;
837 if (ret != 1) {
838 return NT_STATUS_INTERNAL_DB_CORRUPTION;
842 /* allocate the info structure */
843 info = talloc_zero(mem_ctx, union samr_DomainInfo);
844 if (info == NULL) {
845 return NT_STATUS_NO_MEMORY;
848 *r->out.info = info;
850 switch (r->in.level) {
851 case 1:
852 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
853 &info->info1);
854 case 2:
855 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
856 &info->general);
857 case 3:
858 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
859 &info->info3);
860 case 4:
861 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
862 &info->oem);
863 case 5:
864 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
865 &info->info5);
866 case 6:
867 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
868 &info->info6);
869 case 7:
870 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
871 &info->info7);
872 case 8:
873 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
874 &info->info8);
875 case 9:
876 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
877 &info->info9);
878 case 11:
879 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
880 &info->general2);
881 case 12:
882 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
883 &info->info12);
884 case 13:
885 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
886 &info->info13);
887 default:
888 return NT_STATUS_INVALID_INFO_CLASS;
894 samr_SetDomainInfo
896 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
897 struct samr_SetDomainInfo *r)
899 struct dcesrv_handle *h;
900 struct samr_domain_state *d_state;
901 struct ldb_message *msg;
902 int ret;
903 struct ldb_context *sam_ctx;
905 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
907 d_state = h->data;
908 sam_ctx = d_state->sam_ctx;
910 msg = ldb_msg_new(mem_ctx);
911 if (msg == NULL) {
912 return NT_STATUS_NO_MEMORY;
915 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
916 if (!msg->dn) {
917 return NT_STATUS_NO_MEMORY;
920 switch (r->in.level) {
921 case 1:
922 SET_UINT (msg, info1.min_password_length, "minPwdLength");
923 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
924 SET_UINT (msg, info1.password_properties, "pwdProperties");
925 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
926 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
927 break;
928 case 3:
929 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
930 break;
931 case 4:
932 SET_STRING(msg, oem.oem_information, "oEMInformation");
933 break;
935 case 6:
936 case 7:
937 case 9:
938 /* No op, we don't know where to set these */
939 return NT_STATUS_OK;
941 case 12:
943 * It is not possible to set lockout_duration < lockout_window.
944 * (The test is the other way around since the negative numbers
945 * are stored...)
947 * TODO:
948 * This check should be moved to the backend, i.e. to some
949 * ldb module under dsdb/samdb/ldb_modules/ .
951 * This constraint is documented here for the samr rpc service:
952 * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
953 * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
955 * And here for the ldap backend:
956 * MS-ADTS 3.1.1.5.3.2 Constraints
957 * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
959 if (r->in.info->info12.lockout_duration >
960 r->in.info->info12.lockout_window)
962 return NT_STATUS_INVALID_PARAMETER;
964 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
965 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
966 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
967 break;
969 default:
970 /* many info classes are not valid for SetDomainInfo */
971 return NT_STATUS_INVALID_INFO_CLASS;
974 /* modify the samdb record */
975 ret = ldb_modify(sam_ctx, msg);
976 if (ret != LDB_SUCCESS) {
977 DEBUG(1,("Failed to modify record %s: %s\n",
978 ldb_dn_get_linearized(d_state->domain_dn),
979 ldb_errstring(sam_ctx)));
980 return dsdb_ldb_err_to_ntstatus(ret);
983 return NT_STATUS_OK;
987 samr_CreateDomainGroup
989 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
990 struct samr_CreateDomainGroup *r)
992 NTSTATUS status;
993 struct samr_domain_state *d_state;
994 struct samr_account_state *a_state;
995 struct dcesrv_handle *h;
996 const char *groupname;
997 struct dom_sid *group_sid;
998 struct ldb_dn *group_dn;
999 struct dcesrv_handle *g_handle;
1001 ZERO_STRUCTP(r->out.group_handle);
1002 *r->out.rid = 0;
1004 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1006 d_state = h->data;
1008 if (d_state->builtin) {
1009 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
1010 return NT_STATUS_ACCESS_DENIED;
1013 groupname = r->in.name->string;
1015 if (groupname == NULL) {
1016 return NT_STATUS_INVALID_PARAMETER;
1019 status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1020 if (!NT_STATUS_IS_OK(status)) {
1021 return status;
1024 a_state = talloc(mem_ctx, struct samr_account_state);
1025 if (!a_state) {
1026 return NT_STATUS_NO_MEMORY;
1028 a_state->sam_ctx = d_state->sam_ctx;
1029 a_state->access_mask = r->in.access_mask;
1030 a_state->domain_state = talloc_reference(a_state, d_state);
1031 a_state->account_dn = talloc_steal(a_state, group_dn);
1033 a_state->account_name = talloc_steal(a_state, groupname);
1035 /* create the policy handle */
1036 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1037 if (!g_handle) {
1038 return NT_STATUS_NO_MEMORY;
1041 g_handle->data = talloc_steal(g_handle, a_state);
1043 *r->out.group_handle = g_handle->wire_handle;
1044 *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1046 return NT_STATUS_OK;
1051 comparison function for sorting SamEntry array
1053 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1055 return e1->idx - e2->idx;
1059 samr_EnumDomainGroups
1061 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1062 struct samr_EnumDomainGroups *r)
1064 struct dcesrv_handle *h;
1065 struct samr_domain_state *d_state;
1066 struct ldb_message **res;
1067 int i, ldb_cnt;
1068 uint32_t first, count;
1069 struct samr_SamEntry *entries;
1070 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1071 struct samr_SamArray *sam;
1073 *r->out.resume_handle = 0;
1074 *r->out.sam = NULL;
1075 *r->out.num_entries = 0;
1077 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1079 d_state = h->data;
1081 /* search for all domain groups in this domain. This could possibly be
1082 cached and resumed based on resume_key */
1083 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1084 d_state->domain_dn, &res, attrs,
1085 d_state->domain_sid,
1086 "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1087 GTYPE_SECURITY_UNIVERSAL_GROUP,
1088 GTYPE_SECURITY_GLOBAL_GROUP);
1089 if (ldb_cnt < 0) {
1090 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1093 /* convert to SamEntry format */
1094 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1095 if (!entries) {
1096 return NT_STATUS_NO_MEMORY;
1099 count = 0;
1101 for (i=0;i<ldb_cnt;i++) {
1102 struct dom_sid *group_sid;
1104 group_sid = samdb_result_dom_sid(mem_ctx, res[i],
1105 "objectSid");
1106 if (group_sid == NULL) {
1107 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1110 entries[count].idx =
1111 group_sid->sub_auths[group_sid->num_auths-1];
1112 entries[count].name.string =
1113 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1114 count += 1;
1117 /* sort the results by rid */
1118 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1120 /* find the first entry to return */
1121 for (first=0;
1122 first<count && entries[first].idx <= *r->in.resume_handle;
1123 first++) ;
1125 /* return the rest, limit by max_size. Note that we
1126 use the w2k3 element size value of 54 */
1127 *r->out.num_entries = count - first;
1128 *r->out.num_entries = MIN(*r->out.num_entries,
1129 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1131 sam = talloc(mem_ctx, struct samr_SamArray);
1132 if (!sam) {
1133 return NT_STATUS_NO_MEMORY;
1136 sam->entries = entries+first;
1137 sam->count = *r->out.num_entries;
1139 *r->out.sam = sam;
1141 if (first == count) {
1142 return NT_STATUS_OK;
1145 if (*r->out.num_entries < count - first) {
1146 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1147 return STATUS_MORE_ENTRIES;
1150 return NT_STATUS_OK;
1155 samr_CreateUser2
1157 This call uses transactions to ensure we don't get a new conflicting
1158 user while we are processing this, and to ensure the user either
1159 completly exists, or does not.
1161 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1162 struct samr_CreateUser2 *r)
1164 NTSTATUS status;
1165 struct samr_domain_state *d_state;
1166 struct samr_account_state *a_state;
1167 struct dcesrv_handle *h;
1168 struct ldb_dn *dn;
1169 struct dom_sid *sid;
1170 struct dcesrv_handle *u_handle;
1171 const char *account_name;
1173 ZERO_STRUCTP(r->out.user_handle);
1174 *r->out.access_granted = 0;
1175 *r->out.rid = 0;
1177 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1179 d_state = h->data;
1181 if (d_state->builtin) {
1182 DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
1183 return NT_STATUS_ACCESS_DENIED;
1184 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1185 /* Domain trust accounts must be created by the LSA calls */
1186 return NT_STATUS_ACCESS_DENIED;
1188 account_name = r->in.account_name->string;
1190 if (account_name == NULL) {
1191 return NT_STATUS_INVALID_PARAMETER;
1194 status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1195 &sid, &dn);
1196 if (!NT_STATUS_IS_OK(status)) {
1197 return status;
1199 a_state = talloc(mem_ctx, struct samr_account_state);
1200 if (!a_state) {
1201 return NT_STATUS_NO_MEMORY;
1203 a_state->sam_ctx = d_state->sam_ctx;
1204 a_state->access_mask = r->in.access_mask;
1205 a_state->domain_state = talloc_reference(a_state, d_state);
1206 a_state->account_dn = talloc_steal(a_state, dn);
1208 a_state->account_name = talloc_steal(a_state, account_name);
1209 if (!a_state->account_name) {
1210 return NT_STATUS_NO_MEMORY;
1213 /* create the policy handle */
1214 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
1215 if (!u_handle) {
1216 return NT_STATUS_NO_MEMORY;
1219 u_handle->data = talloc_steal(u_handle, a_state);
1221 *r->out.user_handle = u_handle->wire_handle;
1222 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1224 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1226 return NT_STATUS_OK;
1231 samr_CreateUser
1233 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1234 struct samr_CreateUser *r)
1236 struct samr_CreateUser2 r2;
1237 uint32_t access_granted = 0;
1240 /* a simple wrapper around samr_CreateUser2 works nicely */
1242 r2 = (struct samr_CreateUser2) {
1243 .in.domain_handle = r->in.domain_handle,
1244 .in.account_name = r->in.account_name,
1245 .in.acct_flags = ACB_NORMAL,
1246 .in.access_mask = r->in.access_mask,
1247 .out.user_handle = r->out.user_handle,
1248 .out.access_granted = &access_granted,
1249 .out.rid = r->out.rid
1252 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1256 samr_EnumDomainUsers
1258 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1259 struct samr_EnumDomainUsers *r)
1261 struct dcesrv_handle *h;
1262 struct samr_domain_state *d_state;
1263 struct ldb_message **res;
1264 int i, ldb_cnt;
1265 uint32_t first, count;
1266 struct samr_SamEntry *entries;
1267 const char * const attrs[] = { "objectSid", "sAMAccountName",
1268 "userAccountControl", NULL };
1269 struct samr_SamArray *sam;
1271 *r->out.resume_handle = 0;
1272 *r->out.sam = NULL;
1273 *r->out.num_entries = 0;
1275 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1277 d_state = h->data;
1279 /* search for all domain users in this domain. This could possibly be
1280 cached and resumed on resume_key */
1281 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1282 d_state->domain_dn,
1283 &res, attrs,
1284 d_state->domain_sid,
1285 "(objectClass=user)");
1286 if (ldb_cnt < 0) {
1287 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1290 /* convert to SamEntry format */
1291 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1292 if (!entries) {
1293 return NT_STATUS_NO_MEMORY;
1296 count = 0;
1298 for (i=0;i<ldb_cnt;i++) {
1299 /* Check if a mask has been requested */
1300 if (r->in.acct_flags
1301 && ((samdb_result_acct_flags(res[i], NULL) & r->in.acct_flags) == 0)) {
1302 continue;
1304 entries[count].idx = samdb_result_rid_from_sid(mem_ctx, res[i],
1305 "objectSid", 0);
1306 entries[count].name.string = ldb_msg_find_attr_as_string(res[i],
1307 "sAMAccountName", "");
1308 count += 1;
1311 /* sort the results by rid */
1312 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1314 /* find the first entry to return */
1315 for (first=0;
1316 first<count && entries[first].idx <= *r->in.resume_handle;
1317 first++) ;
1319 /* return the rest, limit by max_size. Note that we
1320 use the w2k3 element size value of 54 */
1321 *r->out.num_entries = count - first;
1322 *r->out.num_entries = MIN(*r->out.num_entries,
1323 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1325 sam = talloc(mem_ctx, struct samr_SamArray);
1326 if (!sam) {
1327 return NT_STATUS_NO_MEMORY;
1330 sam->entries = entries+first;
1331 sam->count = *r->out.num_entries;
1333 *r->out.sam = sam;
1335 if (first == count) {
1336 return NT_STATUS_OK;
1339 if (*r->out.num_entries < count - first) {
1340 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1341 return STATUS_MORE_ENTRIES;
1344 return NT_STATUS_OK;
1349 samr_CreateDomAlias
1351 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1352 struct samr_CreateDomAlias *r)
1354 struct samr_domain_state *d_state;
1355 struct samr_account_state *a_state;
1356 struct dcesrv_handle *h;
1357 const char *alias_name;
1358 struct dom_sid *sid;
1359 struct dcesrv_handle *a_handle;
1360 struct ldb_dn *dn;
1361 NTSTATUS status;
1363 ZERO_STRUCTP(r->out.alias_handle);
1364 *r->out.rid = 0;
1366 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1368 d_state = h->data;
1370 if (d_state->builtin) {
1371 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1372 return NT_STATUS_ACCESS_DENIED;
1375 alias_name = r->in.alias_name->string;
1377 if (alias_name == NULL) {
1378 return NT_STATUS_INVALID_PARAMETER;
1381 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1382 if (!NT_STATUS_IS_OK(status)) {
1383 return status;
1386 a_state = talloc(mem_ctx, struct samr_account_state);
1387 if (!a_state) {
1388 return NT_STATUS_NO_MEMORY;
1391 a_state->sam_ctx = d_state->sam_ctx;
1392 a_state->access_mask = r->in.access_mask;
1393 a_state->domain_state = talloc_reference(a_state, d_state);
1394 a_state->account_dn = talloc_steal(a_state, dn);
1396 a_state->account_name = talloc_steal(a_state, alias_name);
1398 /* create the policy handle */
1399 a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1400 if (a_handle == NULL)
1401 return NT_STATUS_NO_MEMORY;
1403 a_handle->data = talloc_steal(a_handle, a_state);
1405 *r->out.alias_handle = a_handle->wire_handle;
1407 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1409 return NT_STATUS_OK;
1414 samr_EnumDomainAliases
1416 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1417 struct samr_EnumDomainAliases *r)
1419 struct dcesrv_handle *h;
1420 struct samr_domain_state *d_state;
1421 struct ldb_message **res;
1422 int i, ldb_cnt;
1423 uint32_t first, count;
1424 struct samr_SamEntry *entries;
1425 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1426 struct samr_SamArray *sam;
1428 *r->out.resume_handle = 0;
1429 *r->out.sam = NULL;
1430 *r->out.num_entries = 0;
1432 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1434 d_state = h->data;
1436 /* search for all domain aliases in this domain. This could possibly be
1437 cached and resumed based on resume_key */
1438 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1439 &res, attrs,
1440 d_state->domain_sid,
1441 "(&(|(grouptype=%d)(grouptype=%d)))"
1442 "(objectclass=group))",
1443 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1444 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1445 if (ldb_cnt < 0) {
1446 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1449 /* convert to SamEntry format */
1450 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1451 if (!entries) {
1452 return NT_STATUS_NO_MEMORY;
1455 count = 0;
1457 for (i=0;i<ldb_cnt;i++) {
1458 struct dom_sid *alias_sid;
1460 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1461 "objectSid");
1463 if (alias_sid == NULL) {
1464 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1467 entries[count].idx =
1468 alias_sid->sub_auths[alias_sid->num_auths-1];
1469 entries[count].name.string =
1470 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1471 count += 1;
1474 /* sort the results by rid */
1475 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1477 /* find the first entry to return */
1478 for (first=0;
1479 first<count && entries[first].idx <= *r->in.resume_handle;
1480 first++) ;
1482 /* return the rest, limit by max_size. Note that we
1483 use the w2k3 element size value of 54 */
1484 *r->out.num_entries = count - first;
1485 *r->out.num_entries = MIN(*r->out.num_entries,
1486 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1488 sam = talloc(mem_ctx, struct samr_SamArray);
1489 if (!sam) {
1490 return NT_STATUS_NO_MEMORY;
1493 sam->entries = entries+first;
1494 sam->count = *r->out.num_entries;
1496 *r->out.sam = sam;
1498 if (first == count) {
1499 return NT_STATUS_OK;
1502 if (*r->out.num_entries < count - first) {
1503 *r->out.resume_handle =
1504 entries[first+*r->out.num_entries-1].idx;
1505 return STATUS_MORE_ENTRIES;
1508 return NT_STATUS_OK;
1513 samr_GetAliasMembership
1515 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1516 struct samr_GetAliasMembership *r)
1518 struct dcesrv_handle *h;
1519 struct samr_domain_state *d_state;
1520 const char *filter;
1521 const char * const attrs[] = { "objectSid", NULL };
1522 struct ldb_message **res;
1523 uint32_t i;
1524 int count = 0;
1526 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1528 d_state = h->data;
1530 filter = talloc_asprintf(mem_ctx,
1531 "(&(|(grouptype=%d)(grouptype=%d))"
1532 "(objectclass=group)(|",
1533 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1534 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1535 if (filter == NULL) {
1536 return NT_STATUS_NO_MEMORY;
1539 for (i=0; i<r->in.sids->num_sids; i++) {
1540 const char *memberdn;
1542 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1543 "distinguishedName",
1544 "(objectSid=%s)",
1545 ldap_encode_ndr_dom_sid(mem_ctx,
1546 r->in.sids->sids[i].sid));
1547 if (memberdn == NULL) {
1548 continue;
1551 filter = talloc_asprintf(mem_ctx, "%s(member=%s)", filter,
1552 memberdn);
1553 if (filter == NULL) {
1554 return NT_STATUS_NO_MEMORY;
1558 /* Find out if we had at least one valid member SID passed - otherwise
1559 * just skip the search. */
1560 if (strstr(filter, "member") != NULL) {
1561 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1562 &res, attrs, d_state->domain_sid,
1563 "%s))", filter);
1564 if (count < 0) {
1565 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1569 r->out.rids->count = 0;
1570 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1571 if (r->out.rids->ids == NULL)
1572 return NT_STATUS_NO_MEMORY;
1574 for (i=0; i<count; i++) {
1575 struct dom_sid *alias_sid;
1577 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1578 if (alias_sid == NULL) {
1579 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1582 r->out.rids->ids[r->out.rids->count] =
1583 alias_sid->sub_auths[alias_sid->num_auths-1];
1584 r->out.rids->count += 1;
1587 return NT_STATUS_OK;
1592 samr_LookupNames
1594 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1595 struct samr_LookupNames *r)
1597 struct dcesrv_handle *h;
1598 struct samr_domain_state *d_state;
1599 uint32_t i, num_mapped;
1600 NTSTATUS status = NT_STATUS_OK;
1601 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1602 int count;
1604 ZERO_STRUCTP(r->out.rids);
1605 ZERO_STRUCTP(r->out.types);
1607 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1609 d_state = h->data;
1611 if (r->in.num_names == 0) {
1612 return NT_STATUS_OK;
1615 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1616 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1617 if (!r->out.rids->ids || !r->out.types->ids) {
1618 return NT_STATUS_NO_MEMORY;
1620 r->out.rids->count = r->in.num_names;
1621 r->out.types->count = r->in.num_names;
1623 num_mapped = 0;
1625 for (i=0;i<r->in.num_names;i++) {
1626 struct ldb_message **res;
1627 struct dom_sid *sid;
1628 uint32_t atype, rtype;
1630 r->out.rids->ids[i] = 0;
1631 r->out.types->ids[i] = SID_NAME_UNKNOWN;
1633 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1634 "sAMAccountName=%s",
1635 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
1636 if (count != 1) {
1637 status = STATUS_SOME_UNMAPPED;
1638 continue;
1641 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
1642 if (sid == NULL) {
1643 status = STATUS_SOME_UNMAPPED;
1644 continue;
1647 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
1648 if (atype == 0) {
1649 status = STATUS_SOME_UNMAPPED;
1650 continue;
1653 rtype = ds_atype_map(atype);
1655 if (rtype == SID_NAME_UNKNOWN) {
1656 status = STATUS_SOME_UNMAPPED;
1657 continue;
1660 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
1661 r->out.types->ids[i] = rtype;
1662 num_mapped++;
1665 if (num_mapped == 0) {
1666 return NT_STATUS_NONE_MAPPED;
1668 return status;
1673 samr_LookupRids
1675 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1676 struct samr_LookupRids *r)
1678 NTSTATUS status;
1679 struct dcesrv_handle *h;
1680 struct samr_domain_state *d_state;
1681 const char **names;
1682 struct lsa_String *lsa_names;
1683 enum lsa_SidType *ids;
1685 ZERO_STRUCTP(r->out.names);
1686 ZERO_STRUCTP(r->out.types);
1688 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1690 d_state = h->data;
1692 if (r->in.num_rids == 0)
1693 return NT_STATUS_OK;
1695 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
1696 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
1697 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
1699 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
1700 return NT_STATUS_NO_MEMORY;
1702 r->out.names->names = lsa_names;
1703 r->out.names->count = r->in.num_rids;
1705 r->out.types->ids = (uint32_t *) ids;
1706 r->out.types->count = r->in.num_rids;
1708 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
1709 r->in.num_rids, r->in.rids, names, ids);
1710 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
1711 uint32_t i;
1712 for (i = 0; i < r->in.num_rids; i++) {
1713 lsa_names[i].string = names[i];
1716 return status;
1721 samr_OpenGroup
1723 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1724 struct samr_OpenGroup *r)
1726 struct samr_domain_state *d_state;
1727 struct samr_account_state *a_state;
1728 struct dcesrv_handle *h;
1729 const char *groupname;
1730 struct dom_sid *sid;
1731 struct ldb_message **msgs;
1732 struct dcesrv_handle *g_handle;
1733 const char * const attrs[2] = { "sAMAccountName", NULL };
1734 int ret;
1736 ZERO_STRUCTP(r->out.group_handle);
1738 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1740 d_state = h->data;
1742 /* form the group SID */
1743 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1744 if (!sid) {
1745 return NT_STATUS_NO_MEMORY;
1748 /* search for the group record */
1749 if (d_state->builtin) {
1750 ret = gendb_search(d_state->sam_ctx,
1751 mem_ctx, d_state->domain_dn, &msgs, attrs,
1752 "(&(objectSid=%s)(objectClass=group)"
1753 "(groupType=%d))",
1754 ldap_encode_ndr_dom_sid(mem_ctx, sid),
1755 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
1756 } else {
1757 ret = gendb_search(d_state->sam_ctx,
1758 mem_ctx, d_state->domain_dn, &msgs, attrs,
1759 "(&(objectSid=%s)(objectClass=group)"
1760 "(|(groupType=%d)(groupType=%d)))",
1761 ldap_encode_ndr_dom_sid(mem_ctx, sid),
1762 GTYPE_SECURITY_UNIVERSAL_GROUP,
1763 GTYPE_SECURITY_GLOBAL_GROUP);
1765 if (ret == 0) {
1766 return NT_STATUS_NO_SUCH_GROUP;
1768 if (ret != 1) {
1769 DEBUG(0,("Found %d records matching sid %s\n",
1770 ret, dom_sid_string(mem_ctx, sid)));
1771 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1774 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
1775 if (groupname == NULL) {
1776 DEBUG(0,("sAMAccountName field missing for sid %s\n",
1777 dom_sid_string(mem_ctx, sid)));
1778 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1781 a_state = talloc(mem_ctx, struct samr_account_state);
1782 if (!a_state) {
1783 return NT_STATUS_NO_MEMORY;
1785 a_state->sam_ctx = d_state->sam_ctx;
1786 a_state->access_mask = r->in.access_mask;
1787 a_state->domain_state = talloc_reference(a_state, d_state);
1788 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
1789 a_state->account_sid = talloc_steal(a_state, sid);
1790 a_state->account_name = talloc_strdup(a_state, groupname);
1791 if (!a_state->account_name) {
1792 return NT_STATUS_NO_MEMORY;
1795 /* create the policy handle */
1796 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1797 if (!g_handle) {
1798 return NT_STATUS_NO_MEMORY;
1801 g_handle->data = talloc_steal(g_handle, a_state);
1803 *r->out.group_handle = g_handle->wire_handle;
1805 return NT_STATUS_OK;
1809 samr_QueryGroupInfo
1811 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1812 struct samr_QueryGroupInfo *r)
1814 struct dcesrv_handle *h;
1815 struct samr_account_state *a_state;
1816 struct ldb_message *msg, **res;
1817 const char * const attrs[4] = { "sAMAccountName", "description",
1818 "numMembers", NULL };
1819 int ret;
1820 union samr_GroupInfo *info;
1822 *r->out.info = NULL;
1824 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1826 a_state = h->data;
1828 /* pull all the group attributes */
1829 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
1830 a_state->account_dn, &res, attrs);
1831 if (ret == 0) {
1832 return NT_STATUS_NO_SUCH_GROUP;
1834 if (ret != 1) {
1835 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1837 msg = res[0];
1839 /* allocate the info structure */
1840 info = talloc_zero(mem_ctx, union samr_GroupInfo);
1841 if (info == NULL) {
1842 return NT_STATUS_NO_MEMORY;
1845 /* Fill in the level */
1846 switch (r->in.level) {
1847 case GROUPINFOALL:
1848 QUERY_STRING(msg, all.name, "sAMAccountName");
1849 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1850 QUERY_UINT (msg, all.num_members, "numMembers")
1851 QUERY_STRING(msg, all.description, "description");
1852 break;
1853 case GROUPINFONAME:
1854 QUERY_STRING(msg, name, "sAMAccountName");
1855 break;
1856 case GROUPINFOATTRIBUTES:
1857 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1858 break;
1859 case GROUPINFODESCRIPTION:
1860 QUERY_STRING(msg, description, "description");
1861 break;
1862 case GROUPINFOALL2:
1863 QUERY_STRING(msg, all2.name, "sAMAccountName");
1864 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1865 QUERY_UINT (msg, all2.num_members, "numMembers")
1866 QUERY_STRING(msg, all2.description, "description");
1867 break;
1868 default:
1869 talloc_free(info);
1870 return NT_STATUS_INVALID_INFO_CLASS;
1873 *r->out.info = info;
1875 return NT_STATUS_OK;
1880 samr_SetGroupInfo
1882 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1883 struct samr_SetGroupInfo *r)
1885 struct dcesrv_handle *h;
1886 struct samr_account_state *g_state;
1887 struct ldb_message *msg;
1888 int ret;
1890 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1892 g_state = h->data;
1894 msg = ldb_msg_new(mem_ctx);
1895 if (msg == NULL) {
1896 return NT_STATUS_NO_MEMORY;
1899 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
1900 if (!msg->dn) {
1901 return NT_STATUS_NO_MEMORY;
1904 switch (r->in.level) {
1905 case GROUPINFODESCRIPTION:
1906 SET_STRING(msg, description, "description");
1907 break;
1908 case GROUPINFONAME:
1909 /* On W2k3 this does not change the name, it changes the
1910 * sAMAccountName attribute */
1911 SET_STRING(msg, name, "sAMAccountName");
1912 break;
1913 case GROUPINFOATTRIBUTES:
1914 /* This does not do anything obviously visible in W2k3 LDAP */
1915 return NT_STATUS_OK;
1916 default:
1917 return NT_STATUS_INVALID_INFO_CLASS;
1920 /* modify the samdb record */
1921 ret = ldb_modify(g_state->sam_ctx, msg);
1922 if (ret != LDB_SUCCESS) {
1923 return dsdb_ldb_err_to_ntstatus(ret);
1926 return NT_STATUS_OK;
1931 samr_AddGroupMember
1933 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1934 struct samr_AddGroupMember *r)
1936 struct dcesrv_handle *h;
1937 struct samr_account_state *a_state;
1938 struct samr_domain_state *d_state;
1939 struct ldb_message *mod;
1940 struct dom_sid *membersid;
1941 const char *memberdn;
1942 struct ldb_result *res;
1943 const char * const attrs[] = { NULL };
1944 int ret;
1946 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1948 a_state = h->data;
1949 d_state = a_state->domain_state;
1951 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1952 if (membersid == NULL) {
1953 return NT_STATUS_NO_MEMORY;
1956 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
1957 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
1958 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
1959 "(objectSid=%s)",
1960 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
1962 if (ret != LDB_SUCCESS) {
1963 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1966 if (res->count == 0) {
1967 return NT_STATUS_NO_SUCH_USER;
1970 if (res->count > 1) {
1971 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1974 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
1976 if (memberdn == NULL)
1977 return NT_STATUS_NO_MEMORY;
1979 mod = ldb_msg_new(mem_ctx);
1980 if (mod == NULL) {
1981 return NT_STATUS_NO_MEMORY;
1984 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
1986 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
1987 memberdn);
1988 if (ret != LDB_SUCCESS) {
1989 return dsdb_ldb_err_to_ntstatus(ret);
1992 ret = ldb_modify(a_state->sam_ctx, mod);
1993 switch (ret) {
1994 case LDB_SUCCESS:
1995 return NT_STATUS_OK;
1996 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1997 return NT_STATUS_MEMBER_IN_GROUP;
1998 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
1999 return NT_STATUS_ACCESS_DENIED;
2000 default:
2001 return dsdb_ldb_err_to_ntstatus(ret);
2007 samr_DeleteDomainGroup
2009 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2010 struct samr_DeleteDomainGroup *r)
2012 struct dcesrv_handle *h;
2013 struct samr_account_state *a_state;
2014 int ret;
2016 *r->out.group_handle = *r->in.group_handle;
2018 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2020 a_state = h->data;
2022 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2023 if (ret != LDB_SUCCESS) {
2024 return dsdb_ldb_err_to_ntstatus(ret);
2027 talloc_free(h);
2028 ZERO_STRUCTP(r->out.group_handle);
2030 return NT_STATUS_OK;
2035 samr_DeleteGroupMember
2037 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2038 struct samr_DeleteGroupMember *r)
2040 struct dcesrv_handle *h;
2041 struct samr_account_state *a_state;
2042 struct samr_domain_state *d_state;
2043 struct ldb_message *mod;
2044 struct dom_sid *membersid;
2045 const char *memberdn;
2046 struct ldb_result *res;
2047 const char * const attrs[] = { NULL };
2048 int ret;
2050 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2052 a_state = h->data;
2053 d_state = a_state->domain_state;
2055 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2056 if (membersid == NULL) {
2057 return NT_STATUS_NO_MEMORY;
2060 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2061 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2062 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2063 "(objectSid=%s)",
2064 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2066 if (ret != LDB_SUCCESS) {
2067 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2070 if (res->count == 0) {
2071 return NT_STATUS_NO_SUCH_USER;
2074 if (res->count > 1) {
2075 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2078 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2080 if (memberdn == NULL)
2081 return NT_STATUS_NO_MEMORY;
2083 mod = ldb_msg_new(mem_ctx);
2084 if (mod == NULL) {
2085 return NT_STATUS_NO_MEMORY;
2088 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2090 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2091 memberdn);
2092 if (ret != LDB_SUCCESS) {
2093 return NT_STATUS_NO_MEMORY;
2096 ret = ldb_modify(a_state->sam_ctx, mod);
2097 switch (ret) {
2098 case LDB_SUCCESS:
2099 return NT_STATUS_OK;
2100 case LDB_ERR_UNWILLING_TO_PERFORM:
2101 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2102 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2103 return NT_STATUS_ACCESS_DENIED;
2104 default:
2105 return dsdb_ldb_err_to_ntstatus(ret);
2111 samr_QueryGroupMember
2113 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2114 struct samr_QueryGroupMember *r)
2116 struct dcesrv_handle *h;
2117 struct samr_account_state *a_state;
2118 struct samr_domain_state *d_state;
2119 struct samr_RidAttrArray *array;
2120 unsigned int i, num_members;
2121 struct dom_sid *members;
2122 NTSTATUS status;
2124 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2126 a_state = h->data;
2127 d_state = a_state->domain_state;
2129 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2130 a_state->account_dn, &members,
2131 &num_members);
2132 if (!NT_STATUS_IS_OK(status)) {
2133 return status;
2136 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2137 if (array == NULL) {
2138 return NT_STATUS_NO_MEMORY;
2141 if (num_members == 0) {
2142 *r->out.rids = array;
2144 return NT_STATUS_OK;
2147 array->rids = talloc_array(array, uint32_t, num_members);
2148 if (array->rids == NULL) {
2149 return NT_STATUS_NO_MEMORY;
2152 array->attributes = talloc_array(array, uint32_t, num_members);
2153 if (array->attributes == NULL) {
2154 return NT_STATUS_NO_MEMORY;
2157 array->count = 0;
2158 for (i=0; i<num_members; i++) {
2159 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2160 continue;
2163 status = dom_sid_split_rid(NULL, &members[i], NULL,
2164 &array->rids[array->count]);
2165 if (!NT_STATUS_IS_OK(status)) {
2166 return status;
2169 array->attributes[array->count] = SE_GROUP_MANDATORY |
2170 SE_GROUP_ENABLED_BY_DEFAULT |
2171 SE_GROUP_ENABLED;
2172 array->count++;
2175 *r->out.rids = array;
2177 return NT_STATUS_OK;
2182 samr_SetMemberAttributesOfGroup
2184 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2185 struct samr_SetMemberAttributesOfGroup *r)
2187 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2192 samr_OpenAlias
2194 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2195 struct samr_OpenAlias *r)
2197 struct samr_domain_state *d_state;
2198 struct samr_account_state *a_state;
2199 struct dcesrv_handle *h;
2200 const char *alias_name;
2201 struct dom_sid *sid;
2202 struct ldb_message **msgs;
2203 struct dcesrv_handle *g_handle;
2204 const char * const attrs[2] = { "sAMAccountName", NULL };
2205 int ret;
2207 ZERO_STRUCTP(r->out.alias_handle);
2209 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2211 d_state = h->data;
2213 /* form the alias SID */
2214 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2215 if (sid == NULL)
2216 return NT_STATUS_NO_MEMORY;
2218 /* search for the group record */
2219 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2220 "(&(objectSid=%s)(objectclass=group)"
2221 "(|(grouptype=%d)(grouptype=%d)))",
2222 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2223 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2224 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2225 if (ret == 0) {
2226 return NT_STATUS_NO_SUCH_ALIAS;
2228 if (ret != 1) {
2229 DEBUG(0,("Found %d records matching sid %s\n",
2230 ret, dom_sid_string(mem_ctx, sid)));
2231 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2234 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2235 if (alias_name == NULL) {
2236 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2237 dom_sid_string(mem_ctx, sid)));
2238 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2241 a_state = talloc(mem_ctx, struct samr_account_state);
2242 if (!a_state) {
2243 return NT_STATUS_NO_MEMORY;
2245 a_state->sam_ctx = d_state->sam_ctx;
2246 a_state->access_mask = r->in.access_mask;
2247 a_state->domain_state = talloc_reference(a_state, d_state);
2248 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2249 a_state->account_sid = talloc_steal(a_state, sid);
2250 a_state->account_name = talloc_strdup(a_state, alias_name);
2251 if (!a_state->account_name) {
2252 return NT_STATUS_NO_MEMORY;
2255 /* create the policy handle */
2256 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2257 if (!g_handle) {
2258 return NT_STATUS_NO_MEMORY;
2261 g_handle->data = talloc_steal(g_handle, a_state);
2263 *r->out.alias_handle = g_handle->wire_handle;
2265 return NT_STATUS_OK;
2270 samr_QueryAliasInfo
2272 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2273 struct samr_QueryAliasInfo *r)
2275 struct dcesrv_handle *h;
2276 struct samr_account_state *a_state;
2277 struct ldb_message *msg, **res;
2278 const char * const attrs[4] = { "sAMAccountName", "description",
2279 "numMembers", NULL };
2280 int ret;
2281 union samr_AliasInfo *info;
2283 *r->out.info = NULL;
2285 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2287 a_state = h->data;
2289 /* pull all the alias attributes */
2290 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2291 a_state->account_dn, &res, attrs);
2292 if (ret == 0) {
2293 return NT_STATUS_NO_SUCH_ALIAS;
2295 if (ret != 1) {
2296 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2298 msg = res[0];
2300 /* allocate the info structure */
2301 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2302 if (info == NULL) {
2303 return NT_STATUS_NO_MEMORY;
2306 switch(r->in.level) {
2307 case ALIASINFOALL:
2308 QUERY_STRING(msg, all.name, "sAMAccountName");
2309 QUERY_UINT (msg, all.num_members, "numMembers");
2310 QUERY_STRING(msg, all.description, "description");
2311 break;
2312 case ALIASINFONAME:
2313 QUERY_STRING(msg, name, "sAMAccountName");
2314 break;
2315 case ALIASINFODESCRIPTION:
2316 QUERY_STRING(msg, description, "description");
2317 break;
2318 default:
2319 talloc_free(info);
2320 return NT_STATUS_INVALID_INFO_CLASS;
2323 *r->out.info = info;
2325 return NT_STATUS_OK;
2330 samr_SetAliasInfo
2332 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2333 struct samr_SetAliasInfo *r)
2335 struct dcesrv_handle *h;
2336 struct samr_account_state *a_state;
2337 struct ldb_message *msg;
2338 int ret;
2340 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2342 a_state = h->data;
2344 msg = ldb_msg_new(mem_ctx);
2345 if (msg == NULL) {
2346 return NT_STATUS_NO_MEMORY;
2349 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2350 if (!msg->dn) {
2351 return NT_STATUS_NO_MEMORY;
2354 switch (r->in.level) {
2355 case ALIASINFODESCRIPTION:
2356 SET_STRING(msg, description, "description");
2357 break;
2358 case ALIASINFONAME:
2359 /* On W2k3 this does not change the name, it changes the
2360 * sAMAccountName attribute */
2361 SET_STRING(msg, name, "sAMAccountName");
2362 break;
2363 default:
2364 return NT_STATUS_INVALID_INFO_CLASS;
2367 /* modify the samdb record */
2368 ret = ldb_modify(a_state->sam_ctx, msg);
2369 if (ret != LDB_SUCCESS) {
2370 return dsdb_ldb_err_to_ntstatus(ret);
2373 return NT_STATUS_OK;
2378 samr_DeleteDomAlias
2380 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2381 struct samr_DeleteDomAlias *r)
2383 struct dcesrv_handle *h;
2384 struct samr_account_state *a_state;
2385 int ret;
2387 *r->out.alias_handle = *r->in.alias_handle;
2389 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2391 a_state = h->data;
2393 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2394 if (ret != LDB_SUCCESS) {
2395 return dsdb_ldb_err_to_ntstatus(ret);
2398 talloc_free(h);
2399 ZERO_STRUCTP(r->out.alias_handle);
2401 return NT_STATUS_OK;
2406 samr_AddAliasMember
2408 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2409 struct samr_AddAliasMember *r)
2411 struct dcesrv_handle *h;
2412 struct samr_account_state *a_state;
2413 struct samr_domain_state *d_state;
2414 struct ldb_message *mod;
2415 struct ldb_message **msgs;
2416 const char * const attrs[] = { NULL };
2417 struct ldb_dn *memberdn = NULL;
2418 int ret;
2419 NTSTATUS status;
2421 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2423 a_state = h->data;
2424 d_state = a_state->domain_state;
2426 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2427 &msgs, attrs, "(objectsid=%s)",
2428 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2430 if (ret == 1) {
2431 memberdn = msgs[0]->dn;
2432 } else if (ret == 0) {
2433 status = samdb_create_foreign_security_principal(
2434 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2435 if (!NT_STATUS_IS_OK(status)) {
2436 return status;
2438 } else {
2439 DEBUG(0,("Found %d records matching sid %s\n",
2440 ret, dom_sid_string(mem_ctx, r->in.sid)));
2441 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2444 if (memberdn == NULL) {
2445 DEBUG(0, ("Could not find memberdn\n"));
2446 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2449 mod = ldb_msg_new(mem_ctx);
2450 if (mod == NULL) {
2451 return NT_STATUS_NO_MEMORY;
2454 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2456 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2457 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2458 if (ret != LDB_SUCCESS) {
2459 return dsdb_ldb_err_to_ntstatus(ret);
2462 ret = ldb_modify(a_state->sam_ctx, mod);
2463 switch (ret) {
2464 case LDB_SUCCESS:
2465 return NT_STATUS_OK;
2466 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2467 return NT_STATUS_MEMBER_IN_GROUP;
2468 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2469 return NT_STATUS_ACCESS_DENIED;
2470 default:
2471 return dsdb_ldb_err_to_ntstatus(ret);
2477 samr_DeleteAliasMember
2479 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2480 struct samr_DeleteAliasMember *r)
2482 struct dcesrv_handle *h;
2483 struct samr_account_state *a_state;
2484 struct samr_domain_state *d_state;
2485 struct ldb_message *mod;
2486 const char *memberdn;
2487 int ret;
2489 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2491 a_state = h->data;
2492 d_state = a_state->domain_state;
2494 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2495 "distinguishedName", "(objectSid=%s)",
2496 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2497 if (memberdn == NULL) {
2498 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2501 mod = ldb_msg_new(mem_ctx);
2502 if (mod == NULL) {
2503 return NT_STATUS_NO_MEMORY;
2506 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2508 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2509 memberdn);
2510 if (ret != LDB_SUCCESS) {
2511 return dsdb_ldb_err_to_ntstatus(ret);
2514 ret = ldb_modify(a_state->sam_ctx, mod);
2515 switch (ret) {
2516 case LDB_SUCCESS:
2517 return NT_STATUS_OK;
2518 case LDB_ERR_UNWILLING_TO_PERFORM:
2519 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2520 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2521 return NT_STATUS_ACCESS_DENIED;
2522 default:
2523 return dsdb_ldb_err_to_ntstatus(ret);
2529 samr_GetMembersInAlias
2531 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2532 struct samr_GetMembersInAlias *r)
2534 struct dcesrv_handle *h;
2535 struct samr_account_state *a_state;
2536 struct samr_domain_state *d_state;
2537 struct lsa_SidPtr *array;
2538 unsigned int i, num_members;
2539 struct dom_sid *members;
2540 NTSTATUS status;
2542 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2544 a_state = h->data;
2545 d_state = a_state->domain_state;
2547 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2548 a_state->account_dn, &members,
2549 &num_members);
2550 if (!NT_STATUS_IS_OK(status)) {
2551 return status;
2554 if (num_members == 0) {
2555 r->out.sids->sids = NULL;
2557 return NT_STATUS_OK;
2560 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
2561 if (array == NULL) {
2562 return NT_STATUS_NO_MEMORY;
2565 for (i=0; i<num_members; i++) {
2566 array[i].sid = &members[i];
2569 r->out.sids->num_sids = num_members;
2570 r->out.sids->sids = array;
2572 return NT_STATUS_OK;
2576 samr_OpenUser
2578 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2579 struct samr_OpenUser *r)
2581 struct samr_domain_state *d_state;
2582 struct samr_account_state *a_state;
2583 struct dcesrv_handle *h;
2584 const char *account_name;
2585 struct dom_sid *sid;
2586 struct ldb_message **msgs;
2587 struct dcesrv_handle *u_handle;
2588 const char * const attrs[2] = { "sAMAccountName", NULL };
2589 int ret;
2591 ZERO_STRUCTP(r->out.user_handle);
2593 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2595 d_state = h->data;
2597 /* form the users SID */
2598 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2599 if (!sid) {
2600 return NT_STATUS_NO_MEMORY;
2603 /* search for the user record */
2604 ret = gendb_search(d_state->sam_ctx,
2605 mem_ctx, d_state->domain_dn, &msgs, attrs,
2606 "(&(objectSid=%s)(objectclass=user))",
2607 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2608 if (ret == 0) {
2609 return NT_STATUS_NO_SUCH_USER;
2611 if (ret != 1) {
2612 DEBUG(0,("Found %d records matching sid %s\n", ret,
2613 dom_sid_string(mem_ctx, sid)));
2614 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2617 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2618 if (account_name == NULL) {
2619 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2620 dom_sid_string(mem_ctx, sid)));
2621 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2624 a_state = talloc(mem_ctx, struct samr_account_state);
2625 if (!a_state) {
2626 return NT_STATUS_NO_MEMORY;
2628 a_state->sam_ctx = d_state->sam_ctx;
2629 a_state->access_mask = r->in.access_mask;
2630 a_state->domain_state = talloc_reference(a_state, d_state);
2631 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2632 a_state->account_sid = talloc_steal(a_state, sid);
2633 a_state->account_name = talloc_strdup(a_state, account_name);
2634 if (!a_state->account_name) {
2635 return NT_STATUS_NO_MEMORY;
2638 /* create the policy handle */
2639 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
2640 if (!u_handle) {
2641 return NT_STATUS_NO_MEMORY;
2644 u_handle->data = talloc_steal(u_handle, a_state);
2646 *r->out.user_handle = u_handle->wire_handle;
2648 return NT_STATUS_OK;
2654 samr_DeleteUser
2656 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2657 struct samr_DeleteUser *r)
2659 struct dcesrv_handle *h;
2660 struct samr_account_state *a_state;
2661 int ret;
2663 *r->out.user_handle = *r->in.user_handle;
2665 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2667 a_state = h->data;
2669 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2670 if (ret != LDB_SUCCESS) {
2671 DEBUG(1, ("Failed to delete user: %s: %s\n",
2672 ldb_dn_get_linearized(a_state->account_dn),
2673 ldb_errstring(a_state->sam_ctx)));
2674 return dsdb_ldb_err_to_ntstatus(ret);
2677 talloc_free(h);
2678 ZERO_STRUCTP(r->out.user_handle);
2680 return NT_STATUS_OK;
2685 samr_QueryUserInfo
2687 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2688 struct samr_QueryUserInfo *r)
2690 struct dcesrv_handle *h;
2691 struct samr_account_state *a_state;
2692 struct ldb_message *msg, **res;
2693 int ret;
2694 struct ldb_context *sam_ctx;
2696 const char * const *attrs = NULL;
2697 union samr_UserInfo *info;
2699 NTSTATUS status;
2701 *r->out.info = NULL;
2703 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2705 a_state = h->data;
2706 sam_ctx = a_state->sam_ctx;
2708 /* fill in the reply */
2709 switch (r->in.level) {
2710 case 1:
2712 static const char * const attrs2[] = {"sAMAccountName",
2713 "displayName",
2714 "primaryroupID",
2715 "description",
2716 "comment",
2717 NULL};
2718 attrs = attrs2;
2719 break;
2721 case 2:
2723 static const char * const attrs2[] = {"comment",
2724 "countryCode",
2725 "codePage",
2726 NULL};
2727 attrs = attrs2;
2728 break;
2730 case 3:
2732 static const char * const attrs2[] = {"sAMAccountName",
2733 "displayName",
2734 "objectSid",
2735 "primaryGroupID",
2736 "homeDirectory",
2737 "homeDrive",
2738 "scriptPath",
2739 "profilePath",
2740 "userWorkstations",
2741 "lastLogon",
2742 "lastLogoff",
2743 "pwdLastSet",
2744 "logonHours",
2745 "badPwdCount",
2746 "badPasswordTime",
2747 "logonCount",
2748 "userAccountControl",
2749 "msDS-User-Account-Control-Computed",
2750 NULL};
2751 attrs = attrs2;
2752 break;
2754 case 4:
2756 static const char * const attrs2[] = {"logonHours",
2757 NULL};
2758 attrs = attrs2;
2759 break;
2761 case 5:
2763 static const char * const attrs2[] = {"sAMAccountName",
2764 "displayName",
2765 "objectSid",
2766 "primaryGroupID",
2767 "homeDirectory",
2768 "homeDrive",
2769 "scriptPath",
2770 "profilePath",
2771 "description",
2772 "userWorkstations",
2773 "lastLogon",
2774 "lastLogoff",
2775 "logonHours",
2776 "badPwdCount",
2777 "badPasswordTime",
2778 "logonCount",
2779 "pwdLastSet",
2780 "accountExpires",
2781 "userAccountControl",
2782 "msDS-User-Account-Control-Computed",
2783 NULL};
2784 attrs = attrs2;
2785 break;
2787 case 6:
2789 static const char * const attrs2[] = {"sAMAccountName",
2790 "displayName",
2791 NULL};
2792 attrs = attrs2;
2793 break;
2795 case 7:
2797 static const char * const attrs2[] = {"sAMAccountName",
2798 NULL};
2799 attrs = attrs2;
2800 break;
2802 case 8:
2804 static const char * const attrs2[] = {"displayName",
2805 NULL};
2806 attrs = attrs2;
2807 break;
2809 case 9:
2811 static const char * const attrs2[] = {"primaryGroupID",
2812 NULL};
2813 attrs = attrs2;
2814 break;
2816 case 10:
2818 static const char * const attrs2[] = {"homeDirectory",
2819 "homeDrive",
2820 NULL};
2821 attrs = attrs2;
2822 break;
2824 case 11:
2826 static const char * const attrs2[] = {"scriptPath",
2827 NULL};
2828 attrs = attrs2;
2829 break;
2831 case 12:
2833 static const char * const attrs2[] = {"profilePath",
2834 NULL};
2835 attrs = attrs2;
2836 break;
2838 case 13:
2840 static const char * const attrs2[] = {"description",
2841 NULL};
2842 attrs = attrs2;
2843 break;
2845 case 14:
2847 static const char * const attrs2[] = {"userWorkstations",
2848 NULL};
2849 attrs = attrs2;
2850 break;
2852 case 16:
2854 static const char * const attrs2[] = {"userAccountControl",
2855 "msDS-User-Account-Control-Computed",
2856 "pwdLastSet",
2857 NULL};
2858 attrs = attrs2;
2859 break;
2861 case 17:
2863 static const char * const attrs2[] = {"accountExpires",
2864 NULL};
2865 attrs = attrs2;
2866 break;
2868 case 18:
2870 return NT_STATUS_NOT_SUPPORTED;
2872 case 20:
2874 static const char * const attrs2[] = {"userParameters",
2875 NULL};
2876 attrs = attrs2;
2877 break;
2879 case 21:
2881 static const char * const attrs2[] = {"lastLogon",
2882 "lastLogoff",
2883 "pwdLastSet",
2884 "accountExpires",
2885 "sAMAccountName",
2886 "displayName",
2887 "homeDirectory",
2888 "homeDrive",
2889 "scriptPath",
2890 "profilePath",
2891 "description",
2892 "userWorkstations",
2893 "comment",
2894 "userParameters",
2895 "objectSid",
2896 "primaryGroupID",
2897 "userAccountControl",
2898 "msDS-User-Account-Control-Computed",
2899 "logonHours",
2900 "badPwdCount",
2901 "badPasswordTime",
2902 "logonCount",
2903 "countryCode",
2904 "codePage",
2905 NULL};
2906 attrs = attrs2;
2907 break;
2909 case 23:
2910 case 24:
2911 case 25:
2912 case 26:
2914 return NT_STATUS_NOT_SUPPORTED;
2916 default:
2918 return NT_STATUS_INVALID_INFO_CLASS;
2922 /* pull all the user attributes */
2923 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2924 a_state->account_dn, &res, attrs);
2925 if (ret == 0) {
2926 return NT_STATUS_NO_SUCH_USER;
2928 if (ret != 1) {
2929 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2931 msg = res[0];
2933 /* allocate the info structure */
2934 info = talloc_zero(mem_ctx, union samr_UserInfo);
2935 if (info == NULL) {
2936 return NT_STATUS_NO_MEMORY;
2939 /* fill in the reply */
2940 switch (r->in.level) {
2941 case 1:
2942 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
2943 QUERY_STRING(msg, info1.full_name, "displayName");
2944 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
2945 QUERY_STRING(msg, info1.description, "description");
2946 QUERY_STRING(msg, info1.comment, "comment");
2947 break;
2949 case 2:
2950 QUERY_STRING(msg, info2.comment, "comment");
2951 QUERY_UINT (msg, info2.country_code, "countryCode");
2952 QUERY_UINT (msg, info2.code_page, "codePage");
2953 break;
2955 case 3:
2956 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
2957 QUERY_STRING(msg, info3.full_name, "displayName");
2958 QUERY_RID (msg, info3.rid, "objectSid");
2959 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
2960 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
2961 QUERY_STRING(msg, info3.home_drive, "homeDrive");
2962 QUERY_STRING(msg, info3.logon_script, "scriptPath");
2963 QUERY_STRING(msg, info3.profile_path, "profilePath");
2964 QUERY_STRING(msg, info3.workstations, "userWorkstations");
2965 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
2966 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
2967 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
2968 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
2969 QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
2970 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
2971 /* level 3 gives the raw badPwdCount value */
2972 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
2973 QUERY_UINT (msg, info3.logon_count, "logonCount");
2974 QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
2975 break;
2977 case 4:
2978 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
2979 break;
2981 case 5:
2982 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
2983 QUERY_STRING(msg, info5.full_name, "displayName");
2984 QUERY_RID (msg, info5.rid, "objectSid");
2985 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
2986 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
2987 QUERY_STRING(msg, info5.home_drive, "homeDrive");
2988 QUERY_STRING(msg, info5.logon_script, "scriptPath");
2989 QUERY_STRING(msg, info5.profile_path, "profilePath");
2990 QUERY_STRING(msg, info5.description, "description");
2991 QUERY_STRING(msg, info5.workstations, "userWorkstations");
2992 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
2993 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
2994 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
2995 QUERY_BPWDCT(msg, info5.bad_password_count, "badPwdCount");
2996 QUERY_UINT (msg, info5.logon_count, "logonCount");
2997 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
2998 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
2999 QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
3000 break;
3002 case 6:
3003 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3004 QUERY_STRING(msg, info6.full_name, "displayName");
3005 break;
3007 case 7:
3008 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3009 break;
3011 case 8:
3012 QUERY_STRING(msg, info8.full_name, "displayName");
3013 break;
3015 case 9:
3016 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3017 break;
3019 case 10:
3020 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3021 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3022 break;
3024 case 11:
3025 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3026 break;
3028 case 12:
3029 QUERY_STRING(msg, info12.profile_path, "profilePath");
3030 break;
3032 case 13:
3033 QUERY_STRING(msg, info13.description, "description");
3034 break;
3036 case 14:
3037 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3038 break;
3040 case 16:
3041 QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3042 break;
3044 case 17:
3045 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3046 break;
3048 case 20:
3049 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
3050 if (!NT_STATUS_IS_OK(status)) {
3051 talloc_free(info);
3052 return status;
3054 break;
3056 case 21:
3057 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3058 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3059 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3060 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3061 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3062 QUERY_FPASSC(msg, info21.force_password_change,"pwdLastSet");
3063 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3064 QUERY_STRING(msg, info21.full_name, "displayName");
3065 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3066 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3067 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3068 QUERY_STRING(msg, info21.profile_path, "profilePath");
3069 QUERY_STRING(msg, info21.description, "description");
3070 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3071 QUERY_STRING(msg, info21.comment, "comment");
3072 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
3073 if (!NT_STATUS_IS_OK(status)) {
3074 talloc_free(info);
3075 return status;
3078 QUERY_RID (msg, info21.rid, "objectSid");
3079 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3080 QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3081 info->info21.fields_present = 0x08FFFFFF;
3082 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3083 QUERY_BPWDCT(msg, info21.bad_password_count, "badPwdCount");
3084 QUERY_UINT (msg, info21.logon_count, "logonCount");
3085 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3086 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3087 } else {
3088 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3090 QUERY_UINT (msg, info21.country_code, "countryCode");
3091 QUERY_UINT (msg, info21.code_page, "codePage");
3092 break;
3095 default:
3096 talloc_free(info);
3097 return NT_STATUS_INVALID_INFO_CLASS;
3100 *r->out.info = info;
3102 return NT_STATUS_OK;
3107 samr_SetUserInfo
3109 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3110 struct samr_SetUserInfo *r)
3112 struct dcesrv_handle *h;
3113 struct samr_account_state *a_state;
3114 struct ldb_message *msg;
3115 int ret;
3116 NTSTATUS status = NT_STATUS_OK;
3117 struct ldb_context *sam_ctx;
3119 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3121 a_state = h->data;
3122 sam_ctx = a_state->sam_ctx;
3124 msg = ldb_msg_new(mem_ctx);
3125 if (msg == NULL) {
3126 return NT_STATUS_NO_MEMORY;
3129 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3130 if (!msg->dn) {
3131 return NT_STATUS_NO_MEMORY;
3134 switch (r->in.level) {
3135 case 2:
3136 SET_STRING(msg, info2.comment, "comment");
3137 SET_UINT (msg, info2.country_code, "countryCode");
3138 SET_UINT (msg, info2.code_page, "codePage");
3139 break;
3141 case 4:
3142 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3143 break;
3145 case 6:
3146 SET_STRING(msg, info6.account_name, "samAccountName");
3147 SET_STRING(msg, info6.full_name, "displayName");
3148 break;
3150 case 7:
3151 SET_STRING(msg, info7.account_name, "samAccountName");
3152 break;
3154 case 8:
3155 SET_STRING(msg, info8.full_name, "displayName");
3156 break;
3158 case 9:
3159 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3160 break;
3162 case 10:
3163 SET_STRING(msg, info10.home_directory, "homeDirectory");
3164 SET_STRING(msg, info10.home_drive, "homeDrive");
3165 break;
3167 case 11:
3168 SET_STRING(msg, info11.logon_script, "scriptPath");
3169 break;
3171 case 12:
3172 SET_STRING(msg, info12.profile_path, "profilePath");
3173 break;
3175 case 13:
3176 SET_STRING(msg, info13.description, "description");
3177 break;
3179 case 14:
3180 SET_STRING(msg, info14.workstations, "userWorkstations");
3181 break;
3183 case 16:
3184 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3185 break;
3187 case 17:
3188 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3189 break;
3191 case 18:
3192 status = samr_set_password_buffers(dce_call,
3193 a_state->sam_ctx,
3194 a_state->account_dn,
3195 a_state->domain_state->domain_dn,
3196 mem_ctx,
3197 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3198 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3199 if (!NT_STATUS_IS_OK(status)) {
3200 return status;
3203 if (r->in.info->info18.password_expired > 0) {
3204 struct ldb_message_element *set_el;
3205 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3206 return NT_STATUS_NO_MEMORY;
3208 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3209 set_el->flags = LDB_FLAG_MOD_REPLACE;
3211 break;
3213 case 20:
3214 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3215 break;
3217 case 21:
3218 if (r->in.info->info21.fields_present == 0)
3219 return NT_STATUS_INVALID_PARAMETER;
3221 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3222 IFSET(SAMR_FIELD_LAST_LOGON)
3223 SET_UINT64(msg, info21.last_logon, "lastLogon");
3224 IFSET(SAMR_FIELD_LAST_LOGOFF)
3225 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3226 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3227 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3228 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3229 SET_STRING(msg, info21.account_name, "samAccountName");
3230 IFSET(SAMR_FIELD_FULL_NAME)
3231 SET_STRING(msg, info21.full_name, "displayName");
3232 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3233 SET_STRING(msg, info21.home_directory, "homeDirectory");
3234 IFSET(SAMR_FIELD_HOME_DRIVE)
3235 SET_STRING(msg, info21.home_drive, "homeDrive");
3236 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3237 SET_STRING(msg, info21.logon_script, "scriptPath");
3238 IFSET(SAMR_FIELD_PROFILE_PATH)
3239 SET_STRING(msg, info21.profile_path, "profilePath");
3240 IFSET(SAMR_FIELD_DESCRIPTION)
3241 SET_STRING(msg, info21.description, "description");
3242 IFSET(SAMR_FIELD_WORKSTATIONS)
3243 SET_STRING(msg, info21.workstations, "userWorkstations");
3244 IFSET(SAMR_FIELD_COMMENT)
3245 SET_STRING(msg, info21.comment, "comment");
3246 IFSET(SAMR_FIELD_PARAMETERS)
3247 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3248 IFSET(SAMR_FIELD_PRIMARY_GID)
3249 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3250 IFSET(SAMR_FIELD_ACCT_FLAGS)
3251 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3252 IFSET(SAMR_FIELD_LOGON_HOURS)
3253 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3254 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3255 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3256 IFSET(SAMR_FIELD_NUM_LOGONS)
3257 SET_UINT (msg, info21.logon_count, "logonCount");
3258 IFSET(SAMR_FIELD_COUNTRY_CODE)
3259 SET_UINT (msg, info21.country_code, "countryCode");
3260 IFSET(SAMR_FIELD_CODE_PAGE)
3261 SET_UINT (msg, info21.code_page, "codePage");
3263 /* password change fields */
3264 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3265 return NT_STATUS_ACCESS_DENIED;
3267 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3268 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3269 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3271 if (r->in.info->info21.lm_password_set) {
3272 if ((r->in.info->info21.lm_owf_password.length != 16)
3273 || (r->in.info->info21.lm_owf_password.size != 16)) {
3274 return NT_STATUS_INVALID_PARAMETER;
3277 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3279 if (r->in.info->info21.nt_password_set) {
3280 if ((r->in.info->info21.nt_owf_password.length != 16)
3281 || (r->in.info->info21.nt_owf_password.size != 16)) {
3282 return NT_STATUS_INVALID_PARAMETER;
3285 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3287 status = samr_set_password_buffers(dce_call,
3288 a_state->sam_ctx,
3289 a_state->account_dn,
3290 a_state->domain_state->domain_dn,
3291 mem_ctx,
3292 lm_pwd_hash,
3293 nt_pwd_hash);
3294 if (!NT_STATUS_IS_OK(status)) {
3295 return status;
3300 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3301 NTTIME t = 0;
3302 struct ldb_message_element *set_el;
3303 if (r->in.info->info21.password_expired
3304 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3305 unix_to_nt_time(&t, time(NULL));
3307 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3308 "pwdLastSet", t) != LDB_SUCCESS) {
3309 return NT_STATUS_NO_MEMORY;
3311 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3312 set_el->flags = LDB_FLAG_MOD_REPLACE;
3314 #undef IFSET
3315 break;
3317 case 23:
3318 if (r->in.info->info23.info.fields_present == 0)
3319 return NT_STATUS_INVALID_PARAMETER;
3321 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3322 IFSET(SAMR_FIELD_LAST_LOGON)
3323 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3324 IFSET(SAMR_FIELD_LAST_LOGOFF)
3325 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3326 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3327 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3328 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3329 SET_STRING(msg, info23.info.account_name, "samAccountName");
3330 IFSET(SAMR_FIELD_FULL_NAME)
3331 SET_STRING(msg, info23.info.full_name, "displayName");
3332 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3333 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3334 IFSET(SAMR_FIELD_HOME_DRIVE)
3335 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3336 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3337 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3338 IFSET(SAMR_FIELD_PROFILE_PATH)
3339 SET_STRING(msg, info23.info.profile_path, "profilePath");
3340 IFSET(SAMR_FIELD_DESCRIPTION)
3341 SET_STRING(msg, info23.info.description, "description");
3342 IFSET(SAMR_FIELD_WORKSTATIONS)
3343 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3344 IFSET(SAMR_FIELD_COMMENT)
3345 SET_STRING(msg, info23.info.comment, "comment");
3346 IFSET(SAMR_FIELD_PARAMETERS)
3347 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3348 IFSET(SAMR_FIELD_PRIMARY_GID)
3349 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3350 IFSET(SAMR_FIELD_ACCT_FLAGS)
3351 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3352 IFSET(SAMR_FIELD_LOGON_HOURS)
3353 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3354 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3355 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3356 IFSET(SAMR_FIELD_NUM_LOGONS)
3357 SET_UINT (msg, info23.info.logon_count, "logonCount");
3359 IFSET(SAMR_FIELD_COUNTRY_CODE)
3360 SET_UINT (msg, info23.info.country_code, "countryCode");
3361 IFSET(SAMR_FIELD_CODE_PAGE)
3362 SET_UINT (msg, info23.info.code_page, "codePage");
3364 /* password change fields */
3365 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3366 return NT_STATUS_ACCESS_DENIED;
3368 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3369 status = samr_set_password(dce_call,
3370 a_state->sam_ctx,
3371 a_state->account_dn,
3372 a_state->domain_state->domain_dn,
3373 mem_ctx,
3374 &r->in.info->info23.password);
3375 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3376 status = samr_set_password(dce_call,
3377 a_state->sam_ctx,
3378 a_state->account_dn,
3379 a_state->domain_state->domain_dn,
3380 mem_ctx,
3381 &r->in.info->info23.password);
3383 if (!NT_STATUS_IS_OK(status)) {
3384 return status;
3387 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3388 NTTIME t = 0;
3389 struct ldb_message_element *set_el;
3390 if (r->in.info->info23.info.password_expired
3391 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3392 unix_to_nt_time(&t, time(NULL));
3394 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3395 "pwdLastSet", t) != LDB_SUCCESS) {
3396 return NT_STATUS_NO_MEMORY;
3398 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3399 set_el->flags = LDB_FLAG_MOD_REPLACE;
3401 #undef IFSET
3402 break;
3404 /* the set password levels are handled separately */
3405 case 24:
3406 status = samr_set_password(dce_call,
3407 a_state->sam_ctx,
3408 a_state->account_dn,
3409 a_state->domain_state->domain_dn,
3410 mem_ctx,
3411 &r->in.info->info24.password);
3412 if (!NT_STATUS_IS_OK(status)) {
3413 return status;
3416 if (r->in.info->info24.password_expired > 0) {
3417 struct ldb_message_element *set_el;
3418 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3419 return NT_STATUS_NO_MEMORY;
3421 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3422 set_el->flags = LDB_FLAG_MOD_REPLACE;
3424 break;
3426 case 25:
3427 if (r->in.info->info25.info.fields_present == 0)
3428 return NT_STATUS_INVALID_PARAMETER;
3430 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3431 IFSET(SAMR_FIELD_LAST_LOGON)
3432 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3433 IFSET(SAMR_FIELD_LAST_LOGOFF)
3434 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3435 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3436 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3437 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3438 SET_STRING(msg, info25.info.account_name, "samAccountName");
3439 IFSET(SAMR_FIELD_FULL_NAME)
3440 SET_STRING(msg, info25.info.full_name, "displayName");
3441 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3442 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3443 IFSET(SAMR_FIELD_HOME_DRIVE)
3444 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3445 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3446 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3447 IFSET(SAMR_FIELD_PROFILE_PATH)
3448 SET_STRING(msg, info25.info.profile_path, "profilePath");
3449 IFSET(SAMR_FIELD_DESCRIPTION)
3450 SET_STRING(msg, info25.info.description, "description");
3451 IFSET(SAMR_FIELD_WORKSTATIONS)
3452 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3453 IFSET(SAMR_FIELD_COMMENT)
3454 SET_STRING(msg, info25.info.comment, "comment");
3455 IFSET(SAMR_FIELD_PARAMETERS)
3456 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3457 IFSET(SAMR_FIELD_PRIMARY_GID)
3458 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3459 IFSET(SAMR_FIELD_ACCT_FLAGS)
3460 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3461 IFSET(SAMR_FIELD_LOGON_HOURS)
3462 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3463 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3464 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
3465 IFSET(SAMR_FIELD_NUM_LOGONS)
3466 SET_UINT (msg, info25.info.logon_count, "logonCount");
3467 IFSET(SAMR_FIELD_COUNTRY_CODE)
3468 SET_UINT (msg, info25.info.country_code, "countryCode");
3469 IFSET(SAMR_FIELD_CODE_PAGE)
3470 SET_UINT (msg, info25.info.code_page, "codePage");
3472 /* password change fields */
3473 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3474 return NT_STATUS_ACCESS_DENIED;
3476 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3477 status = samr_set_password_ex(dce_call,
3478 a_state->sam_ctx,
3479 a_state->account_dn,
3480 a_state->domain_state->domain_dn,
3481 mem_ctx,
3482 &r->in.info->info25.password);
3483 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3484 status = samr_set_password_ex(dce_call,
3485 a_state->sam_ctx,
3486 a_state->account_dn,
3487 a_state->domain_state->domain_dn,
3488 mem_ctx,
3489 &r->in.info->info25.password);
3491 if (!NT_STATUS_IS_OK(status)) {
3492 return status;
3495 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3496 NTTIME t = 0;
3497 struct ldb_message_element *set_el;
3498 if (r->in.info->info25.info.password_expired
3499 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3500 unix_to_nt_time(&t, time(NULL));
3502 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3503 "pwdLastSet", t) != LDB_SUCCESS) {
3504 return NT_STATUS_NO_MEMORY;
3506 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3507 set_el->flags = LDB_FLAG_MOD_REPLACE;
3509 #undef IFSET
3510 break;
3512 /* the set password levels are handled separately */
3513 case 26:
3514 status = samr_set_password_ex(dce_call,
3515 a_state->sam_ctx,
3516 a_state->account_dn,
3517 a_state->domain_state->domain_dn,
3518 mem_ctx,
3519 &r->in.info->info26.password);
3520 if (!NT_STATUS_IS_OK(status)) {
3521 return status;
3524 if (r->in.info->info26.password_expired > 0) {
3525 NTTIME t = 0;
3526 struct ldb_message_element *set_el;
3527 if (r->in.info->info26.password_expired
3528 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3529 unix_to_nt_time(&t, time(NULL));
3531 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3532 "pwdLastSet", t) != LDB_SUCCESS) {
3533 return NT_STATUS_NO_MEMORY;
3535 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3536 set_el->flags = LDB_FLAG_MOD_REPLACE;
3538 break;
3540 default:
3541 /* many info classes are not valid for SetUserInfo */
3542 return NT_STATUS_INVALID_INFO_CLASS;
3545 if (!NT_STATUS_IS_OK(status)) {
3546 return status;
3549 /* modify the samdb record */
3550 if (msg->num_elements > 0) {
3551 ret = ldb_modify(a_state->sam_ctx, msg);
3552 if (ret != LDB_SUCCESS) {
3553 DEBUG(1,("Failed to modify record %s: %s\n",
3554 ldb_dn_get_linearized(a_state->account_dn),
3555 ldb_errstring(a_state->sam_ctx)));
3557 return dsdb_ldb_err_to_ntstatus(ret);
3561 return NT_STATUS_OK;
3566 samr_GetGroupsForUser
3568 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3569 struct samr_GetGroupsForUser *r)
3571 struct dcesrv_handle *h;
3572 struct samr_account_state *a_state;
3573 struct samr_domain_state *d_state;
3574 struct ldb_message **res;
3575 const char * const attrs[2] = { "objectSid", NULL };
3576 struct samr_RidWithAttributeArray *array;
3577 int i, count;
3579 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3581 a_state = h->data;
3582 d_state = a_state->domain_state;
3584 count = samdb_search_domain(a_state->sam_ctx, mem_ctx,
3585 d_state->domain_dn, &res,
3586 attrs, d_state->domain_sid,
3587 "(&(member=%s)(|(grouptype=%d)(grouptype=%d))(objectclass=group))",
3588 ldb_dn_get_linearized(a_state->account_dn),
3589 GTYPE_SECURITY_UNIVERSAL_GROUP,
3590 GTYPE_SECURITY_GLOBAL_GROUP);
3591 if (count < 0)
3592 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3594 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
3595 if (array == NULL)
3596 return NT_STATUS_NO_MEMORY;
3598 array->count = 0;
3599 array->rids = NULL;
3601 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
3602 count + 1);
3603 if (array->rids == NULL)
3604 return NT_STATUS_NO_MEMORY;
3606 /* Adds the primary group */
3607 array->rids[0].rid = samdb_search_uint(a_state->sam_ctx, mem_ctx,
3608 ~0, a_state->account_dn,
3609 "primaryGroupID", NULL);
3610 array->rids[0].attributes = SE_GROUP_MANDATORY
3611 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3612 array->count += 1;
3614 /* Adds the additional groups */
3615 for (i = 0; i < count; i++) {
3616 struct dom_sid *group_sid;
3618 group_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
3619 if (group_sid == NULL) {
3620 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3623 array->rids[i + 1].rid =
3624 group_sid->sub_auths[group_sid->num_auths-1];
3625 array->rids[i + 1].attributes = SE_GROUP_MANDATORY
3626 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3627 array->count += 1;
3630 *r->out.rids = array;
3632 return NT_STATUS_OK;
3637 samr_QueryDisplayInfo
3639 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3640 struct samr_QueryDisplayInfo *r)
3642 struct dcesrv_handle *h;
3643 struct samr_domain_state *d_state;
3644 struct ldb_result *res;
3645 unsigned int i;
3646 uint32_t count;
3647 const char * const attrs[] = { "objectSid", "sAMAccountName",
3648 "displayName", "description", "userAccountControl",
3649 "pwdLastSet", NULL };
3650 struct samr_DispEntryFull *entriesFull = NULL;
3651 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
3652 struct samr_DispEntryAscii *entriesAscii = NULL;
3653 struct samr_DispEntryGeneral *entriesGeneral = NULL;
3654 const char *filter;
3655 int ret;
3657 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3659 d_state = h->data;
3661 switch (r->in.level) {
3662 case 1:
3663 case 4:
3664 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3665 "(sAMAccountType=%d))",
3666 ATYPE_NORMAL_ACCOUNT);
3667 break;
3668 case 2:
3669 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3670 "(sAMAccountType=%d))",
3671 ATYPE_WORKSTATION_TRUST);
3672 break;
3673 case 3:
3674 case 5:
3675 filter = talloc_asprintf(mem_ctx,
3676 "(&(|(groupType=%d)(groupType=%d))"
3677 "(objectClass=group))",
3678 GTYPE_SECURITY_UNIVERSAL_GROUP,
3679 GTYPE_SECURITY_GLOBAL_GROUP);
3680 break;
3681 default:
3682 return NT_STATUS_INVALID_INFO_CLASS;
3685 /* search for all requested objects in all domains. This could
3686 possibly be cached and resumed based on resume_key */
3687 ret = dsdb_search(d_state->sam_ctx, mem_ctx, &res, ldb_get_default_basedn(d_state->sam_ctx),
3688 LDB_SCOPE_SUBTREE, attrs, 0, "%s", filter);
3689 if (ret != LDB_SUCCESS) {
3690 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3692 if ((res->count == 0) || (r->in.max_entries == 0)) {
3693 return NT_STATUS_OK;
3696 switch (r->in.level) {
3697 case 1:
3698 entriesGeneral = talloc_array(mem_ctx,
3699 struct samr_DispEntryGeneral,
3700 res->count);
3701 break;
3702 case 2:
3703 entriesFull = talloc_array(mem_ctx,
3704 struct samr_DispEntryFull,
3705 res->count);
3706 break;
3707 case 3:
3708 entriesFullGroup = talloc_array(mem_ctx,
3709 struct samr_DispEntryFullGroup,
3710 res->count);
3711 break;
3712 case 4:
3713 case 5:
3714 entriesAscii = talloc_array(mem_ctx,
3715 struct samr_DispEntryAscii,
3716 res->count);
3717 break;
3720 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
3721 (entriesAscii == NULL) && (entriesFullGroup == NULL))
3722 return NT_STATUS_NO_MEMORY;
3724 count = 0;
3726 for (i = 0; i < res->count; i++) {
3727 struct dom_sid *objectsid;
3729 objectsid = samdb_result_dom_sid(mem_ctx, res->msgs[i],
3730 "objectSid");
3731 if (objectsid == NULL)
3732 continue;
3734 switch(r->in.level) {
3735 case 1:
3736 entriesGeneral[count].idx = count + 1;
3737 entriesGeneral[count].rid =
3738 objectsid->sub_auths[objectsid->num_auths-1];
3739 entriesGeneral[count].acct_flags =
3740 samdb_result_acct_flags(res->msgs[i], NULL);
3741 entriesGeneral[count].account_name.string =
3742 ldb_msg_find_attr_as_string(res->msgs[i],
3743 "sAMAccountName", "");
3744 entriesGeneral[count].full_name.string =
3745 ldb_msg_find_attr_as_string(res->msgs[i],
3746 "displayName", "");
3747 entriesGeneral[count].description.string =
3748 ldb_msg_find_attr_as_string(res->msgs[i],
3749 "description", "");
3750 break;
3751 case 2:
3752 entriesFull[count].idx = count + 1;
3753 entriesFull[count].rid =
3754 objectsid->sub_auths[objectsid->num_auths-1];
3756 /* No idea why we need to or in ACB_NORMAL here, but this is what Win2k3 seems to do... */
3757 entriesFull[count].acct_flags =
3758 samdb_result_acct_flags(res->msgs[i],
3759 NULL) | ACB_NORMAL;
3760 entriesFull[count].account_name.string =
3761 ldb_msg_find_attr_as_string(res->msgs[i],
3762 "sAMAccountName", "");
3763 entriesFull[count].description.string =
3764 ldb_msg_find_attr_as_string(res->msgs[i],
3765 "description", "");
3766 break;
3767 case 3:
3768 entriesFullGroup[count].idx = count + 1;
3769 entriesFullGroup[count].rid =
3770 objectsid->sub_auths[objectsid->num_auths-1];
3771 /* We get a "7" here for groups */
3772 entriesFullGroup[count].acct_flags
3773 = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3774 entriesFullGroup[count].account_name.string =
3775 ldb_msg_find_attr_as_string(res->msgs[i],
3776 "sAMAccountName", "");
3777 entriesFullGroup[count].description.string =
3778 ldb_msg_find_attr_as_string(res->msgs[i],
3779 "description", "");
3780 break;
3781 case 4:
3782 case 5:
3783 entriesAscii[count].idx = count + 1;
3784 entriesAscii[count].account_name.string =
3785 ldb_msg_find_attr_as_string(res->msgs[i],
3786 "sAMAccountName", "");
3787 break;
3790 count += 1;
3793 *r->out.total_size = count;
3795 if (r->in.start_idx >= count) {
3796 *r->out.returned_size = 0;
3797 switch(r->in.level) {
3798 case 1:
3799 r->out.info->info1.count = *r->out.returned_size;
3800 r->out.info->info1.entries = NULL;
3801 break;
3802 case 2:
3803 r->out.info->info2.count = *r->out.returned_size;
3804 r->out.info->info2.entries = NULL;
3805 break;
3806 case 3:
3807 r->out.info->info3.count = *r->out.returned_size;
3808 r->out.info->info3.entries = NULL;
3809 break;
3810 case 4:
3811 r->out.info->info4.count = *r->out.returned_size;
3812 r->out.info->info4.entries = NULL;
3813 break;
3814 case 5:
3815 r->out.info->info5.count = *r->out.returned_size;
3816 r->out.info->info5.entries = NULL;
3817 break;
3819 } else {
3820 *r->out.returned_size = MIN(count - r->in.start_idx,
3821 r->in.max_entries);
3822 switch(r->in.level) {
3823 case 1:
3824 r->out.info->info1.count = *r->out.returned_size;
3825 r->out.info->info1.entries =
3826 &(entriesGeneral[r->in.start_idx]);
3827 break;
3828 case 2:
3829 r->out.info->info2.count = *r->out.returned_size;
3830 r->out.info->info2.entries =
3831 &(entriesFull[r->in.start_idx]);
3832 break;
3833 case 3:
3834 r->out.info->info3.count = *r->out.returned_size;
3835 r->out.info->info3.entries =
3836 &(entriesFullGroup[r->in.start_idx]);
3837 break;
3838 case 4:
3839 r->out.info->info4.count = *r->out.returned_size;
3840 r->out.info->info4.entries =
3841 &(entriesAscii[r->in.start_idx]);
3842 break;
3843 case 5:
3844 r->out.info->info5.count = *r->out.returned_size;
3845 r->out.info->info5.entries =
3846 &(entriesAscii[r->in.start_idx]);
3847 break;
3851 return (*r->out.returned_size < (count - r->in.start_idx)) ?
3852 STATUS_MORE_ENTRIES : NT_STATUS_OK;
3857 samr_GetDisplayEnumerationIndex
3859 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3860 struct samr_GetDisplayEnumerationIndex *r)
3862 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3867 samr_TestPrivateFunctionsDomain
3869 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3870 struct samr_TestPrivateFunctionsDomain *r)
3872 return NT_STATUS_NOT_IMPLEMENTED;
3877 samr_TestPrivateFunctionsUser
3879 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3880 struct samr_TestPrivateFunctionsUser *r)
3882 return NT_STATUS_NOT_IMPLEMENTED;
3887 samr_GetUserPwInfo
3889 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3890 struct samr_GetUserPwInfo *r)
3892 struct dcesrv_handle *h;
3893 struct samr_account_state *a_state;
3895 ZERO_STRUCTP(r->out.info);
3897 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3899 a_state = h->data;
3901 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
3902 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
3903 NULL);
3904 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
3905 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
3907 return NT_STATUS_OK;
3912 samr_RemoveMemberFromForeignDomain
3914 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
3915 TALLOC_CTX *mem_ctx,
3916 struct samr_RemoveMemberFromForeignDomain *r)
3918 struct dcesrv_handle *h;
3919 struct samr_domain_state *d_state;
3920 const char *memberdn;
3921 struct ldb_message **res;
3922 const char *no_attrs[] = { NULL };
3923 int i, count;
3925 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3927 d_state = h->data;
3929 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3930 "distinguishedName", "(objectSid=%s)",
3931 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3932 /* Nothing to do */
3933 if (memberdn == NULL) {
3934 return NT_STATUS_OK;
3937 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
3938 d_state->domain_dn, &res, no_attrs,
3939 d_state->domain_sid,
3940 "(&(member=%s)(objectClass=group)"
3941 "(|(groupType=%d)(groupType=%d)))",
3942 memberdn,
3943 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
3944 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
3946 if (count < 0)
3947 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3949 for (i=0; i<count; i++) {
3950 struct ldb_message *mod;
3951 int ret;
3953 mod = ldb_msg_new(mem_ctx);
3954 if (mod == NULL) {
3955 return NT_STATUS_NO_MEMORY;
3958 mod->dn = res[i]->dn;
3960 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
3961 "member", memberdn) != LDB_SUCCESS)
3962 return NT_STATUS_NO_MEMORY;
3964 ret = ldb_modify(d_state->sam_ctx, mod);
3965 talloc_free(mod);
3966 if (ret != LDB_SUCCESS) {
3967 return dsdb_ldb_err_to_ntstatus(ret);
3971 return NT_STATUS_OK;
3976 samr_QueryDomainInfo2
3978 just an alias for samr_QueryDomainInfo
3980 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3981 struct samr_QueryDomainInfo2 *r)
3983 struct samr_QueryDomainInfo r1;
3984 NTSTATUS status;
3986 ZERO_STRUCT(r1.out);
3987 r1.in.domain_handle = r->in.domain_handle;
3988 r1.in.level = r->in.level;
3989 r1.out.info = r->out.info;
3991 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
3993 return status;
3998 samr_QueryUserInfo2
4000 just an alias for samr_QueryUserInfo
4002 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4003 struct samr_QueryUserInfo2 *r)
4005 struct samr_QueryUserInfo r1;
4006 NTSTATUS status;
4008 r1 = (struct samr_QueryUserInfo) {
4009 .in.user_handle = r->in.user_handle,
4010 .in.level = r->in.level,
4011 .out.info = r->out.info
4014 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4016 return status;
4021 samr_QueryDisplayInfo2
4023 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4024 struct samr_QueryDisplayInfo2 *r)
4026 struct samr_QueryDisplayInfo q;
4027 NTSTATUS result;
4029 q.in.domain_handle = r->in.domain_handle;
4030 q.in.level = r->in.level;
4031 q.in.start_idx = r->in.start_idx;
4032 q.in.max_entries = r->in.max_entries;
4033 q.in.buf_size = r->in.buf_size;
4034 q.out.total_size = r->out.total_size;
4035 q.out.returned_size = r->out.returned_size;
4036 q.out.info = r->out.info;
4038 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4040 return result;
4045 samr_GetDisplayEnumerationIndex2
4047 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4048 struct samr_GetDisplayEnumerationIndex2 *r)
4050 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4055 samr_QueryDisplayInfo3
4057 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4058 struct samr_QueryDisplayInfo3 *r)
4060 struct samr_QueryDisplayInfo q;
4061 NTSTATUS result;
4063 q.in.domain_handle = r->in.domain_handle;
4064 q.in.level = r->in.level;
4065 q.in.start_idx = r->in.start_idx;
4066 q.in.max_entries = r->in.max_entries;
4067 q.in.buf_size = r->in.buf_size;
4068 q.out.total_size = r->out.total_size;
4069 q.out.returned_size = r->out.returned_size;
4070 q.out.info = r->out.info;
4072 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4074 return result;
4079 samr_AddMultipleMembersToAlias
4081 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4082 struct samr_AddMultipleMembersToAlias *r)
4084 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4089 samr_RemoveMultipleMembersFromAlias
4091 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4092 struct samr_RemoveMultipleMembersFromAlias *r)
4094 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4099 samr_GetDomPwInfo
4101 this fetches the default password properties for a domain
4103 note that w2k3 completely ignores the domain name in this call, and
4104 always returns the information for the servers primary domain
4106 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4107 struct samr_GetDomPwInfo *r)
4109 struct ldb_message **msgs;
4110 int ret;
4111 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4112 struct ldb_context *sam_ctx;
4114 ZERO_STRUCTP(r->out.info);
4116 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
4117 dce_call->conn->dce_ctx->lp_ctx,
4118 dce_call->conn->auth_state.session_info, 0);
4119 if (sam_ctx == NULL) {
4120 return NT_STATUS_INVALID_SYSTEM_SERVICE;
4123 /* The domain name in this call is ignored */
4124 ret = gendb_search_dn(sam_ctx,
4125 mem_ctx, NULL, &msgs, attrs);
4126 if (ret <= 0) {
4127 talloc_free(sam_ctx);
4129 return NT_STATUS_NO_SUCH_DOMAIN;
4131 if (ret > 1) {
4132 talloc_free(msgs);
4133 talloc_free(sam_ctx);
4135 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4138 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
4139 "minPwdLength", 0);
4140 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
4141 "pwdProperties", 1);
4143 talloc_free(msgs);
4144 talloc_unlink(mem_ctx, sam_ctx);
4146 return NT_STATUS_OK;
4151 samr_Connect2
4153 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4154 struct samr_Connect2 *r)
4156 struct samr_Connect c;
4158 c.in.system_name = NULL;
4159 c.in.access_mask = r->in.access_mask;
4160 c.out.connect_handle = r->out.connect_handle;
4162 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4167 samr_SetUserInfo2
4169 just an alias for samr_SetUserInfo
4171 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4172 struct samr_SetUserInfo2 *r)
4174 struct samr_SetUserInfo r2;
4176 r2.in.user_handle = r->in.user_handle;
4177 r2.in.level = r->in.level;
4178 r2.in.info = r->in.info;
4180 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4185 samr_SetBootKeyInformation
4187 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4188 struct samr_SetBootKeyInformation *r)
4190 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4195 samr_GetBootKeyInformation
4197 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4198 struct samr_GetBootKeyInformation *r)
4200 /* Windows Server 2008 returns this */
4201 return NT_STATUS_NOT_SUPPORTED;
4206 samr_Connect3
4208 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4209 struct samr_Connect3 *r)
4211 struct samr_Connect c;
4213 c.in.system_name = NULL;
4214 c.in.access_mask = r->in.access_mask;
4215 c.out.connect_handle = r->out.connect_handle;
4217 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4222 samr_Connect4
4224 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4225 struct samr_Connect4 *r)
4227 struct samr_Connect c;
4229 c.in.system_name = NULL;
4230 c.in.access_mask = r->in.access_mask;
4231 c.out.connect_handle = r->out.connect_handle;
4233 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4238 samr_Connect5
4240 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4241 struct samr_Connect5 *r)
4243 struct samr_Connect c;
4244 NTSTATUS status;
4246 c.in.system_name = NULL;
4247 c.in.access_mask = r->in.access_mask;
4248 c.out.connect_handle = r->out.connect_handle;
4250 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4252 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4253 r->out.info_out->info1.unknown2 = 0;
4254 *r->out.level_out = r->in.level_in;
4256 return status;
4261 samr_RidToSid
4263 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4264 struct samr_RidToSid *r)
4266 struct samr_domain_state *d_state;
4267 struct dcesrv_handle *h;
4269 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4271 d_state = h->data;
4273 /* form the users SID */
4274 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4275 if (!*r->out.sid) {
4276 return NT_STATUS_NO_MEMORY;
4279 return NT_STATUS_OK;
4284 samr_SetDsrmPassword
4286 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4287 struct samr_SetDsrmPassword *r)
4289 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4294 samr_ValidatePassword
4296 For now the call checks the password complexity (if active) and the minimum
4297 password length on level 2 and 3. Level 1 is ignored for now.
4299 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
4300 TALLOC_CTX *mem_ctx,
4301 struct samr_ValidatePassword *r)
4303 struct samr_GetDomPwInfo r2;
4304 struct samr_PwInfo pwInfo;
4305 DATA_BLOB password;
4306 enum samr_ValidationStatus res;
4307 NTSTATUS status;
4308 enum dcerpc_transport_t transport =
4309 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
4311 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
4312 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4315 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
4317 r2.in.domain_name = NULL;
4318 r2.out.info = &pwInfo;
4319 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
4320 if (!NT_STATUS_IS_OK(status)) {
4321 return status;
4324 switch (r->in.level) {
4325 case NetValidateAuthentication:
4326 /* we don't support this yet */
4327 return NT_STATUS_NOT_SUPPORTED;
4328 break;
4329 case NetValidatePasswordChange:
4330 password = data_blob_const(r->in.req->req2.password.string,
4331 r->in.req->req2.password.length);
4332 res = samdb_check_password(&password,
4333 pwInfo.password_properties,
4334 pwInfo.min_password_length);
4335 (*r->out.rep)->ctr2.status = res;
4336 break;
4337 case NetValidatePasswordReset:
4338 password = data_blob_const(r->in.req->req3.password.string,
4339 r->in.req->req3.password.length);
4340 res = samdb_check_password(&password,
4341 pwInfo.password_properties,
4342 pwInfo.min_password_length);
4343 (*r->out.rep)->ctr3.status = res;
4344 break;
4345 default:
4346 return NT_STATUS_INVALID_INFO_CLASS;
4347 break;
4350 return NT_STATUS_OK;
4354 /* include the generated boilerplate */
4355 #include "librpc/gen_ndr/ndr_samr_s.c"