dsdb: Rework samdb_result_acct_flags to use either userAccountControl or msDS-User...
[Samba.git] / source4 / rpc_server / samr / dcesrv_samr.c
blob6877b903b222b447b82ddade7cb93c3fc0937317
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 #define CHECK_FOR_MULTIPLES(value, flag, poss_flags) \
113 do { \
114 if ((value & flag) && ((value & flag) != (value & (poss_flags)))) { \
115 return NT_STATUS_INVALID_PARAMETER; \
117 } while (0) \
119 /* Set account flags, discarding flags that cannot be set with SAMR */
120 #define SET_AFLAGS(msg, field, attr) do { \
121 struct ldb_message_element *set_el; \
122 if ((r->in.info->field & (ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST)) == 0) { \
123 return NT_STATUS_INVALID_PARAMETER; \
125 CHECK_FOR_MULTIPLES(r->in.info->field, ACB_NORMAL, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
126 CHECK_FOR_MULTIPLES(r->in.info->field, ACB_DOMTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
127 CHECK_FOR_MULTIPLES(r->in.info->field, ACB_WSTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
128 CHECK_FOR_MULTIPLES(r->in.info->field, ACB_SVRTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
129 if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, (r->in.info->field & ~(ACB_AUTOLOCK|ACB_PW_EXPIRED))) != 0) { \
130 return NT_STATUS_NO_MEMORY; \
132 set_el = ldb_msg_find_element(msg, attr); \
133 set_el->flags = LDB_FLAG_MOD_REPLACE; \
134 } while (0)
136 #define SET_LHOURS(msg, field, attr) do { \
137 struct ldb_message_element *set_el; \
138 if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
139 return NT_STATUS_NO_MEMORY; \
141 set_el = ldb_msg_find_element(msg, attr); \
142 set_el->flags = LDB_FLAG_MOD_REPLACE; \
143 } while (0)
145 #define SET_PARAMETERS(msg, field, attr) do { \
146 struct ldb_message_element *set_el; \
147 if (r->in.info->field.length != 0) { \
148 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
149 return NT_STATUS_NO_MEMORY; \
151 set_el = ldb_msg_find_element(msg, attr); \
152 set_el->flags = LDB_FLAG_MOD_REPLACE; \
154 } while (0)
159 samr_Connect
161 create a connection to the SAM database
163 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
164 struct samr_Connect *r)
166 struct samr_connect_state *c_state;
167 struct dcesrv_handle *handle;
169 ZERO_STRUCTP(r->out.connect_handle);
171 c_state = talloc(mem_ctx, struct samr_connect_state);
172 if (!c_state) {
173 return NT_STATUS_NO_MEMORY;
176 /* make sure the sam database is accessible */
177 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);
178 if (c_state->sam_ctx == NULL) {
179 talloc_free(c_state);
180 return NT_STATUS_INVALID_SYSTEM_SERVICE;
184 handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
185 if (!handle) {
186 talloc_free(c_state);
187 return NT_STATUS_NO_MEMORY;
190 handle->data = talloc_steal(handle, c_state);
192 c_state->access_mask = r->in.access_mask;
193 *r->out.connect_handle = handle->wire_handle;
195 return NT_STATUS_OK;
200 samr_Close
202 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
203 struct samr_Close *r)
205 struct dcesrv_handle *h;
207 *r->out.handle = *r->in.handle;
209 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
211 talloc_free(h);
213 ZERO_STRUCTP(r->out.handle);
215 return NT_STATUS_OK;
220 samr_SetSecurity
222 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
223 struct samr_SetSecurity *r)
225 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
230 samr_QuerySecurity
232 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
233 struct samr_QuerySecurity *r)
235 struct dcesrv_handle *h;
236 struct sec_desc_buf *sd;
238 *r->out.sdbuf = NULL;
240 DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
242 sd = talloc(mem_ctx, struct sec_desc_buf);
243 if (sd == NULL) {
244 return NT_STATUS_NO_MEMORY;
247 sd->sd = samdb_default_security_descriptor(mem_ctx);
249 *r->out.sdbuf = sd;
251 return NT_STATUS_OK;
256 samr_Shutdown
258 we refuse this operation completely. If a admin wants to shutdown samr
259 in Samba then they should use the samba admin tools to disable the samr pipe
261 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
262 struct samr_Shutdown *r)
264 return NT_STATUS_ACCESS_DENIED;
269 samr_LookupDomain
271 this maps from a domain name to a SID
273 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
274 struct samr_LookupDomain *r)
276 struct samr_connect_state *c_state;
277 struct dcesrv_handle *h;
278 struct dom_sid *sid;
279 const char * const dom_attrs[] = { "objectSid", NULL};
280 struct ldb_message **dom_msgs;
281 int ret;
283 *r->out.sid = NULL;
285 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
287 c_state = h->data;
289 if (r->in.domain_name->string == NULL) {
290 return NT_STATUS_INVALID_PARAMETER;
293 if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
294 ret = gendb_search(c_state->sam_ctx,
295 mem_ctx, NULL, &dom_msgs, dom_attrs,
296 "(objectClass=builtinDomain)");
297 } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
298 ret = gendb_search_dn(c_state->sam_ctx,
299 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
300 &dom_msgs, dom_attrs);
301 } else {
302 return NT_STATUS_NO_SUCH_DOMAIN;
304 if (ret != 1) {
305 return NT_STATUS_NO_SUCH_DOMAIN;
308 sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
309 "objectSid");
311 if (sid == NULL) {
312 return NT_STATUS_NO_SUCH_DOMAIN;
315 *r->out.sid = sid;
317 return NT_STATUS_OK;
322 samr_EnumDomains
324 list the domains in the SAM
326 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
327 struct samr_EnumDomains *r)
329 struct samr_connect_state *c_state;
330 struct dcesrv_handle *h;
331 struct samr_SamArray *array;
332 uint32_t i, start_i;
334 *r->out.resume_handle = 0;
335 *r->out.sam = NULL;
336 *r->out.num_entries = 0;
338 DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
340 c_state = h->data;
342 *r->out.resume_handle = 2;
344 start_i = *r->in.resume_handle;
346 if (start_i >= 2) {
347 /* search past end of list is not an error for this call */
348 return NT_STATUS_OK;
351 array = talloc(mem_ctx, struct samr_SamArray);
352 if (array == NULL) {
353 return NT_STATUS_NO_MEMORY;
356 array->count = 0;
357 array->entries = NULL;
359 array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
360 if (array->entries == NULL) {
361 return NT_STATUS_NO_MEMORY;
364 for (i=0;i<2-start_i;i++) {
365 array->entries[i].idx = start_i + i;
366 if (i == 0) {
367 array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
368 } else {
369 array->entries[i].name.string = "BUILTIN";
373 *r->out.sam = array;
374 *r->out.num_entries = i;
375 array->count = *r->out.num_entries;
377 return NT_STATUS_OK;
382 samr_OpenDomain
384 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
385 struct samr_OpenDomain *r)
387 struct dcesrv_handle *h_conn, *h_domain;
388 struct samr_connect_state *c_state;
389 struct samr_domain_state *d_state;
390 const char * const dom_attrs[] = { "cn", NULL};
391 struct ldb_message **dom_msgs;
392 int ret;
394 ZERO_STRUCTP(r->out.domain_handle);
396 DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
398 c_state = h_conn->data;
400 if (r->in.sid == NULL) {
401 return NT_STATUS_INVALID_PARAMETER;
404 d_state = talloc(mem_ctx, struct samr_domain_state);
405 if (!d_state) {
406 return NT_STATUS_NO_MEMORY;
409 d_state->domain_sid = talloc_steal(d_state, r->in.sid);
411 if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
412 d_state->builtin = true;
413 d_state->domain_name = "BUILTIN";
414 } else {
415 d_state->builtin = false;
416 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
419 ret = gendb_search(c_state->sam_ctx,
420 mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
421 "(objectSid=%s)",
422 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
424 if (ret == 0) {
425 talloc_free(d_state);
426 return NT_STATUS_NO_SUCH_DOMAIN;
427 } else if (ret > 1) {
428 talloc_free(d_state);
429 return NT_STATUS_INTERNAL_DB_CORRUPTION;
430 } else if (ret == -1) {
431 talloc_free(d_state);
432 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
433 return NT_STATUS_INTERNAL_DB_CORRUPTION;
436 d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
437 d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
438 d_state->connect_state = talloc_reference(d_state, c_state);
439 d_state->sam_ctx = c_state->sam_ctx;
440 d_state->access_mask = r->in.access_mask;
442 d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
444 h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
445 if (!h_domain) {
446 talloc_free(d_state);
447 return NT_STATUS_NO_MEMORY;
450 h_domain->data = talloc_steal(h_domain, d_state);
452 *r->out.domain_handle = h_domain->wire_handle;
454 return NT_STATUS_OK;
458 return DomInfo1
460 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
461 TALLOC_CTX *mem_ctx,
462 struct ldb_message **dom_msgs,
463 struct samr_DomInfo1 *info)
465 info->min_password_length =
466 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
467 info->password_history_length =
468 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
469 info->password_properties =
470 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
471 info->max_password_age =
472 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
473 info->min_password_age =
474 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
476 return NT_STATUS_OK;
480 return DomInfo2
482 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
483 TALLOC_CTX *mem_ctx,
484 struct ldb_message **dom_msgs,
485 struct samr_DomGeneralInformation *info)
487 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
488 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
489 "domainReplica",
490 "");
492 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
493 0x8000000000000000LL);
495 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
496 "oEMInformation",
497 "");
498 info->domain_name.string = state->domain_name;
500 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
502 switch (state->role) {
503 case ROLE_ACTIVE_DIRECTORY_DC:
504 /* This pulls the NetBIOS name from the
505 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
506 string */
507 if (samdb_is_pdc(state->sam_ctx)) {
508 info->role = SAMR_ROLE_DOMAIN_PDC;
509 } else {
510 info->role = SAMR_ROLE_DOMAIN_BDC;
512 break;
513 case ROLE_DOMAIN_PDC:
514 case ROLE_DOMAIN_BDC:
515 return NT_STATUS_INTERNAL_ERROR;
516 case ROLE_DOMAIN_MEMBER:
517 info->role = SAMR_ROLE_DOMAIN_MEMBER;
518 break;
519 case ROLE_STANDALONE:
520 info->role = SAMR_ROLE_STANDALONE;
521 break;
524 info->num_users = samdb_search_count(state->sam_ctx, mem_ctx,
525 state->domain_dn,
526 "(objectClass=user)");
527 info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx,
528 state->domain_dn,
529 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
530 GTYPE_SECURITY_UNIVERSAL_GROUP,
531 GTYPE_SECURITY_GLOBAL_GROUP);
532 info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx,
533 state->domain_dn,
534 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
535 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
536 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
538 return NT_STATUS_OK;
542 return DomInfo3
544 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
545 TALLOC_CTX *mem_ctx,
546 struct ldb_message **dom_msgs,
547 struct samr_DomInfo3 *info)
549 info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
550 0x8000000000000000LL);
552 return NT_STATUS_OK;
556 return DomInfo4
558 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
559 TALLOC_CTX *mem_ctx,
560 struct ldb_message **dom_msgs,
561 struct samr_DomOEMInformation *info)
563 info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
564 "oEMInformation",
565 "");
567 return NT_STATUS_OK;
571 return DomInfo5
573 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
574 TALLOC_CTX *mem_ctx,
575 struct ldb_message **dom_msgs,
576 struct samr_DomInfo5 *info)
578 info->domain_name.string = state->domain_name;
580 return NT_STATUS_OK;
584 return DomInfo6
586 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
587 TALLOC_CTX *mem_ctx,
588 struct ldb_message **dom_msgs,
589 struct samr_DomInfo6 *info)
591 /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
592 info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
593 "domainReplica",
594 "");
596 return NT_STATUS_OK;
600 return DomInfo7
602 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
603 TALLOC_CTX *mem_ctx,
604 struct ldb_message **dom_msgs,
605 struct samr_DomInfo7 *info)
608 switch (state->role) {
609 case ROLE_ACTIVE_DIRECTORY_DC:
610 /* This pulls the NetBIOS name from the
611 cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
612 string */
613 if (samdb_is_pdc(state->sam_ctx)) {
614 info->role = SAMR_ROLE_DOMAIN_PDC;
615 } else {
616 info->role = SAMR_ROLE_DOMAIN_BDC;
618 break;
619 case ROLE_DOMAIN_PDC:
620 info->role = SAMR_ROLE_DOMAIN_PDC;
621 break;
622 case ROLE_DOMAIN_MEMBER:
623 info->role = SAMR_ROLE_DOMAIN_MEMBER;
624 break;
625 case ROLE_STANDALONE:
626 info->role = SAMR_ROLE_STANDALONE;
627 break;
630 return NT_STATUS_OK;
634 return DomInfo8
636 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
637 TALLOC_CTX *mem_ctx,
638 struct ldb_message **dom_msgs,
639 struct samr_DomInfo8 *info)
641 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
642 time(NULL));
644 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
645 0x0LL);
647 return NT_STATUS_OK;
651 return DomInfo9
653 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
654 TALLOC_CTX *mem_ctx,
655 struct ldb_message **dom_msgs,
656 struct samr_DomInfo9 *info)
658 info->domain_server_state = DOMAIN_SERVER_ENABLED;
660 return NT_STATUS_OK;
664 return DomInfo11
666 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
667 TALLOC_CTX *mem_ctx,
668 struct ldb_message **dom_msgs,
669 struct samr_DomGeneralInformation2 *info)
671 NTSTATUS status;
672 status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
673 if (!NT_STATUS_IS_OK(status)) {
674 return status;
677 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
678 -18000000000LL);
679 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
680 -18000000000LL);
681 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
683 return NT_STATUS_OK;
687 return DomInfo12
689 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
690 TALLOC_CTX *mem_ctx,
691 struct ldb_message **dom_msgs,
692 struct samr_DomInfo12 *info)
694 info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
695 -18000000000LL);
696 info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
697 -18000000000LL);
698 info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
700 return NT_STATUS_OK;
704 return DomInfo13
706 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
707 TALLOC_CTX *mem_ctx,
708 struct ldb_message **dom_msgs,
709 struct samr_DomInfo13 *info)
711 info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
712 time(NULL));
714 info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
715 0x0LL);
717 info->modified_count_at_last_promotion = 0;
719 return NT_STATUS_OK;
723 samr_QueryDomainInfo
725 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
726 TALLOC_CTX *mem_ctx,
727 struct samr_QueryDomainInfo *r)
729 struct dcesrv_handle *h;
730 struct samr_domain_state *d_state;
731 union samr_DomainInfo *info;
733 struct ldb_message **dom_msgs;
734 const char * const *attrs = NULL;
736 *r->out.info = NULL;
738 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
740 d_state = h->data;
742 switch (r->in.level) {
743 case 1:
745 static const char * const attrs2[] = { "minPwdLength",
746 "pwdHistoryLength",
747 "pwdProperties",
748 "maxPwdAge",
749 "minPwdAge",
750 NULL };
751 attrs = attrs2;
752 break;
754 case 2:
756 static const char * const attrs2[] = {"forceLogoff",
757 "oEMInformation",
758 "modifiedCount",
759 "domainReplica",
760 NULL};
761 attrs = attrs2;
762 break;
764 case 3:
766 static const char * const attrs2[] = {"forceLogoff",
767 NULL};
768 attrs = attrs2;
769 break;
771 case 4:
773 static const char * const attrs2[] = {"oEMInformation",
774 NULL};
775 attrs = attrs2;
776 break;
778 case 5:
780 attrs = NULL;
781 break;
783 case 6:
785 static const char * const attrs2[] = { "domainReplica",
786 NULL };
787 attrs = attrs2;
788 break;
790 case 7:
792 attrs = NULL;
793 break;
795 case 8:
797 static const char * const attrs2[] = { "modifiedCount",
798 "creationTime",
799 NULL };
800 attrs = attrs2;
801 break;
803 case 9:
805 attrs = NULL;
806 break;
808 case 11:
810 static const char * const attrs2[] = { "oEMInformation",
811 "forceLogoff",
812 "modifiedCount",
813 "lockoutDuration",
814 "lockOutObservationWindow",
815 "lockoutThreshold",
816 NULL};
817 attrs = attrs2;
818 break;
820 case 12:
822 static const char * const attrs2[] = { "lockoutDuration",
823 "lockOutObservationWindow",
824 "lockoutThreshold",
825 NULL};
826 attrs = attrs2;
827 break;
829 case 13:
831 static const char * const attrs2[] = { "modifiedCount",
832 "creationTime",
833 NULL };
834 attrs = attrs2;
835 break;
837 default:
839 return NT_STATUS_INVALID_INFO_CLASS;
843 /* some levels don't need a search */
844 if (attrs) {
845 int ret;
846 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
847 d_state->domain_dn, &dom_msgs, attrs);
848 if (ret == 0) {
849 return NT_STATUS_NO_SUCH_DOMAIN;
851 if (ret != 1) {
852 return NT_STATUS_INTERNAL_DB_CORRUPTION;
856 /* allocate the info structure */
857 info = talloc_zero(mem_ctx, union samr_DomainInfo);
858 if (info == NULL) {
859 return NT_STATUS_NO_MEMORY;
862 *r->out.info = info;
864 switch (r->in.level) {
865 case 1:
866 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
867 &info->info1);
868 case 2:
869 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
870 &info->general);
871 case 3:
872 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
873 &info->info3);
874 case 4:
875 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
876 &info->oem);
877 case 5:
878 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
879 &info->info5);
880 case 6:
881 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
882 &info->info6);
883 case 7:
884 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
885 &info->info7);
886 case 8:
887 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
888 &info->info8);
889 case 9:
890 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
891 &info->info9);
892 case 11:
893 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
894 &info->general2);
895 case 12:
896 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
897 &info->info12);
898 case 13:
899 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
900 &info->info13);
901 default:
902 return NT_STATUS_INVALID_INFO_CLASS;
908 samr_SetDomainInfo
910 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
911 struct samr_SetDomainInfo *r)
913 struct dcesrv_handle *h;
914 struct samr_domain_state *d_state;
915 struct ldb_message *msg;
916 int ret;
917 struct ldb_context *sam_ctx;
919 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
921 d_state = h->data;
922 sam_ctx = d_state->sam_ctx;
924 msg = ldb_msg_new(mem_ctx);
925 if (msg == NULL) {
926 return NT_STATUS_NO_MEMORY;
929 msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
930 if (!msg->dn) {
931 return NT_STATUS_NO_MEMORY;
934 switch (r->in.level) {
935 case 1:
936 SET_UINT (msg, info1.min_password_length, "minPwdLength");
937 SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
938 SET_UINT (msg, info1.password_properties, "pwdProperties");
939 SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
940 SET_INT64 (msg, info1.min_password_age, "minPwdAge");
941 break;
942 case 3:
943 SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
944 break;
945 case 4:
946 SET_STRING(msg, oem.oem_information, "oEMInformation");
947 break;
949 case 6:
950 case 7:
951 case 9:
952 /* No op, we don't know where to set these */
953 return NT_STATUS_OK;
955 case 12:
957 * It is not possible to set lockout_duration < lockout_window.
958 * (The test is the other way around since the negative numbers
959 * are stored...)
961 * TODO:
962 * This check should be moved to the backend, i.e. to some
963 * ldb module under dsdb/samdb/ldb_modules/ .
965 * This constraint is documented here for the samr rpc service:
966 * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
967 * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
969 * And here for the ldap backend:
970 * MS-ADTS 3.1.1.5.3.2 Constraints
971 * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
973 if (r->in.info->info12.lockout_duration >
974 r->in.info->info12.lockout_window)
976 return NT_STATUS_INVALID_PARAMETER;
978 SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
979 SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
980 SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
981 break;
983 default:
984 /* many info classes are not valid for SetDomainInfo */
985 return NT_STATUS_INVALID_INFO_CLASS;
988 /* modify the samdb record */
989 ret = ldb_modify(sam_ctx, msg);
990 if (ret != LDB_SUCCESS) {
991 DEBUG(1,("Failed to modify record %s: %s\n",
992 ldb_dn_get_linearized(d_state->domain_dn),
993 ldb_errstring(sam_ctx)));
994 return dsdb_ldb_err_to_ntstatus(ret);
997 return NT_STATUS_OK;
1001 samr_CreateDomainGroup
1003 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1004 struct samr_CreateDomainGroup *r)
1006 NTSTATUS status;
1007 struct samr_domain_state *d_state;
1008 struct samr_account_state *a_state;
1009 struct dcesrv_handle *h;
1010 const char *groupname;
1011 struct dom_sid *group_sid;
1012 struct ldb_dn *group_dn;
1013 struct dcesrv_handle *g_handle;
1015 ZERO_STRUCTP(r->out.group_handle);
1016 *r->out.rid = 0;
1018 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1020 d_state = h->data;
1022 if (d_state->builtin) {
1023 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
1024 return NT_STATUS_ACCESS_DENIED;
1027 groupname = r->in.name->string;
1029 if (groupname == NULL) {
1030 return NT_STATUS_INVALID_PARAMETER;
1033 status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1034 if (!NT_STATUS_IS_OK(status)) {
1035 return status;
1038 a_state = talloc(mem_ctx, struct samr_account_state);
1039 if (!a_state) {
1040 return NT_STATUS_NO_MEMORY;
1042 a_state->sam_ctx = d_state->sam_ctx;
1043 a_state->access_mask = r->in.access_mask;
1044 a_state->domain_state = talloc_reference(a_state, d_state);
1045 a_state->account_dn = talloc_steal(a_state, group_dn);
1047 a_state->account_name = talloc_steal(a_state, groupname);
1049 /* create the policy handle */
1050 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1051 if (!g_handle) {
1052 return NT_STATUS_NO_MEMORY;
1055 g_handle->data = talloc_steal(g_handle, a_state);
1057 *r->out.group_handle = g_handle->wire_handle;
1058 *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1060 return NT_STATUS_OK;
1065 comparison function for sorting SamEntry array
1067 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1069 return e1->idx - e2->idx;
1073 samr_EnumDomainGroups
1075 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1076 struct samr_EnumDomainGroups *r)
1078 struct dcesrv_handle *h;
1079 struct samr_domain_state *d_state;
1080 struct ldb_message **res;
1081 int i, ldb_cnt;
1082 uint32_t first, count;
1083 struct samr_SamEntry *entries;
1084 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1085 struct samr_SamArray *sam;
1087 *r->out.resume_handle = 0;
1088 *r->out.sam = NULL;
1089 *r->out.num_entries = 0;
1091 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1093 d_state = h->data;
1095 /* search for all domain groups in this domain. This could possibly be
1096 cached and resumed based on resume_key */
1097 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1098 d_state->domain_dn, &res, attrs,
1099 d_state->domain_sid,
1100 "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1101 GTYPE_SECURITY_UNIVERSAL_GROUP,
1102 GTYPE_SECURITY_GLOBAL_GROUP);
1103 if (ldb_cnt < 0) {
1104 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1107 /* convert to SamEntry format */
1108 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1109 if (!entries) {
1110 return NT_STATUS_NO_MEMORY;
1113 count = 0;
1115 for (i=0;i<ldb_cnt;i++) {
1116 struct dom_sid *group_sid;
1118 group_sid = samdb_result_dom_sid(mem_ctx, res[i],
1119 "objectSid");
1120 if (group_sid == NULL) {
1121 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1124 entries[count].idx =
1125 group_sid->sub_auths[group_sid->num_auths-1];
1126 entries[count].name.string =
1127 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1128 count += 1;
1131 /* sort the results by rid */
1132 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1134 /* find the first entry to return */
1135 for (first=0;
1136 first<count && entries[first].idx <= *r->in.resume_handle;
1137 first++) ;
1139 /* return the rest, limit by max_size. Note that we
1140 use the w2k3 element size value of 54 */
1141 *r->out.num_entries = count - first;
1142 *r->out.num_entries = MIN(*r->out.num_entries,
1143 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1145 sam = talloc(mem_ctx, struct samr_SamArray);
1146 if (!sam) {
1147 return NT_STATUS_NO_MEMORY;
1150 sam->entries = entries+first;
1151 sam->count = *r->out.num_entries;
1153 *r->out.sam = sam;
1155 if (first == count) {
1156 return NT_STATUS_OK;
1159 if (*r->out.num_entries < count - first) {
1160 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1161 return STATUS_MORE_ENTRIES;
1164 return NT_STATUS_OK;
1169 samr_CreateUser2
1171 This call uses transactions to ensure we don't get a new conflicting
1172 user while we are processing this, and to ensure the user either
1173 completly exists, or does not.
1175 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1176 struct samr_CreateUser2 *r)
1178 NTSTATUS status;
1179 struct samr_domain_state *d_state;
1180 struct samr_account_state *a_state;
1181 struct dcesrv_handle *h;
1182 struct ldb_dn *dn;
1183 struct dom_sid *sid;
1184 struct dcesrv_handle *u_handle;
1185 const char *account_name;
1187 ZERO_STRUCTP(r->out.user_handle);
1188 *r->out.access_granted = 0;
1189 *r->out.rid = 0;
1191 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1193 d_state = h->data;
1195 if (d_state->builtin) {
1196 DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
1197 return NT_STATUS_ACCESS_DENIED;
1198 } else if (r->in.acct_flags == ACB_DOMTRUST) {
1199 /* Domain trust accounts must be created by the LSA calls */
1200 return NT_STATUS_ACCESS_DENIED;
1202 account_name = r->in.account_name->string;
1204 if (account_name == NULL) {
1205 return NT_STATUS_INVALID_PARAMETER;
1208 status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1209 &sid, &dn);
1210 if (!NT_STATUS_IS_OK(status)) {
1211 return status;
1213 a_state = talloc(mem_ctx, struct samr_account_state);
1214 if (!a_state) {
1215 return NT_STATUS_NO_MEMORY;
1217 a_state->sam_ctx = d_state->sam_ctx;
1218 a_state->access_mask = r->in.access_mask;
1219 a_state->domain_state = talloc_reference(a_state, d_state);
1220 a_state->account_dn = talloc_steal(a_state, dn);
1222 a_state->account_name = talloc_steal(a_state, account_name);
1223 if (!a_state->account_name) {
1224 return NT_STATUS_NO_MEMORY;
1227 /* create the policy handle */
1228 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
1229 if (!u_handle) {
1230 return NT_STATUS_NO_MEMORY;
1233 u_handle->data = talloc_steal(u_handle, a_state);
1235 *r->out.user_handle = u_handle->wire_handle;
1236 *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1238 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1240 return NT_STATUS_OK;
1245 samr_CreateUser
1247 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1248 struct samr_CreateUser *r)
1250 struct samr_CreateUser2 r2;
1251 uint32_t access_granted = 0;
1254 /* a simple wrapper around samr_CreateUser2 works nicely */
1255 r2.in.domain_handle = r->in.domain_handle;
1256 r2.in.account_name = r->in.account_name;
1257 r2.in.acct_flags = ACB_NORMAL;
1258 r2.in.access_mask = r->in.access_mask;
1259 r2.out.user_handle = r->out.user_handle;
1260 r2.out.access_granted = &access_granted;
1261 r2.out.rid = r->out.rid;
1263 return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1267 samr_EnumDomainUsers
1269 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1270 struct samr_EnumDomainUsers *r)
1272 struct dcesrv_handle *h;
1273 struct samr_domain_state *d_state;
1274 struct ldb_message **res;
1275 int i, ldb_cnt;
1276 uint32_t first, count;
1277 struct samr_SamEntry *entries;
1278 const char * const attrs[] = { "objectSid", "sAMAccountName",
1279 "userAccountControl", NULL };
1280 struct samr_SamArray *sam;
1282 *r->out.resume_handle = 0;
1283 *r->out.sam = NULL;
1284 *r->out.num_entries = 0;
1286 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1288 d_state = h->data;
1290 /* search for all domain users in this domain. This could possibly be
1291 cached and resumed on resume_key */
1292 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1293 d_state->domain_dn,
1294 &res, attrs,
1295 d_state->domain_sid,
1296 "(objectClass=user)");
1297 if (ldb_cnt < 0) {
1298 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1301 /* convert to SamEntry format */
1302 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1303 if (!entries) {
1304 return NT_STATUS_NO_MEMORY;
1307 count = 0;
1309 for (i=0;i<ldb_cnt;i++) {
1310 /* Check if a mask has been requested */
1311 if (r->in.acct_flags
1312 && ((samdb_result_acct_flags(res[i], NULL) & r->in.acct_flags) == 0)) {
1313 continue;
1315 entries[count].idx = samdb_result_rid_from_sid(mem_ctx, res[i],
1316 "objectSid", 0);
1317 entries[count].name.string = ldb_msg_find_attr_as_string(res[i],
1318 "sAMAccountName", "");
1319 count += 1;
1322 /* sort the results by rid */
1323 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1325 /* find the first entry to return */
1326 for (first=0;
1327 first<count && entries[first].idx <= *r->in.resume_handle;
1328 first++) ;
1330 /* return the rest, limit by max_size. Note that we
1331 use the w2k3 element size value of 54 */
1332 *r->out.num_entries = count - first;
1333 *r->out.num_entries = MIN(*r->out.num_entries,
1334 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1336 sam = talloc(mem_ctx, struct samr_SamArray);
1337 if (!sam) {
1338 return NT_STATUS_NO_MEMORY;
1341 sam->entries = entries+first;
1342 sam->count = *r->out.num_entries;
1344 *r->out.sam = sam;
1346 if (first == count) {
1347 return NT_STATUS_OK;
1350 if (*r->out.num_entries < count - first) {
1351 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1352 return STATUS_MORE_ENTRIES;
1355 return NT_STATUS_OK;
1360 samr_CreateDomAlias
1362 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1363 struct samr_CreateDomAlias *r)
1365 struct samr_domain_state *d_state;
1366 struct samr_account_state *a_state;
1367 struct dcesrv_handle *h;
1368 const char *alias_name;
1369 struct dom_sid *sid;
1370 struct dcesrv_handle *a_handle;
1371 struct ldb_dn *dn;
1372 NTSTATUS status;
1374 ZERO_STRUCTP(r->out.alias_handle);
1375 *r->out.rid = 0;
1377 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1379 d_state = h->data;
1381 if (d_state->builtin) {
1382 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1383 return NT_STATUS_ACCESS_DENIED;
1386 alias_name = r->in.alias_name->string;
1388 if (alias_name == NULL) {
1389 return NT_STATUS_INVALID_PARAMETER;
1392 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1393 if (!NT_STATUS_IS_OK(status)) {
1394 return status;
1397 a_state = talloc(mem_ctx, struct samr_account_state);
1398 if (!a_state) {
1399 return NT_STATUS_NO_MEMORY;
1402 a_state->sam_ctx = d_state->sam_ctx;
1403 a_state->access_mask = r->in.access_mask;
1404 a_state->domain_state = talloc_reference(a_state, d_state);
1405 a_state->account_dn = talloc_steal(a_state, dn);
1407 a_state->account_name = talloc_steal(a_state, alias_name);
1409 /* create the policy handle */
1410 a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1411 if (a_handle == NULL)
1412 return NT_STATUS_NO_MEMORY;
1414 a_handle->data = talloc_steal(a_handle, a_state);
1416 *r->out.alias_handle = a_handle->wire_handle;
1418 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1420 return NT_STATUS_OK;
1425 samr_EnumDomainAliases
1427 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1428 struct samr_EnumDomainAliases *r)
1430 struct dcesrv_handle *h;
1431 struct samr_domain_state *d_state;
1432 struct ldb_message **res;
1433 int i, ldb_cnt;
1434 uint32_t first, count;
1435 struct samr_SamEntry *entries;
1436 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1437 struct samr_SamArray *sam;
1439 *r->out.resume_handle = 0;
1440 *r->out.sam = NULL;
1441 *r->out.num_entries = 0;
1443 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1445 d_state = h->data;
1447 /* search for all domain aliases in this domain. This could possibly be
1448 cached and resumed based on resume_key */
1449 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1450 &res, attrs,
1451 d_state->domain_sid,
1452 "(&(|(grouptype=%d)(grouptype=%d)))"
1453 "(objectclass=group))",
1454 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1455 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1456 if (ldb_cnt < 0) {
1457 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1460 /* convert to SamEntry format */
1461 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1462 if (!entries) {
1463 return NT_STATUS_NO_MEMORY;
1466 count = 0;
1468 for (i=0;i<ldb_cnt;i++) {
1469 struct dom_sid *alias_sid;
1471 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1472 "objectSid");
1474 if (alias_sid == NULL) {
1475 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1478 entries[count].idx =
1479 alias_sid->sub_auths[alias_sid->num_auths-1];
1480 entries[count].name.string =
1481 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1482 count += 1;
1485 /* sort the results by rid */
1486 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1488 /* find the first entry to return */
1489 for (first=0;
1490 first<count && entries[first].idx <= *r->in.resume_handle;
1491 first++) ;
1493 /* return the rest, limit by max_size. Note that we
1494 use the w2k3 element size value of 54 */
1495 *r->out.num_entries = count - first;
1496 *r->out.num_entries = MIN(*r->out.num_entries,
1497 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1499 sam = talloc(mem_ctx, struct samr_SamArray);
1500 if (!sam) {
1501 return NT_STATUS_NO_MEMORY;
1504 sam->entries = entries+first;
1505 sam->count = *r->out.num_entries;
1507 *r->out.sam = sam;
1509 if (first == count) {
1510 return NT_STATUS_OK;
1513 if (*r->out.num_entries < count - first) {
1514 *r->out.resume_handle =
1515 entries[first+*r->out.num_entries-1].idx;
1516 return STATUS_MORE_ENTRIES;
1519 return NT_STATUS_OK;
1524 samr_GetAliasMembership
1526 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1527 struct samr_GetAliasMembership *r)
1529 struct dcesrv_handle *h;
1530 struct samr_domain_state *d_state;
1531 const char *filter;
1532 const char * const attrs[] = { "objectSid", NULL };
1533 struct ldb_message **res;
1534 uint32_t i;
1535 int count = 0;
1537 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1539 d_state = h->data;
1541 filter = talloc_asprintf(mem_ctx,
1542 "(&(|(grouptype=%d)(grouptype=%d))"
1543 "(objectclass=group)(|",
1544 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1545 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1546 if (filter == NULL) {
1547 return NT_STATUS_NO_MEMORY;
1550 for (i=0; i<r->in.sids->num_sids; i++) {
1551 const char *memberdn;
1553 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1554 "distinguishedName",
1555 "(objectSid=%s)",
1556 ldap_encode_ndr_dom_sid(mem_ctx,
1557 r->in.sids->sids[i].sid));
1558 if (memberdn == NULL) {
1559 continue;
1562 filter = talloc_asprintf(mem_ctx, "%s(member=%s)", filter,
1563 memberdn);
1564 if (filter == NULL) {
1565 return NT_STATUS_NO_MEMORY;
1569 /* Find out if we had at least one valid member SID passed - otherwise
1570 * just skip the search. */
1571 if (strstr(filter, "member") != NULL) {
1572 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1573 &res, attrs, d_state->domain_sid,
1574 "%s))", filter);
1575 if (count < 0) {
1576 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1580 r->out.rids->count = 0;
1581 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1582 if (r->out.rids->ids == NULL)
1583 return NT_STATUS_NO_MEMORY;
1585 for (i=0; i<count; i++) {
1586 struct dom_sid *alias_sid;
1588 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1589 if (alias_sid == NULL) {
1590 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1593 r->out.rids->ids[r->out.rids->count] =
1594 alias_sid->sub_auths[alias_sid->num_auths-1];
1595 r->out.rids->count += 1;
1598 return NT_STATUS_OK;
1603 samr_LookupNames
1605 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1606 struct samr_LookupNames *r)
1608 struct dcesrv_handle *h;
1609 struct samr_domain_state *d_state;
1610 uint32_t i, num_mapped;
1611 NTSTATUS status = NT_STATUS_OK;
1612 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1613 int count;
1615 ZERO_STRUCTP(r->out.rids);
1616 ZERO_STRUCTP(r->out.types);
1618 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1620 d_state = h->data;
1622 if (r->in.num_names == 0) {
1623 return NT_STATUS_OK;
1626 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1627 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1628 if (!r->out.rids->ids || !r->out.types->ids) {
1629 return NT_STATUS_NO_MEMORY;
1631 r->out.rids->count = r->in.num_names;
1632 r->out.types->count = r->in.num_names;
1634 num_mapped = 0;
1636 for (i=0;i<r->in.num_names;i++) {
1637 struct ldb_message **res;
1638 struct dom_sid *sid;
1639 uint32_t atype, rtype;
1641 r->out.rids->ids[i] = 0;
1642 r->out.types->ids[i] = SID_NAME_UNKNOWN;
1644 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1645 "sAMAccountName=%s",
1646 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
1647 if (count != 1) {
1648 status = STATUS_SOME_UNMAPPED;
1649 continue;
1652 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
1653 if (sid == NULL) {
1654 status = STATUS_SOME_UNMAPPED;
1655 continue;
1658 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
1659 if (atype == 0) {
1660 status = STATUS_SOME_UNMAPPED;
1661 continue;
1664 rtype = ds_atype_map(atype);
1666 if (rtype == SID_NAME_UNKNOWN) {
1667 status = STATUS_SOME_UNMAPPED;
1668 continue;
1671 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
1672 r->out.types->ids[i] = rtype;
1673 num_mapped++;
1676 if (num_mapped == 0) {
1677 return NT_STATUS_NONE_MAPPED;
1679 return status;
1684 samr_LookupRids
1686 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1687 struct samr_LookupRids *r)
1689 NTSTATUS status;
1690 struct dcesrv_handle *h;
1691 struct samr_domain_state *d_state;
1692 const char **names;
1693 struct lsa_String *lsa_names;
1694 enum lsa_SidType *ids;
1696 ZERO_STRUCTP(r->out.names);
1697 ZERO_STRUCTP(r->out.types);
1699 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1701 d_state = h->data;
1703 if (r->in.num_rids == 0)
1704 return NT_STATUS_OK;
1706 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
1707 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
1708 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
1710 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
1711 return NT_STATUS_NO_MEMORY;
1713 r->out.names->names = lsa_names;
1714 r->out.names->count = r->in.num_rids;
1716 r->out.types->ids = (uint32_t *) ids;
1717 r->out.types->count = r->in.num_rids;
1719 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
1720 r->in.num_rids, r->in.rids, names, ids);
1721 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
1722 uint32_t i;
1723 for (i = 0; i < r->in.num_rids; i++) {
1724 lsa_names[i].string = names[i];
1727 return status;
1732 samr_OpenGroup
1734 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1735 struct samr_OpenGroup *r)
1737 struct samr_domain_state *d_state;
1738 struct samr_account_state *a_state;
1739 struct dcesrv_handle *h;
1740 const char *groupname;
1741 struct dom_sid *sid;
1742 struct ldb_message **msgs;
1743 struct dcesrv_handle *g_handle;
1744 const char * const attrs[2] = { "sAMAccountName", NULL };
1745 int ret;
1747 ZERO_STRUCTP(r->out.group_handle);
1749 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1751 d_state = h->data;
1753 /* form the group SID */
1754 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1755 if (!sid) {
1756 return NT_STATUS_NO_MEMORY;
1759 /* search for the group record */
1760 ret = gendb_search(d_state->sam_ctx,
1761 mem_ctx, d_state->domain_dn, &msgs, attrs,
1762 "(&(objectSid=%s)(objectClass=group)"
1763 "(|(groupType=%d)(groupType=%d)))",
1764 ldap_encode_ndr_dom_sid(mem_ctx, sid),
1765 GTYPE_SECURITY_UNIVERSAL_GROUP,
1766 GTYPE_SECURITY_GLOBAL_GROUP);
1767 if (ret == 0) {
1768 return NT_STATUS_NO_SUCH_GROUP;
1770 if (ret != 1) {
1771 DEBUG(0,("Found %d records matching sid %s\n",
1772 ret, dom_sid_string(mem_ctx, sid)));
1773 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1776 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
1777 if (groupname == NULL) {
1778 DEBUG(0,("sAMAccountName field missing for sid %s\n",
1779 dom_sid_string(mem_ctx, sid)));
1780 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1783 a_state = talloc(mem_ctx, struct samr_account_state);
1784 if (!a_state) {
1785 return NT_STATUS_NO_MEMORY;
1787 a_state->sam_ctx = d_state->sam_ctx;
1788 a_state->access_mask = r->in.access_mask;
1789 a_state->domain_state = talloc_reference(a_state, d_state);
1790 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
1791 a_state->account_sid = talloc_steal(a_state, sid);
1792 a_state->account_name = talloc_strdup(a_state, groupname);
1793 if (!a_state->account_name) {
1794 return NT_STATUS_NO_MEMORY;
1797 /* create the policy handle */
1798 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1799 if (!g_handle) {
1800 return NT_STATUS_NO_MEMORY;
1803 g_handle->data = talloc_steal(g_handle, a_state);
1805 *r->out.group_handle = g_handle->wire_handle;
1807 return NT_STATUS_OK;
1811 samr_QueryGroupInfo
1813 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1814 struct samr_QueryGroupInfo *r)
1816 struct dcesrv_handle *h;
1817 struct samr_account_state *a_state;
1818 struct ldb_message *msg, **res;
1819 const char * const attrs[4] = { "sAMAccountName", "description",
1820 "numMembers", NULL };
1821 int ret;
1822 union samr_GroupInfo *info;
1824 *r->out.info = NULL;
1826 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1828 a_state = h->data;
1830 /* pull all the group attributes */
1831 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
1832 a_state->account_dn, &res, attrs);
1833 if (ret == 0) {
1834 return NT_STATUS_NO_SUCH_GROUP;
1836 if (ret != 1) {
1837 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1839 msg = res[0];
1841 /* allocate the info structure */
1842 info = talloc_zero(mem_ctx, union samr_GroupInfo);
1843 if (info == NULL) {
1844 return NT_STATUS_NO_MEMORY;
1847 /* Fill in the level */
1848 switch (r->in.level) {
1849 case GROUPINFOALL:
1850 QUERY_STRING(msg, all.name, "sAMAccountName");
1851 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1852 QUERY_UINT (msg, all.num_members, "numMembers")
1853 QUERY_STRING(msg, all.description, "description");
1854 break;
1855 case GROUPINFONAME:
1856 QUERY_STRING(msg, name, "sAMAccountName");
1857 break;
1858 case GROUPINFOATTRIBUTES:
1859 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1860 break;
1861 case GROUPINFODESCRIPTION:
1862 QUERY_STRING(msg, description, "description");
1863 break;
1864 case GROUPINFOALL2:
1865 QUERY_STRING(msg, all2.name, "sAMAccountName");
1866 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1867 QUERY_UINT (msg, all2.num_members, "numMembers")
1868 QUERY_STRING(msg, all2.description, "description");
1869 break;
1870 default:
1871 talloc_free(info);
1872 return NT_STATUS_INVALID_INFO_CLASS;
1875 *r->out.info = info;
1877 return NT_STATUS_OK;
1882 samr_SetGroupInfo
1884 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1885 struct samr_SetGroupInfo *r)
1887 struct dcesrv_handle *h;
1888 struct samr_account_state *g_state;
1889 struct ldb_message *msg;
1890 struct ldb_context *sam_ctx;
1891 int ret;
1893 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1895 g_state = h->data;
1896 sam_ctx = g_state->sam_ctx;
1898 msg = ldb_msg_new(mem_ctx);
1899 if (msg == NULL) {
1900 return NT_STATUS_NO_MEMORY;
1903 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
1904 if (!msg->dn) {
1905 return NT_STATUS_NO_MEMORY;
1908 switch (r->in.level) {
1909 case GROUPINFODESCRIPTION:
1910 SET_STRING(msg, description, "description");
1911 break;
1912 case GROUPINFONAME:
1913 /* On W2k3 this does not change the name, it changes the
1914 * sAMAccountName attribute */
1915 SET_STRING(msg, name, "sAMAccountName");
1916 break;
1917 case GROUPINFOATTRIBUTES:
1918 /* This does not do anything obviously visible in W2k3 LDAP */
1919 return NT_STATUS_OK;
1920 default:
1921 return NT_STATUS_INVALID_INFO_CLASS;
1924 /* modify the samdb record */
1925 ret = ldb_modify(g_state->sam_ctx, msg);
1926 if (ret != LDB_SUCCESS) {
1927 return dsdb_ldb_err_to_ntstatus(ret);
1930 return NT_STATUS_OK;
1935 samr_AddGroupMember
1937 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1938 struct samr_AddGroupMember *r)
1940 struct dcesrv_handle *h;
1941 struct samr_account_state *a_state;
1942 struct samr_domain_state *d_state;
1943 struct ldb_message *mod;
1944 struct dom_sid *membersid;
1945 const char *memberdn;
1946 struct ldb_result *res;
1947 const char * const attrs[] = { NULL };
1948 int ret;
1950 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1952 a_state = h->data;
1953 d_state = a_state->domain_state;
1955 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1956 if (membersid == NULL) {
1957 return NT_STATUS_NO_MEMORY;
1960 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
1961 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
1962 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
1963 "(objectSid=%s)",
1964 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
1966 if (ret != LDB_SUCCESS) {
1967 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1970 if (res->count == 0) {
1971 return NT_STATUS_NO_SUCH_USER;
1974 if (res->count > 1) {
1975 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1978 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
1980 if (memberdn == NULL)
1981 return NT_STATUS_NO_MEMORY;
1983 mod = ldb_msg_new(mem_ctx);
1984 if (mod == NULL) {
1985 return NT_STATUS_NO_MEMORY;
1988 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
1990 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
1991 memberdn);
1992 if (ret != LDB_SUCCESS) {
1993 return dsdb_ldb_err_to_ntstatus(ret);
1996 ret = ldb_modify(a_state->sam_ctx, mod);
1997 switch (ret) {
1998 case LDB_SUCCESS:
1999 return NT_STATUS_OK;
2000 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2001 return NT_STATUS_MEMBER_IN_GROUP;
2002 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2003 return NT_STATUS_ACCESS_DENIED;
2004 default:
2005 return dsdb_ldb_err_to_ntstatus(ret);
2011 samr_DeleteDomainGroup
2013 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2014 struct samr_DeleteDomainGroup *r)
2016 struct dcesrv_handle *h;
2017 struct samr_account_state *a_state;
2018 int ret;
2020 *r->out.group_handle = *r->in.group_handle;
2022 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2024 a_state = h->data;
2026 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2027 if (ret != LDB_SUCCESS) {
2028 return dsdb_ldb_err_to_ntstatus(ret);
2031 talloc_free(h);
2032 ZERO_STRUCTP(r->out.group_handle);
2034 return NT_STATUS_OK;
2039 samr_DeleteGroupMember
2041 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2042 struct samr_DeleteGroupMember *r)
2044 struct dcesrv_handle *h;
2045 struct samr_account_state *a_state;
2046 struct samr_domain_state *d_state;
2047 struct ldb_message *mod;
2048 struct dom_sid *membersid;
2049 const char *memberdn;
2050 struct ldb_result *res;
2051 const char * const attrs[] = { NULL };
2052 int ret;
2054 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2056 a_state = h->data;
2057 d_state = a_state->domain_state;
2059 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2060 if (membersid == NULL) {
2061 return NT_STATUS_NO_MEMORY;
2064 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2065 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2066 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2067 "(objectSid=%s)",
2068 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2070 if (ret != LDB_SUCCESS) {
2071 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2074 if (res->count == 0) {
2075 return NT_STATUS_NO_SUCH_USER;
2078 if (res->count > 1) {
2079 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2082 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2084 if (memberdn == NULL)
2085 return NT_STATUS_NO_MEMORY;
2087 mod = ldb_msg_new(mem_ctx);
2088 if (mod == NULL) {
2089 return NT_STATUS_NO_MEMORY;
2092 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2094 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2095 memberdn);
2096 if (ret != LDB_SUCCESS) {
2097 return NT_STATUS_NO_MEMORY;
2100 ret = ldb_modify(a_state->sam_ctx, mod);
2101 switch (ret) {
2102 case LDB_SUCCESS:
2103 return NT_STATUS_OK;
2104 case LDB_ERR_UNWILLING_TO_PERFORM:
2105 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2106 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2107 return NT_STATUS_ACCESS_DENIED;
2108 default:
2109 return dsdb_ldb_err_to_ntstatus(ret);
2115 samr_QueryGroupMember
2117 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2118 struct samr_QueryGroupMember *r)
2120 struct dcesrv_handle *h;
2121 struct samr_account_state *a_state;
2122 struct samr_domain_state *d_state;
2123 struct samr_RidAttrArray *array;
2124 unsigned int i, num_members;
2125 struct dom_sid *members;
2126 NTSTATUS status;
2128 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2130 a_state = h->data;
2131 d_state = a_state->domain_state;
2133 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2134 a_state->account_dn, &members,
2135 &num_members);
2136 if (!NT_STATUS_IS_OK(status)) {
2137 return status;
2140 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2141 if (array == NULL) {
2142 return NT_STATUS_NO_MEMORY;
2145 if (num_members == 0) {
2146 *r->out.rids = array;
2148 return NT_STATUS_OK;
2151 array->rids = talloc_array(array, uint32_t, num_members);
2152 if (array->rids == NULL) {
2153 return NT_STATUS_NO_MEMORY;
2156 array->attributes = talloc_array(array, uint32_t, num_members);
2157 if (array->attributes == NULL) {
2158 return NT_STATUS_NO_MEMORY;
2161 array->count = 0;
2162 for (i=0; i<num_members; i++) {
2163 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2164 continue;
2167 status = dom_sid_split_rid(NULL, &members[i], NULL,
2168 &array->rids[array->count]);
2169 if (!NT_STATUS_IS_OK(status)) {
2170 return status;
2173 array->attributes[array->count] = SE_GROUP_MANDATORY |
2174 SE_GROUP_ENABLED_BY_DEFAULT |
2175 SE_GROUP_ENABLED;
2176 array->count++;
2179 *r->out.rids = array;
2181 return NT_STATUS_OK;
2186 samr_SetMemberAttributesOfGroup
2188 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2189 struct samr_SetMemberAttributesOfGroup *r)
2191 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2196 samr_OpenAlias
2198 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2199 struct samr_OpenAlias *r)
2201 struct samr_domain_state *d_state;
2202 struct samr_account_state *a_state;
2203 struct dcesrv_handle *h;
2204 const char *alias_name;
2205 struct dom_sid *sid;
2206 struct ldb_message **msgs;
2207 struct dcesrv_handle *g_handle;
2208 const char * const attrs[2] = { "sAMAccountName", NULL };
2209 int ret;
2211 ZERO_STRUCTP(r->out.alias_handle);
2213 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2215 d_state = h->data;
2217 /* form the alias SID */
2218 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2219 if (sid == NULL)
2220 return NT_STATUS_NO_MEMORY;
2222 /* search for the group record */
2223 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2224 "(&(objectSid=%s)(objectclass=group)"
2225 "(|(grouptype=%d)(grouptype=%d)))",
2226 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2227 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2228 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2229 if (ret == 0) {
2230 return NT_STATUS_NO_SUCH_ALIAS;
2232 if (ret != 1) {
2233 DEBUG(0,("Found %d records matching sid %s\n",
2234 ret, dom_sid_string(mem_ctx, sid)));
2235 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2238 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2239 if (alias_name == NULL) {
2240 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2241 dom_sid_string(mem_ctx, sid)));
2242 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2245 a_state = talloc(mem_ctx, struct samr_account_state);
2246 if (!a_state) {
2247 return NT_STATUS_NO_MEMORY;
2249 a_state->sam_ctx = d_state->sam_ctx;
2250 a_state->access_mask = r->in.access_mask;
2251 a_state->domain_state = talloc_reference(a_state, d_state);
2252 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2253 a_state->account_sid = talloc_steal(a_state, sid);
2254 a_state->account_name = talloc_strdup(a_state, alias_name);
2255 if (!a_state->account_name) {
2256 return NT_STATUS_NO_MEMORY;
2259 /* create the policy handle */
2260 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2261 if (!g_handle) {
2262 return NT_STATUS_NO_MEMORY;
2265 g_handle->data = talloc_steal(g_handle, a_state);
2267 *r->out.alias_handle = g_handle->wire_handle;
2269 return NT_STATUS_OK;
2274 samr_QueryAliasInfo
2276 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2277 struct samr_QueryAliasInfo *r)
2279 struct dcesrv_handle *h;
2280 struct samr_account_state *a_state;
2281 struct ldb_message *msg, **res;
2282 const char * const attrs[4] = { "sAMAccountName", "description",
2283 "numMembers", NULL };
2284 int ret;
2285 union samr_AliasInfo *info;
2287 *r->out.info = NULL;
2289 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2291 a_state = h->data;
2293 /* pull all the alias attributes */
2294 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2295 a_state->account_dn, &res, attrs);
2296 if (ret == 0) {
2297 return NT_STATUS_NO_SUCH_ALIAS;
2299 if (ret != 1) {
2300 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2302 msg = res[0];
2304 /* allocate the info structure */
2305 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2306 if (info == NULL) {
2307 return NT_STATUS_NO_MEMORY;
2310 switch(r->in.level) {
2311 case ALIASINFOALL:
2312 QUERY_STRING(msg, all.name, "sAMAccountName");
2313 QUERY_UINT (msg, all.num_members, "numMembers");
2314 QUERY_STRING(msg, all.description, "description");
2315 break;
2316 case ALIASINFONAME:
2317 QUERY_STRING(msg, name, "sAMAccountName");
2318 break;
2319 case ALIASINFODESCRIPTION:
2320 QUERY_STRING(msg, description, "description");
2321 break;
2322 default:
2323 talloc_free(info);
2324 return NT_STATUS_INVALID_INFO_CLASS;
2327 *r->out.info = info;
2329 return NT_STATUS_OK;
2334 samr_SetAliasInfo
2336 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2337 struct samr_SetAliasInfo *r)
2339 struct dcesrv_handle *h;
2340 struct samr_account_state *a_state;
2341 struct ldb_message *msg;
2342 struct ldb_context *sam_ctx;
2343 int ret;
2345 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2347 a_state = h->data;
2348 sam_ctx = a_state->sam_ctx;
2350 msg = ldb_msg_new(mem_ctx);
2351 if (msg == NULL) {
2352 return NT_STATUS_NO_MEMORY;
2355 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2356 if (!msg->dn) {
2357 return NT_STATUS_NO_MEMORY;
2360 switch (r->in.level) {
2361 case ALIASINFODESCRIPTION:
2362 SET_STRING(msg, description, "description");
2363 break;
2364 case ALIASINFONAME:
2365 /* On W2k3 this does not change the name, it changes the
2366 * sAMAccountName attribute */
2367 SET_STRING(msg, name, "sAMAccountName");
2368 break;
2369 default:
2370 return NT_STATUS_INVALID_INFO_CLASS;
2373 /* modify the samdb record */
2374 ret = ldb_modify(a_state->sam_ctx, msg);
2375 if (ret != LDB_SUCCESS) {
2376 return dsdb_ldb_err_to_ntstatus(ret);
2379 return NT_STATUS_OK;
2384 samr_DeleteDomAlias
2386 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2387 struct samr_DeleteDomAlias *r)
2389 struct dcesrv_handle *h;
2390 struct samr_account_state *a_state;
2391 int ret;
2393 *r->out.alias_handle = *r->in.alias_handle;
2395 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2397 a_state = h->data;
2399 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2400 if (ret != LDB_SUCCESS) {
2401 return dsdb_ldb_err_to_ntstatus(ret);
2404 talloc_free(h);
2405 ZERO_STRUCTP(r->out.alias_handle);
2407 return NT_STATUS_OK;
2412 samr_AddAliasMember
2414 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2415 struct samr_AddAliasMember *r)
2417 struct dcesrv_handle *h;
2418 struct samr_account_state *a_state;
2419 struct samr_domain_state *d_state;
2420 struct ldb_message *mod;
2421 struct ldb_message **msgs;
2422 const char * const attrs[] = { NULL };
2423 struct ldb_dn *memberdn = NULL;
2424 int ret;
2425 NTSTATUS status;
2427 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2429 a_state = h->data;
2430 d_state = a_state->domain_state;
2432 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2433 &msgs, attrs, "(objectsid=%s)",
2434 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2436 if (ret == 1) {
2437 memberdn = msgs[0]->dn;
2438 } else if (ret == 0) {
2439 status = samdb_create_foreign_security_principal(
2440 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2441 if (!NT_STATUS_IS_OK(status)) {
2442 return status;
2444 } else {
2445 DEBUG(0,("Found %d records matching sid %s\n",
2446 ret, dom_sid_string(mem_ctx, r->in.sid)));
2447 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2450 if (memberdn == NULL) {
2451 DEBUG(0, ("Could not find memberdn\n"));
2452 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2455 mod = ldb_msg_new(mem_ctx);
2456 if (mod == NULL) {
2457 return NT_STATUS_NO_MEMORY;
2460 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2462 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2463 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2464 if (ret != LDB_SUCCESS) {
2465 return dsdb_ldb_err_to_ntstatus(ret);
2468 ret = ldb_modify(a_state->sam_ctx, mod);
2469 switch (ret) {
2470 case LDB_SUCCESS:
2471 return NT_STATUS_OK;
2472 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2473 return NT_STATUS_MEMBER_IN_GROUP;
2474 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2475 return NT_STATUS_ACCESS_DENIED;
2476 default:
2477 return dsdb_ldb_err_to_ntstatus(ret);
2483 samr_DeleteAliasMember
2485 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2486 struct samr_DeleteAliasMember *r)
2488 struct dcesrv_handle *h;
2489 struct samr_account_state *a_state;
2490 struct samr_domain_state *d_state;
2491 struct ldb_message *mod;
2492 const char *memberdn;
2493 int ret;
2495 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2497 a_state = h->data;
2498 d_state = a_state->domain_state;
2500 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2501 "distinguishedName", "(objectSid=%s)",
2502 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2503 if (memberdn == NULL) {
2504 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2507 mod = ldb_msg_new(mem_ctx);
2508 if (mod == NULL) {
2509 return NT_STATUS_NO_MEMORY;
2512 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2514 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2515 memberdn);
2516 if (ret != LDB_SUCCESS) {
2517 return dsdb_ldb_err_to_ntstatus(ret);
2520 ret = ldb_modify(a_state->sam_ctx, mod);
2521 switch (ret) {
2522 case LDB_SUCCESS:
2523 return NT_STATUS_OK;
2524 case LDB_ERR_UNWILLING_TO_PERFORM:
2525 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2526 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2527 return NT_STATUS_ACCESS_DENIED;
2528 default:
2529 return dsdb_ldb_err_to_ntstatus(ret);
2535 samr_GetMembersInAlias
2537 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2538 struct samr_GetMembersInAlias *r)
2540 struct dcesrv_handle *h;
2541 struct samr_account_state *a_state;
2542 struct samr_domain_state *d_state;
2543 struct lsa_SidPtr *array;
2544 unsigned int i, num_members;
2545 struct dom_sid *members;
2546 NTSTATUS status;
2548 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2550 a_state = h->data;
2551 d_state = a_state->domain_state;
2553 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2554 a_state->account_dn, &members,
2555 &num_members);
2556 if (!NT_STATUS_IS_OK(status)) {
2557 return status;
2560 if (num_members == 0) {
2561 r->out.sids->sids = NULL;
2563 return NT_STATUS_OK;
2566 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
2567 if (array == NULL) {
2568 return NT_STATUS_NO_MEMORY;
2571 for (i=0; i<num_members; i++) {
2572 array[i].sid = &members[i];
2575 r->out.sids->num_sids = num_members;
2576 r->out.sids->sids = array;
2578 return NT_STATUS_OK;
2582 samr_OpenUser
2584 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2585 struct samr_OpenUser *r)
2587 struct samr_domain_state *d_state;
2588 struct samr_account_state *a_state;
2589 struct dcesrv_handle *h;
2590 const char *account_name;
2591 struct dom_sid *sid;
2592 struct ldb_message **msgs;
2593 struct dcesrv_handle *u_handle;
2594 const char * const attrs[2] = { "sAMAccountName", NULL };
2595 int ret;
2597 ZERO_STRUCTP(r->out.user_handle);
2599 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2601 d_state = h->data;
2603 /* form the users SID */
2604 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2605 if (!sid) {
2606 return NT_STATUS_NO_MEMORY;
2609 /* search for the user record */
2610 ret = gendb_search(d_state->sam_ctx,
2611 mem_ctx, d_state->domain_dn, &msgs, attrs,
2612 "(&(objectSid=%s)(objectclass=user))",
2613 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2614 if (ret == 0) {
2615 return NT_STATUS_NO_SUCH_USER;
2617 if (ret != 1) {
2618 DEBUG(0,("Found %d records matching sid %s\n", ret,
2619 dom_sid_string(mem_ctx, sid)));
2620 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2623 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2624 if (account_name == NULL) {
2625 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2626 dom_sid_string(mem_ctx, sid)));
2627 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2630 a_state = talloc(mem_ctx, struct samr_account_state);
2631 if (!a_state) {
2632 return NT_STATUS_NO_MEMORY;
2634 a_state->sam_ctx = d_state->sam_ctx;
2635 a_state->access_mask = r->in.access_mask;
2636 a_state->domain_state = talloc_reference(a_state, d_state);
2637 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2638 a_state->account_sid = talloc_steal(a_state, sid);
2639 a_state->account_name = talloc_strdup(a_state, account_name);
2640 if (!a_state->account_name) {
2641 return NT_STATUS_NO_MEMORY;
2644 /* create the policy handle */
2645 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
2646 if (!u_handle) {
2647 return NT_STATUS_NO_MEMORY;
2650 u_handle->data = talloc_steal(u_handle, a_state);
2652 *r->out.user_handle = u_handle->wire_handle;
2654 return NT_STATUS_OK;
2660 samr_DeleteUser
2662 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2663 struct samr_DeleteUser *r)
2665 struct dcesrv_handle *h;
2666 struct samr_account_state *a_state;
2667 int ret;
2669 *r->out.user_handle = *r->in.user_handle;
2671 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2673 a_state = h->data;
2675 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2676 if (ret != LDB_SUCCESS) {
2677 DEBUG(1, ("Failed to delete user: %s: %s\n",
2678 ldb_dn_get_linearized(a_state->account_dn),
2679 ldb_errstring(a_state->sam_ctx)));
2680 return dsdb_ldb_err_to_ntstatus(ret);
2683 talloc_free(h);
2684 ZERO_STRUCTP(r->out.user_handle);
2686 return NT_STATUS_OK;
2691 samr_QueryUserInfo
2693 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2694 struct samr_QueryUserInfo *r)
2696 struct dcesrv_handle *h;
2697 struct samr_account_state *a_state;
2698 struct ldb_message *msg, **res;
2699 int ret;
2700 struct ldb_context *sam_ctx;
2702 const char * const *attrs = NULL;
2703 union samr_UserInfo *info;
2705 *r->out.info = NULL;
2707 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2709 a_state = h->data;
2710 sam_ctx = a_state->sam_ctx;
2712 /* fill in the reply */
2713 switch (r->in.level) {
2714 case 1:
2716 static const char * const attrs2[] = {"sAMAccountName",
2717 "displayName",
2718 "primaryroupID",
2719 "description",
2720 "comment",
2721 NULL};
2722 attrs = attrs2;
2723 break;
2725 case 2:
2727 static const char * const attrs2[] = {"comment",
2728 "countryCode",
2729 "codePage",
2730 NULL};
2731 attrs = attrs2;
2732 break;
2734 case 3:
2736 static const char * const attrs2[] = {"sAMAccountName",
2737 "displayName",
2738 "objectSid",
2739 "primaryGroupID",
2740 "homeDirectory",
2741 "homeDrive",
2742 "scriptPath",
2743 "profilePath",
2744 "userWorkstations",
2745 "lastLogon",
2746 "lastLogoff",
2747 "pwdLastSet",
2748 "logonHours",
2749 "badPwdCount",
2750 "logonCount",
2751 "userAccountControl",
2752 "msDS-User-Account-Control-Computed",
2753 NULL};
2754 attrs = attrs2;
2755 break;
2757 case 4:
2759 static const char * const attrs2[] = {"logonHours",
2760 NULL};
2761 attrs = attrs2;
2762 break;
2764 case 5:
2766 static const char * const attrs2[] = {"sAMAccountName",
2767 "displayName",
2768 "objectSid",
2769 "primaryGroupID",
2770 "homeDirectory",
2771 "homeDrive",
2772 "scriptPath",
2773 "profilePath",
2774 "description",
2775 "userWorkstations",
2776 "lastLogon",
2777 "lastLogoff",
2778 "logonHours",
2779 "badPwdCount",
2780 "logonCount",
2781 "pwdLastSet",
2782 "accountExpires",
2783 "userAccountControl",
2784 "msDS-User-Account-Control-Computed",
2785 NULL};
2786 attrs = attrs2;
2787 break;
2789 case 6:
2791 static const char * const attrs2[] = {"sAMAccountName",
2792 "displayName",
2793 NULL};
2794 attrs = attrs2;
2795 break;
2797 case 7:
2799 static const char * const attrs2[] = {"sAMAccountName",
2800 NULL};
2801 attrs = attrs2;
2802 break;
2804 case 8:
2806 static const char * const attrs2[] = {"displayName",
2807 NULL};
2808 attrs = attrs2;
2809 break;
2811 case 9:
2813 static const char * const attrs2[] = {"primaryGroupID",
2814 NULL};
2815 attrs = attrs2;
2816 break;
2818 case 10:
2820 static const char * const attrs2[] = {"homeDirectory",
2821 "homeDrive",
2822 NULL};
2823 attrs = attrs2;
2824 break;
2826 case 11:
2828 static const char * const attrs2[] = {"scriptPath",
2829 NULL};
2830 attrs = attrs2;
2831 break;
2833 case 12:
2835 static const char * const attrs2[] = {"profilePath",
2836 NULL};
2837 attrs = attrs2;
2838 break;
2840 case 13:
2842 static const char * const attrs2[] = {"description",
2843 NULL};
2844 attrs = attrs2;
2845 break;
2847 case 14:
2849 static const char * const attrs2[] = {"userWorkstations",
2850 NULL};
2851 attrs = attrs2;
2852 break;
2854 case 16:
2856 static const char * const attrs2[] = {"userAccountControl",
2857 "msDS-User-Account-Control-Computed",
2858 "pwdLastSet",
2859 NULL};
2860 attrs = attrs2;
2861 break;
2863 case 17:
2865 static const char * const attrs2[] = {"accountExpires",
2866 NULL};
2867 attrs = attrs2;
2868 break;
2870 case 18:
2872 return NT_STATUS_NOT_SUPPORTED;
2874 case 20:
2876 static const char * const attrs2[] = {"userParameters",
2877 NULL};
2878 attrs = attrs2;
2879 break;
2881 case 21:
2883 static const char * const attrs2[] = {"lastLogon",
2884 "lastLogoff",
2885 "pwdLastSet",
2886 "accountExpires",
2887 "sAMAccountName",
2888 "displayName",
2889 "homeDirectory",
2890 "homeDrive",
2891 "scriptPath",
2892 "profilePath",
2893 "description",
2894 "userWorkstations",
2895 "comment",
2896 "userParameters",
2897 "objectSid",
2898 "primaryGroupID",
2899 "userAccountControl",
2900 "msDS-User-Account-Control-Computed",
2901 "logonHours",
2902 "badPwdCount",
2903 "logonCount",
2904 "countryCode",
2905 "codePage",
2906 NULL};
2907 attrs = attrs2;
2908 break;
2910 case 23:
2911 case 24:
2912 case 25:
2913 case 26:
2915 return NT_STATUS_NOT_SUPPORTED;
2917 default:
2919 return NT_STATUS_INVALID_INFO_CLASS;
2923 /* pull all the user attributes */
2924 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2925 a_state->account_dn, &res, attrs);
2926 if (ret == 0) {
2927 return NT_STATUS_NO_SUCH_USER;
2929 if (ret != 1) {
2930 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2932 msg = res[0];
2934 /* allocate the info structure */
2935 info = talloc_zero(mem_ctx, union samr_UserInfo);
2936 if (info == NULL) {
2937 return NT_STATUS_NO_MEMORY;
2940 /* fill in the reply */
2941 switch (r->in.level) {
2942 case 1:
2943 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
2944 QUERY_STRING(msg, info1.full_name, "displayName");
2945 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
2946 QUERY_STRING(msg, info1.description, "description");
2947 QUERY_STRING(msg, info1.comment, "comment");
2948 break;
2950 case 2:
2951 QUERY_STRING(msg, info2.comment, "comment");
2952 QUERY_UINT (msg, info2.country_code, "countryCode");
2953 QUERY_UINT (msg, info2.code_page, "codePage");
2954 break;
2956 case 3:
2957 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
2958 QUERY_STRING(msg, info3.full_name, "displayName");
2959 QUERY_RID (msg, info3.rid, "objectSid");
2960 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
2961 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
2962 QUERY_STRING(msg, info3.home_drive, "homeDrive");
2963 QUERY_STRING(msg, info3.logon_script, "scriptPath");
2964 QUERY_STRING(msg, info3.profile_path, "profilePath");
2965 QUERY_STRING(msg, info3.workstations, "userWorkstations");
2966 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
2967 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
2968 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
2969 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
2970 QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
2971 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
2972 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
2973 QUERY_UINT (msg, info3.logon_count, "logonCount");
2974 QUERY_AFLAGS(msg, info3.acct_flags, "msDS-User-Account-Control-Computed");
2975 break;
2977 case 4:
2978 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
2979 break;
2981 case 5:
2982 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
2983 QUERY_STRING(msg, info5.full_name, "displayName");
2984 QUERY_RID (msg, info5.rid, "objectSid");
2985 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
2986 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
2987 QUERY_STRING(msg, info5.home_drive, "homeDrive");
2988 QUERY_STRING(msg, info5.logon_script, "scriptPath");
2989 QUERY_STRING(msg, info5.profile_path, "profilePath");
2990 QUERY_STRING(msg, info5.description, "description");
2991 QUERY_STRING(msg, info5.workstations, "userWorkstations");
2992 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
2993 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
2994 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
2995 QUERY_UINT (msg, info5.bad_password_count, "badPwdCount");
2996 QUERY_UINT (msg, info5.logon_count, "logonCount");
2997 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
2998 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
2999 QUERY_AFLAGS(msg, info5.acct_flags, "msDS-User-Account-Control-Computed");
3000 break;
3002 case 6:
3003 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3004 QUERY_STRING(msg, info6.full_name, "displayName");
3005 break;
3007 case 7:
3008 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3009 break;
3011 case 8:
3012 QUERY_STRING(msg, info8.full_name, "displayName");
3013 break;
3015 case 9:
3016 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3017 break;
3019 case 10:
3020 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3021 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3022 break;
3024 case 11:
3025 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3026 break;
3028 case 12:
3029 QUERY_STRING(msg, info12.profile_path, "profilePath");
3030 break;
3032 case 13:
3033 QUERY_STRING(msg, info13.description, "description");
3034 break;
3036 case 14:
3037 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3038 break;
3040 case 16:
3041 QUERY_AFLAGS(msg, info16.acct_flags, "msDS-User-Account-Control-Computed");
3042 break;
3044 case 17:
3045 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3046 break;
3048 case 20:
3049 QUERY_PARAMETERS(msg, info20.parameters, "userParameters");
3050 break;
3052 case 21:
3053 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3054 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3055 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3056 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3057 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3058 QUERY_FPASSC(msg, info21.force_password_change,"pwdLastSet");
3059 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3060 QUERY_STRING(msg, info21.full_name, "displayName");
3061 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3062 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3063 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3064 QUERY_STRING(msg, info21.profile_path, "profilePath");
3065 QUERY_STRING(msg, info21.description, "description");
3066 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3067 QUERY_STRING(msg, info21.comment, "comment");
3068 QUERY_PARAMETERS(msg, info21.parameters, "userParameters");
3069 QUERY_RID (msg, info21.rid, "objectSid");
3070 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3071 QUERY_AFLAGS(msg, info21.acct_flags, "msDS-User-Account-Control-Computed");
3072 info->info21.fields_present = 0x08FFFFFF;
3073 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3074 QUERY_UINT (msg, info21.bad_password_count, "badPwdCount");
3075 QUERY_UINT (msg, info21.logon_count, "logonCount");
3076 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3077 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3078 } else {
3079 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3081 QUERY_UINT (msg, info21.country_code, "countryCode");
3082 QUERY_UINT (msg, info21.code_page, "codePage");
3083 break;
3086 default:
3087 talloc_free(info);
3088 return NT_STATUS_INVALID_INFO_CLASS;
3091 *r->out.info = info;
3093 return NT_STATUS_OK;
3098 samr_SetUserInfo
3100 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3101 struct samr_SetUserInfo *r)
3103 struct dcesrv_handle *h;
3104 struct samr_account_state *a_state;
3105 struct ldb_message *msg;
3106 int ret;
3107 NTSTATUS status = NT_STATUS_OK;
3108 struct ldb_context *sam_ctx;
3110 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3112 a_state = h->data;
3113 sam_ctx = a_state->sam_ctx;
3115 msg = ldb_msg_new(mem_ctx);
3116 if (msg == NULL) {
3117 return NT_STATUS_NO_MEMORY;
3120 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3121 if (!msg->dn) {
3122 return NT_STATUS_NO_MEMORY;
3125 switch (r->in.level) {
3126 case 2:
3127 SET_STRING(msg, info2.comment, "comment");
3128 SET_UINT (msg, info2.country_code, "countryCode");
3129 SET_UINT (msg, info2.code_page, "codePage");
3130 break;
3132 case 4:
3133 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3134 break;
3136 case 6:
3137 SET_STRING(msg, info6.account_name, "samAccountName");
3138 SET_STRING(msg, info6.full_name, "displayName");
3139 break;
3141 case 7:
3142 SET_STRING(msg, info7.account_name, "samAccountName");
3143 break;
3145 case 8:
3146 SET_STRING(msg, info8.full_name, "displayName");
3147 break;
3149 case 9:
3150 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3151 break;
3153 case 10:
3154 SET_STRING(msg, info10.home_directory, "homeDirectory");
3155 SET_STRING(msg, info10.home_drive, "homeDrive");
3156 break;
3158 case 11:
3159 SET_STRING(msg, info11.logon_script, "scriptPath");
3160 break;
3162 case 12:
3163 SET_STRING(msg, info12.profile_path, "profilePath");
3164 break;
3166 case 13:
3167 SET_STRING(msg, info13.description, "description");
3168 break;
3170 case 14:
3171 SET_STRING(msg, info14.workstations, "userWorkstations");
3172 break;
3174 case 16:
3175 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3176 break;
3178 case 17:
3179 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3180 break;
3182 case 18:
3183 status = samr_set_password_buffers(dce_call,
3184 a_state->sam_ctx,
3185 a_state->account_dn,
3186 a_state->domain_state->domain_dn,
3187 mem_ctx,
3188 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3189 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3190 if (!NT_STATUS_IS_OK(status)) {
3191 return status;
3194 if (r->in.info->info18.password_expired > 0) {
3195 struct ldb_message_element *set_el;
3196 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3197 return NT_STATUS_NO_MEMORY;
3199 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3200 set_el->flags = LDB_FLAG_MOD_REPLACE;
3202 break;
3204 case 20:
3205 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3206 break;
3208 case 21:
3209 if (r->in.info->info21.fields_present == 0)
3210 return NT_STATUS_INVALID_PARAMETER;
3212 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3213 IFSET(SAMR_FIELD_LAST_LOGON)
3214 SET_UINT64(msg, info21.last_logon, "lastLogon");
3215 IFSET(SAMR_FIELD_LAST_LOGOFF)
3216 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3217 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3218 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3219 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3220 SET_STRING(msg, info21.account_name, "samAccountName");
3221 IFSET(SAMR_FIELD_FULL_NAME)
3222 SET_STRING(msg, info21.full_name, "displayName");
3223 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3224 SET_STRING(msg, info21.home_directory, "homeDirectory");
3225 IFSET(SAMR_FIELD_HOME_DRIVE)
3226 SET_STRING(msg, info21.home_drive, "homeDrive");
3227 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3228 SET_STRING(msg, info21.logon_script, "scriptPath");
3229 IFSET(SAMR_FIELD_PROFILE_PATH)
3230 SET_STRING(msg, info21.profile_path, "profilePath");
3231 IFSET(SAMR_FIELD_DESCRIPTION)
3232 SET_STRING(msg, info21.description, "description");
3233 IFSET(SAMR_FIELD_WORKSTATIONS)
3234 SET_STRING(msg, info21.workstations, "userWorkstations");
3235 IFSET(SAMR_FIELD_COMMENT)
3236 SET_STRING(msg, info21.comment, "comment");
3237 IFSET(SAMR_FIELD_PARAMETERS)
3238 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3239 IFSET(SAMR_FIELD_PRIMARY_GID)
3240 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3241 IFSET(SAMR_FIELD_ACCT_FLAGS)
3242 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3243 IFSET(SAMR_FIELD_LOGON_HOURS)
3244 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3245 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3246 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3247 IFSET(SAMR_FIELD_NUM_LOGONS)
3248 SET_UINT (msg, info21.logon_count, "logonCount");
3249 IFSET(SAMR_FIELD_COUNTRY_CODE)
3250 SET_UINT (msg, info21.country_code, "countryCode");
3251 IFSET(SAMR_FIELD_CODE_PAGE)
3252 SET_UINT (msg, info21.code_page, "codePage");
3254 /* password change fields */
3255 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3256 return NT_STATUS_ACCESS_DENIED;
3258 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3259 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3260 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3262 if (r->in.info->info21.lm_password_set) {
3263 if ((r->in.info->info21.lm_owf_password.length != 16)
3264 || (r->in.info->info21.lm_owf_password.size != 16)) {
3265 return NT_STATUS_INVALID_PARAMETER;
3268 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3270 if (r->in.info->info21.nt_password_set) {
3271 if ((r->in.info->info21.nt_owf_password.length != 16)
3272 || (r->in.info->info21.nt_owf_password.size != 16)) {
3273 return NT_STATUS_INVALID_PARAMETER;
3276 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3278 status = samr_set_password_buffers(dce_call,
3279 a_state->sam_ctx,
3280 a_state->account_dn,
3281 a_state->domain_state->domain_dn,
3282 mem_ctx,
3283 lm_pwd_hash,
3284 nt_pwd_hash);
3285 if (!NT_STATUS_IS_OK(status)) {
3286 return status;
3291 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3292 NTTIME t = 0;
3293 struct ldb_message_element *set_el;
3294 if (r->in.info->info21.password_expired
3295 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3296 unix_to_nt_time(&t, time(NULL));
3298 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3299 "pwdLastSet", t) != LDB_SUCCESS) {
3300 return NT_STATUS_NO_MEMORY;
3302 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3303 set_el->flags = LDB_FLAG_MOD_REPLACE;
3305 #undef IFSET
3306 break;
3308 case 23:
3309 if (r->in.info->info23.info.fields_present == 0)
3310 return NT_STATUS_INVALID_PARAMETER;
3312 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3313 IFSET(SAMR_FIELD_LAST_LOGON)
3314 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3315 IFSET(SAMR_FIELD_LAST_LOGOFF)
3316 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3317 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3318 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3319 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3320 SET_STRING(msg, info23.info.account_name, "samAccountName");
3321 IFSET(SAMR_FIELD_FULL_NAME)
3322 SET_STRING(msg, info23.info.full_name, "displayName");
3323 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3324 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3325 IFSET(SAMR_FIELD_HOME_DRIVE)
3326 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3327 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3328 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3329 IFSET(SAMR_FIELD_PROFILE_PATH)
3330 SET_STRING(msg, info23.info.profile_path, "profilePath");
3331 IFSET(SAMR_FIELD_DESCRIPTION)
3332 SET_STRING(msg, info23.info.description, "description");
3333 IFSET(SAMR_FIELD_WORKSTATIONS)
3334 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3335 IFSET(SAMR_FIELD_COMMENT)
3336 SET_STRING(msg, info23.info.comment, "comment");
3337 IFSET(SAMR_FIELD_PARAMETERS)
3338 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3339 IFSET(SAMR_FIELD_PRIMARY_GID)
3340 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3341 IFSET(SAMR_FIELD_ACCT_FLAGS)
3342 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3343 IFSET(SAMR_FIELD_LOGON_HOURS)
3344 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3345 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3346 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3347 IFSET(SAMR_FIELD_NUM_LOGONS)
3348 SET_UINT (msg, info23.info.logon_count, "logonCount");
3350 IFSET(SAMR_FIELD_COUNTRY_CODE)
3351 SET_UINT (msg, info23.info.country_code, "countryCode");
3352 IFSET(SAMR_FIELD_CODE_PAGE)
3353 SET_UINT (msg, info23.info.code_page, "codePage");
3355 /* password change fields */
3356 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3357 return NT_STATUS_ACCESS_DENIED;
3359 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3360 status = samr_set_password(dce_call,
3361 a_state->sam_ctx,
3362 a_state->account_dn,
3363 a_state->domain_state->domain_dn,
3364 mem_ctx,
3365 &r->in.info->info23.password);
3366 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3367 status = samr_set_password(dce_call,
3368 a_state->sam_ctx,
3369 a_state->account_dn,
3370 a_state->domain_state->domain_dn,
3371 mem_ctx,
3372 &r->in.info->info23.password);
3374 if (!NT_STATUS_IS_OK(status)) {
3375 return status;
3378 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3379 NTTIME t = 0;
3380 struct ldb_message_element *set_el;
3381 if (r->in.info->info23.info.password_expired
3382 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3383 unix_to_nt_time(&t, time(NULL));
3385 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3386 "pwdLastSet", t) != LDB_SUCCESS) {
3387 return NT_STATUS_NO_MEMORY;
3389 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3390 set_el->flags = LDB_FLAG_MOD_REPLACE;
3392 #undef IFSET
3393 break;
3395 /* the set password levels are handled separately */
3396 case 24:
3397 status = samr_set_password(dce_call,
3398 a_state->sam_ctx,
3399 a_state->account_dn,
3400 a_state->domain_state->domain_dn,
3401 mem_ctx,
3402 &r->in.info->info24.password);
3403 if (!NT_STATUS_IS_OK(status)) {
3404 return status;
3407 if (r->in.info->info24.password_expired > 0) {
3408 struct ldb_message_element *set_el;
3409 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3410 return NT_STATUS_NO_MEMORY;
3412 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3413 set_el->flags = LDB_FLAG_MOD_REPLACE;
3415 break;
3417 case 25:
3418 if (r->in.info->info25.info.fields_present == 0)
3419 return NT_STATUS_INVALID_PARAMETER;
3421 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3422 IFSET(SAMR_FIELD_LAST_LOGON)
3423 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3424 IFSET(SAMR_FIELD_LAST_LOGOFF)
3425 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3426 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3427 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3428 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3429 SET_STRING(msg, info25.info.account_name, "samAccountName");
3430 IFSET(SAMR_FIELD_FULL_NAME)
3431 SET_STRING(msg, info25.info.full_name, "displayName");
3432 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3433 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3434 IFSET(SAMR_FIELD_HOME_DRIVE)
3435 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3436 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3437 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3438 IFSET(SAMR_FIELD_PROFILE_PATH)
3439 SET_STRING(msg, info25.info.profile_path, "profilePath");
3440 IFSET(SAMR_FIELD_DESCRIPTION)
3441 SET_STRING(msg, info25.info.description, "description");
3442 IFSET(SAMR_FIELD_WORKSTATIONS)
3443 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3444 IFSET(SAMR_FIELD_COMMENT)
3445 SET_STRING(msg, info25.info.comment, "comment");
3446 IFSET(SAMR_FIELD_PARAMETERS)
3447 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3448 IFSET(SAMR_FIELD_PRIMARY_GID)
3449 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3450 IFSET(SAMR_FIELD_ACCT_FLAGS)
3451 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3452 IFSET(SAMR_FIELD_LOGON_HOURS)
3453 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3454 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3455 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
3456 IFSET(SAMR_FIELD_NUM_LOGONS)
3457 SET_UINT (msg, info25.info.logon_count, "logonCount");
3458 IFSET(SAMR_FIELD_COUNTRY_CODE)
3459 SET_UINT (msg, info25.info.country_code, "countryCode");
3460 IFSET(SAMR_FIELD_CODE_PAGE)
3461 SET_UINT (msg, info25.info.code_page, "codePage");
3463 /* password change fields */
3464 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3465 return NT_STATUS_ACCESS_DENIED;
3467 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3468 status = samr_set_password_ex(dce_call,
3469 a_state->sam_ctx,
3470 a_state->account_dn,
3471 a_state->domain_state->domain_dn,
3472 mem_ctx,
3473 &r->in.info->info25.password);
3474 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3475 status = samr_set_password_ex(dce_call,
3476 a_state->sam_ctx,
3477 a_state->account_dn,
3478 a_state->domain_state->domain_dn,
3479 mem_ctx,
3480 &r->in.info->info25.password);
3482 if (!NT_STATUS_IS_OK(status)) {
3483 return status;
3486 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3487 NTTIME t = 0;
3488 struct ldb_message_element *set_el;
3489 if (r->in.info->info25.info.password_expired
3490 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3491 unix_to_nt_time(&t, time(NULL));
3493 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3494 "pwdLastSet", t) != LDB_SUCCESS) {
3495 return NT_STATUS_NO_MEMORY;
3497 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3498 set_el->flags = LDB_FLAG_MOD_REPLACE;
3500 #undef IFSET
3501 break;
3503 /* the set password levels are handled separately */
3504 case 26:
3505 status = samr_set_password_ex(dce_call,
3506 a_state->sam_ctx,
3507 a_state->account_dn,
3508 a_state->domain_state->domain_dn,
3509 mem_ctx,
3510 &r->in.info->info26.password);
3511 if (!NT_STATUS_IS_OK(status)) {
3512 return status;
3515 if (r->in.info->info26.password_expired > 0) {
3516 NTTIME t = 0;
3517 struct ldb_message_element *set_el;
3518 if (r->in.info->info26.password_expired
3519 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3520 unix_to_nt_time(&t, time(NULL));
3522 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3523 "pwdLastSet", t) != LDB_SUCCESS) {
3524 return NT_STATUS_NO_MEMORY;
3526 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3527 set_el->flags = LDB_FLAG_MOD_REPLACE;
3529 break;
3531 default:
3532 /* many info classes are not valid for SetUserInfo */
3533 return NT_STATUS_INVALID_INFO_CLASS;
3536 if (!NT_STATUS_IS_OK(status)) {
3537 return status;
3540 /* modify the samdb record */
3541 if (msg->num_elements > 0) {
3542 ret = ldb_modify(a_state->sam_ctx, msg);
3543 if (ret != LDB_SUCCESS) {
3544 DEBUG(1,("Failed to modify record %s: %s\n",
3545 ldb_dn_get_linearized(a_state->account_dn),
3546 ldb_errstring(a_state->sam_ctx)));
3548 return dsdb_ldb_err_to_ntstatus(ret);
3552 return NT_STATUS_OK;
3557 samr_GetGroupsForUser
3559 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3560 struct samr_GetGroupsForUser *r)
3562 struct dcesrv_handle *h;
3563 struct samr_account_state *a_state;
3564 struct samr_domain_state *d_state;
3565 struct ldb_message **res;
3566 const char * const attrs[2] = { "objectSid", NULL };
3567 struct samr_RidWithAttributeArray *array;
3568 int i, count;
3570 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3572 a_state = h->data;
3573 d_state = a_state->domain_state;
3575 count = samdb_search_domain(a_state->sam_ctx, mem_ctx,
3576 d_state->domain_dn, &res,
3577 attrs, d_state->domain_sid,
3578 "(&(member=%s)(|(grouptype=%d)(grouptype=%d))(objectclass=group))",
3579 ldb_dn_get_linearized(a_state->account_dn),
3580 GTYPE_SECURITY_UNIVERSAL_GROUP,
3581 GTYPE_SECURITY_GLOBAL_GROUP);
3582 if (count < 0)
3583 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3585 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
3586 if (array == NULL)
3587 return NT_STATUS_NO_MEMORY;
3589 array->count = 0;
3590 array->rids = NULL;
3592 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
3593 count + 1);
3594 if (array->rids == NULL)
3595 return NT_STATUS_NO_MEMORY;
3597 /* Adds the primary group */
3598 array->rids[0].rid = samdb_search_uint(a_state->sam_ctx, mem_ctx,
3599 ~0, a_state->account_dn,
3600 "primaryGroupID", NULL);
3601 array->rids[0].attributes = SE_GROUP_MANDATORY
3602 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3603 array->count += 1;
3605 /* Adds the additional groups */
3606 for (i = 0; i < count; i++) {
3607 struct dom_sid *group_sid;
3609 group_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
3610 if (group_sid == NULL) {
3611 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3614 array->rids[i + 1].rid =
3615 group_sid->sub_auths[group_sid->num_auths-1];
3616 array->rids[i + 1].attributes = SE_GROUP_MANDATORY
3617 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3618 array->count += 1;
3621 *r->out.rids = array;
3623 return NT_STATUS_OK;
3628 samr_QueryDisplayInfo
3630 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3631 struct samr_QueryDisplayInfo *r)
3633 struct dcesrv_handle *h;
3634 struct samr_domain_state *d_state;
3635 struct ldb_result *res;
3636 unsigned int i;
3637 uint32_t count;
3638 const char * const attrs[] = { "objectSid", "sAMAccountName",
3639 "displayName", "description", "userAccountControl",
3640 "pwdLastSet", NULL };
3641 struct samr_DispEntryFull *entriesFull = NULL;
3642 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
3643 struct samr_DispEntryAscii *entriesAscii = NULL;
3644 struct samr_DispEntryGeneral *entriesGeneral = NULL;
3645 const char *filter;
3646 int ret;
3648 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3650 d_state = h->data;
3652 switch (r->in.level) {
3653 case 1:
3654 case 4:
3655 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3656 "(sAMAccountType=%d))",
3657 ATYPE_NORMAL_ACCOUNT);
3658 break;
3659 case 2:
3660 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3661 "(sAMAccountType=%d))",
3662 ATYPE_WORKSTATION_TRUST);
3663 break;
3664 case 3:
3665 case 5:
3666 filter = talloc_asprintf(mem_ctx,
3667 "(&(|(groupType=%d)(groupType=%d))"
3668 "(objectClass=group))",
3669 GTYPE_SECURITY_UNIVERSAL_GROUP,
3670 GTYPE_SECURITY_GLOBAL_GROUP);
3671 break;
3672 default:
3673 return NT_STATUS_INVALID_INFO_CLASS;
3676 /* search for all requested objects in all domains. This could
3677 possibly be cached and resumed based on resume_key */
3678 ret = dsdb_search(d_state->sam_ctx, mem_ctx, &res, ldb_get_default_basedn(d_state->sam_ctx),
3679 LDB_SCOPE_SUBTREE, attrs, 0, "%s", filter);
3680 if (ret != LDB_SUCCESS) {
3681 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3683 if ((res->count == 0) || (r->in.max_entries == 0)) {
3684 return NT_STATUS_OK;
3687 switch (r->in.level) {
3688 case 1:
3689 entriesGeneral = talloc_array(mem_ctx,
3690 struct samr_DispEntryGeneral,
3691 res->count);
3692 break;
3693 case 2:
3694 entriesFull = talloc_array(mem_ctx,
3695 struct samr_DispEntryFull,
3696 res->count);
3697 break;
3698 case 3:
3699 entriesFullGroup = talloc_array(mem_ctx,
3700 struct samr_DispEntryFullGroup,
3701 res->count);
3702 break;
3703 case 4:
3704 case 5:
3705 entriesAscii = talloc_array(mem_ctx,
3706 struct samr_DispEntryAscii,
3707 res->count);
3708 break;
3711 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
3712 (entriesAscii == NULL) && (entriesFullGroup == NULL))
3713 return NT_STATUS_NO_MEMORY;
3715 count = 0;
3717 for (i = 0; i < res->count; i++) {
3718 struct dom_sid *objectsid;
3720 objectsid = samdb_result_dom_sid(mem_ctx, res->msgs[i],
3721 "objectSid");
3722 if (objectsid == NULL)
3723 continue;
3725 switch(r->in.level) {
3726 case 1:
3727 entriesGeneral[count].idx = count + 1;
3728 entriesGeneral[count].rid =
3729 objectsid->sub_auths[objectsid->num_auths-1];
3730 entriesGeneral[count].acct_flags =
3731 samdb_result_acct_flags(res->msgs[i], NULL);
3732 entriesGeneral[count].account_name.string =
3733 ldb_msg_find_attr_as_string(res->msgs[i],
3734 "sAMAccountName", "");
3735 entriesGeneral[count].full_name.string =
3736 ldb_msg_find_attr_as_string(res->msgs[i],
3737 "displayName", "");
3738 entriesGeneral[count].description.string =
3739 ldb_msg_find_attr_as_string(res->msgs[i],
3740 "description", "");
3741 break;
3742 case 2:
3743 entriesFull[count].idx = count + 1;
3744 entriesFull[count].rid =
3745 objectsid->sub_auths[objectsid->num_auths-1];
3747 /* No idea why we need to or in ACB_NORMAL here, but this is what Win2k3 seems to do... */
3748 entriesFull[count].acct_flags =
3749 samdb_result_acct_flags(res->msgs[i],
3750 NULL) | ACB_NORMAL;
3751 entriesFull[count].account_name.string =
3752 ldb_msg_find_attr_as_string(res->msgs[i],
3753 "sAMAccountName", "");
3754 entriesFull[count].description.string =
3755 ldb_msg_find_attr_as_string(res->msgs[i],
3756 "description", "");
3757 break;
3758 case 3:
3759 entriesFullGroup[count].idx = count + 1;
3760 entriesFullGroup[count].rid =
3761 objectsid->sub_auths[objectsid->num_auths-1];
3762 /* We get a "7" here for groups */
3763 entriesFullGroup[count].acct_flags
3764 = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3765 entriesFullGroup[count].account_name.string =
3766 ldb_msg_find_attr_as_string(res->msgs[i],
3767 "sAMAccountName", "");
3768 entriesFullGroup[count].description.string =
3769 ldb_msg_find_attr_as_string(res->msgs[i],
3770 "description", "");
3771 break;
3772 case 4:
3773 case 5:
3774 entriesAscii[count].idx = count + 1;
3775 entriesAscii[count].account_name.string =
3776 ldb_msg_find_attr_as_string(res->msgs[i],
3777 "sAMAccountName", "");
3778 break;
3781 count += 1;
3784 *r->out.total_size = count;
3786 if (r->in.start_idx >= count) {
3787 *r->out.returned_size = 0;
3788 switch(r->in.level) {
3789 case 1:
3790 r->out.info->info1.count = *r->out.returned_size;
3791 r->out.info->info1.entries = NULL;
3792 break;
3793 case 2:
3794 r->out.info->info2.count = *r->out.returned_size;
3795 r->out.info->info2.entries = NULL;
3796 break;
3797 case 3:
3798 r->out.info->info3.count = *r->out.returned_size;
3799 r->out.info->info3.entries = NULL;
3800 break;
3801 case 4:
3802 r->out.info->info4.count = *r->out.returned_size;
3803 r->out.info->info4.entries = NULL;
3804 break;
3805 case 5:
3806 r->out.info->info5.count = *r->out.returned_size;
3807 r->out.info->info5.entries = NULL;
3808 break;
3810 } else {
3811 *r->out.returned_size = MIN(count - r->in.start_idx,
3812 r->in.max_entries);
3813 switch(r->in.level) {
3814 case 1:
3815 r->out.info->info1.count = *r->out.returned_size;
3816 r->out.info->info1.entries =
3817 &(entriesGeneral[r->in.start_idx]);
3818 break;
3819 case 2:
3820 r->out.info->info2.count = *r->out.returned_size;
3821 r->out.info->info2.entries =
3822 &(entriesFull[r->in.start_idx]);
3823 break;
3824 case 3:
3825 r->out.info->info3.count = *r->out.returned_size;
3826 r->out.info->info3.entries =
3827 &(entriesFullGroup[r->in.start_idx]);
3828 break;
3829 case 4:
3830 r->out.info->info4.count = *r->out.returned_size;
3831 r->out.info->info4.entries =
3832 &(entriesAscii[r->in.start_idx]);
3833 break;
3834 case 5:
3835 r->out.info->info5.count = *r->out.returned_size;
3836 r->out.info->info5.entries =
3837 &(entriesAscii[r->in.start_idx]);
3838 break;
3842 return (*r->out.returned_size < (count - r->in.start_idx)) ?
3843 STATUS_MORE_ENTRIES : NT_STATUS_OK;
3848 samr_GetDisplayEnumerationIndex
3850 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3851 struct samr_GetDisplayEnumerationIndex *r)
3853 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3858 samr_TestPrivateFunctionsDomain
3860 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3861 struct samr_TestPrivateFunctionsDomain *r)
3863 return NT_STATUS_NOT_IMPLEMENTED;
3868 samr_TestPrivateFunctionsUser
3870 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3871 struct samr_TestPrivateFunctionsUser *r)
3873 return NT_STATUS_NOT_IMPLEMENTED;
3878 samr_GetUserPwInfo
3880 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3881 struct samr_GetUserPwInfo *r)
3883 struct dcesrv_handle *h;
3884 struct samr_account_state *a_state;
3886 ZERO_STRUCTP(r->out.info);
3888 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3890 a_state = h->data;
3892 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
3893 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
3894 NULL);
3895 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
3896 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
3898 return NT_STATUS_OK;
3903 samr_RemoveMemberFromForeignDomain
3905 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
3906 TALLOC_CTX *mem_ctx,
3907 struct samr_RemoveMemberFromForeignDomain *r)
3909 struct dcesrv_handle *h;
3910 struct samr_domain_state *d_state;
3911 const char *memberdn;
3912 struct ldb_message **res;
3913 const char *no_attrs[] = { NULL };
3914 int i, count;
3916 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3918 d_state = h->data;
3920 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3921 "distinguishedName", "(objectSid=%s)",
3922 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3923 /* Nothing to do */
3924 if (memberdn == NULL) {
3925 return NT_STATUS_OK;
3928 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
3929 d_state->domain_dn, &res, no_attrs,
3930 d_state->domain_sid,
3931 "(&(member=%s)(objectClass=group)"
3932 "(|(groupType=%d)(groupType=%d)))",
3933 memberdn,
3934 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
3935 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
3937 if (count < 0)
3938 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3940 for (i=0; i<count; i++) {
3941 struct ldb_message *mod;
3942 int ret;
3944 mod = ldb_msg_new(mem_ctx);
3945 if (mod == NULL) {
3946 return NT_STATUS_NO_MEMORY;
3949 mod->dn = res[i]->dn;
3951 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
3952 "member", memberdn) != LDB_SUCCESS)
3953 return NT_STATUS_NO_MEMORY;
3955 ret = ldb_modify(d_state->sam_ctx, mod);
3956 talloc_free(mod);
3957 if (ret != LDB_SUCCESS) {
3958 return dsdb_ldb_err_to_ntstatus(ret);
3962 return NT_STATUS_OK;
3967 samr_QueryDomainInfo2
3969 just an alias for samr_QueryDomainInfo
3971 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3972 struct samr_QueryDomainInfo2 *r)
3974 struct samr_QueryDomainInfo r1;
3975 NTSTATUS status;
3977 ZERO_STRUCT(r1.out);
3978 r1.in.domain_handle = r->in.domain_handle;
3979 r1.in.level = r->in.level;
3980 r1.out.info = r->out.info;
3982 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
3984 return status;
3989 samr_QueryUserInfo2
3991 just an alias for samr_QueryUserInfo
3993 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3994 struct samr_QueryUserInfo2 *r)
3996 struct samr_QueryUserInfo r1;
3997 NTSTATUS status;
3999 r1.in.user_handle = r->in.user_handle;
4000 r1.in.level = r->in.level;
4001 r1.out.info = r->out.info;
4003 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4005 return status;
4010 samr_QueryDisplayInfo2
4012 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4013 struct samr_QueryDisplayInfo2 *r)
4015 struct samr_QueryDisplayInfo q;
4016 NTSTATUS result;
4018 q.in.domain_handle = r->in.domain_handle;
4019 q.in.level = r->in.level;
4020 q.in.start_idx = r->in.start_idx;
4021 q.in.max_entries = r->in.max_entries;
4022 q.in.buf_size = r->in.buf_size;
4023 q.out.total_size = r->out.total_size;
4024 q.out.returned_size = r->out.returned_size;
4025 q.out.info = r->out.info;
4027 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4029 return result;
4034 samr_GetDisplayEnumerationIndex2
4036 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4037 struct samr_GetDisplayEnumerationIndex2 *r)
4039 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4044 samr_QueryDisplayInfo3
4046 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4047 struct samr_QueryDisplayInfo3 *r)
4049 struct samr_QueryDisplayInfo q;
4050 NTSTATUS result;
4052 q.in.domain_handle = r->in.domain_handle;
4053 q.in.level = r->in.level;
4054 q.in.start_idx = r->in.start_idx;
4055 q.in.max_entries = r->in.max_entries;
4056 q.in.buf_size = r->in.buf_size;
4057 q.out.total_size = r->out.total_size;
4058 q.out.returned_size = r->out.returned_size;
4059 q.out.info = r->out.info;
4061 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4063 return result;
4068 samr_AddMultipleMembersToAlias
4070 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4071 struct samr_AddMultipleMembersToAlias *r)
4073 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4078 samr_RemoveMultipleMembersFromAlias
4080 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4081 struct samr_RemoveMultipleMembersFromAlias *r)
4083 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4088 samr_GetDomPwInfo
4090 this fetches the default password properties for a domain
4092 note that w2k3 completely ignores the domain name in this call, and
4093 always returns the information for the servers primary domain
4095 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4096 struct samr_GetDomPwInfo *r)
4098 struct ldb_message **msgs;
4099 int ret;
4100 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4101 struct ldb_context *sam_ctx;
4103 ZERO_STRUCTP(r->out.info);
4105 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
4106 dce_call->conn->dce_ctx->lp_ctx,
4107 dce_call->conn->auth_state.session_info, 0);
4108 if (sam_ctx == NULL) {
4109 return NT_STATUS_INVALID_SYSTEM_SERVICE;
4112 /* The domain name in this call is ignored */
4113 ret = gendb_search_dn(sam_ctx,
4114 mem_ctx, NULL, &msgs, attrs);
4115 if (ret <= 0) {
4116 talloc_free(sam_ctx);
4118 return NT_STATUS_NO_SUCH_DOMAIN;
4120 if (ret > 1) {
4121 talloc_free(msgs);
4122 talloc_free(sam_ctx);
4124 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4127 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
4128 "minPwdLength", 0);
4129 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
4130 "pwdProperties", 1);
4132 talloc_free(msgs);
4133 talloc_unlink(mem_ctx, sam_ctx);
4135 return NT_STATUS_OK;
4140 samr_Connect2
4142 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4143 struct samr_Connect2 *r)
4145 struct samr_Connect c;
4147 c.in.system_name = NULL;
4148 c.in.access_mask = r->in.access_mask;
4149 c.out.connect_handle = r->out.connect_handle;
4151 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4156 samr_SetUserInfo2
4158 just an alias for samr_SetUserInfo
4160 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4161 struct samr_SetUserInfo2 *r)
4163 struct samr_SetUserInfo r2;
4165 r2.in.user_handle = r->in.user_handle;
4166 r2.in.level = r->in.level;
4167 r2.in.info = r->in.info;
4169 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4174 samr_SetBootKeyInformation
4176 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4177 struct samr_SetBootKeyInformation *r)
4179 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4184 samr_GetBootKeyInformation
4186 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4187 struct samr_GetBootKeyInformation *r)
4189 /* Windows Server 2008 returns this */
4190 return NT_STATUS_NOT_SUPPORTED;
4195 samr_Connect3
4197 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4198 struct samr_Connect3 *r)
4200 struct samr_Connect c;
4202 c.in.system_name = NULL;
4203 c.in.access_mask = r->in.access_mask;
4204 c.out.connect_handle = r->out.connect_handle;
4206 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4211 samr_Connect4
4213 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4214 struct samr_Connect4 *r)
4216 struct samr_Connect c;
4218 c.in.system_name = NULL;
4219 c.in.access_mask = r->in.access_mask;
4220 c.out.connect_handle = r->out.connect_handle;
4222 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4227 samr_Connect5
4229 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4230 struct samr_Connect5 *r)
4232 struct samr_Connect c;
4233 NTSTATUS status;
4235 c.in.system_name = NULL;
4236 c.in.access_mask = r->in.access_mask;
4237 c.out.connect_handle = r->out.connect_handle;
4239 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4241 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4242 r->out.info_out->info1.unknown2 = 0;
4243 *r->out.level_out = r->in.level_in;
4245 return status;
4250 samr_RidToSid
4252 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4253 struct samr_RidToSid *r)
4255 struct samr_domain_state *d_state;
4256 struct dcesrv_handle *h;
4258 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4260 d_state = h->data;
4262 /* form the users SID */
4263 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4264 if (!*r->out.sid) {
4265 return NT_STATUS_NO_MEMORY;
4268 return NT_STATUS_OK;
4273 samr_SetDsrmPassword
4275 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4276 struct samr_SetDsrmPassword *r)
4278 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4283 samr_ValidatePassword
4285 For now the call checks the password complexity (if active) and the minimum
4286 password length on level 2 and 3. Level 1 is ignored for now.
4288 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
4289 TALLOC_CTX *mem_ctx,
4290 struct samr_ValidatePassword *r)
4292 struct samr_GetDomPwInfo r2;
4293 struct samr_PwInfo pwInfo;
4294 DATA_BLOB password;
4295 enum samr_ValidationStatus res;
4296 NTSTATUS status;
4297 enum dcerpc_transport_t transport =
4298 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
4300 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
4301 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4304 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
4306 r2.in.domain_name = NULL;
4307 r2.out.info = &pwInfo;
4308 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
4309 if (!NT_STATUS_IS_OK(status)) {
4310 return status;
4313 switch (r->in.level) {
4314 case NetValidateAuthentication:
4315 /* we don't support this yet */
4316 return NT_STATUS_NOT_SUPPORTED;
4317 break;
4318 case NetValidatePasswordChange:
4319 password = data_blob_const(r->in.req->req2.password.string,
4320 r->in.req->req2.password.length);
4321 res = samdb_check_password(&password,
4322 pwInfo.password_properties,
4323 pwInfo.min_password_length);
4324 (*r->out.rep)->ctr2.status = res;
4325 break;
4326 case NetValidatePasswordReset:
4327 password = data_blob_const(r->in.req->req3.password.string,
4328 r->in.req->req3.password.length);
4329 res = samdb_check_password(&password,
4330 pwInfo.password_properties,
4331 pwInfo.min_password_length);
4332 (*r->out.rep)->ctr3.status = res;
4333 break;
4334 default:
4335 return NT_STATUS_INVALID_INFO_CLASS;
4336 break;
4339 return NT_STATUS_OK;
4343 /* include the generated boilerplate */
4344 #include "librpc/gen_ndr/ndr_samr_s.c"