s4:rpc_server/samr: passdown unmodified acct_flags to the ldb layer.
[Samba.git] / source4 / rpc_server / samr / dcesrv_samr.c
blobfb7ad527039ef7e7ad83f49df04ee0fd70a827a6
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_LHOURS(msg, field, attr) \
61 info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
62 #define QUERY_AFLAGS(msg, field, attr) \
63 info->field = samdb_result_acct_flags(msg, attr);
64 #define QUERY_PARAMETERS(msg, field, attr) \
65 info->field = samdb_result_parameters(mem_ctx, msg, attr);
68 /* these are used to make the Set[User|Group]Info code easier to follow */
70 #define SET_STRING(msg, field, attr) do { \
71 struct ldb_message_element *set_el; \
72 if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
73 if (r->in.info->field.string[0] == '\0') { \
74 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
75 return NT_STATUS_NO_MEMORY; \
76 } \
77 } \
78 if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
79 return NT_STATUS_NO_MEMORY; \
80 } \
81 set_el = ldb_msg_find_element(msg, attr); \
82 set_el->flags = LDB_FLAG_MOD_REPLACE; \
83 } while (0)
85 #define SET_UINT(msg, field, attr) do { \
86 struct ldb_message_element *set_el; \
87 if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
88 return NT_STATUS_NO_MEMORY; \
89 } \
90 set_el = ldb_msg_find_element(msg, attr); \
91 set_el->flags = LDB_FLAG_MOD_REPLACE; \
92 } while (0)
94 #define SET_INT64(msg, field, attr) do { \
95 struct ldb_message_element *set_el; \
96 if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
97 return NT_STATUS_NO_MEMORY; \
98 } \
99 set_el = ldb_msg_find_element(msg, attr); \
100 set_el->flags = LDB_FLAG_MOD_REPLACE; \
101 } while (0)
103 #define SET_UINT64(msg, field, attr) do { \
104 struct ldb_message_element *set_el; \
105 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
106 return NT_STATUS_NO_MEMORY; \
108 set_el = ldb_msg_find_element(msg, attr); \
109 set_el->flags = LDB_FLAG_MOD_REPLACE; \
110 } while (0)
112 /* Set account flags, discarding flags that cannot be set with SAMR */
113 #define SET_AFLAGS(msg, field, attr) do { \
114 struct ldb_message_element *set_el; \
115 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
116 return NT_STATUS_NO_MEMORY; \
118 set_el = ldb_msg_find_element(msg, attr); \
119 set_el->flags = LDB_FLAG_MOD_REPLACE; \
120 } while (0)
122 #define SET_LHOURS(msg, field, attr) do { \
123 struct ldb_message_element *set_el; \
124 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
125 return NT_STATUS_NO_MEMORY; \
127 set_el = ldb_msg_find_element(msg, attr); \
128 set_el->flags = LDB_FLAG_MOD_REPLACE; \
129 } while (0)
131 #define SET_PARAMETERS(msg, field, attr) do { \
132 struct ldb_message_element *set_el; \
133 if (r->in.info->field.length != 0) { \
134 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
135 return NT_STATUS_NO_MEMORY; \
137 set_el = ldb_msg_find_element(msg, attr); \
138 set_el->flags = LDB_FLAG_MOD_REPLACE; \
140 } while (0)
145 samr_Connect
147 create a connection to the SAM database
149 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
150 struct samr_Connect *r)
152 struct samr_connect_state *c_state;
153 struct dcesrv_handle *handle;
155 ZERO_STRUCTP(r->out.connect_handle);
157 c_state = talloc(mem_ctx, struct samr_connect_state);
158 if (!c_state) {
159 return NT_STATUS_NO_MEMORY;
162 /* make sure the sam database is accessible */
163 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);
164 if (c_state->sam_ctx == NULL) {
165 talloc_free(c_state);
166 return NT_STATUS_INVALID_SYSTEM_SERVICE;
170 handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
171 if (!handle) {
172 talloc_free(c_state);
173 return NT_STATUS_NO_MEMORY;
176 handle->data = talloc_steal(handle, c_state);
178 c_state->access_mask = r->in.access_mask;
179 *r->out.connect_handle = handle->wire_handle;
181 return NT_STATUS_OK;
186 samr_Close
188 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
189 struct samr_Close *r)
191 struct dcesrv_handle *h;
193 *r->out.handle = *r->in.handle;
195 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
197 talloc_free(h);
199 ZERO_STRUCTP(r->out.handle);
201 return NT_STATUS_OK;
206 samr_SetSecurity
208 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
209 struct samr_SetSecurity *r)
211 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
216 samr_QuerySecurity
218 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
219 struct samr_QuerySecurity *r)
221 struct dcesrv_handle *h;
222 struct sec_desc_buf *sd;
224 *r->out.sdbuf = NULL;
226 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
228 sd = talloc(mem_ctx, struct sec_desc_buf);
229 if (sd == NULL) {
230 return NT_STATUS_NO_MEMORY;
233 sd->sd = samdb_default_security_descriptor(mem_ctx);
235 *r->out.sdbuf = sd;
237 return NT_STATUS_OK;
242 samr_Shutdown
244 we refuse this operation completely. If a admin wants to shutdown samr
245 in Samba then they should use the samba admin tools to disable the samr pipe
247 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
248 struct samr_Shutdown *r)
250 return NT_STATUS_ACCESS_DENIED;
255 samr_LookupDomain
257 this maps from a domain name to a SID
259 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
260 struct samr_LookupDomain *r)
262 struct samr_connect_state *c_state;
263 struct dcesrv_handle *h;
264 struct dom_sid *sid;
265 const char * const dom_attrs[] = { "objectSid", NULL};
266 struct ldb_message **dom_msgs;
267 int ret;
269 *r->out.sid = NULL;
271 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
273 c_state = h->data;
275 if (r->in.domain_name->string == NULL) {
276 return NT_STATUS_INVALID_PARAMETER;
279 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
280 ret = gendb_search(c_state->sam_ctx,
281 mem_ctx, NULL, &dom_msgs, dom_attrs,
282 "(objectClass=builtinDomain)");
283 } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
284 ret = gendb_search_dn(c_state->sam_ctx,
285 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
286 &dom_msgs, dom_attrs);
287 } else {
288 return NT_STATUS_NO_SUCH_DOMAIN;
290 if (ret != 1) {
291 return NT_STATUS_NO_SUCH_DOMAIN;
294 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
295 "objectSid");
297 if (sid == NULL) {
298 return NT_STATUS_NO_SUCH_DOMAIN;
301 *r->out.sid = sid;
303 return NT_STATUS_OK;
308 samr_EnumDomains
310 list the domains in the SAM
312 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
313 struct samr_EnumDomains *r)
315 struct samr_connect_state *c_state;
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 c_state = h->data;
328 *r->out.resume_handle = 2;
330 start_i = *r->in.resume_handle;
332 if (start_i >= 2) {
333 /* search past end of list is not an error for this call */
334 return NT_STATUS_OK;
337 array = talloc(mem_ctx, struct samr_SamArray);
338 if (array == NULL) {
339 return NT_STATUS_NO_MEMORY;
342 array->count = 0;
343 array->entries = NULL;
345 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
346 if (array->entries == NULL) {
347 return NT_STATUS_NO_MEMORY;
350 for (i=0;i<2-start_i;i++) {
351 array->entries[i].idx = start_i + i;
352 if (i == 0) {
353 array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
354 } else {
355 array->entries[i].name.string = "BUILTIN";
359 *r->out.sam = array;
360 *r->out.num_entries = i;
361 array->count = *r->out.num_entries;
363 return NT_STATUS_OK;
368 samr_OpenDomain
370 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
371 struct samr_OpenDomain *r)
373 struct dcesrv_handle *h_conn, *h_domain;
374 struct samr_connect_state *c_state;
375 struct samr_domain_state *d_state;
376 const char * const dom_attrs[] = { "cn", NULL};
377 struct ldb_message **dom_msgs;
378 int ret;
380 ZERO_STRUCTP(r->out.domain_handle);
382 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
384 c_state = h_conn->data;
386 if (r->in.sid == NULL) {
387 return NT_STATUS_INVALID_PARAMETER;
390 d_state = talloc(mem_ctx, struct samr_domain_state);
391 if (!d_state) {
392 return NT_STATUS_NO_MEMORY;
395 d_state->domain_sid = talloc_steal(d_state, r->in.sid);
397 if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
398 d_state->builtin = true;
399 d_state->domain_name = "BUILTIN";
400 } else {
401 d_state->builtin = false;
402 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
405 ret = gendb_search(c_state->sam_ctx,
406 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
407 "(objectSid=%s)",
408 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
410 if (ret == 0) {
411 talloc_free(d_state);
412 return NT_STATUS_NO_SUCH_DOMAIN;
413 } else if (ret > 1) {
414 talloc_free(d_state);
415 return NT_STATUS_INTERNAL_DB_CORRUPTION;
416 } else if (ret == -1) {
417 talloc_free(d_state);
418 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
419 return NT_STATUS_INTERNAL_DB_CORRUPTION;
422 d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
423 d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
424 d_state->connect_state = talloc_reference(d_state, c_state);
425 d_state->sam_ctx = c_state->sam_ctx;
426 d_state->access_mask = r->in.access_mask;
428 d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
430 h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
431 if (!h_domain) {
432 talloc_free(d_state);
433 return NT_STATUS_NO_MEMORY;
436 h_domain->data = talloc_steal(h_domain, d_state);
438 *r->out.domain_handle = h_domain->wire_handle;
440 return NT_STATUS_OK;
444 return DomInfo1
446 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
447 TALLOC_CTX *mem_ctx,
448 struct ldb_message **dom_msgs,
449 struct samr_DomInfo1 *info)
451 info->min_password_length =
452 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
453 info->password_history_length =
454 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
455 info->password_properties =
456 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
457 info->max_password_age =
458 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
459 info->min_password_age =
460 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
462 return NT_STATUS_OK;
466 return DomInfo2
468 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
469 TALLOC_CTX *mem_ctx,
470 struct ldb_message **dom_msgs,
471 struct samr_DomGeneralInformation *info)
473 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
474 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
475 "domainReplica",
476 "");
478 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
479 0x8000000000000000LL);
481 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
482 "oEMInformation",
483 "");
484 info->domain_name.string = state->domain_name;
486 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
488 switch (state->role) {
489 case ROLE_ACTIVE_DIRECTORY_DC:
490 /* This pulls the NetBIOS name from the
491 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
492 string */
493 if (samdb_is_pdc(state->sam_ctx)) {
494 info->role = SAMR_ROLE_DOMAIN_PDC;
495 } else {
496 info->role = SAMR_ROLE_DOMAIN_BDC;
498 break;
499 case ROLE_DOMAIN_PDC:
500 case ROLE_DOMAIN_BDC:
501 return NT_STATUS_INTERNAL_ERROR;
502 case ROLE_DOMAIN_MEMBER:
503 info->role = SAMR_ROLE_DOMAIN_MEMBER;
504 break;
505 case ROLE_STANDALONE:
506 info->role = SAMR_ROLE_STANDALONE;
507 break;
510 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx,
511 state->domain_dn,
512 "(objectClass=user)");
513 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx,
514 state->domain_dn,
515 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
516 GTYPE_SECURITY_UNIVERSAL_GROUP,
517 GTYPE_SECURITY_GLOBAL_GROUP);
518 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx,
519 state->domain_dn,
520 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
521 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
522 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
524 return NT_STATUS_OK;
528 return DomInfo3
530 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
531 TALLOC_CTX *mem_ctx,
532 struct ldb_message **dom_msgs,
533 struct samr_DomInfo3 *info)
535 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
536 0x8000000000000000LL);
538 return NT_STATUS_OK;
542 return DomInfo4
544 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
545 TALLOC_CTX *mem_ctx,
546 struct ldb_message **dom_msgs,
547 struct samr_DomOEMInformation *info)
549 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
550 "oEMInformation",
551 "");
553 return NT_STATUS_OK;
557 return DomInfo5
559 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
560 TALLOC_CTX *mem_ctx,
561 struct ldb_message **dom_msgs,
562 struct samr_DomInfo5 *info)
564 info->domain_name.string = state->domain_name;
566 return NT_STATUS_OK;
570 return DomInfo6
572 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
573 TALLOC_CTX *mem_ctx,
574 struct ldb_message **dom_msgs,
575 struct samr_DomInfo6 *info)
577 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
578 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
579 "domainReplica",
580 "");
582 return NT_STATUS_OK;
586 return DomInfo7
588 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
589 TALLOC_CTX *mem_ctx,
590 struct ldb_message **dom_msgs,
591 struct samr_DomInfo7 *info)
594 switch (state->role) {
595 case ROLE_ACTIVE_DIRECTORY_DC:
596 /* This pulls the NetBIOS name from the
597 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
598 string */
599 if (samdb_is_pdc(state->sam_ctx)) {
600 info->role = SAMR_ROLE_DOMAIN_PDC;
601 } else {
602 info->role = SAMR_ROLE_DOMAIN_BDC;
604 break;
605 case ROLE_DOMAIN_PDC:
606 info->role = SAMR_ROLE_DOMAIN_PDC;
607 break;
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 */
1241 r2.in.domain_handle = r->in.domain_handle;
1242 r2.in.account_name = r->in.account_name;
1243 r2.in.acct_flags = ACB_NORMAL;
1244 r2.in.access_mask = r->in.access_mask;
1245 r2.out.user_handle = r->out.user_handle;
1246 r2.out.access_granted = &access_granted;
1247 r2.out.rid = r->out.rid;
1249 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1253 samr_EnumDomainUsers
1255 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1256 struct samr_EnumDomainUsers *r)
1258 struct dcesrv_handle *h;
1259 struct samr_domain_state *d_state;
1260 struct ldb_message **res;
1261 int i, ldb_cnt;
1262 uint32_t first, count;
1263 struct samr_SamEntry *entries;
1264 const char * const attrs[] = { "objectSid", "sAMAccountName",
1265 "userAccountControl", NULL };
1266 struct samr_SamArray *sam;
1268 *r->out.resume_handle = 0;
1269 *r->out.sam = NULL;
1270 *r->out.num_entries = 0;
1272 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1274 d_state = h->data;
1276 /* search for all domain users in this domain. This could possibly be
1277 cached and resumed on resume_key */
1278 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1279 d_state->domain_dn,
1280 &res, attrs,
1281 d_state->domain_sid,
1282 "(objectClass=user)");
1283 if (ldb_cnt < 0) {
1284 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1287 /* convert to SamEntry format */
1288 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1289 if (!entries) {
1290 return NT_STATUS_NO_MEMORY;
1293 count = 0;
1295 for (i=0;i<ldb_cnt;i++) {
1296 /* Check if a mask has been requested */
1297 if (r->in.acct_flags
1298 && ((samdb_result_acct_flags(res[i], NULL) & r->in.acct_flags) == 0)) {
1299 continue;
1301 entries[count].idx = samdb_result_rid_from_sid(mem_ctx, res[i],
1302 "objectSid", 0);
1303 entries[count].name.string = ldb_msg_find_attr_as_string(res[i],
1304 "sAMAccountName", "");
1305 count += 1;
1308 /* sort the results by rid */
1309 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1311 /* find the first entry to return */
1312 for (first=0;
1313 first<count && entries[first].idx <= *r->in.resume_handle;
1314 first++) ;
1316 /* return the rest, limit by max_size. Note that we
1317 use the w2k3 element size value of 54 */
1318 *r->out.num_entries = count - first;
1319 *r->out.num_entries = MIN(*r->out.num_entries,
1320 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1322 sam = talloc(mem_ctx, struct samr_SamArray);
1323 if (!sam) {
1324 return NT_STATUS_NO_MEMORY;
1327 sam->entries = entries+first;
1328 sam->count = *r->out.num_entries;
1330 *r->out.sam = sam;
1332 if (first == count) {
1333 return NT_STATUS_OK;
1336 if (*r->out.num_entries < count - first) {
1337 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1338 return STATUS_MORE_ENTRIES;
1341 return NT_STATUS_OK;
1346 samr_CreateDomAlias
1348 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1349 struct samr_CreateDomAlias *r)
1351 struct samr_domain_state *d_state;
1352 struct samr_account_state *a_state;
1353 struct dcesrv_handle *h;
1354 const char *alias_name;
1355 struct dom_sid *sid;
1356 struct dcesrv_handle *a_handle;
1357 struct ldb_dn *dn;
1358 NTSTATUS status;
1360 ZERO_STRUCTP(r->out.alias_handle);
1361 *r->out.rid = 0;
1363 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1365 d_state = h->data;
1367 if (d_state->builtin) {
1368 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1369 return NT_STATUS_ACCESS_DENIED;
1372 alias_name = r->in.alias_name->string;
1374 if (alias_name == NULL) {
1375 return NT_STATUS_INVALID_PARAMETER;
1378 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1379 if (!NT_STATUS_IS_OK(status)) {
1380 return status;
1383 a_state = talloc(mem_ctx, struct samr_account_state);
1384 if (!a_state) {
1385 return NT_STATUS_NO_MEMORY;
1388 a_state->sam_ctx = d_state->sam_ctx;
1389 a_state->access_mask = r->in.access_mask;
1390 a_state->domain_state = talloc_reference(a_state, d_state);
1391 a_state->account_dn = talloc_steal(a_state, dn);
1393 a_state->account_name = talloc_steal(a_state, alias_name);
1395 /* create the policy handle */
1396 a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1397 if (a_handle == NULL)
1398 return NT_STATUS_NO_MEMORY;
1400 a_handle->data = talloc_steal(a_handle, a_state);
1402 *r->out.alias_handle = a_handle->wire_handle;
1404 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1406 return NT_STATUS_OK;
1411 samr_EnumDomainAliases
1413 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1414 struct samr_EnumDomainAliases *r)
1416 struct dcesrv_handle *h;
1417 struct samr_domain_state *d_state;
1418 struct ldb_message **res;
1419 int i, ldb_cnt;
1420 uint32_t first, count;
1421 struct samr_SamEntry *entries;
1422 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1423 struct samr_SamArray *sam;
1425 *r->out.resume_handle = 0;
1426 *r->out.sam = NULL;
1427 *r->out.num_entries = 0;
1429 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1431 d_state = h->data;
1433 /* search for all domain aliases in this domain. This could possibly be
1434 cached and resumed based on resume_key */
1435 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1436 &res, attrs,
1437 d_state->domain_sid,
1438 "(&(|(grouptype=%d)(grouptype=%d)))"
1439 "(objectclass=group))",
1440 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1441 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1442 if (ldb_cnt < 0) {
1443 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1446 /* convert to SamEntry format */
1447 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1448 if (!entries) {
1449 return NT_STATUS_NO_MEMORY;
1452 count = 0;
1454 for (i=0;i<ldb_cnt;i++) {
1455 struct dom_sid *alias_sid;
1457 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1458 "objectSid");
1460 if (alias_sid == NULL) {
1461 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1464 entries[count].idx =
1465 alias_sid->sub_auths[alias_sid->num_auths-1];
1466 entries[count].name.string =
1467 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1468 count += 1;
1471 /* sort the results by rid */
1472 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1474 /* find the first entry to return */
1475 for (first=0;
1476 first<count && entries[first].idx <= *r->in.resume_handle;
1477 first++) ;
1479 /* return the rest, limit by max_size. Note that we
1480 use the w2k3 element size value of 54 */
1481 *r->out.num_entries = count - first;
1482 *r->out.num_entries = MIN(*r->out.num_entries,
1483 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1485 sam = talloc(mem_ctx, struct samr_SamArray);
1486 if (!sam) {
1487 return NT_STATUS_NO_MEMORY;
1490 sam->entries = entries+first;
1491 sam->count = *r->out.num_entries;
1493 *r->out.sam = sam;
1495 if (first == count) {
1496 return NT_STATUS_OK;
1499 if (*r->out.num_entries < count - first) {
1500 *r->out.resume_handle =
1501 entries[first+*r->out.num_entries-1].idx;
1502 return STATUS_MORE_ENTRIES;
1505 return NT_STATUS_OK;
1510 samr_GetAliasMembership
1512 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1513 struct samr_GetAliasMembership *r)
1515 struct dcesrv_handle *h;
1516 struct samr_domain_state *d_state;
1517 const char *filter;
1518 const char * const attrs[] = { "objectSid", NULL };
1519 struct ldb_message **res;
1520 uint32_t i;
1521 int count = 0;
1523 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1525 d_state = h->data;
1527 filter = talloc_asprintf(mem_ctx,
1528 "(&(|(grouptype=%d)(grouptype=%d))"
1529 "(objectclass=group)(|",
1530 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1531 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1532 if (filter == NULL) {
1533 return NT_STATUS_NO_MEMORY;
1536 for (i=0; i<r->in.sids->num_sids; i++) {
1537 const char *memberdn;
1539 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1540 "distinguishedName",
1541 "(objectSid=%s)",
1542 ldap_encode_ndr_dom_sid(mem_ctx,
1543 r->in.sids->sids[i].sid));
1544 if (memberdn == NULL) {
1545 continue;
1548 filter = talloc_asprintf(mem_ctx, "%s(member=%s)", filter,
1549 memberdn);
1550 if (filter == NULL) {
1551 return NT_STATUS_NO_MEMORY;
1555 /* Find out if we had at least one valid member SID passed - otherwise
1556 * just skip the search. */
1557 if (strstr(filter, "member") != NULL) {
1558 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1559 &res, attrs, d_state->domain_sid,
1560 "%s))", filter);
1561 if (count < 0) {
1562 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1566 r->out.rids->count = 0;
1567 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1568 if (r->out.rids->ids == NULL)
1569 return NT_STATUS_NO_MEMORY;
1571 for (i=0; i<count; i++) {
1572 struct dom_sid *alias_sid;
1574 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1575 if (alias_sid == NULL) {
1576 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1579 r->out.rids->ids[r->out.rids->count] =
1580 alias_sid->sub_auths[alias_sid->num_auths-1];
1581 r->out.rids->count += 1;
1584 return NT_STATUS_OK;
1589 samr_LookupNames
1591 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1592 struct samr_LookupNames *r)
1594 struct dcesrv_handle *h;
1595 struct samr_domain_state *d_state;
1596 uint32_t i, num_mapped;
1597 NTSTATUS status = NT_STATUS_OK;
1598 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1599 int count;
1601 ZERO_STRUCTP(r->out.rids);
1602 ZERO_STRUCTP(r->out.types);
1604 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1606 d_state = h->data;
1608 if (r->in.num_names == 0) {
1609 return NT_STATUS_OK;
1612 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1613 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1614 if (!r->out.rids->ids || !r->out.types->ids) {
1615 return NT_STATUS_NO_MEMORY;
1617 r->out.rids->count = r->in.num_names;
1618 r->out.types->count = r->in.num_names;
1620 num_mapped = 0;
1622 for (i=0;i<r->in.num_names;i++) {
1623 struct ldb_message **res;
1624 struct dom_sid *sid;
1625 uint32_t atype, rtype;
1627 r->out.rids->ids[i] = 0;
1628 r->out.types->ids[i] = SID_NAME_UNKNOWN;
1630 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1631 "sAMAccountName=%s",
1632 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
1633 if (count != 1) {
1634 status = STATUS_SOME_UNMAPPED;
1635 continue;
1638 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
1639 if (sid == NULL) {
1640 status = STATUS_SOME_UNMAPPED;
1641 continue;
1644 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
1645 if (atype == 0) {
1646 status = STATUS_SOME_UNMAPPED;
1647 continue;
1650 rtype = ds_atype_map(atype);
1652 if (rtype == SID_NAME_UNKNOWN) {
1653 status = STATUS_SOME_UNMAPPED;
1654 continue;
1657 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
1658 r->out.types->ids[i] = rtype;
1659 num_mapped++;
1662 if (num_mapped == 0) {
1663 return NT_STATUS_NONE_MAPPED;
1665 return status;
1670 samr_LookupRids
1672 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1673 struct samr_LookupRids *r)
1675 NTSTATUS status;
1676 struct dcesrv_handle *h;
1677 struct samr_domain_state *d_state;
1678 const char **names;
1679 struct lsa_String *lsa_names;
1680 enum lsa_SidType *ids;
1682 ZERO_STRUCTP(r->out.names);
1683 ZERO_STRUCTP(r->out.types);
1685 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1687 d_state = h->data;
1689 if (r->in.num_rids == 0)
1690 return NT_STATUS_OK;
1692 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
1693 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
1694 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
1696 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
1697 return NT_STATUS_NO_MEMORY;
1699 r->out.names->names = lsa_names;
1700 r->out.names->count = r->in.num_rids;
1702 r->out.types->ids = (uint32_t *) ids;
1703 r->out.types->count = r->in.num_rids;
1705 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
1706 r->in.num_rids, r->in.rids, names, ids);
1707 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
1708 uint32_t i;
1709 for (i = 0; i < r->in.num_rids; i++) {
1710 lsa_names[i].string = names[i];
1713 return status;
1718 samr_OpenGroup
1720 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1721 struct samr_OpenGroup *r)
1723 struct samr_domain_state *d_state;
1724 struct samr_account_state *a_state;
1725 struct dcesrv_handle *h;
1726 const char *groupname;
1727 struct dom_sid *sid;
1728 struct ldb_message **msgs;
1729 struct dcesrv_handle *g_handle;
1730 const char * const attrs[2] = { "sAMAccountName", NULL };
1731 int ret;
1733 ZERO_STRUCTP(r->out.group_handle);
1735 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1737 d_state = h->data;
1739 /* form the group SID */
1740 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1741 if (!sid) {
1742 return NT_STATUS_NO_MEMORY;
1745 /* search for the group record */
1746 ret = gendb_search(d_state->sam_ctx,
1747 mem_ctx, d_state->domain_dn, &msgs, attrs,
1748 "(&(objectSid=%s)(objectClass=group)"
1749 "(|(groupType=%d)(groupType=%d)))",
1750 ldap_encode_ndr_dom_sid(mem_ctx, sid),
1751 GTYPE_SECURITY_UNIVERSAL_GROUP,
1752 GTYPE_SECURITY_GLOBAL_GROUP);
1753 if (ret == 0) {
1754 return NT_STATUS_NO_SUCH_GROUP;
1756 if (ret != 1) {
1757 DEBUG(0,("Found %d records matching sid %s\n",
1758 ret, dom_sid_string(mem_ctx, sid)));
1759 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1762 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
1763 if (groupname == NULL) {
1764 DEBUG(0,("sAMAccountName field missing for sid %s\n",
1765 dom_sid_string(mem_ctx, sid)));
1766 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1769 a_state = talloc(mem_ctx, struct samr_account_state);
1770 if (!a_state) {
1771 return NT_STATUS_NO_MEMORY;
1773 a_state->sam_ctx = d_state->sam_ctx;
1774 a_state->access_mask = r->in.access_mask;
1775 a_state->domain_state = talloc_reference(a_state, d_state);
1776 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
1777 a_state->account_sid = talloc_steal(a_state, sid);
1778 a_state->account_name = talloc_strdup(a_state, groupname);
1779 if (!a_state->account_name) {
1780 return NT_STATUS_NO_MEMORY;
1783 /* create the policy handle */
1784 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1785 if (!g_handle) {
1786 return NT_STATUS_NO_MEMORY;
1789 g_handle->data = talloc_steal(g_handle, a_state);
1791 *r->out.group_handle = g_handle->wire_handle;
1793 return NT_STATUS_OK;
1797 samr_QueryGroupInfo
1799 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1800 struct samr_QueryGroupInfo *r)
1802 struct dcesrv_handle *h;
1803 struct samr_account_state *a_state;
1804 struct ldb_message *msg, **res;
1805 const char * const attrs[4] = { "sAMAccountName", "description",
1806 "numMembers", NULL };
1807 int ret;
1808 union samr_GroupInfo *info;
1810 *r->out.info = NULL;
1812 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1814 a_state = h->data;
1816 /* pull all the group attributes */
1817 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
1818 a_state->account_dn, &res, attrs);
1819 if (ret == 0) {
1820 return NT_STATUS_NO_SUCH_GROUP;
1822 if (ret != 1) {
1823 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1825 msg = res[0];
1827 /* allocate the info structure */
1828 info = talloc_zero(mem_ctx, union samr_GroupInfo);
1829 if (info == NULL) {
1830 return NT_STATUS_NO_MEMORY;
1833 /* Fill in the level */
1834 switch (r->in.level) {
1835 case GROUPINFOALL:
1836 QUERY_STRING(msg, all.name, "sAMAccountName");
1837 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1838 QUERY_UINT (msg, all.num_members, "numMembers")
1839 QUERY_STRING(msg, all.description, "description");
1840 break;
1841 case GROUPINFONAME:
1842 QUERY_STRING(msg, name, "sAMAccountName");
1843 break;
1844 case GROUPINFOATTRIBUTES:
1845 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1846 break;
1847 case GROUPINFODESCRIPTION:
1848 QUERY_STRING(msg, description, "description");
1849 break;
1850 case GROUPINFOALL2:
1851 QUERY_STRING(msg, all2.name, "sAMAccountName");
1852 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1853 QUERY_UINT (msg, all2.num_members, "numMembers")
1854 QUERY_STRING(msg, all2.description, "description");
1855 break;
1856 default:
1857 talloc_free(info);
1858 return NT_STATUS_INVALID_INFO_CLASS;
1861 *r->out.info = info;
1863 return NT_STATUS_OK;
1868 samr_SetGroupInfo
1870 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1871 struct samr_SetGroupInfo *r)
1873 struct dcesrv_handle *h;
1874 struct samr_account_state *g_state;
1875 struct ldb_message *msg;
1876 struct ldb_context *sam_ctx;
1877 int ret;
1879 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1881 g_state = h->data;
1882 sam_ctx = g_state->sam_ctx;
1884 msg = ldb_msg_new(mem_ctx);
1885 if (msg == NULL) {
1886 return NT_STATUS_NO_MEMORY;
1889 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
1890 if (!msg->dn) {
1891 return NT_STATUS_NO_MEMORY;
1894 switch (r->in.level) {
1895 case GROUPINFODESCRIPTION:
1896 SET_STRING(msg, description, "description");
1897 break;
1898 case GROUPINFONAME:
1899 /* On W2k3 this does not change the name, it changes the
1900 * sAMAccountName attribute */
1901 SET_STRING(msg, name, "sAMAccountName");
1902 break;
1903 case GROUPINFOATTRIBUTES:
1904 /* This does not do anything obviously visible in W2k3 LDAP */
1905 return NT_STATUS_OK;
1906 default:
1907 return NT_STATUS_INVALID_INFO_CLASS;
1910 /* modify the samdb record */
1911 ret = ldb_modify(g_state->sam_ctx, msg);
1912 if (ret != LDB_SUCCESS) {
1913 return dsdb_ldb_err_to_ntstatus(ret);
1916 return NT_STATUS_OK;
1921 samr_AddGroupMember
1923 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1924 struct samr_AddGroupMember *r)
1926 struct dcesrv_handle *h;
1927 struct samr_account_state *a_state;
1928 struct samr_domain_state *d_state;
1929 struct ldb_message *mod;
1930 struct dom_sid *membersid;
1931 const char *memberdn;
1932 struct ldb_result *res;
1933 const char * const attrs[] = { NULL };
1934 int ret;
1936 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1938 a_state = h->data;
1939 d_state = a_state->domain_state;
1941 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1942 if (membersid == NULL) {
1943 return NT_STATUS_NO_MEMORY;
1946 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
1947 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
1948 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
1949 "(objectSid=%s)",
1950 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
1952 if (ret != LDB_SUCCESS) {
1953 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1956 if (res->count == 0) {
1957 return NT_STATUS_NO_SUCH_USER;
1960 if (res->count > 1) {
1961 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1964 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
1966 if (memberdn == NULL)
1967 return NT_STATUS_NO_MEMORY;
1969 mod = ldb_msg_new(mem_ctx);
1970 if (mod == NULL) {
1971 return NT_STATUS_NO_MEMORY;
1974 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
1976 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
1977 memberdn);
1978 if (ret != LDB_SUCCESS) {
1979 return dsdb_ldb_err_to_ntstatus(ret);
1982 ret = ldb_modify(a_state->sam_ctx, mod);
1983 switch (ret) {
1984 case LDB_SUCCESS:
1985 return NT_STATUS_OK;
1986 case LDB_ERR_ENTRY_ALREADY_EXISTS:
1987 return NT_STATUS_MEMBER_IN_GROUP;
1988 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
1989 return NT_STATUS_ACCESS_DENIED;
1990 default:
1991 return dsdb_ldb_err_to_ntstatus(ret);
1997 samr_DeleteDomainGroup
1999 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2000 struct samr_DeleteDomainGroup *r)
2002 struct dcesrv_handle *h;
2003 struct samr_account_state *a_state;
2004 int ret;
2006 *r->out.group_handle = *r->in.group_handle;
2008 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2010 a_state = h->data;
2012 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2013 if (ret != LDB_SUCCESS) {
2014 return dsdb_ldb_err_to_ntstatus(ret);
2017 talloc_free(h);
2018 ZERO_STRUCTP(r->out.group_handle);
2020 return NT_STATUS_OK;
2025 samr_DeleteGroupMember
2027 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2028 struct samr_DeleteGroupMember *r)
2030 struct dcesrv_handle *h;
2031 struct samr_account_state *a_state;
2032 struct samr_domain_state *d_state;
2033 struct ldb_message *mod;
2034 struct dom_sid *membersid;
2035 const char *memberdn;
2036 struct ldb_result *res;
2037 const char * const attrs[] = { NULL };
2038 int ret;
2040 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2042 a_state = h->data;
2043 d_state = a_state->domain_state;
2045 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2046 if (membersid == NULL) {
2047 return NT_STATUS_NO_MEMORY;
2050 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2051 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2052 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2053 "(objectSid=%s)",
2054 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2056 if (ret != LDB_SUCCESS) {
2057 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2060 if (res->count == 0) {
2061 return NT_STATUS_NO_SUCH_USER;
2064 if (res->count > 1) {
2065 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2068 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2070 if (memberdn == NULL)
2071 return NT_STATUS_NO_MEMORY;
2073 mod = ldb_msg_new(mem_ctx);
2074 if (mod == NULL) {
2075 return NT_STATUS_NO_MEMORY;
2078 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2080 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2081 memberdn);
2082 if (ret != LDB_SUCCESS) {
2083 return NT_STATUS_NO_MEMORY;
2086 ret = ldb_modify(a_state->sam_ctx, mod);
2087 switch (ret) {
2088 case LDB_SUCCESS:
2089 return NT_STATUS_OK;
2090 case LDB_ERR_UNWILLING_TO_PERFORM:
2091 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2092 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2093 return NT_STATUS_ACCESS_DENIED;
2094 default:
2095 return dsdb_ldb_err_to_ntstatus(ret);
2101 samr_QueryGroupMember
2103 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2104 struct samr_QueryGroupMember *r)
2106 struct dcesrv_handle *h;
2107 struct samr_account_state *a_state;
2108 struct samr_domain_state *d_state;
2109 struct samr_RidAttrArray *array;
2110 unsigned int i, num_members;
2111 struct dom_sid *members;
2112 NTSTATUS status;
2114 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2116 a_state = h->data;
2117 d_state = a_state->domain_state;
2119 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2120 a_state->account_dn, &members,
2121 &num_members);
2122 if (!NT_STATUS_IS_OK(status)) {
2123 return status;
2126 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2127 if (array == NULL) {
2128 return NT_STATUS_NO_MEMORY;
2131 if (num_members == 0) {
2132 *r->out.rids = array;
2134 return NT_STATUS_OK;
2137 array->rids = talloc_array(array, uint32_t, num_members);
2138 if (array->rids == NULL) {
2139 return NT_STATUS_NO_MEMORY;
2142 array->attributes = talloc_array(array, uint32_t, num_members);
2143 if (array->attributes == NULL) {
2144 return NT_STATUS_NO_MEMORY;
2147 array->count = 0;
2148 for (i=0; i<num_members; i++) {
2149 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2150 continue;
2153 status = dom_sid_split_rid(NULL, &members[i], NULL,
2154 &array->rids[array->count]);
2155 if (!NT_STATUS_IS_OK(status)) {
2156 return status;
2159 array->attributes[array->count] = SE_GROUP_MANDATORY |
2160 SE_GROUP_ENABLED_BY_DEFAULT |
2161 SE_GROUP_ENABLED;
2162 array->count++;
2165 *r->out.rids = array;
2167 return NT_STATUS_OK;
2172 samr_SetMemberAttributesOfGroup
2174 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2175 struct samr_SetMemberAttributesOfGroup *r)
2177 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2182 samr_OpenAlias
2184 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2185 struct samr_OpenAlias *r)
2187 struct samr_domain_state *d_state;
2188 struct samr_account_state *a_state;
2189 struct dcesrv_handle *h;
2190 const char *alias_name;
2191 struct dom_sid *sid;
2192 struct ldb_message **msgs;
2193 struct dcesrv_handle *g_handle;
2194 const char * const attrs[2] = { "sAMAccountName", NULL };
2195 int ret;
2197 ZERO_STRUCTP(r->out.alias_handle);
2199 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2201 d_state = h->data;
2203 /* form the alias SID */
2204 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2205 if (sid == NULL)
2206 return NT_STATUS_NO_MEMORY;
2208 /* search for the group record */
2209 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2210 "(&(objectSid=%s)(objectclass=group)"
2211 "(|(grouptype=%d)(grouptype=%d)))",
2212 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2213 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2214 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2215 if (ret == 0) {
2216 return NT_STATUS_NO_SUCH_ALIAS;
2218 if (ret != 1) {
2219 DEBUG(0,("Found %d records matching sid %s\n",
2220 ret, dom_sid_string(mem_ctx, sid)));
2221 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2224 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2225 if (alias_name == NULL) {
2226 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2227 dom_sid_string(mem_ctx, sid)));
2228 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2231 a_state = talloc(mem_ctx, struct samr_account_state);
2232 if (!a_state) {
2233 return NT_STATUS_NO_MEMORY;
2235 a_state->sam_ctx = d_state->sam_ctx;
2236 a_state->access_mask = r->in.access_mask;
2237 a_state->domain_state = talloc_reference(a_state, d_state);
2238 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2239 a_state->account_sid = talloc_steal(a_state, sid);
2240 a_state->account_name = talloc_strdup(a_state, alias_name);
2241 if (!a_state->account_name) {
2242 return NT_STATUS_NO_MEMORY;
2245 /* create the policy handle */
2246 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2247 if (!g_handle) {
2248 return NT_STATUS_NO_MEMORY;
2251 g_handle->data = talloc_steal(g_handle, a_state);
2253 *r->out.alias_handle = g_handle->wire_handle;
2255 return NT_STATUS_OK;
2260 samr_QueryAliasInfo
2262 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2263 struct samr_QueryAliasInfo *r)
2265 struct dcesrv_handle *h;
2266 struct samr_account_state *a_state;
2267 struct ldb_message *msg, **res;
2268 const char * const attrs[4] = { "sAMAccountName", "description",
2269 "numMembers", NULL };
2270 int ret;
2271 union samr_AliasInfo *info;
2273 *r->out.info = NULL;
2275 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2277 a_state = h->data;
2279 /* pull all the alias attributes */
2280 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2281 a_state->account_dn, &res, attrs);
2282 if (ret == 0) {
2283 return NT_STATUS_NO_SUCH_ALIAS;
2285 if (ret != 1) {
2286 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2288 msg = res[0];
2290 /* allocate the info structure */
2291 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2292 if (info == NULL) {
2293 return NT_STATUS_NO_MEMORY;
2296 switch(r->in.level) {
2297 case ALIASINFOALL:
2298 QUERY_STRING(msg, all.name, "sAMAccountName");
2299 QUERY_UINT (msg, all.num_members, "numMembers");
2300 QUERY_STRING(msg, all.description, "description");
2301 break;
2302 case ALIASINFONAME:
2303 QUERY_STRING(msg, name, "sAMAccountName");
2304 break;
2305 case ALIASINFODESCRIPTION:
2306 QUERY_STRING(msg, description, "description");
2307 break;
2308 default:
2309 talloc_free(info);
2310 return NT_STATUS_INVALID_INFO_CLASS;
2313 *r->out.info = info;
2315 return NT_STATUS_OK;
2320 samr_SetAliasInfo
2322 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2323 struct samr_SetAliasInfo *r)
2325 struct dcesrv_handle *h;
2326 struct samr_account_state *a_state;
2327 struct ldb_message *msg;
2328 struct ldb_context *sam_ctx;
2329 int ret;
2331 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2333 a_state = h->data;
2334 sam_ctx = a_state->sam_ctx;
2336 msg = ldb_msg_new(mem_ctx);
2337 if (msg == NULL) {
2338 return NT_STATUS_NO_MEMORY;
2341 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2342 if (!msg->dn) {
2343 return NT_STATUS_NO_MEMORY;
2346 switch (r->in.level) {
2347 case ALIASINFODESCRIPTION:
2348 SET_STRING(msg, description, "description");
2349 break;
2350 case ALIASINFONAME:
2351 /* On W2k3 this does not change the name, it changes the
2352 * sAMAccountName attribute */
2353 SET_STRING(msg, name, "sAMAccountName");
2354 break;
2355 default:
2356 return NT_STATUS_INVALID_INFO_CLASS;
2359 /* modify the samdb record */
2360 ret = ldb_modify(a_state->sam_ctx, msg);
2361 if (ret != LDB_SUCCESS) {
2362 return dsdb_ldb_err_to_ntstatus(ret);
2365 return NT_STATUS_OK;
2370 samr_DeleteDomAlias
2372 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2373 struct samr_DeleteDomAlias *r)
2375 struct dcesrv_handle *h;
2376 struct samr_account_state *a_state;
2377 int ret;
2379 *r->out.alias_handle = *r->in.alias_handle;
2381 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2383 a_state = h->data;
2385 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2386 if (ret != LDB_SUCCESS) {
2387 return dsdb_ldb_err_to_ntstatus(ret);
2390 talloc_free(h);
2391 ZERO_STRUCTP(r->out.alias_handle);
2393 return NT_STATUS_OK;
2398 samr_AddAliasMember
2400 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2401 struct samr_AddAliasMember *r)
2403 struct dcesrv_handle *h;
2404 struct samr_account_state *a_state;
2405 struct samr_domain_state *d_state;
2406 struct ldb_message *mod;
2407 struct ldb_message **msgs;
2408 const char * const attrs[] = { NULL };
2409 struct ldb_dn *memberdn = NULL;
2410 int ret;
2411 NTSTATUS status;
2413 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2415 a_state = h->data;
2416 d_state = a_state->domain_state;
2418 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2419 &msgs, attrs, "(objectsid=%s)",
2420 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2422 if (ret == 1) {
2423 memberdn = msgs[0]->dn;
2424 } else if (ret == 0) {
2425 status = samdb_create_foreign_security_principal(
2426 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2427 if (!NT_STATUS_IS_OK(status)) {
2428 return status;
2430 } else {
2431 DEBUG(0,("Found %d records matching sid %s\n",
2432 ret, dom_sid_string(mem_ctx, r->in.sid)));
2433 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2436 if (memberdn == NULL) {
2437 DEBUG(0, ("Could not find memberdn\n"));
2438 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2441 mod = ldb_msg_new(mem_ctx);
2442 if (mod == NULL) {
2443 return NT_STATUS_NO_MEMORY;
2446 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2448 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2449 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2450 if (ret != LDB_SUCCESS) {
2451 return dsdb_ldb_err_to_ntstatus(ret);
2454 ret = ldb_modify(a_state->sam_ctx, mod);
2455 switch (ret) {
2456 case LDB_SUCCESS:
2457 return NT_STATUS_OK;
2458 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2459 return NT_STATUS_MEMBER_IN_GROUP;
2460 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2461 return NT_STATUS_ACCESS_DENIED;
2462 default:
2463 return dsdb_ldb_err_to_ntstatus(ret);
2469 samr_DeleteAliasMember
2471 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2472 struct samr_DeleteAliasMember *r)
2474 struct dcesrv_handle *h;
2475 struct samr_account_state *a_state;
2476 struct samr_domain_state *d_state;
2477 struct ldb_message *mod;
2478 const char *memberdn;
2479 int ret;
2481 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2483 a_state = h->data;
2484 d_state = a_state->domain_state;
2486 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2487 "distinguishedName", "(objectSid=%s)",
2488 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2489 if (memberdn == NULL) {
2490 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2493 mod = ldb_msg_new(mem_ctx);
2494 if (mod == NULL) {
2495 return NT_STATUS_NO_MEMORY;
2498 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2500 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2501 memberdn);
2502 if (ret != LDB_SUCCESS) {
2503 return dsdb_ldb_err_to_ntstatus(ret);
2506 ret = ldb_modify(a_state->sam_ctx, mod);
2507 switch (ret) {
2508 case LDB_SUCCESS:
2509 return NT_STATUS_OK;
2510 case LDB_ERR_UNWILLING_TO_PERFORM:
2511 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2512 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2513 return NT_STATUS_ACCESS_DENIED;
2514 default:
2515 return dsdb_ldb_err_to_ntstatus(ret);
2521 samr_GetMembersInAlias
2523 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2524 struct samr_GetMembersInAlias *r)
2526 struct dcesrv_handle *h;
2527 struct samr_account_state *a_state;
2528 struct samr_domain_state *d_state;
2529 struct lsa_SidPtr *array;
2530 unsigned int i, num_members;
2531 struct dom_sid *members;
2532 NTSTATUS status;
2534 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2536 a_state = h->data;
2537 d_state = a_state->domain_state;
2539 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2540 a_state->account_dn, &members,
2541 &num_members);
2542 if (!NT_STATUS_IS_OK(status)) {
2543 return status;
2546 if (num_members == 0) {
2547 r->out.sids->sids = NULL;
2549 return NT_STATUS_OK;
2552 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
2553 if (array == NULL) {
2554 return NT_STATUS_NO_MEMORY;
2557 for (i=0; i<num_members; i++) {
2558 array[i].sid = &members[i];
2561 r->out.sids->num_sids = num_members;
2562 r->out.sids->sids = array;
2564 return NT_STATUS_OK;
2568 samr_OpenUser
2570 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2571 struct samr_OpenUser *r)
2573 struct samr_domain_state *d_state;
2574 struct samr_account_state *a_state;
2575 struct dcesrv_handle *h;
2576 const char *account_name;
2577 struct dom_sid *sid;
2578 struct ldb_message **msgs;
2579 struct dcesrv_handle *u_handle;
2580 const char * const attrs[2] = { "sAMAccountName", NULL };
2581 int ret;
2583 ZERO_STRUCTP(r->out.user_handle);
2585 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2587 d_state = h->data;
2589 /* form the users SID */
2590 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2591 if (!sid) {
2592 return NT_STATUS_NO_MEMORY;
2595 /* search for the user record */
2596 ret = gendb_search(d_state->sam_ctx,
2597 mem_ctx, d_state->domain_dn, &msgs, attrs,
2598 "(&(objectSid=%s)(objectclass=user))",
2599 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2600 if (ret == 0) {
2601 return NT_STATUS_NO_SUCH_USER;
2603 if (ret != 1) {
2604 DEBUG(0,("Found %d records matching sid %s\n", ret,
2605 dom_sid_string(mem_ctx, sid)));
2606 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2609 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2610 if (account_name == NULL) {
2611 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2612 dom_sid_string(mem_ctx, sid)));
2613 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2616 a_state = talloc(mem_ctx, struct samr_account_state);
2617 if (!a_state) {
2618 return NT_STATUS_NO_MEMORY;
2620 a_state->sam_ctx = d_state->sam_ctx;
2621 a_state->access_mask = r->in.access_mask;
2622 a_state->domain_state = talloc_reference(a_state, d_state);
2623 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2624 a_state->account_sid = talloc_steal(a_state, sid);
2625 a_state->account_name = talloc_strdup(a_state, account_name);
2626 if (!a_state->account_name) {
2627 return NT_STATUS_NO_MEMORY;
2630 /* create the policy handle */
2631 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
2632 if (!u_handle) {
2633 return NT_STATUS_NO_MEMORY;
2636 u_handle->data = talloc_steal(u_handle, a_state);
2638 *r->out.user_handle = u_handle->wire_handle;
2640 return NT_STATUS_OK;
2646 samr_DeleteUser
2648 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2649 struct samr_DeleteUser *r)
2651 struct dcesrv_handle *h;
2652 struct samr_account_state *a_state;
2653 int ret;
2655 *r->out.user_handle = *r->in.user_handle;
2657 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2659 a_state = h->data;
2661 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2662 if (ret != LDB_SUCCESS) {
2663 DEBUG(1, ("Failed to delete user: %s: %s\n",
2664 ldb_dn_get_linearized(a_state->account_dn),
2665 ldb_errstring(a_state->sam_ctx)));
2666 return dsdb_ldb_err_to_ntstatus(ret);
2669 talloc_free(h);
2670 ZERO_STRUCTP(r->out.user_handle);
2672 return NT_STATUS_OK;
2677 samr_QueryUserInfo
2679 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2680 struct samr_QueryUserInfo *r)
2682 struct dcesrv_handle *h;
2683 struct samr_account_state *a_state;
2684 struct ldb_message *msg, **res;
2685 int ret;
2686 struct ldb_context *sam_ctx;
2688 const char * const *attrs = NULL;
2689 union samr_UserInfo *info;
2691 *r->out.info = NULL;
2693 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2695 a_state = h->data;
2696 sam_ctx = a_state->sam_ctx;
2698 /* fill in the reply */
2699 switch (r->in.level) {
2700 case 1:
2702 static const char * const attrs2[] = {"sAMAccountName",
2703 "displayName",
2704 "primaryroupID",
2705 "description",
2706 "comment",
2707 NULL};
2708 attrs = attrs2;
2709 break;
2711 case 2:
2713 static const char * const attrs2[] = {"comment",
2714 "countryCode",
2715 "codePage",
2716 NULL};
2717 attrs = attrs2;
2718 break;
2720 case 3:
2722 static const char * const attrs2[] = {"sAMAccountName",
2723 "displayName",
2724 "objectSid",
2725 "primaryGroupID",
2726 "homeDirectory",
2727 "homeDrive",
2728 "scriptPath",
2729 "profilePath",
2730 "userWorkstations",
2731 "lastLogon",
2732 "lastLogoff",
2733 "pwdLastSet",
2734 "logonHours",
2735 "badPwdCount",
2736 "logonCount",
2737 "userAccountControl",
2738 "msDS-User-Account-Control-Computed",
2739 NULL};
2740 attrs = attrs2;
2741 break;
2743 case 4:
2745 static const char * const attrs2[] = {"logonHours",
2746 NULL};
2747 attrs = attrs2;
2748 break;
2750 case 5:
2752 static const char * const attrs2[] = {"sAMAccountName",
2753 "displayName",
2754 "objectSid",
2755 "primaryGroupID",
2756 "homeDirectory",
2757 "homeDrive",
2758 "scriptPath",
2759 "profilePath",
2760 "description",
2761 "userWorkstations",
2762 "lastLogon",
2763 "lastLogoff",
2764 "logonHours",
2765 "badPwdCount",
2766 "logonCount",
2767 "pwdLastSet",
2768 "accountExpires",
2769 "userAccountControl",
2770 "msDS-User-Account-Control-Computed",
2771 NULL};
2772 attrs = attrs2;
2773 break;
2775 case 6:
2777 static const char * const attrs2[] = {"sAMAccountName",
2778 "displayName",
2779 NULL};
2780 attrs = attrs2;
2781 break;
2783 case 7:
2785 static const char * const attrs2[] = {"sAMAccountName",
2786 NULL};
2787 attrs = attrs2;
2788 break;
2790 case 8:
2792 static const char * const attrs2[] = {"displayName",
2793 NULL};
2794 attrs = attrs2;
2795 break;
2797 case 9:
2799 static const char * const attrs2[] = {"primaryGroupID",
2800 NULL};
2801 attrs = attrs2;
2802 break;
2804 case 10:
2806 static const char * const attrs2[] = {"homeDirectory",
2807 "homeDrive",
2808 NULL};
2809 attrs = attrs2;
2810 break;
2812 case 11:
2814 static const char * const attrs2[] = {"scriptPath",
2815 NULL};
2816 attrs = attrs2;
2817 break;
2819 case 12:
2821 static const char * const attrs2[] = {"profilePath",
2822 NULL};
2823 attrs = attrs2;
2824 break;
2826 case 13:
2828 static const char * const attrs2[] = {"description",
2829 NULL};
2830 attrs = attrs2;
2831 break;
2833 case 14:
2835 static const char * const attrs2[] = {"userWorkstations",
2836 NULL};
2837 attrs = attrs2;
2838 break;
2840 case 16:
2842 static const char * const attrs2[] = {"userAccountControl",
2843 "msDS-User-Account-Control-Computed",
2844 "pwdLastSet",
2845 NULL};
2846 attrs = attrs2;
2847 break;
2849 case 17:
2851 static const char * const attrs2[] = {"accountExpires",
2852 NULL};
2853 attrs = attrs2;
2854 break;
2856 case 18:
2858 return NT_STATUS_NOT_SUPPORTED;
2860 case 20:
2862 static const char * const attrs2[] = {"userParameters",
2863 NULL};
2864 attrs = attrs2;
2865 break;
2867 case 21:
2869 static const char * const attrs2[] = {"lastLogon",
2870 "lastLogoff",
2871 "pwdLastSet",
2872 "accountExpires",
2873 "sAMAccountName",
2874 "displayName",
2875 "homeDirectory",
2876 "homeDrive",
2877 "scriptPath",
2878 "profilePath",
2879 "description",
2880 "userWorkstations",
2881 "comment",
2882 "userParameters",
2883 "objectSid",
2884 "primaryGroupID",
2885 "userAccountControl",
2886 "msDS-User-Account-Control-Computed",
2887 "logonHours",
2888 "badPwdCount",
2889 "logonCount",
2890 "countryCode",
2891 "codePage",
2892 NULL};
2893 attrs = attrs2;
2894 break;
2896 case 23:
2897 case 24:
2898 case 25:
2899 case 26:
2901 return NT_STATUS_NOT_SUPPORTED;
2903 default:
2905 return NT_STATUS_INVALID_INFO_CLASS;
2909 /* pull all the user attributes */
2910 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2911 a_state->account_dn, &res, attrs);
2912 if (ret == 0) {
2913 return NT_STATUS_NO_SUCH_USER;
2915 if (ret != 1) {
2916 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2918 msg = res[0];
2920 /* allocate the info structure */
2921 info = talloc_zero(mem_ctx, union samr_UserInfo);
2922 if (info == NULL) {
2923 return NT_STATUS_NO_MEMORY;
2926 /* fill in the reply */
2927 switch (r->in.level) {
2928 case 1:
2929 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
2930 QUERY_STRING(msg, info1.full_name, "displayName");
2931 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
2932 QUERY_STRING(msg, info1.description, "description");
2933 QUERY_STRING(msg, info1.comment, "comment");
2934 break;
2936 case 2:
2937 QUERY_STRING(msg, info2.comment, "comment");
2938 QUERY_UINT (msg, info2.country_code, "countryCode");
2939 QUERY_UINT (msg, info2.code_page, "codePage");
2940 break;
2942 case 3:
2943 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
2944 QUERY_STRING(msg, info3.full_name, "displayName");
2945 QUERY_RID (msg, info3.rid, "objectSid");
2946 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
2947 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
2948 QUERY_STRING(msg, info3.home_drive, "homeDrive");
2949 QUERY_STRING(msg, info3.logon_script, "scriptPath");
2950 QUERY_STRING(msg, info3.profile_path, "profilePath");
2951 QUERY_STRING(msg, info3.workstations, "userWorkstations");
2952 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
2953 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
2954 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
2955 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
2956 QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
2957 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
2958 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
2959 QUERY_UINT (msg, info3.logon_count, "logonCount");
2960 QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
2961 break;
2963 case 4:
2964 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
2965 break;
2967 case 5:
2968 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
2969 QUERY_STRING(msg, info5.full_name, "displayName");
2970 QUERY_RID (msg, info5.rid, "objectSid");
2971 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
2972 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
2973 QUERY_STRING(msg, info5.home_drive, "homeDrive");
2974 QUERY_STRING(msg, info5.logon_script, "scriptPath");
2975 QUERY_STRING(msg, info5.profile_path, "profilePath");
2976 QUERY_STRING(msg, info5.description, "description");
2977 QUERY_STRING(msg, info5.workstations, "userWorkstations");
2978 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
2979 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
2980 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
2981 QUERY_UINT (msg, info5.bad_password_count, "badPwdCount");
2982 QUERY_UINT (msg, info5.logon_count, "logonCount");
2983 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
2984 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
2985 QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
2986 break;
2988 case 6:
2989 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
2990 QUERY_STRING(msg, info6.full_name, "displayName");
2991 break;
2993 case 7:
2994 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
2995 break;
2997 case 8:
2998 QUERY_STRING(msg, info8.full_name, "displayName");
2999 break;
3001 case 9:
3002 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3003 break;
3005 case 10:
3006 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3007 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3008 break;
3010 case 11:
3011 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3012 break;
3014 case 12:
3015 QUERY_STRING(msg, info12.profile_path, "profilePath");
3016 break;
3018 case 13:
3019 QUERY_STRING(msg, info13.description, "description");
3020 break;
3022 case 14:
3023 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3024 break;
3026 case 16:
3027 QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3028 break;
3030 case 17:
3031 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3032 break;
3034 case 20:
3035 QUERY_PARAMETERS(msg, info20.parameters, "userParameters");
3036 break;
3038 case 21:
3039 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3040 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3041 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3042 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3043 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3044 QUERY_FPASSC(msg, info21.force_password_change,"pwdLastSet");
3045 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3046 QUERY_STRING(msg, info21.full_name, "displayName");
3047 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3048 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3049 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3050 QUERY_STRING(msg, info21.profile_path, "profilePath");
3051 QUERY_STRING(msg, info21.description, "description");
3052 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3053 QUERY_STRING(msg, info21.comment, "comment");
3054 QUERY_PARAMETERS(msg, info21.parameters, "userParameters");
3055 QUERY_RID (msg, info21.rid, "objectSid");
3056 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3057 QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3058 info->info21.fields_present = 0x08FFFFFF;
3059 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3060 QUERY_UINT (msg, info21.bad_password_count, "badPwdCount");
3061 QUERY_UINT (msg, info21.logon_count, "logonCount");
3062 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3063 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3064 } else {
3065 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3067 QUERY_UINT (msg, info21.country_code, "countryCode");
3068 QUERY_UINT (msg, info21.code_page, "codePage");
3069 break;
3072 default:
3073 talloc_free(info);
3074 return NT_STATUS_INVALID_INFO_CLASS;
3077 *r->out.info = info;
3079 return NT_STATUS_OK;
3084 samr_SetUserInfo
3086 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3087 struct samr_SetUserInfo *r)
3089 struct dcesrv_handle *h;
3090 struct samr_account_state *a_state;
3091 struct ldb_message *msg;
3092 int ret;
3093 NTSTATUS status = NT_STATUS_OK;
3094 struct ldb_context *sam_ctx;
3096 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3098 a_state = h->data;
3099 sam_ctx = a_state->sam_ctx;
3101 msg = ldb_msg_new(mem_ctx);
3102 if (msg == NULL) {
3103 return NT_STATUS_NO_MEMORY;
3106 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3107 if (!msg->dn) {
3108 return NT_STATUS_NO_MEMORY;
3111 switch (r->in.level) {
3112 case 2:
3113 SET_STRING(msg, info2.comment, "comment");
3114 SET_UINT (msg, info2.country_code, "countryCode");
3115 SET_UINT (msg, info2.code_page, "codePage");
3116 break;
3118 case 4:
3119 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3120 break;
3122 case 6:
3123 SET_STRING(msg, info6.account_name, "samAccountName");
3124 SET_STRING(msg, info6.full_name, "displayName");
3125 break;
3127 case 7:
3128 SET_STRING(msg, info7.account_name, "samAccountName");
3129 break;
3131 case 8:
3132 SET_STRING(msg, info8.full_name, "displayName");
3133 break;
3135 case 9:
3136 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3137 break;
3139 case 10:
3140 SET_STRING(msg, info10.home_directory, "homeDirectory");
3141 SET_STRING(msg, info10.home_drive, "homeDrive");
3142 break;
3144 case 11:
3145 SET_STRING(msg, info11.logon_script, "scriptPath");
3146 break;
3148 case 12:
3149 SET_STRING(msg, info12.profile_path, "profilePath");
3150 break;
3152 case 13:
3153 SET_STRING(msg, info13.description, "description");
3154 break;
3156 case 14:
3157 SET_STRING(msg, info14.workstations, "userWorkstations");
3158 break;
3160 case 16:
3161 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3162 break;
3164 case 17:
3165 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3166 break;
3168 case 18:
3169 status = samr_set_password_buffers(dce_call,
3170 a_state->sam_ctx,
3171 a_state->account_dn,
3172 a_state->domain_state->domain_dn,
3173 mem_ctx,
3174 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3175 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3176 if (!NT_STATUS_IS_OK(status)) {
3177 return status;
3180 if (r->in.info->info18.password_expired > 0) {
3181 struct ldb_message_element *set_el;
3182 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3183 return NT_STATUS_NO_MEMORY;
3185 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3186 set_el->flags = LDB_FLAG_MOD_REPLACE;
3188 break;
3190 case 20:
3191 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3192 break;
3194 case 21:
3195 if (r->in.info->info21.fields_present == 0)
3196 return NT_STATUS_INVALID_PARAMETER;
3198 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3199 IFSET(SAMR_FIELD_LAST_LOGON)
3200 SET_UINT64(msg, info21.last_logon, "lastLogon");
3201 IFSET(SAMR_FIELD_LAST_LOGOFF)
3202 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3203 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3204 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3205 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3206 SET_STRING(msg, info21.account_name, "samAccountName");
3207 IFSET(SAMR_FIELD_FULL_NAME)
3208 SET_STRING(msg, info21.full_name, "displayName");
3209 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3210 SET_STRING(msg, info21.home_directory, "homeDirectory");
3211 IFSET(SAMR_FIELD_HOME_DRIVE)
3212 SET_STRING(msg, info21.home_drive, "homeDrive");
3213 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3214 SET_STRING(msg, info21.logon_script, "scriptPath");
3215 IFSET(SAMR_FIELD_PROFILE_PATH)
3216 SET_STRING(msg, info21.profile_path, "profilePath");
3217 IFSET(SAMR_FIELD_DESCRIPTION)
3218 SET_STRING(msg, info21.description, "description");
3219 IFSET(SAMR_FIELD_WORKSTATIONS)
3220 SET_STRING(msg, info21.workstations, "userWorkstations");
3221 IFSET(SAMR_FIELD_COMMENT)
3222 SET_STRING(msg, info21.comment, "comment");
3223 IFSET(SAMR_FIELD_PARAMETERS)
3224 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3225 IFSET(SAMR_FIELD_PRIMARY_GID)
3226 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3227 IFSET(SAMR_FIELD_ACCT_FLAGS)
3228 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3229 IFSET(SAMR_FIELD_LOGON_HOURS)
3230 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3231 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3232 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3233 IFSET(SAMR_FIELD_NUM_LOGONS)
3234 SET_UINT (msg, info21.logon_count, "logonCount");
3235 IFSET(SAMR_FIELD_COUNTRY_CODE)
3236 SET_UINT (msg, info21.country_code, "countryCode");
3237 IFSET(SAMR_FIELD_CODE_PAGE)
3238 SET_UINT (msg, info21.code_page, "codePage");
3240 /* password change fields */
3241 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3242 return NT_STATUS_ACCESS_DENIED;
3244 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3245 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3246 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3248 if (r->in.info->info21.lm_password_set) {
3249 if ((r->in.info->info21.lm_owf_password.length != 16)
3250 || (r->in.info->info21.lm_owf_password.size != 16)) {
3251 return NT_STATUS_INVALID_PARAMETER;
3254 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3256 if (r->in.info->info21.nt_password_set) {
3257 if ((r->in.info->info21.nt_owf_password.length != 16)
3258 || (r->in.info->info21.nt_owf_password.size != 16)) {
3259 return NT_STATUS_INVALID_PARAMETER;
3262 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3264 status = samr_set_password_buffers(dce_call,
3265 a_state->sam_ctx,
3266 a_state->account_dn,
3267 a_state->domain_state->domain_dn,
3268 mem_ctx,
3269 lm_pwd_hash,
3270 nt_pwd_hash);
3271 if (!NT_STATUS_IS_OK(status)) {
3272 return status;
3277 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3278 NTTIME t = 0;
3279 struct ldb_message_element *set_el;
3280 if (r->in.info->info21.password_expired
3281 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3282 unix_to_nt_time(&t, time(NULL));
3284 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3285 "pwdLastSet", t) != LDB_SUCCESS) {
3286 return NT_STATUS_NO_MEMORY;
3288 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3289 set_el->flags = LDB_FLAG_MOD_REPLACE;
3291 #undef IFSET
3292 break;
3294 case 23:
3295 if (r->in.info->info23.info.fields_present == 0)
3296 return NT_STATUS_INVALID_PARAMETER;
3298 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3299 IFSET(SAMR_FIELD_LAST_LOGON)
3300 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3301 IFSET(SAMR_FIELD_LAST_LOGOFF)
3302 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3303 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3304 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3305 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3306 SET_STRING(msg, info23.info.account_name, "samAccountName");
3307 IFSET(SAMR_FIELD_FULL_NAME)
3308 SET_STRING(msg, info23.info.full_name, "displayName");
3309 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3310 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3311 IFSET(SAMR_FIELD_HOME_DRIVE)
3312 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3313 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3314 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3315 IFSET(SAMR_FIELD_PROFILE_PATH)
3316 SET_STRING(msg, info23.info.profile_path, "profilePath");
3317 IFSET(SAMR_FIELD_DESCRIPTION)
3318 SET_STRING(msg, info23.info.description, "description");
3319 IFSET(SAMR_FIELD_WORKSTATIONS)
3320 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3321 IFSET(SAMR_FIELD_COMMENT)
3322 SET_STRING(msg, info23.info.comment, "comment");
3323 IFSET(SAMR_FIELD_PARAMETERS)
3324 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3325 IFSET(SAMR_FIELD_PRIMARY_GID)
3326 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3327 IFSET(SAMR_FIELD_ACCT_FLAGS)
3328 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3329 IFSET(SAMR_FIELD_LOGON_HOURS)
3330 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3331 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3332 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3333 IFSET(SAMR_FIELD_NUM_LOGONS)
3334 SET_UINT (msg, info23.info.logon_count, "logonCount");
3336 IFSET(SAMR_FIELD_COUNTRY_CODE)
3337 SET_UINT (msg, info23.info.country_code, "countryCode");
3338 IFSET(SAMR_FIELD_CODE_PAGE)
3339 SET_UINT (msg, info23.info.code_page, "codePage");
3341 /* password change fields */
3342 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3343 return NT_STATUS_ACCESS_DENIED;
3345 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3346 status = samr_set_password(dce_call,
3347 a_state->sam_ctx,
3348 a_state->account_dn,
3349 a_state->domain_state->domain_dn,
3350 mem_ctx,
3351 &r->in.info->info23.password);
3352 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3353 status = samr_set_password(dce_call,
3354 a_state->sam_ctx,
3355 a_state->account_dn,
3356 a_state->domain_state->domain_dn,
3357 mem_ctx,
3358 &r->in.info->info23.password);
3360 if (!NT_STATUS_IS_OK(status)) {
3361 return status;
3364 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3365 NTTIME t = 0;
3366 struct ldb_message_element *set_el;
3367 if (r->in.info->info23.info.password_expired
3368 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3369 unix_to_nt_time(&t, time(NULL));
3371 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3372 "pwdLastSet", t) != LDB_SUCCESS) {
3373 return NT_STATUS_NO_MEMORY;
3375 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3376 set_el->flags = LDB_FLAG_MOD_REPLACE;
3378 #undef IFSET
3379 break;
3381 /* the set password levels are handled separately */
3382 case 24:
3383 status = samr_set_password(dce_call,
3384 a_state->sam_ctx,
3385 a_state->account_dn,
3386 a_state->domain_state->domain_dn,
3387 mem_ctx,
3388 &r->in.info->info24.password);
3389 if (!NT_STATUS_IS_OK(status)) {
3390 return status;
3393 if (r->in.info->info24.password_expired > 0) {
3394 struct ldb_message_element *set_el;
3395 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != 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 break;
3403 case 25:
3404 if (r->in.info->info25.info.fields_present == 0)
3405 return NT_STATUS_INVALID_PARAMETER;
3407 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3408 IFSET(SAMR_FIELD_LAST_LOGON)
3409 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3410 IFSET(SAMR_FIELD_LAST_LOGOFF)
3411 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3412 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3413 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3414 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3415 SET_STRING(msg, info25.info.account_name, "samAccountName");
3416 IFSET(SAMR_FIELD_FULL_NAME)
3417 SET_STRING(msg, info25.info.full_name, "displayName");
3418 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3419 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3420 IFSET(SAMR_FIELD_HOME_DRIVE)
3421 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3422 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3423 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3424 IFSET(SAMR_FIELD_PROFILE_PATH)
3425 SET_STRING(msg, info25.info.profile_path, "profilePath");
3426 IFSET(SAMR_FIELD_DESCRIPTION)
3427 SET_STRING(msg, info25.info.description, "description");
3428 IFSET(SAMR_FIELD_WORKSTATIONS)
3429 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3430 IFSET(SAMR_FIELD_COMMENT)
3431 SET_STRING(msg, info25.info.comment, "comment");
3432 IFSET(SAMR_FIELD_PARAMETERS)
3433 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3434 IFSET(SAMR_FIELD_PRIMARY_GID)
3435 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3436 IFSET(SAMR_FIELD_ACCT_FLAGS)
3437 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3438 IFSET(SAMR_FIELD_LOGON_HOURS)
3439 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3440 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3441 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
3442 IFSET(SAMR_FIELD_NUM_LOGONS)
3443 SET_UINT (msg, info25.info.logon_count, "logonCount");
3444 IFSET(SAMR_FIELD_COUNTRY_CODE)
3445 SET_UINT (msg, info25.info.country_code, "countryCode");
3446 IFSET(SAMR_FIELD_CODE_PAGE)
3447 SET_UINT (msg, info25.info.code_page, "codePage");
3449 /* password change fields */
3450 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3451 return NT_STATUS_ACCESS_DENIED;
3453 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3454 status = samr_set_password_ex(dce_call,
3455 a_state->sam_ctx,
3456 a_state->account_dn,
3457 a_state->domain_state->domain_dn,
3458 mem_ctx,
3459 &r->in.info->info25.password);
3460 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3461 status = samr_set_password_ex(dce_call,
3462 a_state->sam_ctx,
3463 a_state->account_dn,
3464 a_state->domain_state->domain_dn,
3465 mem_ctx,
3466 &r->in.info->info25.password);
3468 if (!NT_STATUS_IS_OK(status)) {
3469 return status;
3472 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3473 NTTIME t = 0;
3474 struct ldb_message_element *set_el;
3475 if (r->in.info->info25.info.password_expired
3476 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3477 unix_to_nt_time(&t, time(NULL));
3479 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3480 "pwdLastSet", t) != LDB_SUCCESS) {
3481 return NT_STATUS_NO_MEMORY;
3483 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3484 set_el->flags = LDB_FLAG_MOD_REPLACE;
3486 #undef IFSET
3487 break;
3489 /* the set password levels are handled separately */
3490 case 26:
3491 status = samr_set_password_ex(dce_call,
3492 a_state->sam_ctx,
3493 a_state->account_dn,
3494 a_state->domain_state->domain_dn,
3495 mem_ctx,
3496 &r->in.info->info26.password);
3497 if (!NT_STATUS_IS_OK(status)) {
3498 return status;
3501 if (r->in.info->info26.password_expired > 0) {
3502 NTTIME t = 0;
3503 struct ldb_message_element *set_el;
3504 if (r->in.info->info26.password_expired
3505 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3506 unix_to_nt_time(&t, time(NULL));
3508 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3509 "pwdLastSet", t) != LDB_SUCCESS) {
3510 return NT_STATUS_NO_MEMORY;
3512 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3513 set_el->flags = LDB_FLAG_MOD_REPLACE;
3515 break;
3517 default:
3518 /* many info classes are not valid for SetUserInfo */
3519 return NT_STATUS_INVALID_INFO_CLASS;
3522 if (!NT_STATUS_IS_OK(status)) {
3523 return status;
3526 /* modify the samdb record */
3527 if (msg->num_elements > 0) {
3528 ret = ldb_modify(a_state->sam_ctx, msg);
3529 if (ret != LDB_SUCCESS) {
3530 DEBUG(1,("Failed to modify record %s: %s\n",
3531 ldb_dn_get_linearized(a_state->account_dn),
3532 ldb_errstring(a_state->sam_ctx)));
3534 return dsdb_ldb_err_to_ntstatus(ret);
3538 return NT_STATUS_OK;
3543 samr_GetGroupsForUser
3545 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3546 struct samr_GetGroupsForUser *r)
3548 struct dcesrv_handle *h;
3549 struct samr_account_state *a_state;
3550 struct samr_domain_state *d_state;
3551 struct ldb_message **res;
3552 const char * const attrs[2] = { "objectSid", NULL };
3553 struct samr_RidWithAttributeArray *array;
3554 int i, count;
3556 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3558 a_state = h->data;
3559 d_state = a_state->domain_state;
3561 count = samdb_search_domain(a_state->sam_ctx, mem_ctx,
3562 d_state->domain_dn, &res,
3563 attrs, d_state->domain_sid,
3564 "(&(member=%s)(|(grouptype=%d)(grouptype=%d))(objectclass=group))",
3565 ldb_dn_get_linearized(a_state->account_dn),
3566 GTYPE_SECURITY_UNIVERSAL_GROUP,
3567 GTYPE_SECURITY_GLOBAL_GROUP);
3568 if (count < 0)
3569 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3571 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
3572 if (array == NULL)
3573 return NT_STATUS_NO_MEMORY;
3575 array->count = 0;
3576 array->rids = NULL;
3578 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
3579 count + 1);
3580 if (array->rids == NULL)
3581 return NT_STATUS_NO_MEMORY;
3583 /* Adds the primary group */
3584 array->rids[0].rid = samdb_search_uint(a_state->sam_ctx, mem_ctx,
3585 ~0, a_state->account_dn,
3586 "primaryGroupID", NULL);
3587 array->rids[0].attributes = SE_GROUP_MANDATORY
3588 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3589 array->count += 1;
3591 /* Adds the additional groups */
3592 for (i = 0; i < count; i++) {
3593 struct dom_sid *group_sid;
3595 group_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
3596 if (group_sid == NULL) {
3597 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3600 array->rids[i + 1].rid =
3601 group_sid->sub_auths[group_sid->num_auths-1];
3602 array->rids[i + 1].attributes = SE_GROUP_MANDATORY
3603 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3604 array->count += 1;
3607 *r->out.rids = array;
3609 return NT_STATUS_OK;
3614 samr_QueryDisplayInfo
3616 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3617 struct samr_QueryDisplayInfo *r)
3619 struct dcesrv_handle *h;
3620 struct samr_domain_state *d_state;
3621 struct ldb_result *res;
3622 unsigned int i;
3623 uint32_t count;
3624 const char * const attrs[] = { "objectSid", "sAMAccountName",
3625 "displayName", "description", "userAccountControl",
3626 "pwdLastSet", NULL };
3627 struct samr_DispEntryFull *entriesFull = NULL;
3628 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
3629 struct samr_DispEntryAscii *entriesAscii = NULL;
3630 struct samr_DispEntryGeneral *entriesGeneral = NULL;
3631 const char *filter;
3632 int ret;
3634 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3636 d_state = h->data;
3638 switch (r->in.level) {
3639 case 1:
3640 case 4:
3641 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3642 "(sAMAccountType=%d))",
3643 ATYPE_NORMAL_ACCOUNT);
3644 break;
3645 case 2:
3646 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3647 "(sAMAccountType=%d))",
3648 ATYPE_WORKSTATION_TRUST);
3649 break;
3650 case 3:
3651 case 5:
3652 filter = talloc_asprintf(mem_ctx,
3653 "(&(|(groupType=%d)(groupType=%d))"
3654 "(objectClass=group))",
3655 GTYPE_SECURITY_UNIVERSAL_GROUP,
3656 GTYPE_SECURITY_GLOBAL_GROUP);
3657 break;
3658 default:
3659 return NT_STATUS_INVALID_INFO_CLASS;
3662 /* search for all requested objects in all domains. This could
3663 possibly be cached and resumed based on resume_key */
3664 ret = dsdb_search(d_state->sam_ctx, mem_ctx, &res, ldb_get_default_basedn(d_state->sam_ctx),
3665 LDB_SCOPE_SUBTREE, attrs, 0, "%s", filter);
3666 if (ret != LDB_SUCCESS) {
3667 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3669 if ((res->count == 0) || (r->in.max_entries == 0)) {
3670 return NT_STATUS_OK;
3673 switch (r->in.level) {
3674 case 1:
3675 entriesGeneral = talloc_array(mem_ctx,
3676 struct samr_DispEntryGeneral,
3677 res->count);
3678 break;
3679 case 2:
3680 entriesFull = talloc_array(mem_ctx,
3681 struct samr_DispEntryFull,
3682 res->count);
3683 break;
3684 case 3:
3685 entriesFullGroup = talloc_array(mem_ctx,
3686 struct samr_DispEntryFullGroup,
3687 res->count);
3688 break;
3689 case 4:
3690 case 5:
3691 entriesAscii = talloc_array(mem_ctx,
3692 struct samr_DispEntryAscii,
3693 res->count);
3694 break;
3697 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
3698 (entriesAscii == NULL) && (entriesFullGroup == NULL))
3699 return NT_STATUS_NO_MEMORY;
3701 count = 0;
3703 for (i = 0; i < res->count; i++) {
3704 struct dom_sid *objectsid;
3706 objectsid = samdb_result_dom_sid(mem_ctx, res->msgs[i],
3707 "objectSid");
3708 if (objectsid == NULL)
3709 continue;
3711 switch(r->in.level) {
3712 case 1:
3713 entriesGeneral[count].idx = count + 1;
3714 entriesGeneral[count].rid =
3715 objectsid->sub_auths[objectsid->num_auths-1];
3716 entriesGeneral[count].acct_flags =
3717 samdb_result_acct_flags(res->msgs[i], NULL);
3718 entriesGeneral[count].account_name.string =
3719 ldb_msg_find_attr_as_string(res->msgs[i],
3720 "sAMAccountName", "");
3721 entriesGeneral[count].full_name.string =
3722 ldb_msg_find_attr_as_string(res->msgs[i],
3723 "displayName", "");
3724 entriesGeneral[count].description.string =
3725 ldb_msg_find_attr_as_string(res->msgs[i],
3726 "description", "");
3727 break;
3728 case 2:
3729 entriesFull[count].idx = count + 1;
3730 entriesFull[count].rid =
3731 objectsid->sub_auths[objectsid->num_auths-1];
3733 /* No idea why we need to or in ACB_NORMAL here, but this is what Win2k3 seems to do... */
3734 entriesFull[count].acct_flags =
3735 samdb_result_acct_flags(res->msgs[i],
3736 NULL) | ACB_NORMAL;
3737 entriesFull[count].account_name.string =
3738 ldb_msg_find_attr_as_string(res->msgs[i],
3739 "sAMAccountName", "");
3740 entriesFull[count].description.string =
3741 ldb_msg_find_attr_as_string(res->msgs[i],
3742 "description", "");
3743 break;
3744 case 3:
3745 entriesFullGroup[count].idx = count + 1;
3746 entriesFullGroup[count].rid =
3747 objectsid->sub_auths[objectsid->num_auths-1];
3748 /* We get a "7" here for groups */
3749 entriesFullGroup[count].acct_flags
3750 = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3751 entriesFullGroup[count].account_name.string =
3752 ldb_msg_find_attr_as_string(res->msgs[i],
3753 "sAMAccountName", "");
3754 entriesFullGroup[count].description.string =
3755 ldb_msg_find_attr_as_string(res->msgs[i],
3756 "description", "");
3757 break;
3758 case 4:
3759 case 5:
3760 entriesAscii[count].idx = count + 1;
3761 entriesAscii[count].account_name.string =
3762 ldb_msg_find_attr_as_string(res->msgs[i],
3763 "sAMAccountName", "");
3764 break;
3767 count += 1;
3770 *r->out.total_size = count;
3772 if (r->in.start_idx >= count) {
3773 *r->out.returned_size = 0;
3774 switch(r->in.level) {
3775 case 1:
3776 r->out.info->info1.count = *r->out.returned_size;
3777 r->out.info->info1.entries = NULL;
3778 break;
3779 case 2:
3780 r->out.info->info2.count = *r->out.returned_size;
3781 r->out.info->info2.entries = NULL;
3782 break;
3783 case 3:
3784 r->out.info->info3.count = *r->out.returned_size;
3785 r->out.info->info3.entries = NULL;
3786 break;
3787 case 4:
3788 r->out.info->info4.count = *r->out.returned_size;
3789 r->out.info->info4.entries = NULL;
3790 break;
3791 case 5:
3792 r->out.info->info5.count = *r->out.returned_size;
3793 r->out.info->info5.entries = NULL;
3794 break;
3796 } else {
3797 *r->out.returned_size = MIN(count - r->in.start_idx,
3798 r->in.max_entries);
3799 switch(r->in.level) {
3800 case 1:
3801 r->out.info->info1.count = *r->out.returned_size;
3802 r->out.info->info1.entries =
3803 &(entriesGeneral[r->in.start_idx]);
3804 break;
3805 case 2:
3806 r->out.info->info2.count = *r->out.returned_size;
3807 r->out.info->info2.entries =
3808 &(entriesFull[r->in.start_idx]);
3809 break;
3810 case 3:
3811 r->out.info->info3.count = *r->out.returned_size;
3812 r->out.info->info3.entries =
3813 &(entriesFullGroup[r->in.start_idx]);
3814 break;
3815 case 4:
3816 r->out.info->info4.count = *r->out.returned_size;
3817 r->out.info->info4.entries =
3818 &(entriesAscii[r->in.start_idx]);
3819 break;
3820 case 5:
3821 r->out.info->info5.count = *r->out.returned_size;
3822 r->out.info->info5.entries =
3823 &(entriesAscii[r->in.start_idx]);
3824 break;
3828 return (*r->out.returned_size < (count - r->in.start_idx)) ?
3829 STATUS_MORE_ENTRIES : NT_STATUS_OK;
3834 samr_GetDisplayEnumerationIndex
3836 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3837 struct samr_GetDisplayEnumerationIndex *r)
3839 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3844 samr_TestPrivateFunctionsDomain
3846 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3847 struct samr_TestPrivateFunctionsDomain *r)
3849 return NT_STATUS_NOT_IMPLEMENTED;
3854 samr_TestPrivateFunctionsUser
3856 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3857 struct samr_TestPrivateFunctionsUser *r)
3859 return NT_STATUS_NOT_IMPLEMENTED;
3864 samr_GetUserPwInfo
3866 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3867 struct samr_GetUserPwInfo *r)
3869 struct dcesrv_handle *h;
3870 struct samr_account_state *a_state;
3872 ZERO_STRUCTP(r->out.info);
3874 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3876 a_state = h->data;
3878 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
3879 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
3880 NULL);
3881 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
3882 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
3884 return NT_STATUS_OK;
3889 samr_RemoveMemberFromForeignDomain
3891 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
3892 TALLOC_CTX *mem_ctx,
3893 struct samr_RemoveMemberFromForeignDomain *r)
3895 struct dcesrv_handle *h;
3896 struct samr_domain_state *d_state;
3897 const char *memberdn;
3898 struct ldb_message **res;
3899 const char *no_attrs[] = { NULL };
3900 int i, count;
3902 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3904 d_state = h->data;
3906 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3907 "distinguishedName", "(objectSid=%s)",
3908 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3909 /* Nothing to do */
3910 if (memberdn == NULL) {
3911 return NT_STATUS_OK;
3914 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
3915 d_state->domain_dn, &res, no_attrs,
3916 d_state->domain_sid,
3917 "(&(member=%s)(objectClass=group)"
3918 "(|(groupType=%d)(groupType=%d)))",
3919 memberdn,
3920 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
3921 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
3923 if (count < 0)
3924 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3926 for (i=0; i<count; i++) {
3927 struct ldb_message *mod;
3928 int ret;
3930 mod = ldb_msg_new(mem_ctx);
3931 if (mod == NULL) {
3932 return NT_STATUS_NO_MEMORY;
3935 mod->dn = res[i]->dn;
3937 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
3938 "member", memberdn) != LDB_SUCCESS)
3939 return NT_STATUS_NO_MEMORY;
3941 ret = ldb_modify(d_state->sam_ctx, mod);
3942 talloc_free(mod);
3943 if (ret != LDB_SUCCESS) {
3944 return dsdb_ldb_err_to_ntstatus(ret);
3948 return NT_STATUS_OK;
3953 samr_QueryDomainInfo2
3955 just an alias for samr_QueryDomainInfo
3957 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3958 struct samr_QueryDomainInfo2 *r)
3960 struct samr_QueryDomainInfo r1;
3961 NTSTATUS status;
3963 ZERO_STRUCT(r1.out);
3964 r1.in.domain_handle = r->in.domain_handle;
3965 r1.in.level = r->in.level;
3966 r1.out.info = r->out.info;
3968 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
3970 return status;
3975 samr_QueryUserInfo2
3977 just an alias for samr_QueryUserInfo
3979 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3980 struct samr_QueryUserInfo2 *r)
3982 struct samr_QueryUserInfo r1;
3983 NTSTATUS status;
3985 r1.in.user_handle = r->in.user_handle;
3986 r1.in.level = r->in.level;
3987 r1.out.info = r->out.info;
3989 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
3991 return status;
3996 samr_QueryDisplayInfo2
3998 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3999 struct samr_QueryDisplayInfo2 *r)
4001 struct samr_QueryDisplayInfo q;
4002 NTSTATUS result;
4004 q.in.domain_handle = r->in.domain_handle;
4005 q.in.level = r->in.level;
4006 q.in.start_idx = r->in.start_idx;
4007 q.in.max_entries = r->in.max_entries;
4008 q.in.buf_size = r->in.buf_size;
4009 q.out.total_size = r->out.total_size;
4010 q.out.returned_size = r->out.returned_size;
4011 q.out.info = r->out.info;
4013 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4015 return result;
4020 samr_GetDisplayEnumerationIndex2
4022 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4023 struct samr_GetDisplayEnumerationIndex2 *r)
4025 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4030 samr_QueryDisplayInfo3
4032 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4033 struct samr_QueryDisplayInfo3 *r)
4035 struct samr_QueryDisplayInfo q;
4036 NTSTATUS result;
4038 q.in.domain_handle = r->in.domain_handle;
4039 q.in.level = r->in.level;
4040 q.in.start_idx = r->in.start_idx;
4041 q.in.max_entries = r->in.max_entries;
4042 q.in.buf_size = r->in.buf_size;
4043 q.out.total_size = r->out.total_size;
4044 q.out.returned_size = r->out.returned_size;
4045 q.out.info = r->out.info;
4047 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4049 return result;
4054 samr_AddMultipleMembersToAlias
4056 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4057 struct samr_AddMultipleMembersToAlias *r)
4059 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4064 samr_RemoveMultipleMembersFromAlias
4066 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4067 struct samr_RemoveMultipleMembersFromAlias *r)
4069 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4074 samr_GetDomPwInfo
4076 this fetches the default password properties for a domain
4078 note that w2k3 completely ignores the domain name in this call, and
4079 always returns the information for the servers primary domain
4081 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4082 struct samr_GetDomPwInfo *r)
4084 struct ldb_message **msgs;
4085 int ret;
4086 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4087 struct ldb_context *sam_ctx;
4089 ZERO_STRUCTP(r->out.info);
4091 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
4092 dce_call->conn->dce_ctx->lp_ctx,
4093 dce_call->conn->auth_state.session_info, 0);
4094 if (sam_ctx == NULL) {
4095 return NT_STATUS_INVALID_SYSTEM_SERVICE;
4098 /* The domain name in this call is ignored */
4099 ret = gendb_search_dn(sam_ctx,
4100 mem_ctx, NULL, &msgs, attrs);
4101 if (ret <= 0) {
4102 talloc_free(sam_ctx);
4104 return NT_STATUS_NO_SUCH_DOMAIN;
4106 if (ret > 1) {
4107 talloc_free(msgs);
4108 talloc_free(sam_ctx);
4110 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4113 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
4114 "minPwdLength", 0);
4115 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
4116 "pwdProperties", 1);
4118 talloc_free(msgs);
4119 talloc_unlink(mem_ctx, sam_ctx);
4121 return NT_STATUS_OK;
4126 samr_Connect2
4128 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4129 struct samr_Connect2 *r)
4131 struct samr_Connect c;
4133 c.in.system_name = NULL;
4134 c.in.access_mask = r->in.access_mask;
4135 c.out.connect_handle = r->out.connect_handle;
4137 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4142 samr_SetUserInfo2
4144 just an alias for samr_SetUserInfo
4146 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4147 struct samr_SetUserInfo2 *r)
4149 struct samr_SetUserInfo r2;
4151 r2.in.user_handle = r->in.user_handle;
4152 r2.in.level = r->in.level;
4153 r2.in.info = r->in.info;
4155 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4160 samr_SetBootKeyInformation
4162 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4163 struct samr_SetBootKeyInformation *r)
4165 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4170 samr_GetBootKeyInformation
4172 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4173 struct samr_GetBootKeyInformation *r)
4175 /* Windows Server 2008 returns this */
4176 return NT_STATUS_NOT_SUPPORTED;
4181 samr_Connect3
4183 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4184 struct samr_Connect3 *r)
4186 struct samr_Connect c;
4188 c.in.system_name = NULL;
4189 c.in.access_mask = r->in.access_mask;
4190 c.out.connect_handle = r->out.connect_handle;
4192 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4197 samr_Connect4
4199 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4200 struct samr_Connect4 *r)
4202 struct samr_Connect c;
4204 c.in.system_name = NULL;
4205 c.in.access_mask = r->in.access_mask;
4206 c.out.connect_handle = r->out.connect_handle;
4208 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4213 samr_Connect5
4215 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4216 struct samr_Connect5 *r)
4218 struct samr_Connect c;
4219 NTSTATUS status;
4221 c.in.system_name = NULL;
4222 c.in.access_mask = r->in.access_mask;
4223 c.out.connect_handle = r->out.connect_handle;
4225 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4227 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4228 r->out.info_out->info1.unknown2 = 0;
4229 *r->out.level_out = r->in.level_in;
4231 return status;
4236 samr_RidToSid
4238 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4239 struct samr_RidToSid *r)
4241 struct samr_domain_state *d_state;
4242 struct dcesrv_handle *h;
4244 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4246 d_state = h->data;
4248 /* form the users SID */
4249 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4250 if (!*r->out.sid) {
4251 return NT_STATUS_NO_MEMORY;
4254 return NT_STATUS_OK;
4259 samr_SetDsrmPassword
4261 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4262 struct samr_SetDsrmPassword *r)
4264 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4269 samr_ValidatePassword
4271 For now the call checks the password complexity (if active) and the minimum
4272 password length on level 2 and 3. Level 1 is ignored for now.
4274 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
4275 TALLOC_CTX *mem_ctx,
4276 struct samr_ValidatePassword *r)
4278 struct samr_GetDomPwInfo r2;
4279 struct samr_PwInfo pwInfo;
4280 DATA_BLOB password;
4281 enum samr_ValidationStatus res;
4282 NTSTATUS status;
4283 enum dcerpc_transport_t transport =
4284 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
4286 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
4287 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4290 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
4292 r2.in.domain_name = NULL;
4293 r2.out.info = &pwInfo;
4294 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
4295 if (!NT_STATUS_IS_OK(status)) {
4296 return status;
4299 switch (r->in.level) {
4300 case NetValidateAuthentication:
4301 /* we don't support this yet */
4302 return NT_STATUS_NOT_SUPPORTED;
4303 break;
4304 case NetValidatePasswordChange:
4305 password = data_blob_const(r->in.req->req2.password.string,
4306 r->in.req->req2.password.length);
4307 res = samdb_check_password(&password,
4308 pwInfo.password_properties,
4309 pwInfo.min_password_length);
4310 (*r->out.rep)->ctr2.status = res;
4311 break;
4312 case NetValidatePasswordReset:
4313 password = data_blob_const(r->in.req->req3.password.string,
4314 r->in.req->req3.password.length);
4315 res = samdb_check_password(&password,
4316 pwInfo.password_properties,
4317 pwInfo.min_password_length);
4318 (*r->out.rep)->ctr3.status = res;
4319 break;
4320 default:
4321 return NT_STATUS_INVALID_INFO_CLASS;
4322 break;
4325 return NT_STATUS_OK;
4329 /* include the generated boilerplate */
4330 #include "librpc/gen_ndr/ndr_samr_s.c"