docs: Fix variable list in man vfs_crossrename.
[Samba.git] / source4 / rpc_server / samr / dcesrv_samr.c
blob7279fe02f724f1ff6defa3a749b3cbcbe67c260a
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(sam_ctx, mem_ctx, msg, a_state->domain_state->domain_dn);
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(d_state->sam_ctx, mem_ctx,
1313 res[i], d_state->domain_dn) & r->in.acct_flags) == 0)) {
1314 continue;
1316 entries[count].idx = samdb_result_rid_from_sid(mem_ctx, res[i],
1317 "objectSid", 0);
1318 entries[count].name.string = ldb_msg_find_attr_as_string(res[i],
1319 "sAMAccountName", "");
1320 count += 1;
1323 /* sort the results by rid */
1324 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1326 /* find the first entry to return */
1327 for (first=0;
1328 first<count && entries[first].idx <= *r->in.resume_handle;
1329 first++) ;
1331 /* return the rest, limit by max_size. Note that we
1332 use the w2k3 element size value of 54 */
1333 *r->out.num_entries = count - first;
1334 *r->out.num_entries = MIN(*r->out.num_entries,
1335 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1337 sam = talloc(mem_ctx, struct samr_SamArray);
1338 if (!sam) {
1339 return NT_STATUS_NO_MEMORY;
1342 sam->entries = entries+first;
1343 sam->count = *r->out.num_entries;
1345 *r->out.sam = sam;
1347 if (first == count) {
1348 return NT_STATUS_OK;
1351 if (*r->out.num_entries < count - first) {
1352 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1353 return STATUS_MORE_ENTRIES;
1356 return NT_STATUS_OK;
1361 samr_CreateDomAlias
1363 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1364 struct samr_CreateDomAlias *r)
1366 struct samr_domain_state *d_state;
1367 struct samr_account_state *a_state;
1368 struct dcesrv_handle *h;
1369 const char *alias_name;
1370 struct dom_sid *sid;
1371 struct dcesrv_handle *a_handle;
1372 struct ldb_dn *dn;
1373 NTSTATUS status;
1375 ZERO_STRUCTP(r->out.alias_handle);
1376 *r->out.rid = 0;
1378 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1380 d_state = h->data;
1382 if (d_state->builtin) {
1383 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1384 return NT_STATUS_ACCESS_DENIED;
1387 alias_name = r->in.alias_name->string;
1389 if (alias_name == NULL) {
1390 return NT_STATUS_INVALID_PARAMETER;
1393 status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1394 if (!NT_STATUS_IS_OK(status)) {
1395 return status;
1398 a_state = talloc(mem_ctx, struct samr_account_state);
1399 if (!a_state) {
1400 return NT_STATUS_NO_MEMORY;
1403 a_state->sam_ctx = d_state->sam_ctx;
1404 a_state->access_mask = r->in.access_mask;
1405 a_state->domain_state = talloc_reference(a_state, d_state);
1406 a_state->account_dn = talloc_steal(a_state, dn);
1408 a_state->account_name = talloc_steal(a_state, alias_name);
1410 /* create the policy handle */
1411 a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1412 if (a_handle == NULL)
1413 return NT_STATUS_NO_MEMORY;
1415 a_handle->data = talloc_steal(a_handle, a_state);
1417 *r->out.alias_handle = a_handle->wire_handle;
1419 *r->out.rid = sid->sub_auths[sid->num_auths-1];
1421 return NT_STATUS_OK;
1426 samr_EnumDomainAliases
1428 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1429 struct samr_EnumDomainAliases *r)
1431 struct dcesrv_handle *h;
1432 struct samr_domain_state *d_state;
1433 struct ldb_message **res;
1434 int i, ldb_cnt;
1435 uint32_t first, count;
1436 struct samr_SamEntry *entries;
1437 const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1438 struct samr_SamArray *sam;
1440 *r->out.resume_handle = 0;
1441 *r->out.sam = NULL;
1442 *r->out.num_entries = 0;
1444 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1446 d_state = h->data;
1448 /* search for all domain aliases in this domain. This could possibly be
1449 cached and resumed based on resume_key */
1450 ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1451 &res, attrs,
1452 d_state->domain_sid,
1453 "(&(|(grouptype=%d)(grouptype=%d)))"
1454 "(objectclass=group))",
1455 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1456 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1457 if (ldb_cnt < 0) {
1458 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1461 /* convert to SamEntry format */
1462 entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1463 if (!entries) {
1464 return NT_STATUS_NO_MEMORY;
1467 count = 0;
1469 for (i=0;i<ldb_cnt;i++) {
1470 struct dom_sid *alias_sid;
1472 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1473 "objectSid");
1475 if (alias_sid == NULL) {
1476 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1479 entries[count].idx =
1480 alias_sid->sub_auths[alias_sid->num_auths-1];
1481 entries[count].name.string =
1482 ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1483 count += 1;
1486 /* sort the results by rid */
1487 TYPESAFE_QSORT(entries, count, compare_SamEntry);
1489 /* find the first entry to return */
1490 for (first=0;
1491 first<count && entries[first].idx <= *r->in.resume_handle;
1492 first++) ;
1494 /* return the rest, limit by max_size. Note that we
1495 use the w2k3 element size value of 54 */
1496 *r->out.num_entries = count - first;
1497 *r->out.num_entries = MIN(*r->out.num_entries,
1498 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1500 sam = talloc(mem_ctx, struct samr_SamArray);
1501 if (!sam) {
1502 return NT_STATUS_NO_MEMORY;
1505 sam->entries = entries+first;
1506 sam->count = *r->out.num_entries;
1508 *r->out.sam = sam;
1510 if (first == count) {
1511 return NT_STATUS_OK;
1514 if (*r->out.num_entries < count - first) {
1515 *r->out.resume_handle =
1516 entries[first+*r->out.num_entries-1].idx;
1517 return STATUS_MORE_ENTRIES;
1520 return NT_STATUS_OK;
1525 samr_GetAliasMembership
1527 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1528 struct samr_GetAliasMembership *r)
1530 struct dcesrv_handle *h;
1531 struct samr_domain_state *d_state;
1532 const char *filter;
1533 const char * const attrs[] = { "objectSid", NULL };
1534 struct ldb_message **res;
1535 uint32_t i;
1536 int count = 0;
1538 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1540 d_state = h->data;
1542 filter = talloc_asprintf(mem_ctx,
1543 "(&(|(grouptype=%d)(grouptype=%d))"
1544 "(objectclass=group)(|",
1545 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1546 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1547 if (filter == NULL) {
1548 return NT_STATUS_NO_MEMORY;
1551 for (i=0; i<r->in.sids->num_sids; i++) {
1552 const char *memberdn;
1554 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
1555 "distinguishedName",
1556 "(objectSid=%s)",
1557 ldap_encode_ndr_dom_sid(mem_ctx,
1558 r->in.sids->sids[i].sid));
1559 if (memberdn == NULL) {
1560 continue;
1563 filter = talloc_asprintf(mem_ctx, "%s(member=%s)", filter,
1564 memberdn);
1565 if (filter == NULL) {
1566 return NT_STATUS_NO_MEMORY;
1570 /* Find out if we had at least one valid member SID passed - otherwise
1571 * just skip the search. */
1572 if (strstr(filter, "member") != NULL) {
1573 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1574 &res, attrs, d_state->domain_sid,
1575 "%s))", filter);
1576 if (count < 0) {
1577 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1581 r->out.rids->count = 0;
1582 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1583 if (r->out.rids->ids == NULL)
1584 return NT_STATUS_NO_MEMORY;
1586 for (i=0; i<count; i++) {
1587 struct dom_sid *alias_sid;
1589 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1590 if (alias_sid == NULL) {
1591 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1594 r->out.rids->ids[r->out.rids->count] =
1595 alias_sid->sub_auths[alias_sid->num_auths-1];
1596 r->out.rids->count += 1;
1599 return NT_STATUS_OK;
1604 samr_LookupNames
1606 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1607 struct samr_LookupNames *r)
1609 struct dcesrv_handle *h;
1610 struct samr_domain_state *d_state;
1611 uint32_t i, num_mapped;
1612 NTSTATUS status = NT_STATUS_OK;
1613 const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1614 int count;
1616 ZERO_STRUCTP(r->out.rids);
1617 ZERO_STRUCTP(r->out.types);
1619 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1621 d_state = h->data;
1623 if (r->in.num_names == 0) {
1624 return NT_STATUS_OK;
1627 r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1628 r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1629 if (!r->out.rids->ids || !r->out.types->ids) {
1630 return NT_STATUS_NO_MEMORY;
1632 r->out.rids->count = r->in.num_names;
1633 r->out.types->count = r->in.num_names;
1635 num_mapped = 0;
1637 for (i=0;i<r->in.num_names;i++) {
1638 struct ldb_message **res;
1639 struct dom_sid *sid;
1640 uint32_t atype, rtype;
1642 r->out.rids->ids[i] = 0;
1643 r->out.types->ids[i] = SID_NAME_UNKNOWN;
1645 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1646 "sAMAccountName=%s",
1647 ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
1648 if (count != 1) {
1649 status = STATUS_SOME_UNMAPPED;
1650 continue;
1653 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
1654 if (sid == NULL) {
1655 status = STATUS_SOME_UNMAPPED;
1656 continue;
1659 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
1660 if (atype == 0) {
1661 status = STATUS_SOME_UNMAPPED;
1662 continue;
1665 rtype = ds_atype_map(atype);
1667 if (rtype == SID_NAME_UNKNOWN) {
1668 status = STATUS_SOME_UNMAPPED;
1669 continue;
1672 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
1673 r->out.types->ids[i] = rtype;
1674 num_mapped++;
1677 if (num_mapped == 0) {
1678 return NT_STATUS_NONE_MAPPED;
1680 return status;
1685 samr_LookupRids
1687 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1688 struct samr_LookupRids *r)
1690 NTSTATUS status;
1691 struct dcesrv_handle *h;
1692 struct samr_domain_state *d_state;
1693 const char **names;
1694 struct lsa_String *lsa_names;
1695 enum lsa_SidType *ids;
1697 ZERO_STRUCTP(r->out.names);
1698 ZERO_STRUCTP(r->out.types);
1700 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1702 d_state = h->data;
1704 if (r->in.num_rids == 0)
1705 return NT_STATUS_OK;
1707 lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
1708 names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
1709 ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
1711 if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
1712 return NT_STATUS_NO_MEMORY;
1714 r->out.names->names = lsa_names;
1715 r->out.names->count = r->in.num_rids;
1717 r->out.types->ids = (uint32_t *) ids;
1718 r->out.types->count = r->in.num_rids;
1720 status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
1721 r->in.num_rids, r->in.rids, names, ids);
1722 if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
1723 uint32_t i;
1724 for (i = 0; i < r->in.num_rids; i++) {
1725 lsa_names[i].string = names[i];
1728 return status;
1733 samr_OpenGroup
1735 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1736 struct samr_OpenGroup *r)
1738 struct samr_domain_state *d_state;
1739 struct samr_account_state *a_state;
1740 struct dcesrv_handle *h;
1741 const char *groupname;
1742 struct dom_sid *sid;
1743 struct ldb_message **msgs;
1744 struct dcesrv_handle *g_handle;
1745 const char * const attrs[2] = { "sAMAccountName", NULL };
1746 int ret;
1748 ZERO_STRUCTP(r->out.group_handle);
1750 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1752 d_state = h->data;
1754 /* form the group SID */
1755 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1756 if (!sid) {
1757 return NT_STATUS_NO_MEMORY;
1760 /* search for the group record */
1761 ret = gendb_search(d_state->sam_ctx,
1762 mem_ctx, d_state->domain_dn, &msgs, attrs,
1763 "(&(objectSid=%s)(objectClass=group)"
1764 "(|(groupType=%d)(groupType=%d)))",
1765 ldap_encode_ndr_dom_sid(mem_ctx, sid),
1766 GTYPE_SECURITY_UNIVERSAL_GROUP,
1767 GTYPE_SECURITY_GLOBAL_GROUP);
1768 if (ret == 0) {
1769 return NT_STATUS_NO_SUCH_GROUP;
1771 if (ret != 1) {
1772 DEBUG(0,("Found %d records matching sid %s\n",
1773 ret, dom_sid_string(mem_ctx, sid)));
1774 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1777 groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
1778 if (groupname == NULL) {
1779 DEBUG(0,("sAMAccountName field missing for sid %s\n",
1780 dom_sid_string(mem_ctx, sid)));
1781 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1784 a_state = talloc(mem_ctx, struct samr_account_state);
1785 if (!a_state) {
1786 return NT_STATUS_NO_MEMORY;
1788 a_state->sam_ctx = d_state->sam_ctx;
1789 a_state->access_mask = r->in.access_mask;
1790 a_state->domain_state = talloc_reference(a_state, d_state);
1791 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
1792 a_state->account_sid = talloc_steal(a_state, sid);
1793 a_state->account_name = talloc_strdup(a_state, groupname);
1794 if (!a_state->account_name) {
1795 return NT_STATUS_NO_MEMORY;
1798 /* create the policy handle */
1799 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1800 if (!g_handle) {
1801 return NT_STATUS_NO_MEMORY;
1804 g_handle->data = talloc_steal(g_handle, a_state);
1806 *r->out.group_handle = g_handle->wire_handle;
1808 return NT_STATUS_OK;
1812 samr_QueryGroupInfo
1814 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1815 struct samr_QueryGroupInfo *r)
1817 struct dcesrv_handle *h;
1818 struct samr_account_state *a_state;
1819 struct ldb_message *msg, **res;
1820 const char * const attrs[4] = { "sAMAccountName", "description",
1821 "numMembers", NULL };
1822 int ret;
1823 union samr_GroupInfo *info;
1825 *r->out.info = NULL;
1827 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1829 a_state = h->data;
1831 /* pull all the group attributes */
1832 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
1833 a_state->account_dn, &res, attrs);
1834 if (ret == 0) {
1835 return NT_STATUS_NO_SUCH_GROUP;
1837 if (ret != 1) {
1838 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1840 msg = res[0];
1842 /* allocate the info structure */
1843 info = talloc_zero(mem_ctx, union samr_GroupInfo);
1844 if (info == NULL) {
1845 return NT_STATUS_NO_MEMORY;
1848 /* Fill in the level */
1849 switch (r->in.level) {
1850 case GROUPINFOALL:
1851 QUERY_STRING(msg, all.name, "sAMAccountName");
1852 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1853 QUERY_UINT (msg, all.num_members, "numMembers")
1854 QUERY_STRING(msg, all.description, "description");
1855 break;
1856 case GROUPINFONAME:
1857 QUERY_STRING(msg, name, "sAMAccountName");
1858 break;
1859 case GROUPINFOATTRIBUTES:
1860 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1861 break;
1862 case GROUPINFODESCRIPTION:
1863 QUERY_STRING(msg, description, "description");
1864 break;
1865 case GROUPINFOALL2:
1866 QUERY_STRING(msg, all2.name, "sAMAccountName");
1867 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1868 QUERY_UINT (msg, all2.num_members, "numMembers")
1869 QUERY_STRING(msg, all2.description, "description");
1870 break;
1871 default:
1872 talloc_free(info);
1873 return NT_STATUS_INVALID_INFO_CLASS;
1876 *r->out.info = info;
1878 return NT_STATUS_OK;
1883 samr_SetGroupInfo
1885 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1886 struct samr_SetGroupInfo *r)
1888 struct dcesrv_handle *h;
1889 struct samr_account_state *g_state;
1890 struct ldb_message *msg;
1891 struct ldb_context *sam_ctx;
1892 int ret;
1894 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1896 g_state = h->data;
1897 sam_ctx = g_state->sam_ctx;
1899 msg = ldb_msg_new(mem_ctx);
1900 if (msg == NULL) {
1901 return NT_STATUS_NO_MEMORY;
1904 msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
1905 if (!msg->dn) {
1906 return NT_STATUS_NO_MEMORY;
1909 switch (r->in.level) {
1910 case GROUPINFODESCRIPTION:
1911 SET_STRING(msg, description, "description");
1912 break;
1913 case GROUPINFONAME:
1914 /* On W2k3 this does not change the name, it changes the
1915 * sAMAccountName attribute */
1916 SET_STRING(msg, name, "sAMAccountName");
1917 break;
1918 case GROUPINFOATTRIBUTES:
1919 /* This does not do anything obviously visible in W2k3 LDAP */
1920 return NT_STATUS_OK;
1921 default:
1922 return NT_STATUS_INVALID_INFO_CLASS;
1925 /* modify the samdb record */
1926 ret = ldb_modify(g_state->sam_ctx, msg);
1927 if (ret != LDB_SUCCESS) {
1928 return dsdb_ldb_err_to_ntstatus(ret);
1931 return NT_STATUS_OK;
1936 samr_AddGroupMember
1938 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1939 struct samr_AddGroupMember *r)
1941 struct dcesrv_handle *h;
1942 struct samr_account_state *a_state;
1943 struct samr_domain_state *d_state;
1944 struct ldb_message *mod;
1945 struct dom_sid *membersid;
1946 const char *memberdn;
1947 struct ldb_result *res;
1948 const char * const attrs[] = { NULL };
1949 int ret;
1951 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1953 a_state = h->data;
1954 d_state = a_state->domain_state;
1956 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1957 if (membersid == NULL) {
1958 return NT_STATUS_NO_MEMORY;
1961 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
1962 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
1963 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
1964 "(objectSid=%s)",
1965 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
1967 if (ret != LDB_SUCCESS) {
1968 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1971 if (res->count == 0) {
1972 return NT_STATUS_NO_SUCH_USER;
1975 if (res->count > 1) {
1976 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1979 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
1981 if (memberdn == NULL)
1982 return NT_STATUS_NO_MEMORY;
1984 mod = ldb_msg_new(mem_ctx);
1985 if (mod == NULL) {
1986 return NT_STATUS_NO_MEMORY;
1989 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
1991 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
1992 memberdn);
1993 if (ret != LDB_SUCCESS) {
1994 return dsdb_ldb_err_to_ntstatus(ret);
1997 ret = ldb_modify(a_state->sam_ctx, mod);
1998 switch (ret) {
1999 case LDB_SUCCESS:
2000 return NT_STATUS_OK;
2001 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2002 return NT_STATUS_MEMBER_IN_GROUP;
2003 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2004 return NT_STATUS_ACCESS_DENIED;
2005 default:
2006 return dsdb_ldb_err_to_ntstatus(ret);
2012 samr_DeleteDomainGroup
2014 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2015 struct samr_DeleteDomainGroup *r)
2017 struct dcesrv_handle *h;
2018 struct samr_account_state *a_state;
2019 int ret;
2021 *r->out.group_handle = *r->in.group_handle;
2023 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2025 a_state = h->data;
2027 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2028 if (ret != LDB_SUCCESS) {
2029 return dsdb_ldb_err_to_ntstatus(ret);
2032 talloc_free(h);
2033 ZERO_STRUCTP(r->out.group_handle);
2035 return NT_STATUS_OK;
2040 samr_DeleteGroupMember
2042 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2043 struct samr_DeleteGroupMember *r)
2045 struct dcesrv_handle *h;
2046 struct samr_account_state *a_state;
2047 struct samr_domain_state *d_state;
2048 struct ldb_message *mod;
2049 struct dom_sid *membersid;
2050 const char *memberdn;
2051 struct ldb_result *res;
2052 const char * const attrs[] = { NULL };
2053 int ret;
2055 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2057 a_state = h->data;
2058 d_state = a_state->domain_state;
2060 membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2061 if (membersid == NULL) {
2062 return NT_STATUS_NO_MEMORY;
2065 /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2066 ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2067 d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2068 "(objectSid=%s)",
2069 ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2071 if (ret != LDB_SUCCESS) {
2072 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2075 if (res->count == 0) {
2076 return NT_STATUS_NO_SUCH_USER;
2079 if (res->count > 1) {
2080 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2083 memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2085 if (memberdn == NULL)
2086 return NT_STATUS_NO_MEMORY;
2088 mod = ldb_msg_new(mem_ctx);
2089 if (mod == NULL) {
2090 return NT_STATUS_NO_MEMORY;
2093 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2095 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2096 memberdn);
2097 if (ret != LDB_SUCCESS) {
2098 return NT_STATUS_NO_MEMORY;
2101 ret = ldb_modify(a_state->sam_ctx, mod);
2102 switch (ret) {
2103 case LDB_SUCCESS:
2104 return NT_STATUS_OK;
2105 case LDB_ERR_UNWILLING_TO_PERFORM:
2106 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2107 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2108 return NT_STATUS_ACCESS_DENIED;
2109 default:
2110 return dsdb_ldb_err_to_ntstatus(ret);
2116 samr_QueryGroupMember
2118 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2119 struct samr_QueryGroupMember *r)
2121 struct dcesrv_handle *h;
2122 struct samr_account_state *a_state;
2123 struct samr_domain_state *d_state;
2124 struct samr_RidAttrArray *array;
2125 unsigned int i, num_members;
2126 struct dom_sid *members;
2127 NTSTATUS status;
2129 DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2131 a_state = h->data;
2132 d_state = a_state->domain_state;
2134 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2135 a_state->account_dn, &members,
2136 &num_members);
2137 if (!NT_STATUS_IS_OK(status)) {
2138 return status;
2141 array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2142 if (array == NULL) {
2143 return NT_STATUS_NO_MEMORY;
2146 if (num_members == 0) {
2147 *r->out.rids = array;
2149 return NT_STATUS_OK;
2152 array->rids = talloc_array(array, uint32_t, num_members);
2153 if (array->rids == NULL) {
2154 return NT_STATUS_NO_MEMORY;
2157 array->attributes = talloc_array(array, uint32_t, num_members);
2158 if (array->attributes == NULL) {
2159 return NT_STATUS_NO_MEMORY;
2162 array->count = 0;
2163 for (i=0; i<num_members; i++) {
2164 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2165 continue;
2168 status = dom_sid_split_rid(NULL, &members[i], NULL,
2169 &array->rids[array->count]);
2170 if (!NT_STATUS_IS_OK(status)) {
2171 return status;
2174 array->attributes[array->count] = SE_GROUP_MANDATORY |
2175 SE_GROUP_ENABLED_BY_DEFAULT |
2176 SE_GROUP_ENABLED;
2177 array->count++;
2180 *r->out.rids = array;
2182 return NT_STATUS_OK;
2187 samr_SetMemberAttributesOfGroup
2189 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2190 struct samr_SetMemberAttributesOfGroup *r)
2192 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2197 samr_OpenAlias
2199 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2200 struct samr_OpenAlias *r)
2202 struct samr_domain_state *d_state;
2203 struct samr_account_state *a_state;
2204 struct dcesrv_handle *h;
2205 const char *alias_name;
2206 struct dom_sid *sid;
2207 struct ldb_message **msgs;
2208 struct dcesrv_handle *g_handle;
2209 const char * const attrs[2] = { "sAMAccountName", NULL };
2210 int ret;
2212 ZERO_STRUCTP(r->out.alias_handle);
2214 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2216 d_state = h->data;
2218 /* form the alias SID */
2219 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2220 if (sid == NULL)
2221 return NT_STATUS_NO_MEMORY;
2223 /* search for the group record */
2224 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2225 "(&(objectSid=%s)(objectclass=group)"
2226 "(|(grouptype=%d)(grouptype=%d)))",
2227 ldap_encode_ndr_dom_sid(mem_ctx, sid),
2228 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2229 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2230 if (ret == 0) {
2231 return NT_STATUS_NO_SUCH_ALIAS;
2233 if (ret != 1) {
2234 DEBUG(0,("Found %d records matching sid %s\n",
2235 ret, dom_sid_string(mem_ctx, sid)));
2236 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2239 alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2240 if (alias_name == NULL) {
2241 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2242 dom_sid_string(mem_ctx, sid)));
2243 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2246 a_state = talloc(mem_ctx, struct samr_account_state);
2247 if (!a_state) {
2248 return NT_STATUS_NO_MEMORY;
2250 a_state->sam_ctx = d_state->sam_ctx;
2251 a_state->access_mask = r->in.access_mask;
2252 a_state->domain_state = talloc_reference(a_state, d_state);
2253 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2254 a_state->account_sid = talloc_steal(a_state, sid);
2255 a_state->account_name = talloc_strdup(a_state, alias_name);
2256 if (!a_state->account_name) {
2257 return NT_STATUS_NO_MEMORY;
2260 /* create the policy handle */
2261 g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2262 if (!g_handle) {
2263 return NT_STATUS_NO_MEMORY;
2266 g_handle->data = talloc_steal(g_handle, a_state);
2268 *r->out.alias_handle = g_handle->wire_handle;
2270 return NT_STATUS_OK;
2275 samr_QueryAliasInfo
2277 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2278 struct samr_QueryAliasInfo *r)
2280 struct dcesrv_handle *h;
2281 struct samr_account_state *a_state;
2282 struct ldb_message *msg, **res;
2283 const char * const attrs[4] = { "sAMAccountName", "description",
2284 "numMembers", NULL };
2285 int ret;
2286 union samr_AliasInfo *info;
2288 *r->out.info = NULL;
2290 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2292 a_state = h->data;
2294 /* pull all the alias attributes */
2295 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2296 a_state->account_dn, &res, attrs);
2297 if (ret == 0) {
2298 return NT_STATUS_NO_SUCH_ALIAS;
2300 if (ret != 1) {
2301 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2303 msg = res[0];
2305 /* allocate the info structure */
2306 info = talloc_zero(mem_ctx, union samr_AliasInfo);
2307 if (info == NULL) {
2308 return NT_STATUS_NO_MEMORY;
2311 switch(r->in.level) {
2312 case ALIASINFOALL:
2313 QUERY_STRING(msg, all.name, "sAMAccountName");
2314 QUERY_UINT (msg, all.num_members, "numMembers");
2315 QUERY_STRING(msg, all.description, "description");
2316 break;
2317 case ALIASINFONAME:
2318 QUERY_STRING(msg, name, "sAMAccountName");
2319 break;
2320 case ALIASINFODESCRIPTION:
2321 QUERY_STRING(msg, description, "description");
2322 break;
2323 default:
2324 talloc_free(info);
2325 return NT_STATUS_INVALID_INFO_CLASS;
2328 *r->out.info = info;
2330 return NT_STATUS_OK;
2335 samr_SetAliasInfo
2337 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2338 struct samr_SetAliasInfo *r)
2340 struct dcesrv_handle *h;
2341 struct samr_account_state *a_state;
2342 struct ldb_message *msg;
2343 struct ldb_context *sam_ctx;
2344 int ret;
2346 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2348 a_state = h->data;
2349 sam_ctx = a_state->sam_ctx;
2351 msg = ldb_msg_new(mem_ctx);
2352 if (msg == NULL) {
2353 return NT_STATUS_NO_MEMORY;
2356 msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2357 if (!msg->dn) {
2358 return NT_STATUS_NO_MEMORY;
2361 switch (r->in.level) {
2362 case ALIASINFODESCRIPTION:
2363 SET_STRING(msg, description, "description");
2364 break;
2365 case ALIASINFONAME:
2366 /* On W2k3 this does not change the name, it changes the
2367 * sAMAccountName attribute */
2368 SET_STRING(msg, name, "sAMAccountName");
2369 break;
2370 default:
2371 return NT_STATUS_INVALID_INFO_CLASS;
2374 /* modify the samdb record */
2375 ret = ldb_modify(a_state->sam_ctx, msg);
2376 if (ret != LDB_SUCCESS) {
2377 return dsdb_ldb_err_to_ntstatus(ret);
2380 return NT_STATUS_OK;
2385 samr_DeleteDomAlias
2387 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2388 struct samr_DeleteDomAlias *r)
2390 struct dcesrv_handle *h;
2391 struct samr_account_state *a_state;
2392 int ret;
2394 *r->out.alias_handle = *r->in.alias_handle;
2396 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2398 a_state = h->data;
2400 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2401 if (ret != LDB_SUCCESS) {
2402 return dsdb_ldb_err_to_ntstatus(ret);
2405 talloc_free(h);
2406 ZERO_STRUCTP(r->out.alias_handle);
2408 return NT_STATUS_OK;
2413 samr_AddAliasMember
2415 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2416 struct samr_AddAliasMember *r)
2418 struct dcesrv_handle *h;
2419 struct samr_account_state *a_state;
2420 struct samr_domain_state *d_state;
2421 struct ldb_message *mod;
2422 struct ldb_message **msgs;
2423 const char * const attrs[] = { NULL };
2424 struct ldb_dn *memberdn = NULL;
2425 int ret;
2426 NTSTATUS status;
2428 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2430 a_state = h->data;
2431 d_state = a_state->domain_state;
2433 ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2434 &msgs, attrs, "(objectsid=%s)",
2435 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2437 if (ret == 1) {
2438 memberdn = msgs[0]->dn;
2439 } else if (ret == 0) {
2440 status = samdb_create_foreign_security_principal(
2441 d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2442 if (!NT_STATUS_IS_OK(status)) {
2443 return status;
2445 } else {
2446 DEBUG(0,("Found %d records matching sid %s\n",
2447 ret, dom_sid_string(mem_ctx, r->in.sid)));
2448 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2451 if (memberdn == NULL) {
2452 DEBUG(0, ("Could not find memberdn\n"));
2453 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2456 mod = ldb_msg_new(mem_ctx);
2457 if (mod == NULL) {
2458 return NT_STATUS_NO_MEMORY;
2461 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2463 ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2464 ldb_dn_alloc_linearized(mem_ctx, memberdn));
2465 if (ret != LDB_SUCCESS) {
2466 return dsdb_ldb_err_to_ntstatus(ret);
2469 ret = ldb_modify(a_state->sam_ctx, mod);
2470 switch (ret) {
2471 case LDB_SUCCESS:
2472 return NT_STATUS_OK;
2473 case LDB_ERR_ENTRY_ALREADY_EXISTS:
2474 return NT_STATUS_MEMBER_IN_GROUP;
2475 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2476 return NT_STATUS_ACCESS_DENIED;
2477 default:
2478 return dsdb_ldb_err_to_ntstatus(ret);
2484 samr_DeleteAliasMember
2486 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2487 struct samr_DeleteAliasMember *r)
2489 struct dcesrv_handle *h;
2490 struct samr_account_state *a_state;
2491 struct samr_domain_state *d_state;
2492 struct ldb_message *mod;
2493 const char *memberdn;
2494 int ret;
2496 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2498 a_state = h->data;
2499 d_state = a_state->domain_state;
2501 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2502 "distinguishedName", "(objectSid=%s)",
2503 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2504 if (memberdn == NULL) {
2505 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2508 mod = ldb_msg_new(mem_ctx);
2509 if (mod == NULL) {
2510 return NT_STATUS_NO_MEMORY;
2513 mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2515 ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2516 memberdn);
2517 if (ret != LDB_SUCCESS) {
2518 return dsdb_ldb_err_to_ntstatus(ret);
2521 ret = ldb_modify(a_state->sam_ctx, mod);
2522 switch (ret) {
2523 case LDB_SUCCESS:
2524 return NT_STATUS_OK;
2525 case LDB_ERR_UNWILLING_TO_PERFORM:
2526 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2527 case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2528 return NT_STATUS_ACCESS_DENIED;
2529 default:
2530 return dsdb_ldb_err_to_ntstatus(ret);
2536 samr_GetMembersInAlias
2538 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2539 struct samr_GetMembersInAlias *r)
2541 struct dcesrv_handle *h;
2542 struct samr_account_state *a_state;
2543 struct samr_domain_state *d_state;
2544 struct lsa_SidPtr *array;
2545 unsigned int i, num_members;
2546 struct dom_sid *members;
2547 NTSTATUS status;
2549 DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2551 a_state = h->data;
2552 d_state = a_state->domain_state;
2554 status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2555 a_state->account_dn, &members,
2556 &num_members);
2557 if (!NT_STATUS_IS_OK(status)) {
2558 return status;
2561 if (num_members == 0) {
2562 r->out.sids->sids = NULL;
2564 return NT_STATUS_OK;
2567 array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
2568 if (array == NULL) {
2569 return NT_STATUS_NO_MEMORY;
2572 for (i=0; i<num_members; i++) {
2573 array[i].sid = &members[i];
2576 r->out.sids->num_sids = num_members;
2577 r->out.sids->sids = array;
2579 return NT_STATUS_OK;
2583 samr_OpenUser
2585 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2586 struct samr_OpenUser *r)
2588 struct samr_domain_state *d_state;
2589 struct samr_account_state *a_state;
2590 struct dcesrv_handle *h;
2591 const char *account_name;
2592 struct dom_sid *sid;
2593 struct ldb_message **msgs;
2594 struct dcesrv_handle *u_handle;
2595 const char * const attrs[2] = { "sAMAccountName", NULL };
2596 int ret;
2598 ZERO_STRUCTP(r->out.user_handle);
2600 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2602 d_state = h->data;
2604 /* form the users SID */
2605 sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2606 if (!sid) {
2607 return NT_STATUS_NO_MEMORY;
2610 /* search for the user record */
2611 ret = gendb_search(d_state->sam_ctx,
2612 mem_ctx, d_state->domain_dn, &msgs, attrs,
2613 "(&(objectSid=%s)(objectclass=user))",
2614 ldap_encode_ndr_dom_sid(mem_ctx, sid));
2615 if (ret == 0) {
2616 return NT_STATUS_NO_SUCH_USER;
2618 if (ret != 1) {
2619 DEBUG(0,("Found %d records matching sid %s\n", ret,
2620 dom_sid_string(mem_ctx, sid)));
2621 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2624 account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2625 if (account_name == NULL) {
2626 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2627 dom_sid_string(mem_ctx, sid)));
2628 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2631 a_state = talloc(mem_ctx, struct samr_account_state);
2632 if (!a_state) {
2633 return NT_STATUS_NO_MEMORY;
2635 a_state->sam_ctx = d_state->sam_ctx;
2636 a_state->access_mask = r->in.access_mask;
2637 a_state->domain_state = talloc_reference(a_state, d_state);
2638 a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2639 a_state->account_sid = talloc_steal(a_state, sid);
2640 a_state->account_name = talloc_strdup(a_state, account_name);
2641 if (!a_state->account_name) {
2642 return NT_STATUS_NO_MEMORY;
2645 /* create the policy handle */
2646 u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
2647 if (!u_handle) {
2648 return NT_STATUS_NO_MEMORY;
2651 u_handle->data = talloc_steal(u_handle, a_state);
2653 *r->out.user_handle = u_handle->wire_handle;
2655 return NT_STATUS_OK;
2661 samr_DeleteUser
2663 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2664 struct samr_DeleteUser *r)
2666 struct dcesrv_handle *h;
2667 struct samr_account_state *a_state;
2668 int ret;
2670 *r->out.user_handle = *r->in.user_handle;
2672 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2674 a_state = h->data;
2676 ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2677 if (ret != LDB_SUCCESS) {
2678 DEBUG(1, ("Failed to delete user: %s: %s\n",
2679 ldb_dn_get_linearized(a_state->account_dn),
2680 ldb_errstring(a_state->sam_ctx)));
2681 return dsdb_ldb_err_to_ntstatus(ret);
2684 talloc_free(h);
2685 ZERO_STRUCTP(r->out.user_handle);
2687 return NT_STATUS_OK;
2692 samr_QueryUserInfo
2694 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2695 struct samr_QueryUserInfo *r)
2697 struct dcesrv_handle *h;
2698 struct samr_account_state *a_state;
2699 struct ldb_message *msg, **res;
2700 int ret;
2701 struct ldb_context *sam_ctx;
2703 const char * const *attrs = NULL;
2704 union samr_UserInfo *info;
2706 *r->out.info = NULL;
2708 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2710 a_state = h->data;
2711 sam_ctx = a_state->sam_ctx;
2713 /* fill in the reply */
2714 switch (r->in.level) {
2715 case 1:
2717 static const char * const attrs2[] = {"sAMAccountName",
2718 "displayName",
2719 "primaryroupID",
2720 "description",
2721 "comment",
2722 NULL};
2723 attrs = attrs2;
2724 break;
2726 case 2:
2728 static const char * const attrs2[] = {"comment",
2729 "countryCode",
2730 "codePage",
2731 NULL};
2732 attrs = attrs2;
2733 break;
2735 case 3:
2737 static const char * const attrs2[] = {"sAMAccountName",
2738 "displayName",
2739 "objectSid",
2740 "primaryGroupID",
2741 "homeDirectory",
2742 "homeDrive",
2743 "scriptPath",
2744 "profilePath",
2745 "userWorkstations",
2746 "lastLogon",
2747 "lastLogoff",
2748 "pwdLastSet",
2749 "logonHours",
2750 "badPwdCount",
2751 "logonCount",
2752 "userAccountControl",
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 NULL};
2785 attrs = attrs2;
2786 break;
2788 case 6:
2790 static const char * const attrs2[] = {"sAMAccountName",
2791 "displayName",
2792 NULL};
2793 attrs = attrs2;
2794 break;
2796 case 7:
2798 static const char * const attrs2[] = {"sAMAccountName",
2799 NULL};
2800 attrs = attrs2;
2801 break;
2803 case 8:
2805 static const char * const attrs2[] = {"displayName",
2806 NULL};
2807 attrs = attrs2;
2808 break;
2810 case 9:
2812 static const char * const attrs2[] = {"primaryGroupID",
2813 NULL};
2814 attrs = attrs2;
2815 break;
2817 case 10:
2819 static const char * const attrs2[] = {"homeDirectory",
2820 "homeDrive",
2821 NULL};
2822 attrs = attrs2;
2823 break;
2825 case 11:
2827 static const char * const attrs2[] = {"scriptPath",
2828 NULL};
2829 attrs = attrs2;
2830 break;
2832 case 12:
2834 static const char * const attrs2[] = {"profilePath",
2835 NULL};
2836 attrs = attrs2;
2837 break;
2839 case 13:
2841 static const char * const attrs2[] = {"description",
2842 NULL};
2843 attrs = attrs2;
2844 break;
2846 case 14:
2848 static const char * const attrs2[] = {"userWorkstations",
2849 NULL};
2850 attrs = attrs2;
2851 break;
2853 case 16:
2855 static const char * const attrs2[] = {"userAccountControl",
2856 "pwdLastSet",
2857 NULL};
2858 attrs = attrs2;
2859 break;
2861 case 17:
2863 static const char * const attrs2[] = {"accountExpires",
2864 NULL};
2865 attrs = attrs2;
2866 break;
2868 case 18:
2870 return NT_STATUS_NOT_SUPPORTED;
2872 case 20:
2874 static const char * const attrs2[] = {"userParameters",
2875 NULL};
2876 attrs = attrs2;
2877 break;
2879 case 21:
2881 static const char * const attrs2[] = {"lastLogon",
2882 "lastLogoff",
2883 "pwdLastSet",
2884 "accountExpires",
2885 "sAMAccountName",
2886 "displayName",
2887 "homeDirectory",
2888 "homeDrive",
2889 "scriptPath",
2890 "profilePath",
2891 "description",
2892 "userWorkstations",
2893 "comment",
2894 "userParameters",
2895 "objectSid",
2896 "primaryGroupID",
2897 "userAccountControl",
2898 "logonHours",
2899 "badPwdCount",
2900 "logonCount",
2901 "countryCode",
2902 "codePage",
2903 NULL};
2904 attrs = attrs2;
2905 break;
2907 case 23:
2908 case 24:
2909 case 25:
2910 case 26:
2912 return NT_STATUS_NOT_SUPPORTED;
2914 default:
2916 return NT_STATUS_INVALID_INFO_CLASS;
2920 /* pull all the user attributes */
2921 ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2922 a_state->account_dn, &res, attrs);
2923 if (ret == 0) {
2924 return NT_STATUS_NO_SUCH_USER;
2926 if (ret != 1) {
2927 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2929 msg = res[0];
2931 /* allocate the info structure */
2932 info = talloc_zero(mem_ctx, union samr_UserInfo);
2933 if (info == NULL) {
2934 return NT_STATUS_NO_MEMORY;
2937 /* fill in the reply */
2938 switch (r->in.level) {
2939 case 1:
2940 QUERY_STRING(msg, info1.account_name, "sAMAccountName");
2941 QUERY_STRING(msg, info1.full_name, "displayName");
2942 QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
2943 QUERY_STRING(msg, info1.description, "description");
2944 QUERY_STRING(msg, info1.comment, "comment");
2945 break;
2947 case 2:
2948 QUERY_STRING(msg, info2.comment, "comment");
2949 QUERY_UINT (msg, info2.country_code, "countryCode");
2950 QUERY_UINT (msg, info2.code_page, "codePage");
2951 break;
2953 case 3:
2954 QUERY_STRING(msg, info3.account_name, "sAMAccountName");
2955 QUERY_STRING(msg, info3.full_name, "displayName");
2956 QUERY_RID (msg, info3.rid, "objectSid");
2957 QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
2958 QUERY_STRING(msg, info3.home_directory, "homeDirectory");
2959 QUERY_STRING(msg, info3.home_drive, "homeDrive");
2960 QUERY_STRING(msg, info3.logon_script, "scriptPath");
2961 QUERY_STRING(msg, info3.profile_path, "profilePath");
2962 QUERY_STRING(msg, info3.workstations, "userWorkstations");
2963 QUERY_UINT64(msg, info3.last_logon, "lastLogon");
2964 QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
2965 QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
2966 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
2967 QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
2968 QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
2969 QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
2970 QUERY_UINT (msg, info3.logon_count, "logonCount");
2971 QUERY_AFLAGS(msg, info3.acct_flags, "userAccountControl");
2972 break;
2974 case 4:
2975 QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
2976 break;
2978 case 5:
2979 QUERY_STRING(msg, info5.account_name, "sAMAccountName");
2980 QUERY_STRING(msg, info5.full_name, "displayName");
2981 QUERY_RID (msg, info5.rid, "objectSid");
2982 QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
2983 QUERY_STRING(msg, info5.home_directory, "homeDirectory");
2984 QUERY_STRING(msg, info5.home_drive, "homeDrive");
2985 QUERY_STRING(msg, info5.logon_script, "scriptPath");
2986 QUERY_STRING(msg, info5.profile_path, "profilePath");
2987 QUERY_STRING(msg, info5.description, "description");
2988 QUERY_STRING(msg, info5.workstations, "userWorkstations");
2989 QUERY_UINT64(msg, info5.last_logon, "lastLogon");
2990 QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
2991 QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
2992 QUERY_UINT (msg, info5.bad_password_count, "badPwdCount");
2993 QUERY_UINT (msg, info5.logon_count, "logonCount");
2994 QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
2995 QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
2996 QUERY_AFLAGS(msg, info5.acct_flags, "userAccountControl");
2997 break;
2999 case 6:
3000 QUERY_STRING(msg, info6.account_name, "sAMAccountName");
3001 QUERY_STRING(msg, info6.full_name, "displayName");
3002 break;
3004 case 7:
3005 QUERY_STRING(msg, info7.account_name, "sAMAccountName");
3006 break;
3008 case 8:
3009 QUERY_STRING(msg, info8.full_name, "displayName");
3010 break;
3012 case 9:
3013 QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
3014 break;
3016 case 10:
3017 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3018 QUERY_STRING(msg, info10.home_drive, "homeDrive");
3019 break;
3021 case 11:
3022 QUERY_STRING(msg, info11.logon_script, "scriptPath");
3023 break;
3025 case 12:
3026 QUERY_STRING(msg, info12.profile_path, "profilePath");
3027 break;
3029 case 13:
3030 QUERY_STRING(msg, info13.description, "description");
3031 break;
3033 case 14:
3034 QUERY_STRING(msg, info14.workstations, "userWorkstations");
3035 break;
3037 case 16:
3038 QUERY_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3039 break;
3041 case 17:
3042 QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
3043 break;
3045 case 20:
3046 QUERY_PARAMETERS(msg, info20.parameters, "userParameters");
3047 break;
3049 case 21:
3050 QUERY_UINT64(msg, info21.last_logon, "lastLogon");
3051 QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
3052 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3053 QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
3054 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3055 QUERY_FPASSC(msg, info21.force_password_change,"pwdLastSet");
3056 QUERY_STRING(msg, info21.account_name, "sAMAccountName");
3057 QUERY_STRING(msg, info21.full_name, "displayName");
3058 QUERY_STRING(msg, info21.home_directory, "homeDirectory");
3059 QUERY_STRING(msg, info21.home_drive, "homeDrive");
3060 QUERY_STRING(msg, info21.logon_script, "scriptPath");
3061 QUERY_STRING(msg, info21.profile_path, "profilePath");
3062 QUERY_STRING(msg, info21.description, "description");
3063 QUERY_STRING(msg, info21.workstations, "userWorkstations");
3064 QUERY_STRING(msg, info21.comment, "comment");
3065 QUERY_PARAMETERS(msg, info21.parameters, "userParameters");
3066 QUERY_RID (msg, info21.rid, "objectSid");
3067 QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
3068 QUERY_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3069 info->info21.fields_present = 0x08FFFFFF;
3070 QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
3071 QUERY_UINT (msg, info21.bad_password_count, "badPwdCount");
3072 QUERY_UINT (msg, info21.logon_count, "logonCount");
3073 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3074 info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3075 } else {
3076 info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3078 QUERY_UINT (msg, info21.country_code, "countryCode");
3079 QUERY_UINT (msg, info21.code_page, "codePage");
3080 break;
3083 default:
3084 talloc_free(info);
3085 return NT_STATUS_INVALID_INFO_CLASS;
3088 *r->out.info = info;
3090 return NT_STATUS_OK;
3095 samr_SetUserInfo
3097 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3098 struct samr_SetUserInfo *r)
3100 struct dcesrv_handle *h;
3101 struct samr_account_state *a_state;
3102 struct ldb_message *msg;
3103 int ret;
3104 NTSTATUS status = NT_STATUS_OK;
3105 struct ldb_context *sam_ctx;
3107 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3109 a_state = h->data;
3110 sam_ctx = a_state->sam_ctx;
3112 msg = ldb_msg_new(mem_ctx);
3113 if (msg == NULL) {
3114 return NT_STATUS_NO_MEMORY;
3117 msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3118 if (!msg->dn) {
3119 return NT_STATUS_NO_MEMORY;
3122 switch (r->in.level) {
3123 case 2:
3124 SET_STRING(msg, info2.comment, "comment");
3125 SET_UINT (msg, info2.country_code, "countryCode");
3126 SET_UINT (msg, info2.code_page, "codePage");
3127 break;
3129 case 4:
3130 SET_LHOURS(msg, info4.logon_hours, "logonHours");
3131 break;
3133 case 6:
3134 SET_STRING(msg, info6.account_name, "samAccountName");
3135 SET_STRING(msg, info6.full_name, "displayName");
3136 break;
3138 case 7:
3139 SET_STRING(msg, info7.account_name, "samAccountName");
3140 break;
3142 case 8:
3143 SET_STRING(msg, info8.full_name, "displayName");
3144 break;
3146 case 9:
3147 SET_UINT(msg, info9.primary_gid, "primaryGroupID");
3148 break;
3150 case 10:
3151 SET_STRING(msg, info10.home_directory, "homeDirectory");
3152 SET_STRING(msg, info10.home_drive, "homeDrive");
3153 break;
3155 case 11:
3156 SET_STRING(msg, info11.logon_script, "scriptPath");
3157 break;
3159 case 12:
3160 SET_STRING(msg, info12.profile_path, "profilePath");
3161 break;
3163 case 13:
3164 SET_STRING(msg, info13.description, "description");
3165 break;
3167 case 14:
3168 SET_STRING(msg, info14.workstations, "userWorkstations");
3169 break;
3171 case 16:
3172 SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
3173 break;
3175 case 17:
3176 SET_UINT64(msg, info17.acct_expiry, "accountExpires");
3177 break;
3179 case 18:
3180 status = samr_set_password_buffers(dce_call,
3181 a_state->sam_ctx,
3182 a_state->account_dn,
3183 a_state->domain_state->domain_dn,
3184 mem_ctx,
3185 r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3186 r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3187 if (!NT_STATUS_IS_OK(status)) {
3188 return status;
3191 if (r->in.info->info18.password_expired > 0) {
3192 struct ldb_message_element *set_el;
3193 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3194 return NT_STATUS_NO_MEMORY;
3196 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3197 set_el->flags = LDB_FLAG_MOD_REPLACE;
3199 break;
3201 case 20:
3202 SET_PARAMETERS(msg, info20.parameters, "userParameters");
3203 break;
3205 case 21:
3206 if (r->in.info->info21.fields_present == 0)
3207 return NT_STATUS_INVALID_PARAMETER;
3209 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3210 IFSET(SAMR_FIELD_LAST_LOGON)
3211 SET_UINT64(msg, info21.last_logon, "lastLogon");
3212 IFSET(SAMR_FIELD_LAST_LOGOFF)
3213 SET_UINT64(msg, info21.last_logoff, "lastLogoff");
3214 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3215 SET_UINT64(msg, info21.acct_expiry, "accountExpires");
3216 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3217 SET_STRING(msg, info21.account_name, "samAccountName");
3218 IFSET(SAMR_FIELD_FULL_NAME)
3219 SET_STRING(msg, info21.full_name, "displayName");
3220 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3221 SET_STRING(msg, info21.home_directory, "homeDirectory");
3222 IFSET(SAMR_FIELD_HOME_DRIVE)
3223 SET_STRING(msg, info21.home_drive, "homeDrive");
3224 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3225 SET_STRING(msg, info21.logon_script, "scriptPath");
3226 IFSET(SAMR_FIELD_PROFILE_PATH)
3227 SET_STRING(msg, info21.profile_path, "profilePath");
3228 IFSET(SAMR_FIELD_DESCRIPTION)
3229 SET_STRING(msg, info21.description, "description");
3230 IFSET(SAMR_FIELD_WORKSTATIONS)
3231 SET_STRING(msg, info21.workstations, "userWorkstations");
3232 IFSET(SAMR_FIELD_COMMENT)
3233 SET_STRING(msg, info21.comment, "comment");
3234 IFSET(SAMR_FIELD_PARAMETERS)
3235 SET_PARAMETERS(msg, info21.parameters, "userParameters");
3236 IFSET(SAMR_FIELD_PRIMARY_GID)
3237 SET_UINT(msg, info21.primary_gid, "primaryGroupID");
3238 IFSET(SAMR_FIELD_ACCT_FLAGS)
3239 SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
3240 IFSET(SAMR_FIELD_LOGON_HOURS)
3241 SET_LHOURS(msg, info21.logon_hours, "logonHours");
3242 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3243 SET_UINT (msg, info21.bad_password_count, "badPwdCount");
3244 IFSET(SAMR_FIELD_NUM_LOGONS)
3245 SET_UINT (msg, info21.logon_count, "logonCount");
3246 IFSET(SAMR_FIELD_COUNTRY_CODE)
3247 SET_UINT (msg, info21.country_code, "countryCode");
3248 IFSET(SAMR_FIELD_CODE_PAGE)
3249 SET_UINT (msg, info21.code_page, "codePage");
3251 /* password change fields */
3252 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3253 return NT_STATUS_ACCESS_DENIED;
3255 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3256 | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3257 uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3259 if (r->in.info->info21.lm_password_set) {
3260 if ((r->in.info->info21.lm_owf_password.length != 16)
3261 || (r->in.info->info21.lm_owf_password.size != 16)) {
3262 return NT_STATUS_INVALID_PARAMETER;
3265 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3267 if (r->in.info->info21.nt_password_set) {
3268 if ((r->in.info->info21.nt_owf_password.length != 16)
3269 || (r->in.info->info21.nt_owf_password.size != 16)) {
3270 return NT_STATUS_INVALID_PARAMETER;
3273 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3275 status = samr_set_password_buffers(dce_call,
3276 a_state->sam_ctx,
3277 a_state->account_dn,
3278 a_state->domain_state->domain_dn,
3279 mem_ctx,
3280 lm_pwd_hash,
3281 nt_pwd_hash);
3282 if (!NT_STATUS_IS_OK(status)) {
3283 return status;
3288 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3289 NTTIME t = 0;
3290 struct ldb_message_element *set_el;
3291 if (r->in.info->info21.password_expired
3292 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3293 unix_to_nt_time(&t, time(NULL));
3295 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3296 "pwdLastSet", t) != LDB_SUCCESS) {
3297 return NT_STATUS_NO_MEMORY;
3299 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3300 set_el->flags = LDB_FLAG_MOD_REPLACE;
3302 #undef IFSET
3303 break;
3305 case 23:
3306 if (r->in.info->info23.info.fields_present == 0)
3307 return NT_STATUS_INVALID_PARAMETER;
3309 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3310 IFSET(SAMR_FIELD_LAST_LOGON)
3311 SET_UINT64(msg, info23.info.last_logon, "lastLogon");
3312 IFSET(SAMR_FIELD_LAST_LOGOFF)
3313 SET_UINT64(msg, info23.info.last_logoff, "lastLogoff");
3314 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3315 SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
3316 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3317 SET_STRING(msg, info23.info.account_name, "samAccountName");
3318 IFSET(SAMR_FIELD_FULL_NAME)
3319 SET_STRING(msg, info23.info.full_name, "displayName");
3320 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3321 SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3322 IFSET(SAMR_FIELD_HOME_DRIVE)
3323 SET_STRING(msg, info23.info.home_drive, "homeDrive");
3324 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3325 SET_STRING(msg, info23.info.logon_script, "scriptPath");
3326 IFSET(SAMR_FIELD_PROFILE_PATH)
3327 SET_STRING(msg, info23.info.profile_path, "profilePath");
3328 IFSET(SAMR_FIELD_DESCRIPTION)
3329 SET_STRING(msg, info23.info.description, "description");
3330 IFSET(SAMR_FIELD_WORKSTATIONS)
3331 SET_STRING(msg, info23.info.workstations, "userWorkstations");
3332 IFSET(SAMR_FIELD_COMMENT)
3333 SET_STRING(msg, info23.info.comment, "comment");
3334 IFSET(SAMR_FIELD_PARAMETERS)
3335 SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3336 IFSET(SAMR_FIELD_PRIMARY_GID)
3337 SET_UINT(msg, info23.info.primary_gid, "primaryGroupID");
3338 IFSET(SAMR_FIELD_ACCT_FLAGS)
3339 SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
3340 IFSET(SAMR_FIELD_LOGON_HOURS)
3341 SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
3342 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3343 SET_UINT (msg, info23.info.bad_password_count, "badPwdCount");
3344 IFSET(SAMR_FIELD_NUM_LOGONS)
3345 SET_UINT (msg, info23.info.logon_count, "logonCount");
3347 IFSET(SAMR_FIELD_COUNTRY_CODE)
3348 SET_UINT (msg, info23.info.country_code, "countryCode");
3349 IFSET(SAMR_FIELD_CODE_PAGE)
3350 SET_UINT (msg, info23.info.code_page, "codePage");
3352 /* password change fields */
3353 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3354 return NT_STATUS_ACCESS_DENIED;
3356 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3357 status = samr_set_password(dce_call,
3358 a_state->sam_ctx,
3359 a_state->account_dn,
3360 a_state->domain_state->domain_dn,
3361 mem_ctx,
3362 &r->in.info->info23.password);
3363 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3364 status = samr_set_password(dce_call,
3365 a_state->sam_ctx,
3366 a_state->account_dn,
3367 a_state->domain_state->domain_dn,
3368 mem_ctx,
3369 &r->in.info->info23.password);
3371 if (!NT_STATUS_IS_OK(status)) {
3372 return status;
3375 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3376 NTTIME t = 0;
3377 struct ldb_message_element *set_el;
3378 if (r->in.info->info23.info.password_expired
3379 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3380 unix_to_nt_time(&t, time(NULL));
3382 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3383 "pwdLastSet", t) != LDB_SUCCESS) {
3384 return NT_STATUS_NO_MEMORY;
3386 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3387 set_el->flags = LDB_FLAG_MOD_REPLACE;
3389 #undef IFSET
3390 break;
3392 /* the set password levels are handled separately */
3393 case 24:
3394 status = samr_set_password(dce_call,
3395 a_state->sam_ctx,
3396 a_state->account_dn,
3397 a_state->domain_state->domain_dn,
3398 mem_ctx,
3399 &r->in.info->info24.password);
3400 if (!NT_STATUS_IS_OK(status)) {
3401 return status;
3404 if (r->in.info->info24.password_expired > 0) {
3405 struct ldb_message_element *set_el;
3406 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3407 return NT_STATUS_NO_MEMORY;
3409 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3410 set_el->flags = LDB_FLAG_MOD_REPLACE;
3412 break;
3414 case 25:
3415 if (r->in.info->info25.info.fields_present == 0)
3416 return NT_STATUS_INVALID_PARAMETER;
3418 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3419 IFSET(SAMR_FIELD_LAST_LOGON)
3420 SET_UINT64(msg, info25.info.last_logon, "lastLogon");
3421 IFSET(SAMR_FIELD_LAST_LOGOFF)
3422 SET_UINT64(msg, info25.info.last_logoff, "lastLogoff");
3423 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3424 SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
3425 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3426 SET_STRING(msg, info25.info.account_name, "samAccountName");
3427 IFSET(SAMR_FIELD_FULL_NAME)
3428 SET_STRING(msg, info25.info.full_name, "displayName");
3429 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3430 SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3431 IFSET(SAMR_FIELD_HOME_DRIVE)
3432 SET_STRING(msg, info25.info.home_drive, "homeDrive");
3433 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3434 SET_STRING(msg, info25.info.logon_script, "scriptPath");
3435 IFSET(SAMR_FIELD_PROFILE_PATH)
3436 SET_STRING(msg, info25.info.profile_path, "profilePath");
3437 IFSET(SAMR_FIELD_DESCRIPTION)
3438 SET_STRING(msg, info25.info.description, "description");
3439 IFSET(SAMR_FIELD_WORKSTATIONS)
3440 SET_STRING(msg, info25.info.workstations, "userWorkstations");
3441 IFSET(SAMR_FIELD_COMMENT)
3442 SET_STRING(msg, info25.info.comment, "comment");
3443 IFSET(SAMR_FIELD_PARAMETERS)
3444 SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
3445 IFSET(SAMR_FIELD_PRIMARY_GID)
3446 SET_UINT(msg, info25.info.primary_gid, "primaryGroupID");
3447 IFSET(SAMR_FIELD_ACCT_FLAGS)
3448 SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
3449 IFSET(SAMR_FIELD_LOGON_HOURS)
3450 SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
3451 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3452 SET_UINT (msg, info25.info.bad_password_count, "badPwdCount");
3453 IFSET(SAMR_FIELD_NUM_LOGONS)
3454 SET_UINT (msg, info25.info.logon_count, "logonCount");
3455 IFSET(SAMR_FIELD_COUNTRY_CODE)
3456 SET_UINT (msg, info25.info.country_code, "countryCode");
3457 IFSET(SAMR_FIELD_CODE_PAGE)
3458 SET_UINT (msg, info25.info.code_page, "codePage");
3460 /* password change fields */
3461 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
3462 return NT_STATUS_ACCESS_DENIED;
3464 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3465 status = samr_set_password_ex(dce_call,
3466 a_state->sam_ctx,
3467 a_state->account_dn,
3468 a_state->domain_state->domain_dn,
3469 mem_ctx,
3470 &r->in.info->info25.password);
3471 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3472 status = samr_set_password_ex(dce_call,
3473 a_state->sam_ctx,
3474 a_state->account_dn,
3475 a_state->domain_state->domain_dn,
3476 mem_ctx,
3477 &r->in.info->info25.password);
3479 if (!NT_STATUS_IS_OK(status)) {
3480 return status;
3483 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3484 NTTIME t = 0;
3485 struct ldb_message_element *set_el;
3486 if (r->in.info->info25.info.password_expired
3487 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3488 unix_to_nt_time(&t, time(NULL));
3490 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3491 "pwdLastSet", t) != LDB_SUCCESS) {
3492 return NT_STATUS_NO_MEMORY;
3494 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3495 set_el->flags = LDB_FLAG_MOD_REPLACE;
3497 #undef IFSET
3498 break;
3500 /* the set password levels are handled separately */
3501 case 26:
3502 status = samr_set_password_ex(dce_call,
3503 a_state->sam_ctx,
3504 a_state->account_dn,
3505 a_state->domain_state->domain_dn,
3506 mem_ctx,
3507 &r->in.info->info26.password);
3508 if (!NT_STATUS_IS_OK(status)) {
3509 return status;
3512 if (r->in.info->info26.password_expired > 0) {
3513 NTTIME t = 0;
3514 struct ldb_message_element *set_el;
3515 if (r->in.info->info26.password_expired
3516 == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3517 unix_to_nt_time(&t, time(NULL));
3519 if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg,
3520 "pwdLastSet", t) != LDB_SUCCESS) {
3521 return NT_STATUS_NO_MEMORY;
3523 set_el = ldb_msg_find_element(msg, "pwdLastSet");
3524 set_el->flags = LDB_FLAG_MOD_REPLACE;
3526 break;
3528 default:
3529 /* many info classes are not valid for SetUserInfo */
3530 return NT_STATUS_INVALID_INFO_CLASS;
3533 if (!NT_STATUS_IS_OK(status)) {
3534 return status;
3537 /* modify the samdb record */
3538 if (msg->num_elements > 0) {
3539 ret = ldb_modify(a_state->sam_ctx, msg);
3540 if (ret != LDB_SUCCESS) {
3541 DEBUG(1,("Failed to modify record %s: %s\n",
3542 ldb_dn_get_linearized(a_state->account_dn),
3543 ldb_errstring(a_state->sam_ctx)));
3545 return dsdb_ldb_err_to_ntstatus(ret);
3549 return NT_STATUS_OK;
3554 samr_GetGroupsForUser
3556 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3557 struct samr_GetGroupsForUser *r)
3559 struct dcesrv_handle *h;
3560 struct samr_account_state *a_state;
3561 struct samr_domain_state *d_state;
3562 struct ldb_message **res;
3563 const char * const attrs[2] = { "objectSid", NULL };
3564 struct samr_RidWithAttributeArray *array;
3565 int i, count;
3567 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3569 a_state = h->data;
3570 d_state = a_state->domain_state;
3572 count = samdb_search_domain(a_state->sam_ctx, mem_ctx,
3573 d_state->domain_dn, &res,
3574 attrs, d_state->domain_sid,
3575 "(&(member=%s)(|(grouptype=%d)(grouptype=%d))(objectclass=group))",
3576 ldb_dn_get_linearized(a_state->account_dn),
3577 GTYPE_SECURITY_UNIVERSAL_GROUP,
3578 GTYPE_SECURITY_GLOBAL_GROUP);
3579 if (count < 0)
3580 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3582 array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
3583 if (array == NULL)
3584 return NT_STATUS_NO_MEMORY;
3586 array->count = 0;
3587 array->rids = NULL;
3589 array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
3590 count + 1);
3591 if (array->rids == NULL)
3592 return NT_STATUS_NO_MEMORY;
3594 /* Adds the primary group */
3595 array->rids[0].rid = samdb_search_uint(a_state->sam_ctx, mem_ctx,
3596 ~0, a_state->account_dn,
3597 "primaryGroupID", NULL);
3598 array->rids[0].attributes = SE_GROUP_MANDATORY
3599 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3600 array->count += 1;
3602 /* Adds the additional groups */
3603 for (i = 0; i < count; i++) {
3604 struct dom_sid *group_sid;
3606 group_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
3607 if (group_sid == NULL) {
3608 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3611 array->rids[i + 1].rid =
3612 group_sid->sub_auths[group_sid->num_auths-1];
3613 array->rids[i + 1].attributes = SE_GROUP_MANDATORY
3614 | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3615 array->count += 1;
3618 *r->out.rids = array;
3620 return NT_STATUS_OK;
3625 samr_QueryDisplayInfo
3627 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3628 struct samr_QueryDisplayInfo *r)
3630 struct dcesrv_handle *h;
3631 struct samr_domain_state *d_state;
3632 struct ldb_result *res;
3633 unsigned int i;
3634 uint32_t count;
3635 const char * const attrs[] = { "objectSid", "sAMAccountName",
3636 "displayName", "description", "userAccountControl",
3637 "pwdLastSet", NULL };
3638 struct samr_DispEntryFull *entriesFull = NULL;
3639 struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
3640 struct samr_DispEntryAscii *entriesAscii = NULL;
3641 struct samr_DispEntryGeneral *entriesGeneral = NULL;
3642 const char *filter;
3643 int ret;
3645 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3647 d_state = h->data;
3649 switch (r->in.level) {
3650 case 1:
3651 case 4:
3652 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3653 "(sAMAccountType=%d))",
3654 ATYPE_NORMAL_ACCOUNT);
3655 break;
3656 case 2:
3657 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
3658 "(sAMAccountType=%d))",
3659 ATYPE_WORKSTATION_TRUST);
3660 break;
3661 case 3:
3662 case 5:
3663 filter = talloc_asprintf(mem_ctx,
3664 "(&(|(groupType=%d)(groupType=%d))"
3665 "(objectClass=group))",
3666 GTYPE_SECURITY_UNIVERSAL_GROUP,
3667 GTYPE_SECURITY_GLOBAL_GROUP);
3668 break;
3669 default:
3670 return NT_STATUS_INVALID_INFO_CLASS;
3673 /* search for all requested objects in all domains. This could
3674 possibly be cached and resumed based on resume_key */
3675 ret = dsdb_search(d_state->sam_ctx, mem_ctx, &res, ldb_get_default_basedn(d_state->sam_ctx),
3676 LDB_SCOPE_SUBTREE, attrs, 0, "%s", filter);
3677 if (ret != LDB_SUCCESS) {
3678 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3680 if ((res->count == 0) || (r->in.max_entries == 0)) {
3681 return NT_STATUS_OK;
3684 switch (r->in.level) {
3685 case 1:
3686 entriesGeneral = talloc_array(mem_ctx,
3687 struct samr_DispEntryGeneral,
3688 res->count);
3689 break;
3690 case 2:
3691 entriesFull = talloc_array(mem_ctx,
3692 struct samr_DispEntryFull,
3693 res->count);
3694 break;
3695 case 3:
3696 entriesFullGroup = talloc_array(mem_ctx,
3697 struct samr_DispEntryFullGroup,
3698 res->count);
3699 break;
3700 case 4:
3701 case 5:
3702 entriesAscii = talloc_array(mem_ctx,
3703 struct samr_DispEntryAscii,
3704 res->count);
3705 break;
3708 if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
3709 (entriesAscii == NULL) && (entriesFullGroup == NULL))
3710 return NT_STATUS_NO_MEMORY;
3712 count = 0;
3714 for (i = 0; i < res->count; i++) {
3715 struct dom_sid *objectsid;
3717 objectsid = samdb_result_dom_sid(mem_ctx, res->msgs[i],
3718 "objectSid");
3719 if (objectsid == NULL)
3720 continue;
3722 switch(r->in.level) {
3723 case 1:
3724 entriesGeneral[count].idx = count + 1;
3725 entriesGeneral[count].rid =
3726 objectsid->sub_auths[objectsid->num_auths-1];
3727 entriesGeneral[count].acct_flags =
3728 samdb_result_acct_flags(d_state->sam_ctx,
3729 mem_ctx,
3730 res->msgs[i],
3731 d_state->domain_dn);
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(d_state->sam_ctx,
3750 mem_ctx,
3751 res->msgs[i],
3752 d_state->domain_dn) | ACB_NORMAL;
3753 entriesFull[count].account_name.string =
3754 ldb_msg_find_attr_as_string(res->msgs[i],
3755 "sAMAccountName", "");
3756 entriesFull[count].description.string =
3757 ldb_msg_find_attr_as_string(res->msgs[i],
3758 "description", "");
3759 break;
3760 case 3:
3761 entriesFullGroup[count].idx = count + 1;
3762 entriesFullGroup[count].rid =
3763 objectsid->sub_auths[objectsid->num_auths-1];
3764 /* We get a "7" here for groups */
3765 entriesFullGroup[count].acct_flags
3766 = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
3767 entriesFullGroup[count].account_name.string =
3768 ldb_msg_find_attr_as_string(res->msgs[i],
3769 "sAMAccountName", "");
3770 entriesFullGroup[count].description.string =
3771 ldb_msg_find_attr_as_string(res->msgs[i],
3772 "description", "");
3773 break;
3774 case 4:
3775 case 5:
3776 entriesAscii[count].idx = count + 1;
3777 entriesAscii[count].account_name.string =
3778 ldb_msg_find_attr_as_string(res->msgs[i],
3779 "sAMAccountName", "");
3780 break;
3783 count += 1;
3786 *r->out.total_size = count;
3788 if (r->in.start_idx >= count) {
3789 *r->out.returned_size = 0;
3790 switch(r->in.level) {
3791 case 1:
3792 r->out.info->info1.count = *r->out.returned_size;
3793 r->out.info->info1.entries = NULL;
3794 break;
3795 case 2:
3796 r->out.info->info2.count = *r->out.returned_size;
3797 r->out.info->info2.entries = NULL;
3798 break;
3799 case 3:
3800 r->out.info->info3.count = *r->out.returned_size;
3801 r->out.info->info3.entries = NULL;
3802 break;
3803 case 4:
3804 r->out.info->info4.count = *r->out.returned_size;
3805 r->out.info->info4.entries = NULL;
3806 break;
3807 case 5:
3808 r->out.info->info5.count = *r->out.returned_size;
3809 r->out.info->info5.entries = NULL;
3810 break;
3812 } else {
3813 *r->out.returned_size = MIN(count - r->in.start_idx,
3814 r->in.max_entries);
3815 switch(r->in.level) {
3816 case 1:
3817 r->out.info->info1.count = *r->out.returned_size;
3818 r->out.info->info1.entries =
3819 &(entriesGeneral[r->in.start_idx]);
3820 break;
3821 case 2:
3822 r->out.info->info2.count = *r->out.returned_size;
3823 r->out.info->info2.entries =
3824 &(entriesFull[r->in.start_idx]);
3825 break;
3826 case 3:
3827 r->out.info->info3.count = *r->out.returned_size;
3828 r->out.info->info3.entries =
3829 &(entriesFullGroup[r->in.start_idx]);
3830 break;
3831 case 4:
3832 r->out.info->info4.count = *r->out.returned_size;
3833 r->out.info->info4.entries =
3834 &(entriesAscii[r->in.start_idx]);
3835 break;
3836 case 5:
3837 r->out.info->info5.count = *r->out.returned_size;
3838 r->out.info->info5.entries =
3839 &(entriesAscii[r->in.start_idx]);
3840 break;
3844 return (*r->out.returned_size < (count - r->in.start_idx)) ?
3845 STATUS_MORE_ENTRIES : NT_STATUS_OK;
3850 samr_GetDisplayEnumerationIndex
3852 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3853 struct samr_GetDisplayEnumerationIndex *r)
3855 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
3860 samr_TestPrivateFunctionsDomain
3862 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3863 struct samr_TestPrivateFunctionsDomain *r)
3865 return NT_STATUS_NOT_IMPLEMENTED;
3870 samr_TestPrivateFunctionsUser
3872 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3873 struct samr_TestPrivateFunctionsUser *r)
3875 return NT_STATUS_NOT_IMPLEMENTED;
3880 samr_GetUserPwInfo
3882 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3883 struct samr_GetUserPwInfo *r)
3885 struct dcesrv_handle *h;
3886 struct samr_account_state *a_state;
3888 ZERO_STRUCTP(r->out.info);
3890 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3892 a_state = h->data;
3894 r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
3895 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
3896 NULL);
3897 r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
3898 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
3900 return NT_STATUS_OK;
3905 samr_RemoveMemberFromForeignDomain
3907 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
3908 TALLOC_CTX *mem_ctx,
3909 struct samr_RemoveMemberFromForeignDomain *r)
3911 struct dcesrv_handle *h;
3912 struct samr_domain_state *d_state;
3913 const char *memberdn;
3914 struct ldb_message **res;
3915 const char *no_attrs[] = { NULL };
3916 int i, count;
3918 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3920 d_state = h->data;
3922 memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3923 "distinguishedName", "(objectSid=%s)",
3924 ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3925 /* Nothing to do */
3926 if (memberdn == NULL) {
3927 return NT_STATUS_OK;
3930 count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
3931 d_state->domain_dn, &res, no_attrs,
3932 d_state->domain_sid,
3933 "(&(member=%s)(objectClass=group)"
3934 "(|(groupType=%d)(groupType=%d)))",
3935 memberdn,
3936 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
3937 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
3939 if (count < 0)
3940 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3942 for (i=0; i<count; i++) {
3943 struct ldb_message *mod;
3944 int ret;
3946 mod = ldb_msg_new(mem_ctx);
3947 if (mod == NULL) {
3948 return NT_STATUS_NO_MEMORY;
3951 mod->dn = res[i]->dn;
3953 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
3954 "member", memberdn) != LDB_SUCCESS)
3955 return NT_STATUS_NO_MEMORY;
3957 ret = ldb_modify(d_state->sam_ctx, mod);
3958 talloc_free(mod);
3959 if (ret != LDB_SUCCESS) {
3960 return dsdb_ldb_err_to_ntstatus(ret);
3964 return NT_STATUS_OK;
3969 samr_QueryDomainInfo2
3971 just an alias for samr_QueryDomainInfo
3973 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3974 struct samr_QueryDomainInfo2 *r)
3976 struct samr_QueryDomainInfo r1;
3977 NTSTATUS status;
3979 ZERO_STRUCT(r1.out);
3980 r1.in.domain_handle = r->in.domain_handle;
3981 r1.in.level = r->in.level;
3982 r1.out.info = r->out.info;
3984 status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
3986 return status;
3991 samr_QueryUserInfo2
3993 just an alias for samr_QueryUserInfo
3995 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3996 struct samr_QueryUserInfo2 *r)
3998 struct samr_QueryUserInfo r1;
3999 NTSTATUS status;
4001 r1.in.user_handle = r->in.user_handle;
4002 r1.in.level = r->in.level;
4003 r1.out.info = r->out.info;
4005 status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4007 return status;
4012 samr_QueryDisplayInfo2
4014 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4015 struct samr_QueryDisplayInfo2 *r)
4017 struct samr_QueryDisplayInfo q;
4018 NTSTATUS result;
4020 q.in.domain_handle = r->in.domain_handle;
4021 q.in.level = r->in.level;
4022 q.in.start_idx = r->in.start_idx;
4023 q.in.max_entries = r->in.max_entries;
4024 q.in.buf_size = r->in.buf_size;
4025 q.out.total_size = r->out.total_size;
4026 q.out.returned_size = r->out.returned_size;
4027 q.out.info = r->out.info;
4029 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4031 return result;
4036 samr_GetDisplayEnumerationIndex2
4038 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4039 struct samr_GetDisplayEnumerationIndex2 *r)
4041 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4046 samr_QueryDisplayInfo3
4048 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4049 struct samr_QueryDisplayInfo3 *r)
4051 struct samr_QueryDisplayInfo q;
4052 NTSTATUS result;
4054 q.in.domain_handle = r->in.domain_handle;
4055 q.in.level = r->in.level;
4056 q.in.start_idx = r->in.start_idx;
4057 q.in.max_entries = r->in.max_entries;
4058 q.in.buf_size = r->in.buf_size;
4059 q.out.total_size = r->out.total_size;
4060 q.out.returned_size = r->out.returned_size;
4061 q.out.info = r->out.info;
4063 result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4065 return result;
4070 samr_AddMultipleMembersToAlias
4072 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4073 struct samr_AddMultipleMembersToAlias *r)
4075 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4080 samr_RemoveMultipleMembersFromAlias
4082 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4083 struct samr_RemoveMultipleMembersFromAlias *r)
4085 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4090 samr_GetDomPwInfo
4092 this fetches the default password properties for a domain
4094 note that w2k3 completely ignores the domain name in this call, and
4095 always returns the information for the servers primary domain
4097 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4098 struct samr_GetDomPwInfo *r)
4100 struct ldb_message **msgs;
4101 int ret;
4102 const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
4103 struct ldb_context *sam_ctx;
4105 ZERO_STRUCTP(r->out.info);
4107 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
4108 dce_call->conn->dce_ctx->lp_ctx,
4109 dce_call->conn->auth_state.session_info, 0);
4110 if (sam_ctx == NULL) {
4111 return NT_STATUS_INVALID_SYSTEM_SERVICE;
4114 /* The domain name in this call is ignored */
4115 ret = gendb_search_dn(sam_ctx,
4116 mem_ctx, NULL, &msgs, attrs);
4117 if (ret <= 0) {
4118 talloc_free(sam_ctx);
4120 return NT_STATUS_NO_SUCH_DOMAIN;
4122 if (ret > 1) {
4123 talloc_free(msgs);
4124 talloc_free(sam_ctx);
4126 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4129 r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
4130 "minPwdLength", 0);
4131 r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
4132 "pwdProperties", 1);
4134 talloc_free(msgs);
4135 talloc_unlink(mem_ctx, sam_ctx);
4137 return NT_STATUS_OK;
4142 samr_Connect2
4144 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4145 struct samr_Connect2 *r)
4147 struct samr_Connect c;
4149 c.in.system_name = NULL;
4150 c.in.access_mask = r->in.access_mask;
4151 c.out.connect_handle = r->out.connect_handle;
4153 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4158 samr_SetUserInfo2
4160 just an alias for samr_SetUserInfo
4162 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4163 struct samr_SetUserInfo2 *r)
4165 struct samr_SetUserInfo r2;
4167 r2.in.user_handle = r->in.user_handle;
4168 r2.in.level = r->in.level;
4169 r2.in.info = r->in.info;
4171 return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
4176 samr_SetBootKeyInformation
4178 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4179 struct samr_SetBootKeyInformation *r)
4181 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4186 samr_GetBootKeyInformation
4188 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4189 struct samr_GetBootKeyInformation *r)
4191 /* Windows Server 2008 returns this */
4192 return NT_STATUS_NOT_SUPPORTED;
4197 samr_Connect3
4199 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4200 struct samr_Connect3 *r)
4202 struct samr_Connect c;
4204 c.in.system_name = NULL;
4205 c.in.access_mask = r->in.access_mask;
4206 c.out.connect_handle = r->out.connect_handle;
4208 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4213 samr_Connect4
4215 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4216 struct samr_Connect4 *r)
4218 struct samr_Connect c;
4220 c.in.system_name = NULL;
4221 c.in.access_mask = r->in.access_mask;
4222 c.out.connect_handle = r->out.connect_handle;
4224 return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4229 samr_Connect5
4231 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4232 struct samr_Connect5 *r)
4234 struct samr_Connect c;
4235 NTSTATUS status;
4237 c.in.system_name = NULL;
4238 c.in.access_mask = r->in.access_mask;
4239 c.out.connect_handle = r->out.connect_handle;
4241 status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
4243 r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
4244 r->out.info_out->info1.unknown2 = 0;
4245 *r->out.level_out = r->in.level_in;
4247 return status;
4252 samr_RidToSid
4254 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4255 struct samr_RidToSid *r)
4257 struct samr_domain_state *d_state;
4258 struct dcesrv_handle *h;
4260 DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4262 d_state = h->data;
4264 /* form the users SID */
4265 *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
4266 if (!*r->out.sid) {
4267 return NT_STATUS_NO_MEMORY;
4270 return NT_STATUS_OK;
4275 samr_SetDsrmPassword
4277 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4278 struct samr_SetDsrmPassword *r)
4280 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4285 samr_ValidatePassword
4287 For now the call checks the password complexity (if active) and the minimum
4288 password length on level 2 and 3. Level 1 is ignored for now.
4290 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
4291 TALLOC_CTX *mem_ctx,
4292 struct samr_ValidatePassword *r)
4294 struct samr_GetDomPwInfo r2;
4295 struct samr_PwInfo pwInfo;
4296 DATA_BLOB password;
4297 enum samr_ValidationStatus res;
4298 NTSTATUS status;
4299 enum dcerpc_transport_t transport = dce_call->conn->endpoint->ep_description->transport;
4301 if (transport != NCACN_IP_TCP && transport != NCALRPC) {
4302 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
4305 (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
4307 r2.in.domain_name = NULL;
4308 r2.out.info = &pwInfo;
4309 status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
4310 if (!NT_STATUS_IS_OK(status)) {
4311 return status;
4314 switch (r->in.level) {
4315 case NetValidateAuthentication:
4316 /* we don't support this yet */
4317 return NT_STATUS_NOT_SUPPORTED;
4318 break;
4319 case NetValidatePasswordChange:
4320 password = data_blob_const(r->in.req->req2.password.string,
4321 r->in.req->req2.password.length);
4322 res = samdb_check_password(&password,
4323 pwInfo.password_properties,
4324 pwInfo.min_password_length);
4325 (*r->out.rep)->ctr2.status = res;
4326 break;
4327 case NetValidatePasswordReset:
4328 password = data_blob_const(r->in.req->req3.password.string,
4329 r->in.req->req3.password.length);
4330 res = samdb_check_password(&password,
4331 pwInfo.password_properties,
4332 pwInfo.min_password_length);
4333 (*r->out.rep)->ctr3.status = res;
4334 break;
4335 default:
4336 return NT_STATUS_INVALID_INFO_CLASS;
4337 break;
4340 return NT_STATUS_OK;
4344 /* include the generated boilerplate */
4345 #include "librpc/gen_ndr/ndr_samr_s.c"