s4/torture: Fix misplaced positional arguments for u64 comparison
[gd/samba-autobuild/.git] / source4 / rpc_server / samr / dcesrv_samr.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    endpoint server for the samr pipe
5
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
10
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.
15
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.
20
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/>.
23 */
24
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"
43
44 #undef strcasecmp
45
46 #define DCESRV_INTERFACE_SAMR_BIND(context, iface) \
47        dcesrv_interface_samr_bind(context, iface)
48 static NTSTATUS dcesrv_interface_samr_bind(struct dcesrv_connection_context *context,
49                                              const struct dcesrv_interface *iface)
50 {
51         return dcesrv_interface_bind_reject_connect(context, iface);
52 }
53
54 /* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
55
56 #define QUERY_STRING(msg, field, attr) \
57         info->field.string = ldb_msg_find_attr_as_string(msg, attr, "");
58 #define QUERY_UINT(msg, field, attr) \
59         info->field = ldb_msg_find_attr_as_uint(msg, attr, 0);
60 #define QUERY_RID(msg, field, attr) \
61         info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
62 #define QUERY_UINT64(msg, field, attr) \
63         info->field = ldb_msg_find_attr_as_uint64(msg, attr, 0);
64 #define QUERY_APASSC(msg, field, attr) \
65         info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
66                                                          a_state->domain_state->domain_dn, msg, attr);
67 #define QUERY_BPWDCT(msg, field, attr) \
68         info->field = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, \
69                                                          a_state->domain_state->domain_dn, msg);
70 #define QUERY_LHOURS(msg, field, attr) \
71         info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
72 #define QUERY_AFLAGS(msg, field, attr) \
73         info->field = samdb_result_acct_flags(msg, attr);
74
75
76 /* these are used to make the Set[User|Group]Info code easier to follow */
77
78 #define SET_STRING(msg, field, attr) do {                               \
79         struct ldb_message_element *set_el;                             \
80         if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
81         if (r->in.info->field.string[0] == '\0') {                      \
82                 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
83                         return NT_STATUS_NO_MEMORY;                     \
84                 }                                                       \
85         }                                                               \
86         if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
87                 return NT_STATUS_NO_MEMORY;                             \
88         }                                                               \
89         set_el = ldb_msg_find_element(msg, attr);                       \
90         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
91 } while (0)
92
93 #define SET_UINT(msg, field, attr) do {                                 \
94         struct ldb_message_element *set_el;                             \
95         if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
96                 return NT_STATUS_NO_MEMORY;                             \
97         }                                                               \
98         set_el = ldb_msg_find_element(msg, attr);                       \
99         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
100 } while (0)
101
102 #define SET_INT64(msg, field, attr) do {                                \
103         struct ldb_message_element *set_el;                             \
104         if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
105                 return NT_STATUS_NO_MEMORY;                             \
106         }                                                               \
107         set_el = ldb_msg_find_element(msg, attr);                       \
108         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
109 } while (0)
110
111 #define SET_UINT64(msg, field, attr) do {                               \
112         struct ldb_message_element *set_el;                             \
113         if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
114                 return NT_STATUS_NO_MEMORY;                             \
115         }                                                               \
116         set_el = ldb_msg_find_element(msg, attr);                       \
117         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
118 } while (0)
119
120 /* Set account flags, discarding flags that cannot be set with SAMR */
121 #define SET_AFLAGS(msg, field, attr) do {                               \
122         struct ldb_message_element *set_el;                             \
123         if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
124                 return NT_STATUS_NO_MEMORY;                             \
125         }                                                               \
126         set_el = ldb_msg_find_element(msg, attr);                       \
127         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
128 } while (0)
129
130 #define SET_LHOURS(msg, field, attr) do {                               \
131         struct ldb_message_element *set_el;                             \
132         if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
133                 return NT_STATUS_NO_MEMORY;                             \
134         }                                                               \
135         set_el = ldb_msg_find_element(msg, attr);                       \
136         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
137 } while (0)
138
139 #define SET_PARAMETERS(msg, field, attr) do {                           \
140         struct ldb_message_element *set_el;                             \
141         if (r->in.info->field.length != 0) {                            \
142                 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
143                         return NT_STATUS_NO_MEMORY;                     \
144                 }                                                       \
145                 set_el = ldb_msg_find_element(msg, attr);               \
146                 set_el->flags = LDB_FLAG_MOD_REPLACE;                   \
147         }                                                               \
148 } while (0)
149
150 /*
151  * Clear a GUID cache
152  */
153 static void clear_guid_cache(struct samr_guid_cache *cache)
154 {
155         cache->handle = 0;
156         cache->size = 0;
157         TALLOC_FREE(cache->entries);
158 }
159
160 /*
161  * initialize a GUID cache
162  */
163 static void initialize_guid_cache(struct samr_guid_cache *cache)
164 {
165         cache->handle = 0;
166         cache->size = 0;
167         cache->entries = NULL;
168 }
169
170 static NTSTATUS load_guid_cache(
171         struct samr_guid_cache *cache,
172         struct samr_domain_state *d_state,
173         unsigned int ldb_cnt,
174         struct ldb_message **res)
175 {
176         NTSTATUS status = NT_STATUS_OK;
177         unsigned int i;
178         TALLOC_CTX *frame = talloc_stackframe();
179
180         clear_guid_cache(cache);
181
182         /*
183          * Store the GUID's in the cache.
184          */
185         cache->handle = 0;
186         cache->size = ldb_cnt;
187         cache->entries = talloc_array(d_state, struct GUID, ldb_cnt);
188         if (cache->entries == NULL) {
189                 clear_guid_cache(cache);
190                 status = NT_STATUS_NO_MEMORY;
191                 goto exit;
192         }
193
194         /*
195          * Extract a list of the GUIDs for all the matching objects
196          * we cache just the GUIDS to reduce the memory overhead of
197          * the result cache.
198          */
199         for (i = 0; i < ldb_cnt; i++) {
200                 cache->entries[i] = samdb_result_guid(res[i], "objectGUID");
201         }
202 exit:
203         TALLOC_FREE(frame);
204         return status;
205 }
206
207 /*
208   samr_Connect
209
210   create a connection to the SAM database
211 */
212 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
213                              struct samr_Connect *r)
214 {
215         struct samr_connect_state *c_state;
216         struct dcesrv_handle *handle;
217
218         ZERO_STRUCTP(r->out.connect_handle);
219
220         c_state = talloc(mem_ctx, struct samr_connect_state);
221         if (!c_state) {
222                 return NT_STATUS_NO_MEMORY;
223         }
224
225         /* make sure the sam database is accessible */
226         c_state->sam_ctx = dcesrv_samdb_connect_as_user(c_state, dce_call);
227         if (c_state->sam_ctx == NULL) {
228                 talloc_free(c_state);
229                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
230         }
231
232         handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_CONNECT);
233         if (!handle) {
234                 talloc_free(c_state);
235                 return NT_STATUS_NO_MEMORY;
236         }
237
238         handle->data = talloc_steal(handle, c_state);
239
240         c_state->access_mask = r->in.access_mask;
241         *r->out.connect_handle = handle->wire_handle;
242
243         return NT_STATUS_OK;
244 }
245
246
247 /*
248   samr_Close
249 */
250 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
251                            struct samr_Close *r)
252 {
253         struct dcesrv_handle *h;
254
255         *r->out.handle = *r->in.handle;
256
257         DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
258
259         talloc_free(h);
260
261         ZERO_STRUCTP(r->out.handle);
262
263         return NT_STATUS_OK;
264 }
265
266
267 /*
268   samr_SetSecurity
269 */
270 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
271                                  struct samr_SetSecurity *r)
272 {
273         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
274 }
275
276
277 /*
278   samr_QuerySecurity
279 */
280 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
281                                    struct samr_QuerySecurity *r)
282 {
283         struct dcesrv_handle *h;
284         struct sec_desc_buf *sd;
285
286         *r->out.sdbuf = NULL;
287
288         DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
289
290         sd = talloc(mem_ctx, struct sec_desc_buf);
291         if (sd == NULL) {
292                 return NT_STATUS_NO_MEMORY;
293         }
294
295         sd->sd = samdb_default_security_descriptor(mem_ctx);
296
297         *r->out.sdbuf = sd;
298
299         return NT_STATUS_OK;
300 }
301
302
303 /*
304   samr_Shutdown
305
306   we refuse this operation completely. If a admin wants to shutdown samr
307   in Samba then they should use the samba admin tools to disable the samr pipe
308 */
309 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
310                               struct samr_Shutdown *r)
311 {
312         return NT_STATUS_ACCESS_DENIED;
313 }
314
315
316 /*
317   samr_LookupDomain
318
319   this maps from a domain name to a SID
320 */
321 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
322                                   struct samr_LookupDomain *r)
323 {
324         struct samr_connect_state *c_state;
325         struct dcesrv_handle *h;
326         struct dom_sid *sid;
327         const char * const dom_attrs[] = { "objectSid", NULL};
328         struct ldb_message **dom_msgs;
329         int ret;
330
331         *r->out.sid = NULL;
332
333         DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
334
335         c_state = h->data;
336
337         if (r->in.domain_name->string == NULL) {
338                 return NT_STATUS_INVALID_PARAMETER;
339         }
340
341         if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
342                 ret = gendb_search(c_state->sam_ctx,
343                                    mem_ctx, NULL, &dom_msgs, dom_attrs,
344                                    "(objectClass=builtinDomain)");
345         } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
346                 ret = gendb_search_dn(c_state->sam_ctx,
347                                       mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
348                                       &dom_msgs, dom_attrs);
349         } else {
350                 return NT_STATUS_NO_SUCH_DOMAIN;
351         }
352         if (ret != 1) {
353                 return NT_STATUS_NO_SUCH_DOMAIN;
354         }
355
356         sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
357                                    "objectSid");
358
359         if (sid == NULL) {
360                 return NT_STATUS_NO_SUCH_DOMAIN;
361         }
362
363         *r->out.sid = sid;
364
365         return NT_STATUS_OK;
366 }
367
368
369 /*
370   samr_EnumDomains
371
372   list the domains in the SAM
373 */
374 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
375                                  struct samr_EnumDomains *r)
376 {
377         struct dcesrv_handle *h;
378         struct samr_SamArray *array;
379         uint32_t i, start_i;
380
381         *r->out.resume_handle = 0;
382         *r->out.sam = NULL;
383         *r->out.num_entries = 0;
384
385         DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
386
387         *r->out.resume_handle = 2;
388
389         start_i = *r->in.resume_handle;
390
391         if (start_i >= 2) {
392                 /* search past end of list is not an error for this call */
393                 return NT_STATUS_OK;
394         }
395
396         array = talloc(mem_ctx, struct samr_SamArray);
397         if (array == NULL) {
398                 return NT_STATUS_NO_MEMORY;
399         }
400
401         array->count = 0;
402         array->entries = NULL;
403
404         array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
405         if (array->entries == NULL) {
406                 return NT_STATUS_NO_MEMORY;
407         }
408
409         for (i=0;i<2-start_i;i++) {
410                 array->entries[i].idx = start_i + i;
411                 if (i == 0) {
412                         array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
413                 } else {
414                         array->entries[i].name.string = "BUILTIN";
415                 }
416         }
417
418         *r->out.sam = array;
419         *r->out.num_entries = i;
420         array->count = *r->out.num_entries;
421
422         return NT_STATUS_OK;
423 }
424
425
426 /*
427   samr_OpenDomain
428 */
429 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
430                                 struct samr_OpenDomain *r)
431 {
432         struct dcesrv_handle *h_conn, *h_domain;
433         struct samr_connect_state *c_state;
434         struct samr_domain_state *d_state;
435         const char * const dom_attrs[] = { "cn", NULL};
436         struct ldb_message **dom_msgs;
437         int ret;
438         unsigned int i;
439
440         ZERO_STRUCTP(r->out.domain_handle);
441
442         DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
443
444         c_state = h_conn->data;
445
446         if (r->in.sid == NULL) {
447                 return NT_STATUS_INVALID_PARAMETER;
448         }
449
450         d_state = talloc(mem_ctx, struct samr_domain_state);
451         if (!d_state) {
452                 return NT_STATUS_NO_MEMORY;
453         }
454
455         d_state->domain_sid = talloc_steal(d_state, r->in.sid);
456
457         if (dom_sid_equal(d_state->domain_sid, &global_sid_Builtin)) {
458                 d_state->builtin = true;
459                 d_state->domain_name = "BUILTIN";
460         } else {
461                 d_state->builtin = false;
462                 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
463         }
464
465         ret = gendb_search(c_state->sam_ctx,
466                            mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
467                            "(objectSid=%s)",
468                            ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
469
470         if (ret == 0) {
471                 talloc_free(d_state);
472                 return NT_STATUS_NO_SUCH_DOMAIN;
473         } else if (ret > 1) {
474                 talloc_free(d_state);
475                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
476         } else if (ret == -1) {
477                 talloc_free(d_state);
478                 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
479                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
480         }
481
482         d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
483         d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
484         d_state->connect_state = talloc_reference(d_state, c_state);
485         d_state->sam_ctx = c_state->sam_ctx;
486         d_state->access_mask = r->in.access_mask;
487         d_state->domain_users_cached = NULL;
488
489         d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
490
491         for (i = 0; i < SAMR_LAST_CACHE; i++) {
492                 initialize_guid_cache(&d_state->guid_caches[i]);
493         }
494
495         h_domain = dcesrv_handle_create(dce_call, SAMR_HANDLE_DOMAIN);
496         if (!h_domain) {
497                 talloc_free(d_state);
498                 return NT_STATUS_NO_MEMORY;
499         }
500
501         h_domain->data = talloc_steal(h_domain, d_state);
502
503         *r->out.domain_handle = h_domain->wire_handle;
504
505         return NT_STATUS_OK;
506 }
507
508 /*
509   return DomInfo1
510 */
511 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
512                                           TALLOC_CTX *mem_ctx,
513                                           struct ldb_message **dom_msgs,
514                                           struct samr_DomInfo1 *info)
515 {
516         info->min_password_length =
517                 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
518         info->password_history_length =
519                 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
520         info->password_properties =
521                 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
522         info->max_password_age =
523                 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
524         info->min_password_age =
525                 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
526
527         return NT_STATUS_OK;
528 }
529
530 /*
531   return DomInfo2
532 */
533 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
534                                                        TALLOC_CTX *mem_ctx,
535                                                        struct ldb_message **dom_msgs,
536                                                        struct samr_DomGeneralInformation *info)
537 {
538         size_t count = 0;
539         const enum ldb_scope scope = LDB_SCOPE_SUBTREE;
540         int ret = 0;
541
542         /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
543         info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
544                                                            "domainReplica",
545                                                            "");
546
547         info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
548                                                             0x8000000000000000LL);
549
550         info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
551                                                                    "oEMInformation",
552                                                                    "");
553         info->domain_name.string  = state->domain_name;
554
555         info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
556                                                  0);
557         switch (state->role) {
558         case ROLE_ACTIVE_DIRECTORY_DC:
559                 /* This pulls the NetBIOS name from the
560                    cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
561                    string */
562                 if (samdb_is_pdc(state->sam_ctx)) {
563                         info->role = SAMR_ROLE_DOMAIN_PDC;
564                 } else {
565                         info->role = SAMR_ROLE_DOMAIN_BDC;
566                 }
567                 break;
568         case ROLE_DOMAIN_PDC:
569         case ROLE_DOMAIN_BDC:
570         case ROLE_IPA_DC:
571         case ROLE_AUTO:
572                 return NT_STATUS_INTERNAL_ERROR;
573         case ROLE_DOMAIN_MEMBER:
574                 info->role = SAMR_ROLE_DOMAIN_MEMBER;
575                 break;
576         case ROLE_STANDALONE:
577                 info->role = SAMR_ROLE_STANDALONE;
578                 break;
579         }
580
581         /*
582          * Users are not meant to be in BUILTIN
583          * so to speed up the query we do not filter on domain_sid
584          */
585         ret = dsdb_domain_count(
586                 state->sam_ctx,
587                 &count,
588                 state->domain_dn,
589                 NULL,
590                 scope,
591                 "(objectClass=user)");
592         if (ret != LDB_SUCCESS || count > UINT32_MAX) {
593                 goto error;
594         }
595         info->num_users = count;
596
597         /*
598          * Groups are not meant to be in BUILTIN
599          * so to speed up the query we do not filter on domain_sid
600          */
601         ret = dsdb_domain_count(
602                 state->sam_ctx,
603                 &count,
604                 state->domain_dn,
605                 NULL,
606                 scope,
607                 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
608                 GTYPE_SECURITY_UNIVERSAL_GROUP,
609                 GTYPE_SECURITY_GLOBAL_GROUP);
610         if (ret != LDB_SUCCESS || count > UINT32_MAX) {
611                 goto error;
612         }
613         info->num_groups = count;
614
615         ret = dsdb_domain_count(
616                 state->sam_ctx,
617                 &count,
618                 state->domain_dn,
619                 state->domain_sid,
620                 scope,
621                 "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
622                 GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
623                 GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
624         if (ret != LDB_SUCCESS || count > UINT32_MAX) {
625                 goto error;
626         }
627         info->num_aliases = count;
628
629         return NT_STATUS_OK;
630
631 error:
632         if (count > UINT32_MAX) {
633                 return NT_STATUS_INTEGER_OVERFLOW;
634         }
635         return dsdb_ldb_err_to_ntstatus(ret);
636
637 }
638
639 /*
640   return DomInfo3
641 */
642 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
643                                           TALLOC_CTX *mem_ctx,
644                                           struct ldb_message **dom_msgs,
645                                           struct samr_DomInfo3 *info)
646 {
647         info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
648                                                       0x8000000000000000LL);
649
650         return NT_STATUS_OK;
651 }
652
653 /*
654   return DomInfo4
655 */
656 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
657                                    TALLOC_CTX *mem_ctx,
658                                     struct ldb_message **dom_msgs,
659                                    struct samr_DomOEMInformation *info)
660 {
661         info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
662                                                                    "oEMInformation",
663                                                                    "");
664
665         return NT_STATUS_OK;
666 }
667
668 /*
669   return DomInfo5
670 */
671 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
672                                           TALLOC_CTX *mem_ctx,
673                                           struct ldb_message **dom_msgs,
674                                           struct samr_DomInfo5 *info)
675 {
676         info->domain_name.string  = state->domain_name;
677
678         return NT_STATUS_OK;
679 }
680
681 /*
682   return DomInfo6
683 */
684 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
685                                           TALLOC_CTX *mem_ctx,
686                                           struct ldb_message **dom_msgs,
687                                           struct samr_DomInfo6 *info)
688 {
689         /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
690         info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
691                                                            "domainReplica",
692                                                            "");
693
694         return NT_STATUS_OK;
695 }
696
697 /*
698   return DomInfo7
699 */
700 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
701                                           TALLOC_CTX *mem_ctx,
702                                           struct ldb_message **dom_msgs,
703                                           struct samr_DomInfo7 *info)
704 {
705
706         switch (state->role) {
707         case ROLE_ACTIVE_DIRECTORY_DC:
708                 /* This pulls the NetBIOS name from the
709                    cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
710                    string */
711                 if (samdb_is_pdc(state->sam_ctx)) {
712                         info->role = SAMR_ROLE_DOMAIN_PDC;
713                 } else {
714                         info->role = SAMR_ROLE_DOMAIN_BDC;
715                 }
716                 break;
717         case ROLE_DOMAIN_PDC:
718         case ROLE_DOMAIN_BDC:
719         case ROLE_IPA_DC:
720         case ROLE_AUTO:
721                 return NT_STATUS_INTERNAL_ERROR;
722         case ROLE_DOMAIN_MEMBER:
723                 info->role = SAMR_ROLE_DOMAIN_MEMBER;
724                 break;
725         case ROLE_STANDALONE:
726                 info->role = SAMR_ROLE_STANDALONE;
727                 break;
728         }
729
730         return NT_STATUS_OK;
731 }
732
733 /*
734   return DomInfo8
735 */
736 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
737                                           TALLOC_CTX *mem_ctx,
738                                           struct ldb_message **dom_msgs,
739                                           struct samr_DomInfo8 *info)
740 {
741         info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
742                                                time(NULL));
743
744         info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
745                                                      0x0LL);
746
747         return NT_STATUS_OK;
748 }
749
750 /*
751   return DomInfo9
752 */
753 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
754                                           TALLOC_CTX *mem_ctx,
755                                           struct ldb_message **dom_msgs,
756                                           struct samr_DomInfo9 *info)
757 {
758         info->domain_server_state = DOMAIN_SERVER_ENABLED;
759
760         return NT_STATUS_OK;
761 }
762
763 /*
764   return DomInfo11
765 */
766 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
767                                                         TALLOC_CTX *mem_ctx,
768                                                         struct ldb_message **dom_msgs,
769                                                         struct samr_DomGeneralInformation2 *info)
770 {
771         NTSTATUS status;
772         status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
773         if (!NT_STATUS_IS_OK(status)) {
774                 return status;
775         }
776
777         info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
778                                                     -18000000000LL);
779         info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
780                                                     -18000000000LL);
781         info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
782
783         return NT_STATUS_OK;
784 }
785
786 /*
787   return DomInfo12
788 */
789 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
790                                            TALLOC_CTX *mem_ctx,
791                                            struct ldb_message **dom_msgs,
792                                            struct samr_DomInfo12 *info)
793 {
794         info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
795                                                     -18000000000LL);
796         info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
797                                                     -18000000000LL);
798         info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
799
800         return NT_STATUS_OK;
801 }
802
803 /*
804   return DomInfo13
805 */
806 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
807                                            TALLOC_CTX *mem_ctx,
808                                            struct ldb_message **dom_msgs,
809                                            struct samr_DomInfo13 *info)
810 {
811         info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
812                                                time(NULL));
813
814         info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
815                                                      0x0LL);
816
817         info->modified_count_at_last_promotion = 0;
818
819         return NT_STATUS_OK;
820 }
821
822 /*
823   samr_QueryDomainInfo
824 */
825 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
826                                             TALLOC_CTX *mem_ctx,
827                                             struct samr_QueryDomainInfo *r)
828 {
829         struct dcesrv_handle *h;
830         struct samr_domain_state *d_state;
831         union samr_DomainInfo *info;
832
833         struct ldb_message **dom_msgs;
834         const char * const *attrs = NULL;
835
836         *r->out.info = NULL;
837
838         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
839
840         d_state = h->data;
841
842         switch (r->in.level) {
843         case 1:
844         {
845                 static const char * const attrs2[] = { "minPwdLength",
846                                                        "pwdHistoryLength",
847                                                        "pwdProperties",
848                                                        "maxPwdAge",
849                                                        "minPwdAge",
850                                                        NULL };
851                 attrs = attrs2;
852                 break;
853         }
854         case 2:
855         {
856                 static const char * const attrs2[] = {"forceLogoff",
857                                                       "oEMInformation",
858                                                       "modifiedCount",
859                                                       "domainReplica",
860                                                       NULL};
861                 attrs = attrs2;
862                 break;
863         }
864         case 3:
865         {
866                 static const char * const attrs2[] = {"forceLogoff",
867                                                       NULL};
868                 attrs = attrs2;
869                 break;
870         }
871         case 4:
872         {
873                 static const char * const attrs2[] = {"oEMInformation",
874                                                       NULL};
875                 attrs = attrs2;
876                 break;
877         }
878         case 5:
879         {
880                 attrs = NULL;
881                 break;
882         }
883         case 6:
884         {
885                 static const char * const attrs2[] = { "domainReplica",
886                                                        NULL };
887                 attrs = attrs2;
888                 break;
889         }
890         case 7:
891         {
892                 attrs = NULL;
893                 break;
894         }
895         case 8:
896         {
897                 static const char * const attrs2[] = { "modifiedCount",
898                                                        "creationTime",
899                                                        NULL };
900                 attrs = attrs2;
901                 break;
902         }
903         case 9:
904         {
905                 attrs = NULL;
906                 break;
907         }
908         case 11:
909         {
910                 static const char * const attrs2[] = { "oEMInformation",
911                                                        "forceLogoff",
912                                                        "modifiedCount",
913                                                        "lockoutDuration",
914                                                        "lockOutObservationWindow",
915                                                        "lockoutThreshold",
916                                                        NULL};
917                 attrs = attrs2;
918                 break;
919         }
920         case 12:
921         {
922                 static const char * const attrs2[] = { "lockoutDuration",
923                                                        "lockOutObservationWindow",
924                                                        "lockoutThreshold",
925                                                        NULL};
926                 attrs = attrs2;
927                 break;
928         }
929         case 13:
930         {
931                 static const char * const attrs2[] = { "modifiedCount",
932                                                        "creationTime",
933                                                        NULL };
934                 attrs = attrs2;
935                 break;
936         }
937         default:
938         {
939                 return NT_STATUS_INVALID_INFO_CLASS;
940         }
941         }
942
943         /* some levels don't need a search */
944         if (attrs) {
945                 int ret;
946                 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
947                                       d_state->domain_dn, &dom_msgs, attrs);
948                 if (ret == 0) {
949                         return NT_STATUS_NO_SUCH_DOMAIN;
950                 }
951                 if (ret != 1) {
952                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
953                 }
954         }
955
956         /* allocate the info structure */
957         info = talloc_zero(mem_ctx, union samr_DomainInfo);
958         if (info == NULL) {
959                 return NT_STATUS_NO_MEMORY;
960         }
961
962         *r->out.info = info;
963
964         switch (r->in.level) {
965         case 1:
966                 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
967                                                  &info->info1);
968         case 2:
969                 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
970                                                               &info->general);
971         case 3:
972                 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
973                                                  &info->info3);
974         case 4:
975                 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
976                                                           &info->oem);
977         case 5:
978                 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
979                                                  &info->info5);
980         case 6:
981                 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
982                                                  &info->info6);
983         case 7:
984                 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
985                                                  &info->info7);
986         case 8:
987                 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
988                                                  &info->info8);
989         case 9:
990                 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
991                                                  &info->info9);
992         case 11:
993                 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
994                                                                &info->general2);
995         case 12:
996                 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
997                                                   &info->info12);
998         case 13:
999                 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
1000                                                   &info->info13);
1001         default:
1002                 return NT_STATUS_INVALID_INFO_CLASS;
1003         }
1004 }
1005
1006
1007 /*
1008   samr_SetDomainInfo
1009 */
1010 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1011                        struct samr_SetDomainInfo *r)
1012 {
1013         struct dcesrv_handle *h;
1014         struct samr_domain_state *d_state;
1015         struct ldb_message *msg;
1016         int ret;
1017         struct ldb_context *sam_ctx;
1018
1019         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1020
1021         d_state = h->data;
1022         sam_ctx = d_state->sam_ctx;
1023
1024         msg = ldb_msg_new(mem_ctx);
1025         if (msg == NULL) {
1026                 return NT_STATUS_NO_MEMORY;
1027         }
1028
1029         msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
1030         if (!msg->dn) {
1031                 return NT_STATUS_NO_MEMORY;
1032         }
1033
1034         switch (r->in.level) {
1035         case 1:
1036                 SET_UINT  (msg, info1.min_password_length,     "minPwdLength");
1037                 SET_UINT  (msg, info1.password_history_length, "pwdHistoryLength");
1038                 SET_UINT  (msg, info1.password_properties,     "pwdProperties");
1039                 SET_INT64  (msg, info1.max_password_age,       "maxPwdAge");
1040                 SET_INT64  (msg, info1.min_password_age,       "minPwdAge");
1041                 break;
1042         case 3:
1043                 SET_UINT64  (msg, info3.force_logoff_time,     "forceLogoff");
1044                 break;
1045         case 4:
1046                 SET_STRING(msg, oem.oem_information,           "oEMInformation");
1047                 break;
1048
1049         case 6:
1050         case 7:
1051         case 9:
1052                 /* No op, we don't know where to set these */
1053                 return NT_STATUS_OK;
1054
1055         case 12:
1056                 /*
1057                  * It is not possible to set lockout_duration < lockout_window.
1058                  * (The test is the other way around since the negative numbers
1059                  *  are stored...)
1060                  *
1061                  * TODO:
1062                  *   This check should be moved to the backend, i.e. to some
1063                  *   ldb module under dsdb/samdb/ldb_modules/ .
1064                  *
1065                  * This constraint is documented here for the samr rpc service:
1066                  * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
1067                  * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
1068                  *
1069                  * And here for the ldap backend:
1070                  * MS-ADTS 3.1.1.5.3.2 Constraints
1071                  * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
1072                  */
1073                 if (r->in.info->info12.lockout_duration >
1074                     r->in.info->info12.lockout_window)
1075                 {
1076                         return NT_STATUS_INVALID_PARAMETER;
1077                 }
1078                 SET_INT64  (msg, info12.lockout_duration,      "lockoutDuration");
1079                 SET_INT64  (msg, info12.lockout_window,        "lockOutObservationWindow");
1080                 SET_INT64  (msg, info12.lockout_threshold,     "lockoutThreshold");
1081                 break;
1082
1083         default:
1084                 /* many info classes are not valid for SetDomainInfo */
1085                 return NT_STATUS_INVALID_INFO_CLASS;
1086         }
1087
1088         /* modify the samdb record */
1089         ret = ldb_modify(sam_ctx, msg);
1090         if (ret != LDB_SUCCESS) {
1091                 DEBUG(1,("Failed to modify record %s: %s\n",
1092                          ldb_dn_get_linearized(d_state->domain_dn),
1093                          ldb_errstring(sam_ctx)));
1094                 return dsdb_ldb_err_to_ntstatus(ret);
1095         }
1096
1097         return NT_STATUS_OK;
1098 }
1099
1100 /*
1101   samr_CreateDomainGroup
1102 */
1103 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1104                                        struct samr_CreateDomainGroup *r)
1105 {
1106         NTSTATUS status;
1107         struct samr_domain_state *d_state;
1108         struct samr_account_state *a_state;
1109         struct dcesrv_handle *h;
1110         const char *groupname;
1111         struct dom_sid *group_sid;
1112         struct ldb_dn *group_dn;
1113         struct dcesrv_handle *g_handle;
1114
1115         ZERO_STRUCTP(r->out.group_handle);
1116         *r->out.rid = 0;
1117
1118         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1119
1120         d_state = h->data;
1121
1122         if (d_state->builtin) {
1123                 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain\n"));
1124                 return NT_STATUS_ACCESS_DENIED;
1125         }
1126
1127         groupname = r->in.name->string;
1128
1129         if (groupname == NULL) {
1130                 return NT_STATUS_INVALID_PARAMETER;
1131         }
1132
1133         status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1134         if (!NT_STATUS_IS_OK(status)) {
1135                 return status;
1136         }
1137
1138         a_state = talloc(mem_ctx, struct samr_account_state);
1139         if (!a_state) {
1140                 return NT_STATUS_NO_MEMORY;
1141         }
1142         a_state->sam_ctx = d_state->sam_ctx;
1143         a_state->access_mask = r->in.access_mask;
1144         a_state->domain_state = talloc_reference(a_state, d_state);
1145         a_state->account_dn = talloc_steal(a_state, group_dn);
1146
1147         a_state->account_name = talloc_steal(a_state, groupname);
1148
1149         /* create the policy handle */
1150         g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
1151         if (!g_handle) {
1152                 return NT_STATUS_NO_MEMORY;
1153         }
1154
1155         g_handle->data = talloc_steal(g_handle, a_state);
1156
1157         *r->out.group_handle = g_handle->wire_handle;
1158         *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1159
1160         return NT_STATUS_OK;
1161 }
1162
1163
1164 /*
1165   comparison function for sorting SamEntry array
1166 */
1167 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1168 {
1169         return e1->idx - e2->idx;
1170 }
1171
1172 static int compare_msgRid(struct ldb_message **m1, struct ldb_message **m2) {
1173         struct dom_sid *sid1 = NULL;
1174         struct dom_sid *sid2 = NULL;
1175         uint32_t rid1;
1176         uint32_t rid2;
1177         int res = 0;
1178         NTSTATUS status;
1179         TALLOC_CTX *frame = talloc_stackframe();
1180
1181         sid1 = samdb_result_dom_sid(frame, *m1, "objectSid");
1182         sid2 = samdb_result_dom_sid(frame, *m2, "objectSid");
1183
1184         /*
1185          * If entries don't have a SID we want to sort them to the end of
1186          * the list.
1187          */
1188         if (sid1 == NULL && sid2 == NULL) {
1189                 res = 0;
1190                 goto exit;
1191         } else if (sid2 == NULL) {
1192                 res = 1;
1193                 goto exit;
1194         } else if (sid1 == NULL) {
1195                 res = -1;
1196                 goto exit;
1197         }
1198
1199         /*
1200          * Get and compare the rids, if we fail to extract a rid treat it as a
1201          * missing SID and sort to the end of the list
1202          */
1203         status = dom_sid_split_rid(NULL, sid1, NULL, &rid1);
1204         if (!NT_STATUS_IS_OK(status)) {
1205                 res = 1;
1206                 goto exit;
1207         }
1208
1209         status = dom_sid_split_rid(NULL, sid2, NULL, &rid2);
1210         if (!NT_STATUS_IS_OK(status)) {
1211                 res = -1;
1212                 goto exit;
1213         }
1214
1215         if (rid1 == rid2) {
1216                 res = 0;
1217         }
1218         else if (rid1 > rid2) {
1219                 res = 1;
1220         }
1221         else {
1222                 res = -1;
1223         }
1224 exit:
1225         TALLOC_FREE(frame);
1226         return res;
1227 }
1228
1229 /*
1230   samr_EnumDomainGroups
1231 */
1232 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1233                                       struct samr_EnumDomainGroups *r)
1234 {
1235         struct dcesrv_handle *h;
1236         struct samr_domain_state *d_state;
1237         struct ldb_message **res;
1238         uint32_t i;
1239         uint32_t count;
1240         uint32_t results;
1241         uint32_t max_entries;
1242         uint32_t remaining_entries;
1243         uint32_t resume_handle;
1244         struct samr_SamEntry *entries;
1245         const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1246         const char * const cache_attrs[] = { "objectSid", "objectGUID", NULL };
1247         struct samr_SamArray *sam;
1248         struct samr_guid_cache *cache = NULL;
1249
1250         *r->out.resume_handle = 0;
1251         *r->out.sam = NULL;
1252         *r->out.num_entries = 0;
1253
1254         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1255
1256         d_state = h->data;
1257         cache = &d_state->guid_caches[SAMR_ENUM_DOMAIN_GROUPS_CACHE];
1258
1259         /*
1260          * If the resume_handle is zero, query the database and cache the
1261          * matching GUID's
1262          */
1263         if (*r->in.resume_handle == 0) {
1264                 NTSTATUS status;
1265                 int ldb_cnt;
1266                 clear_guid_cache(cache);
1267                 /*
1268                  * search for all domain groups in this domain.
1269                  */
1270                 ldb_cnt = samdb_search_domain(
1271                     d_state->sam_ctx,
1272                     mem_ctx,
1273                     d_state->domain_dn,
1274                     &res,
1275                     cache_attrs,
1276                     d_state->domain_sid,
1277                     "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1278                     GTYPE_SECURITY_UNIVERSAL_GROUP,
1279                     GTYPE_SECURITY_GLOBAL_GROUP);
1280                 if (ldb_cnt < 0) {
1281                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1282                 }
1283                 /*
1284                  * Sort the results into RID order, while the spec states there
1285                  * is no order, Windows appears to sort the results by RID and
1286                  * so it is possible that there are clients that depend on
1287                  * this ordering
1288                  */
1289                 TYPESAFE_QSORT(res, ldb_cnt, compare_msgRid);
1290
1291                 /*
1292                  * cache the sorted GUID's
1293                  */
1294                 status = load_guid_cache(cache, d_state, ldb_cnt, res);
1295                 TALLOC_FREE(res);
1296                 if (!NT_STATUS_IS_OK(status)) {
1297                         return status;
1298                 }
1299                 cache->handle = 0;
1300         }
1301
1302
1303         /*
1304          * If the resume handle is out of range we return an empty response
1305          * and invalidate the cache.
1306          *
1307          * From the specification:
1308          * Servers SHOULD validate that EnumerationContext is an expected
1309          * value for the server's implementation. Windows does NOT validate
1310          * the input, though the result of malformed information merely results
1311          * in inconsistent output to the client.
1312          */
1313         if (*r->in.resume_handle >= cache->size) {
1314                 clear_guid_cache(cache);
1315                 sam = talloc(mem_ctx, struct samr_SamArray);
1316                 if (!sam) {
1317                         return NT_STATUS_NO_MEMORY;
1318                 }
1319                 sam->entries = NULL;
1320                 sam->count = 0;
1321
1322                 *r->out.sam = sam;
1323                 *r->out.resume_handle = 0;
1324                 return NT_STATUS_OK;
1325         }
1326
1327
1328         /*
1329          * Calculate the number of entries to return limit by max_size.
1330          * Note that we use the w2k3 element size value of 54
1331          */
1332         max_entries = 1 + (r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER);
1333         remaining_entries = cache->size - *r->in.resume_handle;
1334         results = MIN(remaining_entries, max_entries);
1335
1336         /*
1337          * Process the list of result GUID's.
1338          * Read the details of each object and populate the Entries
1339          * for the current level.
1340          */
1341         count = 0;
1342         resume_handle = *r->in.resume_handle;
1343         entries = talloc_array(mem_ctx, struct samr_SamEntry, results);
1344         if (entries == NULL) {
1345                 clear_guid_cache(cache);
1346                 return NT_STATUS_NO_MEMORY;
1347         }
1348         for (i = 0; i < results; i++) {
1349                 struct dom_sid *objectsid;
1350                 uint32_t rid;
1351                 struct ldb_result *rec;
1352                 const uint32_t idx = *r->in.resume_handle + i;
1353                 int ret;
1354                 NTSTATUS status;
1355                 const char *name = NULL;
1356                 resume_handle++;
1357                 /*
1358                  * Read an object from disk using the GUID as the key
1359                  *
1360                  * If the object can not be read, or it does not have a SID
1361                  * it is ignored.
1362                  *
1363                  * As a consequence of this, if all the remaining GUID's
1364                  * have been deleted an empty result will be returned.
1365                  * i.e. even if the previous call returned a non zero
1366                  * resume_handle it is possible for no results to be returned.
1367                  *
1368                  */
1369                 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
1370                                              mem_ctx,
1371                                              &rec,
1372                                              &cache->entries[idx],
1373                                              attrs,
1374                                              0);
1375                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1376                         struct GUID_txt_buf guid_buf;
1377                         DBG_WARNING(
1378                             "GUID [%s] not found\n",
1379                             GUID_buf_string(&cache->entries[idx], &guid_buf));
1380                         continue;
1381                 } else if (ret != LDB_SUCCESS) {
1382                         clear_guid_cache(cache);
1383                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1384                 }
1385
1386                 objectsid = samdb_result_dom_sid(mem_ctx,
1387                                                  rec->msgs[0],
1388                                                  "objectSID");
1389                 if (objectsid == NULL) {
1390                         struct GUID_txt_buf guid_buf;
1391                         DBG_WARNING(
1392                             "objectSID for GUID [%s] not found\n",
1393                             GUID_buf_string(&cache->entries[idx], &guid_buf));
1394                         continue;
1395                 }
1396                 status = dom_sid_split_rid(NULL,
1397                                            objectsid,
1398                                            NULL,
1399                                            &rid);
1400                 if (!NT_STATUS_IS_OK(status)) {
1401                         struct dom_sid_buf sid_buf;
1402                         struct GUID_txt_buf guid_buf;
1403                         DBG_WARNING(
1404                             "objectSID [%s] for GUID [%s] invalid\n",
1405                             dom_sid_str_buf(objectsid, &sid_buf),
1406                             GUID_buf_string(&cache->entries[idx], &guid_buf));
1407                         continue;
1408                 }
1409
1410                 entries[count].idx = rid;
1411                 name = ldb_msg_find_attr_as_string(
1412                     rec->msgs[0], "sAMAccountName", "");
1413                 entries[count].name.string = talloc_strdup(entries, name);
1414                 count++;
1415         }
1416
1417         sam = talloc(mem_ctx, struct samr_SamArray);
1418         if (!sam) {
1419                 clear_guid_cache(cache);
1420                 return NT_STATUS_NO_MEMORY;
1421         }
1422
1423         sam->entries = entries;
1424         sam->count = count;
1425
1426         *r->out.sam = sam;
1427         *r->out.resume_handle = resume_handle;
1428         *r->out.num_entries = count;
1429
1430         /*
1431          * Signal no more results by returning zero resume handle,
1432          * the cache is also cleared at this point
1433          */
1434         if (*r->out.resume_handle >= cache->size) {
1435                 *r->out.resume_handle = 0;
1436                 clear_guid_cache(cache);
1437                 return NT_STATUS_OK;
1438         }
1439         /*
1440          * There are more results to be returned.
1441          */
1442         return STATUS_MORE_ENTRIES;
1443 }
1444
1445
1446 /*
1447   samr_CreateUser2
1448
1449   This call uses transactions to ensure we don't get a new conflicting
1450   user while we are processing this, and to ensure the user either
1451   completely exists, or does not.
1452 */
1453 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1454                                  struct samr_CreateUser2 *r)
1455 {
1456         NTSTATUS status;
1457         struct samr_domain_state *d_state;
1458         struct samr_account_state *a_state;
1459         struct dcesrv_handle *h;
1460         struct ldb_dn *dn;
1461         struct dom_sid *sid;
1462         struct dcesrv_handle *u_handle;
1463         const char *account_name;
1464
1465         ZERO_STRUCTP(r->out.user_handle);
1466         *r->out.access_granted = 0;
1467         *r->out.rid = 0;
1468
1469         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1470
1471         d_state = h->data;
1472
1473         if (d_state->builtin) {
1474                 DEBUG(5, ("Cannot create a user in the BUILTIN domain\n"));
1475                 return NT_STATUS_ACCESS_DENIED;
1476         } else if (r->in.acct_flags == ACB_DOMTRUST) {
1477                 /* Domain trust accounts must be created by the LSA calls */
1478                 return NT_STATUS_ACCESS_DENIED;
1479         }
1480         account_name = r->in.account_name->string;
1481
1482         if (account_name == NULL) {
1483                 return NT_STATUS_INVALID_PARAMETER;
1484         }
1485
1486         status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1487                                &sid, &dn);
1488         if (!NT_STATUS_IS_OK(status)) {
1489                 return status;
1490         }
1491         a_state = talloc(mem_ctx, struct samr_account_state);
1492         if (!a_state) {
1493                 return NT_STATUS_NO_MEMORY;
1494         }
1495         a_state->sam_ctx = d_state->sam_ctx;
1496         a_state->access_mask = r->in.access_mask;
1497         a_state->domain_state = talloc_reference(a_state, d_state);
1498         a_state->account_dn = talloc_steal(a_state, dn);
1499
1500         a_state->account_name = talloc_steal(a_state, account_name);
1501         if (!a_state->account_name) {
1502                 return NT_STATUS_NO_MEMORY;
1503         }
1504
1505         /* create the policy handle */
1506         u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
1507         if (!u_handle) {
1508                 return NT_STATUS_NO_MEMORY;
1509         }
1510
1511         u_handle->data = talloc_steal(u_handle, a_state);
1512
1513         *r->out.user_handle = u_handle->wire_handle;
1514         *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1515
1516         *r->out.rid = sid->sub_auths[sid->num_auths-1];
1517
1518         return NT_STATUS_OK;
1519 }
1520
1521
1522 /*
1523   samr_CreateUser
1524 */
1525 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1526                                 struct samr_CreateUser *r)
1527 {
1528         struct samr_CreateUser2 r2;
1529         uint32_t access_granted = 0;
1530
1531
1532         /* a simple wrapper around samr_CreateUser2 works nicely */
1533
1534         r2 = (struct samr_CreateUser2) {
1535                 .in.domain_handle = r->in.domain_handle,
1536                 .in.account_name = r->in.account_name,
1537                 .in.acct_flags = ACB_NORMAL,
1538                 .in.access_mask = r->in.access_mask,
1539                 .out.user_handle = r->out.user_handle,
1540                 .out.access_granted = &access_granted,
1541                 .out.rid = r->out.rid
1542         };
1543
1544         return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1545 }
1546
1547 struct enum_dom_users_ctx {
1548         struct samr_SamEntry *entries;
1549         uint32_t num_entries;
1550         uint32_t acct_flags;
1551         struct dom_sid *domain_sid;
1552 };
1553
1554 static int user_iterate_callback(struct ldb_request *req,
1555                                  struct ldb_reply *ares);
1556
1557 /*
1558  * Iterate users and add all those that match a domain SID and pass an acct
1559  * flags check to an array of SamEntry objects.
1560  */
1561 static int user_iterate_callback(struct ldb_request *req,
1562                                  struct ldb_reply *ares)
1563 {
1564         struct enum_dom_users_ctx *ac =\
1565                 talloc_get_type(req->context, struct enum_dom_users_ctx);
1566         int ret = LDB_ERR_OPERATIONS_ERROR;
1567
1568         if (!ares) {
1569                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1570         }
1571         if (ares->error != LDB_SUCCESS) {
1572                 return ldb_request_done(req, ares->error);
1573         }
1574
1575         switch (ares->type) {
1576         case LDB_REPLY_ENTRY:
1577         {
1578                 struct ldb_message *msg = ares->message;
1579                 const struct ldb_val *val;
1580                 struct samr_SamEntry *ent;
1581                 struct dom_sid objectsid;
1582                 uint32_t rid;
1583                 size_t entries_array_len = 0;
1584                 NTSTATUS status;
1585                 ssize_t sid_size;
1586
1587                 if (ac->acct_flags && ((samdb_result_acct_flags(msg, NULL) &
1588                                         ac->acct_flags) == 0)) {
1589                         ret = LDB_SUCCESS;
1590                         break;
1591                 }
1592
1593                 val = ldb_msg_find_ldb_val(msg, "objectSID");
1594                 if (val == NULL) {
1595                         DBG_WARNING("objectSID for DN %s not found\n",
1596                                     ldb_dn_get_linearized(msg->dn));
1597                         ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1598                         break;
1599                 }
1600
1601                 sid_size = sid_parse(val->data, val->length, &objectsid);
1602                 if (sid_size == -1) {
1603                         struct dom_sid_buf sid_buf;
1604                         DBG_WARNING("objectsid [%s] for DN [%s] invalid\n",
1605                                     dom_sid_str_buf(&objectsid, &sid_buf),
1606                                     ldb_dn_get_linearized(msg->dn));
1607                         ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1608                         break;
1609                 }
1610
1611                 if (!dom_sid_in_domain(ac->domain_sid, &objectsid)) {
1612                         /* Ignore if user isn't in the domain */
1613                         ret = LDB_SUCCESS;
1614                         break;
1615                 }
1616
1617                 status = dom_sid_split_rid(ares, &objectsid, NULL, &rid);
1618                 if (!NT_STATUS_IS_OK(status)) {
1619                         struct dom_sid_buf sid_buf;
1620                         DBG_WARNING("Couldn't split RID from "
1621                                     "SID [%s] of DN [%s]\n",
1622                                     dom_sid_str_buf(&objectsid, &sid_buf),
1623                                     ldb_dn_get_linearized(msg->dn));
1624                         ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1625                         break;
1626                 }
1627
1628                 entries_array_len = talloc_array_length(ac->entries);
1629                 if (ac->num_entries >= entries_array_len) {
1630                         if (entries_array_len * 2 < entries_array_len) {
1631                                 ret = ldb_request_done(req,
1632                                         LDB_ERR_OPERATIONS_ERROR);
1633                                 break;
1634                         }
1635                         ac->entries = talloc_realloc(ac,
1636                                                      ac->entries,
1637                                                      struct samr_SamEntry,
1638                                                      entries_array_len * 2);
1639                         if (ac->entries == NULL) {
1640                                 ret = ldb_request_done(req,
1641                                         LDB_ERR_OPERATIONS_ERROR);
1642                                 break;
1643                         }
1644                 }
1645
1646                 ent = &(ac->entries[ac->num_entries++]);
1647                 val = ldb_msg_find_ldb_val(msg, "samaccountname");
1648                 if (val == NULL) {
1649                         DBG_WARNING("samaccountname attribute not found\n");
1650                         ret = ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
1651                         break;
1652                 }
1653                 ent->name.string = talloc_steal(ac->entries,
1654                                                 (char *)val->data);
1655                 ent->idx = rid;
1656                 ret = LDB_SUCCESS;
1657                 break;
1658         }
1659         case LDB_REPLY_DONE:
1660         {
1661                 if (ac->num_entries != 0 &&
1662                     ac->num_entries != talloc_array_length(ac->entries)) {
1663                         ac->entries = talloc_realloc(ac,
1664                                                      ac->entries,
1665                                                      struct samr_SamEntry,
1666                                                      ac->num_entries);
1667                         if (ac->entries == NULL) {
1668                                 ret = ldb_request_done(req,
1669                                         LDB_ERR_OPERATIONS_ERROR);
1670                                 break;
1671                         }
1672                 }
1673                 ret = ldb_request_done(req, LDB_SUCCESS);
1674                 break;
1675         }
1676         case LDB_REPLY_REFERRAL:
1677         {
1678                 ret = LDB_SUCCESS;
1679                 break;
1680         }
1681         default:
1682                 /* Doesn't happen */
1683                 ret = LDB_ERR_OPERATIONS_ERROR;
1684         }
1685         TALLOC_FREE(ares);
1686
1687         return ret;
1688 }
1689
1690 /*
1691  * samr_EnumDomainUsers
1692  * The previous implementation did an initial search and stored a list of
1693  * matching GUIDs on the connection handle's domain state, then did direct
1694  * GUID lookups for each record in a page indexed by resume_handle. That
1695  * approach was memory efficient, requiring only 16 bytes per record, but
1696  * was too slow for winbind which needs this RPC call for getpwent.
1697  *
1698  * Now we use an iterate pattern to populate a cached list of the rids and
1699  * names for each record. This improves runtime performance but requires
1700  * about 200 bytes per record which will mean for a 100k database we use
1701  * about 2MB, which is fine. The speedup achieved by this new approach is
1702  * around 50%.
1703  */
1704 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call,
1705                                             TALLOC_CTX *mem_ctx,
1706                                             struct samr_EnumDomainUsers *r)
1707 {
1708         struct dcesrv_handle *h;
1709         struct samr_domain_state *d_state;
1710         uint32_t results;
1711         uint32_t max_entries;
1712         uint32_t num_entries;
1713         uint32_t remaining_entries;
1714         struct samr_SamEntry *entries;
1715         const char * const attrs[] = { "objectSid", "sAMAccountName",
1716                 "userAccountControl", NULL };
1717         struct samr_SamArray *sam;
1718         struct ldb_request *req;
1719
1720         *r->out.resume_handle = 0;
1721         *r->out.sam = NULL;
1722         *r->out.num_entries = 0;
1723
1724         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1725
1726         d_state = h->data;
1727         entries = d_state->domain_users_cached;
1728
1729         /*
1730          * If the resume_handle is zero, query the database and cache the
1731          * matching entries.
1732          */
1733         if (*r->in.resume_handle == 0) {
1734                 int ret;
1735                 struct enum_dom_users_ctx *ac;
1736                 if (entries != NULL) {
1737                         talloc_free(entries);
1738                         d_state->domain_users_cached = NULL;
1739                 }
1740
1741                 ac = talloc(mem_ctx, struct enum_dom_users_ctx);
1742                 ac->num_entries = 0;
1743                 ac->domain_sid = d_state->domain_sid;
1744                 ac->entries = talloc_array(ac,
1745                                            struct samr_SamEntry,
1746                                            100);
1747                 if (ac->entries == NULL) {
1748                         talloc_free(ac);
1749                         return NT_STATUS_NO_MEMORY;
1750                 }
1751                 ac->acct_flags = r->in.acct_flags;
1752
1753                 ret = ldb_build_search_req(&req,
1754                                            d_state->sam_ctx,
1755                                            mem_ctx,
1756                                            d_state->domain_dn,
1757                                            LDB_SCOPE_SUBTREE,
1758                                            "(objectClass=user)",
1759                                            attrs,
1760                                            NULL,
1761                                            ac,
1762                                            user_iterate_callback,
1763                                            NULL);
1764                 if (ret != LDB_SUCCESS) {
1765                         talloc_free(ac);
1766                         return dsdb_ldb_err_to_ntstatus(ret);
1767                 }
1768
1769                 ret = ldb_request(d_state->sam_ctx, req);
1770                 if (ret != LDB_SUCCESS) {
1771                         talloc_free(ac);
1772                         return dsdb_ldb_err_to_ntstatus(ret);
1773                 }
1774
1775                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1776                 if (ret != LDB_SUCCESS) {
1777                         return dsdb_ldb_err_to_ntstatus(ret);
1778                 }
1779
1780                 if (ac->num_entries == 0) {
1781                         DBG_WARNING("No users in domain %s\n",
1782                                     ldb_dn_get_linearized(d_state->domain_dn));
1783                         talloc_free(ac);
1784
1785                         /*
1786                          * test_EnumDomainUsers_all() expects that r.out.sam
1787                          * should be non-NULL, even if we have no entries.
1788                          */
1789                         sam = talloc_zero(mem_ctx, struct samr_SamArray);
1790                         if (sam == NULL) {
1791                                 return NT_STATUS_NO_MEMORY;
1792                         }
1793                         *r->out.sam = sam;
1794
1795                         return NT_STATUS_OK;
1796                 }
1797
1798                 entries = talloc_steal(d_state, ac->entries);
1799                 d_state->domain_users_cached = entries;
1800                 num_entries = ac->num_entries;
1801                 talloc_free(ac);
1802
1803                 /*
1804                  * Sort the entries into RID order, while the spec states there
1805                  * is no order, Windows appears to sort the results by RID and
1806                  * so it is possible that there are clients that depend on
1807                  * this ordering
1808                  */
1809                 TYPESAFE_QSORT(entries, num_entries, compare_SamEntry);
1810         } else {
1811                 num_entries = talloc_array_length(entries);
1812         }
1813
1814         /*
1815          * If the resume handle is out of range we return an empty response
1816          * and invalidate the cache.
1817          *
1818          * From the specification:
1819          * Servers SHOULD validate that EnumerationContext is an expected
1820          * value for the server's implementation. Windows does NOT validate
1821          * the input, though the result of malformed information merely results
1822          * in inconsistent output to the client.
1823          */
1824         if (*r->in.resume_handle >= num_entries) {
1825                 talloc_free(entries);
1826                 d_state->domain_users_cached = NULL;
1827                 sam = talloc(mem_ctx, struct samr_SamArray);
1828                 if (!sam) {
1829                         return NT_STATUS_NO_MEMORY;
1830                 }
1831                 sam->entries = NULL;
1832                 sam->count = 0;
1833
1834                 *r->out.sam = sam;
1835                 *r->out.resume_handle = 0;
1836                 return NT_STATUS_OK;
1837         }
1838
1839         /*
1840          * Calculate the number of entries to return limit by max_size.
1841          * Note that we use the w2k3 element size value of 54
1842          */
1843         max_entries = 1 + (r->in.max_size / SAMR_ENUM_USERS_MULTIPLIER);
1844         remaining_entries = num_entries - *r->in.resume_handle;
1845         results = MIN(remaining_entries, max_entries);
1846
1847         sam = talloc(mem_ctx, struct samr_SamArray);
1848         if (!sam) {
1849                 d_state->domain_users_cached = NULL;
1850                 return NT_STATUS_NO_MEMORY;
1851         }
1852
1853         sam->entries = entries + *r->in.resume_handle;
1854         sam->count = results;
1855
1856         *r->out.sam = sam;
1857         *r->out.resume_handle = *r->in.resume_handle + results;
1858         *r->out.num_entries = results;
1859
1860         /*
1861          * Signal no more results by returning zero resume handle,
1862          * the cache is also cleared at this point
1863          */
1864         if (*r->out.resume_handle >= num_entries) {
1865                 *r->out.resume_handle = 0;
1866                 return NT_STATUS_OK;
1867         }
1868         /*
1869          * There are more results to be returned.
1870          */
1871         return STATUS_MORE_ENTRIES;
1872 }
1873
1874
1875 /*
1876   samr_CreateDomAlias
1877 */
1878 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1879                        struct samr_CreateDomAlias *r)
1880 {
1881         struct samr_domain_state *d_state;
1882         struct samr_account_state *a_state;
1883         struct dcesrv_handle *h;
1884         const char *alias_name;
1885         struct dom_sid *sid;
1886         struct dcesrv_handle *a_handle;
1887         struct ldb_dn *dn;
1888         NTSTATUS status;
1889
1890         ZERO_STRUCTP(r->out.alias_handle);
1891         *r->out.rid = 0;
1892
1893         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1894
1895         d_state = h->data;
1896
1897         if (d_state->builtin) {
1898                 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain\n"));
1899                 return NT_STATUS_ACCESS_DENIED;
1900         }
1901
1902         alias_name = r->in.alias_name->string;
1903
1904         if (alias_name == NULL) {
1905                 return NT_STATUS_INVALID_PARAMETER;
1906         }
1907
1908         status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1909         if (!NT_STATUS_IS_OK(status)) {
1910                 return status;
1911         }
1912
1913         a_state = talloc(mem_ctx, struct samr_account_state);
1914         if (!a_state) {
1915                 return NT_STATUS_NO_MEMORY;
1916         }
1917
1918         a_state->sam_ctx = d_state->sam_ctx;
1919         a_state->access_mask = r->in.access_mask;
1920         a_state->domain_state = talloc_reference(a_state, d_state);
1921         a_state->account_dn = talloc_steal(a_state, dn);
1922
1923         a_state->account_name = talloc_steal(a_state, alias_name);
1924
1925         /* create the policy handle */
1926         a_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
1927         if (a_handle == NULL)
1928                 return NT_STATUS_NO_MEMORY;
1929
1930         a_handle->data = talloc_steal(a_handle, a_state);
1931
1932         *r->out.alias_handle = a_handle->wire_handle;
1933
1934         *r->out.rid = sid->sub_auths[sid->num_auths-1];
1935
1936         return NT_STATUS_OK;
1937 }
1938
1939
1940 /*
1941   samr_EnumDomainAliases
1942 */
1943 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1944                        struct samr_EnumDomainAliases *r)
1945 {
1946         struct dcesrv_handle *h;
1947         struct samr_domain_state *d_state;
1948         struct ldb_message **res;
1949         int i, ldb_cnt;
1950         uint32_t first, count;
1951         struct samr_SamEntry *entries;
1952         const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1953         struct samr_SamArray *sam;
1954
1955         *r->out.resume_handle = 0;
1956         *r->out.sam = NULL;
1957         *r->out.num_entries = 0;
1958
1959         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1960
1961         d_state = h->data;
1962
1963         /* search for all domain aliases in this domain. This could possibly be
1964            cached and resumed based on resume_key */
1965         ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1966                                       &res, attrs,
1967                                       d_state->domain_sid,
1968                                       "(&(|(grouptype=%d)(grouptype=%d)))"
1969                                       "(objectclass=group))",
1970                                       GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1971                                       GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1972         if (ldb_cnt < 0) {
1973                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1974         }
1975
1976         /* convert to SamEntry format */
1977         entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1978         if (!entries) {
1979                 return NT_STATUS_NO_MEMORY;
1980         }
1981
1982         count = 0;
1983
1984         for (i=0;i<ldb_cnt;i++) {
1985                 struct dom_sid *alias_sid;
1986
1987                 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1988                                                  "objectSid");
1989
1990                 if (alias_sid == NULL) {
1991                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1992                 }
1993
1994                 entries[count].idx =
1995                         alias_sid->sub_auths[alias_sid->num_auths-1];
1996                 entries[count].name.string =
1997                         ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1998                 count += 1;
1999         }
2000
2001         /* sort the results by rid */
2002         TYPESAFE_QSORT(entries, count, compare_SamEntry);
2003
2004         /* find the first entry to return */
2005         for (first=0;
2006              first<count && entries[first].idx <= *r->in.resume_handle;
2007              first++) ;
2008
2009         /* return the rest, limit by max_size. Note that we
2010            use the w2k3 element size value of 54 */
2011         *r->out.num_entries = count - first;
2012         *r->out.num_entries = MIN(*r->out.num_entries,
2013                                   1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
2014
2015         sam = talloc(mem_ctx, struct samr_SamArray);
2016         if (!sam) {
2017                 return NT_STATUS_NO_MEMORY;
2018         }
2019
2020         sam->entries = entries+first;
2021         sam->count = *r->out.num_entries;
2022
2023         *r->out.sam = sam;
2024
2025         if (first == count) {
2026                 return NT_STATUS_OK;
2027         }
2028
2029         if (*r->out.num_entries < count - first) {
2030                 *r->out.resume_handle =
2031                         entries[first+*r->out.num_entries-1].idx;
2032                 return STATUS_MORE_ENTRIES;
2033         }
2034
2035         return NT_STATUS_OK;
2036 }
2037
2038
2039 /*
2040   samr_GetAliasMembership
2041 */
2042 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2043                        struct samr_GetAliasMembership *r)
2044 {
2045         struct dcesrv_handle *h;
2046         struct samr_domain_state *d_state;
2047         char *filter;
2048         const char * const attrs[] = { "objectSid", NULL };
2049         struct ldb_message **res;
2050         uint32_t i;
2051         int count = 0;
2052
2053         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2054
2055         d_state = h->data;
2056
2057         filter = talloc_asprintf(mem_ctx,
2058                                  "(&(|(grouptype=%d)(grouptype=%d))"
2059                                  "(objectclass=group)(|",
2060                                  GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2061                                  GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2062         if (filter == NULL) {
2063                 return NT_STATUS_NO_MEMORY;
2064         }
2065
2066         for (i=0; i<r->in.sids->num_sids; i++) {
2067                 struct dom_sid_buf buf;
2068
2069                 filter = talloc_asprintf_append(
2070                         filter,
2071                         "(member=<SID=%s>)",
2072                         dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
2073
2074                 if (filter == NULL) {
2075                         return NT_STATUS_NO_MEMORY;
2076                 }
2077         }
2078
2079         /* Find out if we had at least one valid member SID passed - otherwise
2080          * just skip the search. */
2081         if (strstr(filter, "member") != NULL) {
2082                 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
2083                                             &res, attrs, d_state->domain_sid,
2084                                             "%s))", filter);
2085                 if (count < 0) {
2086                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
2087                 }
2088         }
2089
2090         r->out.rids->count = 0;
2091         r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
2092         if (r->out.rids->ids == NULL)
2093                 return NT_STATUS_NO_MEMORY;
2094
2095         for (i=0; i<count; i++) {
2096                 struct dom_sid *alias_sid;
2097
2098                 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
2099                 if (alias_sid == NULL) {
2100                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
2101                 }
2102
2103                 r->out.rids->ids[r->out.rids->count] =
2104                         alias_sid->sub_auths[alias_sid->num_auths-1];
2105                 r->out.rids->count += 1;
2106         }
2107
2108         return NT_STATUS_OK;
2109 }
2110
2111
2112 /*
2113   samr_LookupNames
2114 */
2115 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2116                                  struct samr_LookupNames *r)
2117 {
2118         struct dcesrv_handle *h;
2119         struct samr_domain_state *d_state;
2120         uint32_t i, num_mapped;
2121         NTSTATUS status = NT_STATUS_OK;
2122         const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
2123         int count;
2124
2125         ZERO_STRUCTP(r->out.rids);
2126         ZERO_STRUCTP(r->out.types);
2127
2128         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2129
2130         d_state = h->data;
2131
2132         if (r->in.num_names == 0) {
2133                 return NT_STATUS_OK;
2134         }
2135
2136         r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
2137         r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
2138         if (!r->out.rids->ids || !r->out.types->ids) {
2139                 return NT_STATUS_NO_MEMORY;
2140         }
2141         r->out.rids->count = r->in.num_names;
2142         r->out.types->count = r->in.num_names;
2143
2144         num_mapped = 0;
2145
2146         for (i=0;i<r->in.num_names;i++) {
2147                 struct ldb_message **res;
2148                 struct dom_sid *sid;
2149                 uint32_t atype, rtype;
2150
2151                 r->out.rids->ids[i] = 0;
2152                 r->out.types->ids[i] = SID_NAME_UNKNOWN;
2153
2154                 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
2155                                      "sAMAccountName=%s",
2156                                      ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
2157                 if (count != 1) {
2158                         status = STATUS_SOME_UNMAPPED;
2159                         continue;
2160                 }
2161
2162                 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
2163                 if (sid == NULL) {
2164                         status = STATUS_SOME_UNMAPPED;
2165                         continue;
2166                 }
2167
2168                 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
2169                 if (atype == 0) {
2170                         status = STATUS_SOME_UNMAPPED;
2171                         continue;
2172                 }
2173
2174                 rtype = ds_atype_map(atype);
2175
2176                 if (rtype == SID_NAME_UNKNOWN) {
2177                         status = STATUS_SOME_UNMAPPED;
2178                         continue;
2179                 }
2180
2181                 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
2182                 r->out.types->ids[i] = rtype;
2183                 num_mapped++;
2184         }
2185
2186         if (num_mapped == 0) {
2187                 return NT_STATUS_NONE_MAPPED;
2188         }
2189         return status;
2190 }
2191
2192
2193 /*
2194   samr_LookupRids
2195 */
2196 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2197                        struct samr_LookupRids *r)
2198 {
2199         NTSTATUS status;
2200         struct dcesrv_handle *h;
2201         struct samr_domain_state *d_state;
2202         const char **names;
2203         struct lsa_String *lsa_names;
2204         enum lsa_SidType *ids;
2205
2206         ZERO_STRUCTP(r->out.names);
2207         ZERO_STRUCTP(r->out.types);
2208
2209         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2210
2211         d_state = h->data;
2212
2213         if (r->in.num_rids == 0)
2214                 return NT_STATUS_OK;
2215
2216         lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
2217         names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
2218         ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
2219
2220         if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
2221                 return NT_STATUS_NO_MEMORY;
2222
2223         r->out.names->names = lsa_names;
2224         r->out.names->count = r->in.num_rids;
2225
2226         r->out.types->ids = (uint32_t *) ids;
2227         r->out.types->count = r->in.num_rids;
2228
2229         status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
2230                                   r->in.num_rids, r->in.rids, names, ids);
2231         if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
2232                 uint32_t i;
2233                 for (i = 0; i < r->in.num_rids; i++) {
2234                         lsa_names[i].string = names[i];
2235                 }
2236         }
2237         return status;
2238 }
2239
2240
2241 /*
2242   samr_OpenGroup
2243 */
2244 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2245                        struct samr_OpenGroup *r)
2246 {
2247         struct samr_domain_state *d_state;
2248         struct samr_account_state *a_state;
2249         struct dcesrv_handle *h;
2250         const char *groupname;
2251         struct dom_sid *sid;
2252         struct ldb_message **msgs;
2253         struct dcesrv_handle *g_handle;
2254         const char * const attrs[2] = { "sAMAccountName", NULL };
2255         int ret;
2256
2257         ZERO_STRUCTP(r->out.group_handle);
2258
2259         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2260
2261         d_state = h->data;
2262
2263         /* form the group SID */
2264         sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2265         if (!sid) {
2266                 return NT_STATUS_NO_MEMORY;
2267         }
2268
2269         /* search for the group record */
2270         if (d_state->builtin) {
2271                 ret = gendb_search(d_state->sam_ctx,
2272                                    mem_ctx, d_state->domain_dn, &msgs, attrs,
2273                                    "(&(objectSid=%s)(objectClass=group)"
2274                                    "(groupType=%d))",
2275                                    ldap_encode_ndr_dom_sid(mem_ctx, sid),
2276                                    GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
2277         } else {
2278                 ret = gendb_search(d_state->sam_ctx,
2279                                    mem_ctx, d_state->domain_dn, &msgs, attrs,
2280                                    "(&(objectSid=%s)(objectClass=group)"
2281                                    "(|(groupType=%d)(groupType=%d)))",
2282                                    ldap_encode_ndr_dom_sid(mem_ctx, sid),
2283                                    GTYPE_SECURITY_UNIVERSAL_GROUP,
2284                                    GTYPE_SECURITY_GLOBAL_GROUP);
2285         }
2286         if (ret == 0) {
2287                 return NT_STATUS_NO_SUCH_GROUP;
2288         }
2289         if (ret != 1) {
2290                 DEBUG(0,("Found %d records matching sid %s\n",
2291                          ret, dom_sid_string(mem_ctx, sid)));
2292                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2293         }
2294
2295         groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2296         if (groupname == NULL) {
2297                 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2298                          dom_sid_string(mem_ctx, sid)));
2299                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2300         }
2301
2302         a_state = talloc(mem_ctx, struct samr_account_state);
2303         if (!a_state) {
2304                 return NT_STATUS_NO_MEMORY;
2305         }
2306         a_state->sam_ctx = d_state->sam_ctx;
2307         a_state->access_mask = r->in.access_mask;
2308         a_state->domain_state = talloc_reference(a_state, d_state);
2309         a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2310         a_state->account_sid = talloc_steal(a_state, sid);
2311         a_state->account_name = talloc_strdup(a_state, groupname);
2312         if (!a_state->account_name) {
2313                 return NT_STATUS_NO_MEMORY;
2314         }
2315
2316         /* create the policy handle */
2317         g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_GROUP);
2318         if (!g_handle) {
2319                 return NT_STATUS_NO_MEMORY;
2320         }
2321
2322         g_handle->data = talloc_steal(g_handle, a_state);
2323
2324         *r->out.group_handle = g_handle->wire_handle;
2325
2326         return NT_STATUS_OK;
2327 }
2328
2329 /*
2330   samr_QueryGroupInfo
2331 */
2332 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2333                        struct samr_QueryGroupInfo *r)
2334 {
2335         struct dcesrv_handle *h;
2336         struct samr_account_state *a_state;
2337         struct ldb_message *msg, **res;
2338         const char * const attrs[4] = { "sAMAccountName", "description",
2339                                         "numMembers", NULL };
2340         int ret;
2341         union samr_GroupInfo *info;
2342
2343         *r->out.info = NULL;
2344
2345         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2346
2347         a_state = h->data;
2348
2349         /* pull all the group attributes */
2350         ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2351                               a_state->account_dn, &res, attrs);
2352         if (ret == 0) {
2353                 return NT_STATUS_NO_SUCH_GROUP;
2354         }
2355         if (ret != 1) {
2356                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2357         }
2358         msg = res[0];
2359
2360         /* allocate the info structure */
2361         info = talloc_zero(mem_ctx, union samr_GroupInfo);
2362         if (info == NULL) {
2363                 return NT_STATUS_NO_MEMORY;
2364         }
2365
2366         /* Fill in the level */
2367         switch (r->in.level) {
2368         case GROUPINFOALL:
2369                 QUERY_STRING(msg, all.name,        "sAMAccountName");
2370                 info->all.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2371                 QUERY_UINT  (msg, all.num_members,      "numMembers")
2372                 QUERY_STRING(msg, all.description, "description");
2373                 break;
2374         case GROUPINFONAME:
2375                 QUERY_STRING(msg, name,            "sAMAccountName");
2376                 break;
2377         case GROUPINFOATTRIBUTES:
2378                 info->attributes.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2379                 break;
2380         case GROUPINFODESCRIPTION:
2381                 QUERY_STRING(msg, description, "description");
2382                 break;
2383         case GROUPINFOALL2:
2384                 QUERY_STRING(msg, all2.name,        "sAMAccountName");
2385                 info->all.attributes = SE_GROUP_DEFAULT_FLAGS; /* Do like w2k3 */
2386                 QUERY_UINT  (msg, all2.num_members,      "numMembers")
2387                 QUERY_STRING(msg, all2.description, "description");
2388                 break;
2389         default:
2390                 talloc_free(info);
2391                 return NT_STATUS_INVALID_INFO_CLASS;
2392         }
2393
2394         *r->out.info = info;
2395
2396         return NT_STATUS_OK;
2397 }
2398
2399
2400 /*
2401   samr_SetGroupInfo
2402 */
2403 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2404                                   struct samr_SetGroupInfo *r)
2405 {
2406         struct dcesrv_handle *h;
2407         struct samr_account_state *g_state;
2408         struct ldb_message *msg;
2409         int ret;
2410
2411         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2412
2413         g_state = h->data;
2414
2415         msg = ldb_msg_new(mem_ctx);
2416         if (msg == NULL) {
2417                 return NT_STATUS_NO_MEMORY;
2418         }
2419
2420         msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
2421         if (!msg->dn) {
2422                 return NT_STATUS_NO_MEMORY;
2423         }
2424
2425         switch (r->in.level) {
2426         case GROUPINFODESCRIPTION:
2427                 SET_STRING(msg, description,         "description");
2428                 break;
2429         case GROUPINFONAME:
2430                 /* On W2k3 this does not change the name, it changes the
2431                  * sAMAccountName attribute */
2432                 SET_STRING(msg, name,                "sAMAccountName");
2433                 break;
2434         case GROUPINFOATTRIBUTES:
2435                 /* This does not do anything obviously visible in W2k3 LDAP */
2436                 return NT_STATUS_OK;
2437         default:
2438                 return NT_STATUS_INVALID_INFO_CLASS;
2439         }
2440
2441         /* modify the samdb record */
2442         ret = ldb_modify(g_state->sam_ctx, msg);
2443         if (ret != LDB_SUCCESS) {
2444                 return dsdb_ldb_err_to_ntstatus(ret);
2445         }
2446
2447         return NT_STATUS_OK;
2448 }
2449
2450
2451 /*
2452   samr_AddGroupMember
2453 */
2454 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2455                        struct samr_AddGroupMember *r)
2456 {
2457         struct dcesrv_handle *h;
2458         struct samr_account_state *a_state;
2459         struct samr_domain_state *d_state;
2460         struct ldb_message *mod;
2461         struct dom_sid *membersid;
2462         const char *memberdn;
2463         struct ldb_result *res;
2464         const char * const attrs[] = { NULL };
2465         int ret;
2466
2467         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2468
2469         a_state = h->data;
2470         d_state = a_state->domain_state;
2471
2472         membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2473         if (membersid == NULL) {
2474                 return NT_STATUS_NO_MEMORY;
2475         }
2476
2477         /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2478         ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2479                          d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2480                          "(objectSid=%s)",
2481                          ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2482
2483         if (ret != LDB_SUCCESS) {
2484                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2485         }
2486
2487         if (res->count == 0) {
2488                 return NT_STATUS_NO_SUCH_USER;
2489         }
2490
2491         if (res->count > 1) {
2492                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2493         }
2494
2495         memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2496
2497         if (memberdn == NULL)
2498                 return NT_STATUS_NO_MEMORY;
2499
2500         mod = ldb_msg_new(mem_ctx);
2501         if (mod == NULL) {
2502                 return NT_STATUS_NO_MEMORY;
2503         }
2504
2505         mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2506
2507         ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2508                                                                 memberdn);
2509         if (ret != LDB_SUCCESS) {
2510                 return dsdb_ldb_err_to_ntstatus(ret);
2511         }
2512
2513         ret = ldb_modify(a_state->sam_ctx, mod);
2514         switch (ret) {
2515         case LDB_SUCCESS:
2516                 return NT_STATUS_OK;
2517         case LDB_ERR_ENTRY_ALREADY_EXISTS:
2518                 return NT_STATUS_MEMBER_IN_GROUP;
2519         case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2520                 return NT_STATUS_ACCESS_DENIED;
2521         default:
2522                 return dsdb_ldb_err_to_ntstatus(ret);
2523         }
2524 }
2525
2526
2527 /*
2528   samr_DeleteDomainGroup
2529 */
2530 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2531                        struct samr_DeleteDomainGroup *r)
2532 {
2533         struct dcesrv_handle *h;
2534         struct samr_account_state *a_state;
2535         int ret;
2536
2537         *r->out.group_handle = *r->in.group_handle;
2538
2539         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2540
2541         a_state = h->data;
2542
2543         ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2544         if (ret != LDB_SUCCESS) {
2545                 return dsdb_ldb_err_to_ntstatus(ret);
2546         }
2547
2548         talloc_free(h);
2549         ZERO_STRUCTP(r->out.group_handle);
2550
2551         return NT_STATUS_OK;
2552 }
2553
2554
2555 /*
2556   samr_DeleteGroupMember
2557 */
2558 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2559                        struct samr_DeleteGroupMember *r)
2560 {
2561         struct dcesrv_handle *h;
2562         struct samr_account_state *a_state;
2563         struct samr_domain_state *d_state;
2564         struct ldb_message *mod;
2565         struct dom_sid *membersid;
2566         const char *memberdn;
2567         struct ldb_result *res;
2568         const char * const attrs[] = { NULL };
2569         int ret;
2570
2571         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2572
2573         a_state = h->data;
2574         d_state = a_state->domain_state;
2575
2576         membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2577         if (membersid == NULL) {
2578                 return NT_STATUS_NO_MEMORY;
2579         }
2580
2581         /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2582         ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2583                          d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2584                          "(objectSid=%s)",
2585                          ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2586
2587         if (ret != LDB_SUCCESS) {
2588                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2589         }
2590
2591         if (res->count == 0) {
2592                 return NT_STATUS_NO_SUCH_USER;
2593         }
2594
2595         if (res->count > 1) {
2596                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2597         }
2598
2599         memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2600
2601         if (memberdn == NULL)
2602                 return NT_STATUS_NO_MEMORY;
2603
2604         mod = ldb_msg_new(mem_ctx);
2605         if (mod == NULL) {
2606                 return NT_STATUS_NO_MEMORY;
2607         }
2608
2609         mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2610
2611         ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2612                                                                 memberdn);
2613         if (ret != LDB_SUCCESS) {
2614                 return NT_STATUS_NO_MEMORY;
2615         }
2616
2617         ret = ldb_modify(a_state->sam_ctx, mod);
2618         switch (ret) {
2619         case LDB_SUCCESS:
2620                 return NT_STATUS_OK;
2621         case LDB_ERR_UNWILLING_TO_PERFORM:
2622         case LDB_ERR_NO_SUCH_ATTRIBUTE:
2623                 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2624         case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2625                 return NT_STATUS_ACCESS_DENIED;
2626         default:
2627                 return dsdb_ldb_err_to_ntstatus(ret);
2628         }
2629 }
2630
2631
2632 /*
2633   samr_QueryGroupMember
2634 */
2635 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2636                                       struct samr_QueryGroupMember *r)
2637 {
2638         struct dcesrv_handle *h;
2639         struct samr_account_state *a_state;
2640         struct samr_domain_state *d_state;
2641         struct samr_RidAttrArray *array;
2642         unsigned int i, num_members;
2643         struct dom_sid *members;
2644         NTSTATUS status;
2645
2646         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2647
2648         a_state = h->data;
2649         d_state = a_state->domain_state;
2650
2651         status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2652                                      a_state->account_dn, &members,
2653                                      &num_members);
2654         if (!NT_STATUS_IS_OK(status)) {
2655                 return status;
2656         }
2657
2658         array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2659         if (array == NULL) {
2660                 return NT_STATUS_NO_MEMORY;
2661         }
2662
2663         if (num_members == 0) {
2664                 *r->out.rids = array;
2665
2666                 return NT_STATUS_OK;
2667         }
2668
2669         array->rids = talloc_array(array, uint32_t, num_members);
2670         if (array->rids == NULL) {
2671                 return NT_STATUS_NO_MEMORY;
2672         }
2673
2674         array->attributes = talloc_array(array, uint32_t, num_members);
2675         if (array->attributes == NULL) {
2676                 return NT_STATUS_NO_MEMORY;
2677         }
2678
2679         array->count = 0;
2680         for (i=0; i<num_members; i++) {
2681                 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2682                         continue;
2683                 }
2684
2685                 status = dom_sid_split_rid(NULL, &members[i], NULL,
2686                                            &array->rids[array->count]);
2687                 if (!NT_STATUS_IS_OK(status)) {
2688                         return status;
2689                 }
2690
2691                 array->attributes[array->count] = SE_GROUP_DEFAULT_FLAGS;
2692                 array->count++;
2693         }
2694
2695         *r->out.rids = array;
2696
2697         return NT_STATUS_OK;
2698 }
2699
2700
2701 /*
2702   samr_SetMemberAttributesOfGroup
2703 */
2704 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2705                        struct samr_SetMemberAttributesOfGroup *r)
2706 {
2707         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2708 }
2709
2710
2711 /*
2712   samr_OpenAlias
2713 */
2714 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2715                        struct samr_OpenAlias *r)
2716 {
2717         struct samr_domain_state *d_state;
2718         struct samr_account_state *a_state;
2719         struct dcesrv_handle *h;
2720         const char *alias_name;
2721         struct dom_sid *sid;
2722         struct ldb_message **msgs;
2723         struct dcesrv_handle *g_handle;
2724         const char * const attrs[2] = { "sAMAccountName", NULL };
2725         int ret;
2726
2727         ZERO_STRUCTP(r->out.alias_handle);
2728
2729         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2730
2731         d_state = h->data;
2732
2733         /* form the alias SID */
2734         sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2735         if (sid == NULL)
2736                 return NT_STATUS_NO_MEMORY;
2737
2738         /* search for the group record */
2739         ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2740                            "(&(objectSid=%s)(objectclass=group)"
2741                            "(|(grouptype=%d)(grouptype=%d)))",
2742                            ldap_encode_ndr_dom_sid(mem_ctx, sid),
2743                            GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2744                            GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2745         if (ret == 0) {
2746                 return NT_STATUS_NO_SUCH_ALIAS;
2747         }
2748         if (ret != 1) {
2749                 DEBUG(0,("Found %d records matching sid %s\n",
2750                          ret, dom_sid_string(mem_ctx, sid)));
2751                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2752         }
2753
2754         alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2755         if (alias_name == NULL) {
2756                 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2757                          dom_sid_string(mem_ctx, sid)));
2758                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2759         }
2760
2761         a_state = talloc(mem_ctx, struct samr_account_state);
2762         if (!a_state) {
2763                 return NT_STATUS_NO_MEMORY;
2764         }
2765         a_state->sam_ctx = d_state->sam_ctx;
2766         a_state->access_mask = r->in.access_mask;
2767         a_state->domain_state = talloc_reference(a_state, d_state);
2768         a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2769         a_state->account_sid = talloc_steal(a_state, sid);
2770         a_state->account_name = talloc_strdup(a_state, alias_name);
2771         if (!a_state->account_name) {
2772                 return NT_STATUS_NO_MEMORY;
2773         }
2774
2775         /* create the policy handle */
2776         g_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_ALIAS);
2777         if (!g_handle) {
2778                 return NT_STATUS_NO_MEMORY;
2779         }
2780
2781         g_handle->data = talloc_steal(g_handle, a_state);
2782
2783         *r->out.alias_handle = g_handle->wire_handle;
2784
2785         return NT_STATUS_OK;
2786 }
2787
2788
2789 /*
2790   samr_QueryAliasInfo
2791 */
2792 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2793                        struct samr_QueryAliasInfo *r)
2794 {
2795         struct dcesrv_handle *h;
2796         struct samr_account_state *a_state;
2797         struct ldb_message *msg, **res;
2798         const char * const attrs[4] = { "sAMAccountName", "description",
2799                                         "numMembers", NULL };
2800         int ret;
2801         union samr_AliasInfo *info;
2802
2803         *r->out.info = NULL;
2804
2805         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2806
2807         a_state = h->data;
2808
2809         /* pull all the alias attributes */
2810         ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2811                               a_state->account_dn, &res, attrs);
2812         if (ret == 0) {
2813                 return NT_STATUS_NO_SUCH_ALIAS;
2814         }
2815         if (ret != 1) {
2816                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2817         }
2818         msg = res[0];
2819
2820         /* allocate the info structure */
2821         info = talloc_zero(mem_ctx, union samr_AliasInfo);
2822         if (info == NULL) {
2823                 return NT_STATUS_NO_MEMORY;
2824         }
2825
2826         switch(r->in.level) {
2827         case ALIASINFOALL:
2828                 QUERY_STRING(msg, all.name, "sAMAccountName");
2829                 QUERY_UINT  (msg, all.num_members, "numMembers");
2830                 QUERY_STRING(msg, all.description, "description");
2831                 break;
2832         case ALIASINFONAME:
2833                 QUERY_STRING(msg, name, "sAMAccountName");
2834                 break;
2835         case ALIASINFODESCRIPTION:
2836                 QUERY_STRING(msg, description, "description");
2837                 break;
2838         default:
2839                 talloc_free(info);
2840                 return NT_STATUS_INVALID_INFO_CLASS;
2841         }
2842
2843         *r->out.info = info;
2844
2845         return NT_STATUS_OK;
2846 }
2847
2848
2849 /*
2850   samr_SetAliasInfo
2851 */
2852 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2853                        struct samr_SetAliasInfo *r)
2854 {
2855         struct dcesrv_handle *h;
2856         struct samr_account_state *a_state;
2857         struct ldb_message *msg;
2858         int ret;
2859
2860         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2861
2862         a_state = h->data;
2863
2864         msg = ldb_msg_new(mem_ctx);
2865         if (msg == NULL) {
2866                 return NT_STATUS_NO_MEMORY;
2867         }
2868
2869         msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2870         if (!msg->dn) {
2871                 return NT_STATUS_NO_MEMORY;
2872         }
2873
2874         switch (r->in.level) {
2875         case ALIASINFODESCRIPTION:
2876                 SET_STRING(msg, description,         "description");
2877                 break;
2878         case ALIASINFONAME:
2879                 /* On W2k3 this does not change the name, it changes the
2880                  * sAMAccountName attribute */
2881                 SET_STRING(msg, name,                "sAMAccountName");
2882                 break;
2883         default:
2884                 return NT_STATUS_INVALID_INFO_CLASS;
2885         }
2886
2887         /* modify the samdb record */
2888         ret = ldb_modify(a_state->sam_ctx, msg);
2889         if (ret != LDB_SUCCESS) {
2890                 return dsdb_ldb_err_to_ntstatus(ret);
2891         }
2892
2893         return NT_STATUS_OK;
2894 }
2895
2896
2897 /*
2898   samr_DeleteDomAlias
2899 */
2900 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2901                        struct samr_DeleteDomAlias *r)
2902 {
2903         struct dcesrv_handle *h;
2904         struct samr_account_state *a_state;
2905         int ret;
2906
2907         *r->out.alias_handle = *r->in.alias_handle;
2908
2909         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2910
2911         a_state = h->data;
2912
2913         ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2914         if (ret != LDB_SUCCESS) {
2915                 return dsdb_ldb_err_to_ntstatus(ret);
2916         }
2917
2918         talloc_free(h);
2919         ZERO_STRUCTP(r->out.alias_handle);
2920
2921         return NT_STATUS_OK;
2922 }
2923
2924
2925 /*
2926   samr_AddAliasMember
2927 */
2928 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2929                        struct samr_AddAliasMember *r)
2930 {
2931         struct dcesrv_handle *h;
2932         struct samr_account_state *a_state;
2933         struct samr_domain_state *d_state;
2934         struct ldb_message *mod;
2935         struct ldb_message **msgs;
2936         const char * const attrs[] = { NULL };
2937         struct ldb_dn *memberdn = NULL;
2938         int ret;
2939         NTSTATUS status;
2940
2941         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2942
2943         a_state = h->data;
2944         d_state = a_state->domain_state;
2945
2946         ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2947                            &msgs, attrs, "(objectsid=%s)",
2948                            ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2949
2950         if (ret == 1) {
2951                 memberdn = msgs[0]->dn;
2952         } else if (ret == 0) {
2953                 status = samdb_create_foreign_security_principal(
2954                         d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2955                 if (!NT_STATUS_IS_OK(status)) {
2956                         return status;
2957                 }
2958         } else {
2959                 DEBUG(0,("Found %d records matching sid %s\n",
2960                          ret, dom_sid_string(mem_ctx, r->in.sid)));
2961                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2962         }
2963
2964         if (memberdn == NULL) {
2965                 DEBUG(0, ("Could not find memberdn\n"));
2966                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2967         }
2968
2969         mod = ldb_msg_new(mem_ctx);
2970         if (mod == NULL) {
2971                 return NT_STATUS_NO_MEMORY;
2972         }
2973
2974         mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2975
2976         ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2977                                  ldb_dn_alloc_linearized(mem_ctx, memberdn));
2978         if (ret != LDB_SUCCESS) {
2979                 return dsdb_ldb_err_to_ntstatus(ret);
2980         }
2981
2982         ret = ldb_modify(a_state->sam_ctx, mod);
2983         switch (ret) {
2984         case LDB_SUCCESS:
2985                 return NT_STATUS_OK;
2986         case LDB_ERR_ENTRY_ALREADY_EXISTS:
2987                 return NT_STATUS_MEMBER_IN_GROUP;
2988         case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2989                 return NT_STATUS_ACCESS_DENIED;
2990         default:
2991                 return dsdb_ldb_err_to_ntstatus(ret);
2992         }
2993 }
2994
2995
2996 /*
2997   samr_DeleteAliasMember
2998 */
2999 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3000                        struct samr_DeleteAliasMember *r)
3001 {
3002         struct dcesrv_handle *h;
3003         struct samr_account_state *a_state;
3004         struct samr_domain_state *d_state;
3005         struct ldb_message *mod;
3006         const char *memberdn;
3007         int ret;
3008
3009         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
3010
3011         a_state = h->data;
3012         d_state = a_state->domain_state;
3013
3014         memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
3015                                        "distinguishedName", "(objectSid=%s)",
3016                                        ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
3017         if (memberdn == NULL) {
3018                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
3019         }
3020
3021         mod = ldb_msg_new(mem_ctx);
3022         if (mod == NULL) {
3023                 return NT_STATUS_NO_MEMORY;
3024         }
3025
3026         mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
3027
3028         ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
3029                                                                  memberdn);
3030         if (ret != LDB_SUCCESS) {
3031                 return dsdb_ldb_err_to_ntstatus(ret);
3032         }
3033
3034         ret = ldb_modify(a_state->sam_ctx, mod);
3035         switch (ret) {
3036         case LDB_SUCCESS:
3037                 return NT_STATUS_OK;
3038         case LDB_ERR_UNWILLING_TO_PERFORM:
3039                 return NT_STATUS_MEMBER_NOT_IN_GROUP;
3040         case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
3041                 return NT_STATUS_ACCESS_DENIED;
3042         default:
3043                 return dsdb_ldb_err_to_ntstatus(ret);
3044         }
3045 }
3046
3047
3048 /*
3049   samr_GetMembersInAlias
3050 */
3051 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3052                        struct samr_GetMembersInAlias *r)
3053 {
3054         struct dcesrv_handle *h;
3055         struct samr_account_state *a_state;
3056         struct samr_domain_state *d_state;
3057         struct lsa_SidPtr *array;
3058         unsigned int i, num_members;
3059         struct dom_sid *members;
3060         NTSTATUS status;
3061
3062         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
3063
3064         a_state = h->data;
3065         d_state = a_state->domain_state;
3066
3067         status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
3068                                      a_state->account_dn, &members,
3069                                      &num_members);
3070         if (!NT_STATUS_IS_OK(status)) {
3071                 return status;
3072         }
3073
3074         if (num_members == 0) {
3075                 r->out.sids->sids = NULL;
3076
3077                 return NT_STATUS_OK;
3078         }
3079
3080         array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
3081         if (array == NULL) {
3082                 return NT_STATUS_NO_MEMORY;
3083         }
3084
3085         for (i=0; i<num_members; i++) {
3086                 array[i].sid = &members[i];
3087         }
3088
3089         r->out.sids->num_sids = num_members;
3090         r->out.sids->sids = array;
3091
3092         return NT_STATUS_OK;
3093 }
3094
3095 /*
3096   samr_OpenUser
3097 */
3098 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3099                               struct samr_OpenUser *r)
3100 {
3101         struct samr_domain_state *d_state;
3102         struct samr_account_state *a_state;
3103         struct dcesrv_handle *h;
3104         const char *account_name;
3105         struct dom_sid *sid;
3106         struct ldb_message **msgs;
3107         struct dcesrv_handle *u_handle;
3108         const char * const attrs[2] = { "sAMAccountName", NULL };
3109         int ret;
3110
3111         ZERO_STRUCTP(r->out.user_handle);
3112
3113         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
3114
3115         d_state = h->data;
3116
3117         /* form the users SID */
3118         sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
3119         if (!sid) {
3120                 return NT_STATUS_NO_MEMORY;
3121         }
3122
3123         /* search for the user record */
3124         ret = gendb_search(d_state->sam_ctx,
3125                            mem_ctx, d_state->domain_dn, &msgs, attrs,
3126                            "(&(objectSid=%s)(objectclass=user))",
3127                            ldap_encode_ndr_dom_sid(mem_ctx, sid));
3128         if (ret == 0) {
3129                 return NT_STATUS_NO_SUCH_USER;
3130         }
3131         if (ret != 1) {
3132                 DEBUG(0,("Found %d records matching sid %s\n", ret,
3133                          dom_sid_string(mem_ctx, sid)));
3134                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3135         }
3136
3137         account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
3138         if (account_name == NULL) {
3139                 DEBUG(0,("sAMAccountName field missing for sid %s\n",
3140                          dom_sid_string(mem_ctx, sid)));
3141                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3142         }
3143
3144         a_state = talloc(mem_ctx, struct samr_account_state);
3145         if (!a_state) {
3146                 return NT_STATUS_NO_MEMORY;
3147         }
3148         a_state->sam_ctx = d_state->sam_ctx;
3149         a_state->access_mask = r->in.access_mask;
3150         a_state->domain_state = talloc_reference(a_state, d_state);
3151         a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
3152         a_state->account_sid = talloc_steal(a_state, sid);
3153         a_state->account_name = talloc_strdup(a_state, account_name);
3154         if (!a_state->account_name) {
3155                 return NT_STATUS_NO_MEMORY;
3156         }
3157
3158         /* create the policy handle */
3159         u_handle = dcesrv_handle_create(dce_call, SAMR_HANDLE_USER);
3160         if (!u_handle) {
3161                 return NT_STATUS_NO_MEMORY;
3162         }
3163
3164         u_handle->data = talloc_steal(u_handle, a_state);
3165
3166         *r->out.user_handle = u_handle->wire_handle;
3167
3168         return NT_STATUS_OK;
3169
3170 }
3171
3172
3173 /*
3174   samr_DeleteUser
3175 */
3176 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3177                                 struct samr_DeleteUser *r)
3178 {
3179         struct dcesrv_handle *h;
3180         struct samr_account_state *a_state;
3181         int ret;
3182
3183         *r->out.user_handle = *r->in.user_handle;
3184
3185         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3186
3187         a_state = h->data;
3188
3189         ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
3190         if (ret != LDB_SUCCESS) {
3191                 DEBUG(1, ("Failed to delete user: %s: %s\n",
3192                           ldb_dn_get_linearized(a_state->account_dn),
3193                           ldb_errstring(a_state->sam_ctx)));
3194                 return dsdb_ldb_err_to_ntstatus(ret);
3195         }
3196
3197         talloc_free(h);
3198         ZERO_STRUCTP(r->out.user_handle);
3199
3200         return NT_STATUS_OK;
3201 }
3202
3203
3204 /*
3205   samr_QueryUserInfo
3206 */
3207 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3208                                    struct samr_QueryUserInfo *r)
3209 {
3210         struct dcesrv_handle *h;
3211         struct samr_account_state *a_state;
3212         struct ldb_message *msg, **res;
3213         int ret;
3214         struct ldb_context *sam_ctx;
3215
3216         const char * const *attrs = NULL;
3217         union samr_UserInfo *info;
3218
3219         NTSTATUS status;
3220
3221         *r->out.info = NULL;
3222
3223         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3224
3225         a_state = h->data;
3226         sam_ctx = a_state->sam_ctx;
3227
3228         /* fill in the reply */
3229         switch (r->in.level) {
3230         case 1:
3231         {
3232                 static const char * const attrs2[] = {"sAMAccountName",
3233                                                       "displayName",
3234                                                       "primaryGroupID",
3235                                                       "description",
3236                                                       "comment",
3237                                                       NULL};
3238                 attrs = attrs2;
3239                 break;
3240         }
3241         case 2:
3242         {
3243                 static const char * const attrs2[] = {"comment",
3244                                                       "countryCode",
3245                                                       "codePage",
3246                                                       NULL};
3247                 attrs = attrs2;
3248                 break;
3249         }
3250         case 3:
3251         {
3252                 static const char * const attrs2[] = {"sAMAccountName",
3253                                                       "displayName",
3254                                                       "objectSid",
3255                                                       "primaryGroupID",
3256                                                       "homeDirectory",
3257                                                       "homeDrive",
3258                                                       "scriptPath",
3259                                                       "profilePath",
3260                                                       "userWorkstations",
3261                                                       "lastLogon",
3262                                                       "lastLogoff",
3263                                                       "pwdLastSet",
3264                                                       "msDS-UserPasswordExpiryTimeComputed",
3265                                                       "logonHours",
3266                                                       "badPwdCount",
3267                                                       "badPasswordTime",
3268                                                       "logonCount",
3269                                                       "userAccountControl",
3270                                                       "msDS-User-Account-Control-Computed",
3271                                                       NULL};
3272                 attrs = attrs2;
3273                 break;
3274         }
3275         case 4:
3276         {
3277                 static const char * const attrs2[] = {"logonHours",
3278                                                       NULL};
3279                 attrs = attrs2;
3280                 break;
3281         }
3282         case 5:
3283         {
3284                 static const char * const attrs2[] = {"sAMAccountName",
3285                                                       "displayName",
3286                                                       "objectSid",
3287                                                       "primaryGroupID",
3288                                                       "homeDirectory",
3289                                                       "homeDrive",
3290                                                       "scriptPath",
3291                                                       "profilePath",
3292                                                       "description",
3293                                                       "userWorkstations",
3294                                                       "lastLogon",
3295                                                       "lastLogoff",
3296                                                       "logonHours",
3297                                                       "badPwdCount",
3298                                                       "badPasswordTime",
3299                                                       "logonCount",
3300                                                       "pwdLastSet",
3301                                                       "msDS-ResultantPSO",
3302                                                       "msDS-UserPasswordExpiryTimeComputed",
3303                                                       "accountExpires",
3304                                                       "userAccountControl",
3305                                                       "msDS-User-Account-Control-Computed",
3306                                                       NULL};
3307                 attrs = attrs2;
3308                 break;
3309         }
3310         case 6:
3311         {
3312                 static const char * const attrs2[] = {"sAMAccountName",
3313                                                       "displayName",
3314                                                       NULL};
3315                 attrs = attrs2;
3316                 break;
3317         }
3318         case 7:
3319         {
3320                 static const char * const attrs2[] = {"sAMAccountName",
3321                                                       NULL};
3322                 attrs = attrs2;
3323                 break;
3324         }
3325         case 8:
3326         {
3327                 static const char * const attrs2[] = {"displayName",
3328                                                       NULL};
3329                 attrs = attrs2;
3330                 break;
3331         }
3332         case 9:
3333         {
3334                 static const char * const attrs2[] = {"primaryGroupID",
3335                                                       NULL};
3336                 attrs = attrs2;
3337                 break;
3338         }
3339         case 10:
3340         {
3341                 static const char * const attrs2[] = {"homeDirectory",
3342                                                       "homeDrive",
3343                                                       NULL};
3344                 attrs = attrs2;
3345                 break;
3346         }
3347         case 11:
3348         {
3349                 static const char * const attrs2[] = {"scriptPath",
3350                                                       NULL};
3351                 attrs = attrs2;
3352                 break;
3353         }
3354         case 12:
3355         {
3356                 static const char * const attrs2[] = {"profilePath",
3357                                                       NULL};
3358                 attrs = attrs2;
3359                 break;
3360         }
3361         case 13:
3362         {
3363                 static const char * const attrs2[] = {"description",
3364                                                       NULL};
3365                 attrs = attrs2;
3366                 break;
3367         }
3368         case 14:
3369         {
3370                 static const char * const attrs2[] = {"userWorkstations",
3371                                                       NULL};
3372                 attrs = attrs2;
3373                 break;
3374         }
3375         case 16:
3376         {
3377                 static const char * const attrs2[] = {"userAccountControl",
3378                                                       "msDS-User-Account-Control-Computed",
3379                                                       "pwdLastSet",
3380                                                       "msDS-UserPasswordExpiryTimeComputed",
3381                                                       NULL};
3382                 attrs = attrs2;
3383                 break;
3384         }
3385         case 17:
3386         {
3387                 static const char * const attrs2[] = {"accountExpires",
3388                                                       NULL};
3389                 attrs = attrs2;
3390                 break;
3391         }
3392         case 18:
3393         {
3394                 return NT_STATUS_NOT_SUPPORTED;
3395         }
3396         case 20:
3397         {
3398                 static const char * const attrs2[] = {"userParameters",
3399                                                       NULL};
3400                 attrs = attrs2;
3401                 break;
3402         }
3403         case 21:
3404         {
3405                 static const char * const attrs2[] = {"lastLogon",
3406                                                       "lastLogoff",
3407                                                       "pwdLastSet",
3408                                                       "msDS-ResultantPSO",
3409                                                       "msDS-UserPasswordExpiryTimeComputed",
3410                                                       "accountExpires",
3411                                                       "sAMAccountName",
3412                                                       "displayName",
3413                                                       "homeDirectory",
3414                                                       "homeDrive",
3415                                                       "scriptPath",
3416                                                       "profilePath",
3417                                                       "description",
3418                                                       "userWorkstations",
3419                                                       "comment",
3420                                                       "userParameters",
3421                                                       "objectSid",
3422                                                       "primaryGroupID",
3423                                                       "userAccountControl",
3424                                                       "msDS-User-Account-Control-Computed",
3425                                                       "logonHours",
3426                                                       "badPwdCount",
3427                                                       "badPasswordTime",
3428                                                       "logonCount",
3429                                                       "countryCode",
3430                                                       "codePage",
3431                                                       NULL};
3432                 attrs = attrs2;
3433                 break;
3434         }
3435         case 23:
3436         case 24:
3437         case 25:
3438         case 26:
3439         {
3440                 return NT_STATUS_NOT_SUPPORTED;
3441         }
3442         default:
3443         {
3444                 return NT_STATUS_INVALID_INFO_CLASS;
3445         }
3446         }
3447
3448         /* pull all the user attributes */
3449         ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
3450                               a_state->account_dn, &res, attrs);
3451         if (ret == 0) {
3452                 return NT_STATUS_NO_SUCH_USER;
3453         }
3454         if (ret != 1) {
3455                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3456         }
3457         msg = res[0];
3458
3459         /* allocate the info structure */
3460         info = talloc_zero(mem_ctx, union samr_UserInfo);
3461         if (info == NULL) {
3462                 return NT_STATUS_NO_MEMORY;
3463         }
3464
3465         /* fill in the reply */
3466         switch (r->in.level) {
3467         case 1:
3468                 QUERY_STRING(msg, info1.account_name,          "sAMAccountName");
3469                 QUERY_STRING(msg, info1.full_name,             "displayName");
3470                 QUERY_UINT  (msg, info1.primary_gid,           "primaryGroupID");
3471                 QUERY_STRING(msg, info1.description,           "description");
3472                 QUERY_STRING(msg, info1.comment,               "comment");
3473                 break;
3474
3475         case 2:
3476                 QUERY_STRING(msg, info2.comment,               "comment");
3477                 QUERY_UINT  (msg, info2.country_code,          "countryCode");
3478                 QUERY_UINT  (msg, info2.code_page,             "codePage");
3479                 break;
3480
3481         case 3:
3482                 QUERY_STRING(msg, info3.account_name,          "sAMAccountName");
3483                 QUERY_STRING(msg, info3.full_name,             "displayName");
3484                 QUERY_RID   (msg, info3.rid,                   "objectSid");
3485                 QUERY_UINT  (msg, info3.primary_gid,           "primaryGroupID");
3486                 QUERY_STRING(msg, info3.home_directory,        "homeDirectory");
3487                 QUERY_STRING(msg, info3.home_drive,            "homeDrive");
3488                 QUERY_STRING(msg, info3.logon_script,          "scriptPath");
3489                 QUERY_STRING(msg, info3.profile_path,          "profilePath");
3490                 QUERY_STRING(msg, info3.workstations,          "userWorkstations");
3491                 QUERY_UINT64(msg, info3.last_logon,            "lastLogon");
3492                 QUERY_UINT64(msg, info3.last_logoff,           "lastLogoff");
3493                 QUERY_UINT64(msg, info3.last_password_change,  "pwdLastSet");
3494                 QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
3495                 QUERY_UINT64(msg, info3.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3496                 QUERY_LHOURS(msg, info3.logon_hours,           "logonHours");
3497                 /* level 3 gives the raw badPwdCount value */
3498                 QUERY_UINT  (msg, info3.bad_password_count,    "badPwdCount");
3499                 QUERY_UINT  (msg, info3.logon_count,           "logonCount");
3500                 QUERY_AFLAGS(msg, info3.acct_flags,            "msDS-User-Account-Control-Computed");
3501                 break;
3502
3503         case 4:
3504                 QUERY_LHOURS(msg, info4.logon_hours,           "logonHours");
3505                 break;
3506
3507         case 5:
3508                 QUERY_STRING(msg, info5.account_name,          "sAMAccountName");
3509                 QUERY_STRING(msg, info5.full_name,             "displayName");
3510                 QUERY_RID   (msg, info5.rid,                   "objectSid");
3511                 QUERY_UINT  (msg, info5.primary_gid,           "primaryGroupID");
3512                 QUERY_STRING(msg, info5.home_directory,        "homeDirectory");
3513                 QUERY_STRING(msg, info5.home_drive,            "homeDrive");
3514                 QUERY_STRING(msg, info5.logon_script,          "scriptPath");
3515                 QUERY_STRING(msg, info5.profile_path,          "profilePath");
3516                 QUERY_STRING(msg, info5.description,           "description");
3517                 QUERY_STRING(msg, info5.workstations,          "userWorkstations");
3518                 QUERY_UINT64(msg, info5.last_logon,            "lastLogon");
3519                 QUERY_UINT64(msg, info5.last_logoff,           "lastLogoff");
3520                 QUERY_LHOURS(msg, info5.logon_hours,           "logonHours");
3521                 QUERY_BPWDCT(msg, info5.bad_password_count,    "badPwdCount");
3522                 QUERY_UINT  (msg, info5.logon_count,           "logonCount");
3523                 QUERY_UINT64(msg, info5.last_password_change,  "pwdLastSet");
3524                 QUERY_UINT64(msg, info5.acct_expiry,           "accountExpires");
3525                 QUERY_AFLAGS(msg, info5.acct_flags,            "msDS-User-Account-Control-Computed");
3526                 break;
3527
3528         case 6:
3529                 QUERY_STRING(msg, info6.account_name,   "sAMAccountName");
3530                 QUERY_STRING(msg, info6.full_name,      "displayName");
3531                 break;
3532
3533         case 7:
3534                 QUERY_STRING(msg, info7.account_name,   "sAMAccountName");
3535                 break;
3536
3537         case 8:
3538                 QUERY_STRING(msg, info8.full_name,      "displayName");
3539                 break;
3540
3541         case 9:
3542                 QUERY_UINT  (msg, info9.primary_gid,    "primaryGroupID");
3543                 break;
3544
3545         case 10:
3546                 QUERY_STRING(msg, info10.home_directory,"homeDirectory");
3547                 QUERY_STRING(msg, info10.home_drive,    "homeDrive");
3548                 break;
3549
3550         case 11:
3551                 QUERY_STRING(msg, info11.logon_script,  "scriptPath");
3552                 break;
3553
3554         case 12:
3555                 QUERY_STRING(msg, info12.profile_path,  "profilePath");
3556                 break;
3557
3558         case 13:
3559                 QUERY_STRING(msg, info13.description,   "description");
3560                 break;
3561
3562         case 14:
3563                 QUERY_STRING(msg, info14.workstations,  "userWorkstations");
3564                 break;
3565
3566         case 16:
3567                 QUERY_AFLAGS(msg, info16.acct_flags,    "msDS-User-Account-Control-Computed");
3568                 break;
3569
3570         case 17:
3571                 QUERY_UINT64(msg, info17.acct_expiry,   "accountExpires");
3572                 break;
3573
3574         case 20:
3575                 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info20.parameters);
3576                 if (!NT_STATUS_IS_OK(status)) {
3577                         talloc_free(info);
3578                         return status;
3579                 }
3580                 break;
3581
3582         case 21:
3583                 QUERY_UINT64(msg, info21.last_logon,           "lastLogon");
3584                 QUERY_UINT64(msg, info21.last_logoff,          "lastLogoff");
3585                 QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
3586                 QUERY_UINT64(msg, info21.acct_expiry,          "accountExpires");
3587                 QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
3588                 QUERY_UINT64(msg, info21.force_password_change, "msDS-UserPasswordExpiryTimeComputed");
3589                 QUERY_STRING(msg, info21.account_name,         "sAMAccountName");
3590                 QUERY_STRING(msg, info21.full_name,            "displayName");
3591                 QUERY_STRING(msg, info21.home_directory,       "homeDirectory");
3592                 QUERY_STRING(msg, info21.home_drive,           "homeDrive");
3593                 QUERY_STRING(msg, info21.logon_script,         "scriptPath");
3594                 QUERY_STRING(msg, info21.profile_path,         "profilePath");
3595                 QUERY_STRING(msg, info21.description,          "description");
3596                 QUERY_STRING(msg, info21.workstations,         "userWorkstations");
3597                 QUERY_STRING(msg, info21.comment,              "comment");
3598                 status = samdb_result_parameters(mem_ctx, msg, "userParameters", &info->info21.parameters);
3599                 if (!NT_STATUS_IS_OK(status)) {
3600                         talloc_free(info);
3601                         return status;
3602                 }
3603
3604                 QUERY_RID   (msg, info21.rid,                  "objectSid");
3605                 QUERY_UINT  (msg, info21.primary_gid,          "primaryGroupID");
3606                 QUERY_AFLAGS(msg, info21.acct_flags,           "msDS-User-Account-Control-Computed");
3607                 info->info21.fields_present = 0x08FFFFFF;
3608                 QUERY_LHOURS(msg, info21.logon_hours,          "logonHours");
3609                 QUERY_BPWDCT(msg, info21.bad_password_count,   "badPwdCount");
3610                 QUERY_UINT  (msg, info21.logon_count,          "logonCount");
3611                 if ((info->info21.acct_flags & ACB_PW_EXPIRED) != 0) {
3612                         info->info21.password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
3613                 } else {
3614                         info->info21.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
3615                 }
3616                 QUERY_UINT  (msg, info21.country_code,         "countryCode");
3617                 QUERY_UINT  (msg, info21.code_page,            "codePage");
3618                 break;
3619
3620
3621         default:
3622                 talloc_free(info);
3623                 return NT_STATUS_INVALID_INFO_CLASS;
3624         }
3625
3626         *r->out.info = info;
3627
3628         return NT_STATUS_OK;
3629 }
3630
3631
3632 /*
3633   samr_SetUserInfo
3634 */
3635 static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
3636                                  struct samr_SetUserInfo *r)
3637 {
3638         struct dcesrv_handle *h;
3639         struct samr_account_state *a_state;
3640         struct ldb_message *msg;
3641         int ret;
3642         NTSTATUS status = NT_STATUS_OK;
3643         struct ldb_context *sam_ctx;
3644         DATA_BLOB session_key = data_blob_null;
3645
3646         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
3647
3648         a_state = h->data;
3649         sam_ctx = a_state->sam_ctx;
3650
3651         msg = ldb_msg_new(mem_ctx);
3652         if (msg == NULL) {
3653                 return NT_STATUS_NO_MEMORY;
3654         }
3655
3656         msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
3657         if (!msg->dn) {
3658                 return NT_STATUS_NO_MEMORY;
3659         }
3660
3661         ret = ldb_transaction_start(sam_ctx);
3662         if (ret != LDB_SUCCESS) {
3663                 DBG_ERR("Failed to start a transaction: %s\n",
3664                         ldb_errstring(sam_ctx));
3665                 return NT_STATUS_LOCK_NOT_GRANTED;
3666         }
3667
3668         switch (r->in.level) {
3669         case 2:
3670                 SET_STRING(msg, info2.comment,          "comment");
3671                 SET_UINT  (msg, info2.country_code,     "countryCode");
3672                 SET_UINT  (msg, info2.code_page,        "codePage");
3673                 break;
3674
3675         case 4:
3676                 SET_LHOURS(msg, info4.logon_hours,      "logonHours");
3677                 break;
3678
3679         case 6:
3680                 SET_STRING(msg, info6.account_name,     "samAccountName");
3681                 SET_STRING(msg, info6.full_name,        "displayName");
3682                 break;
3683
3684         case 7:
3685                 SET_STRING(msg, info7.account_name,     "samAccountName");
3686                 break;
3687
3688         case 8:
3689                 SET_STRING(msg, info8.full_name,        "displayName");
3690                 break;
3691
3692         case 9:
3693                 SET_UINT(msg, info9.primary_gid,        "primaryGroupID");
3694                 break;
3695
3696         case 10:
3697                 SET_STRING(msg, info10.home_directory,  "homeDirectory");
3698                 SET_STRING(msg, info10.home_drive,      "homeDrive");
3699                 break;
3700
3701         case 11:
3702                 SET_STRING(msg, info11.logon_script,    "scriptPath");
3703                 break;
3704
3705         case 12:
3706                 SET_STRING(msg, info12.profile_path,    "profilePath");
3707                 break;
3708
3709         case 13:
3710                 SET_STRING(msg, info13.description,     "description");
3711                 break;
3712
3713         case 14:
3714                 SET_STRING(msg, info14.workstations,    "userWorkstations");
3715                 break;
3716
3717         case 16:
3718                 SET_AFLAGS(msg, info16.acct_flags,      "userAccountControl");
3719                 break;
3720
3721         case 17:
3722                 SET_UINT64(msg, info17.acct_expiry,     "accountExpires");
3723                 break;
3724
3725         case 18:
3726                 status = samr_set_password_buffers(dce_call,
3727                                                    sam_ctx,
3728                                                    a_state->account_dn,
3729                                                    mem_ctx,
3730                                                    r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3731                                                    r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3732                 if (!NT_STATUS_IS_OK(status)) {
3733                         goto done;
3734                 }
3735
3736                 if (r->in.info->info18.password_expired > 0) {
3737                         struct ldb_message_element *set_el;
3738                         if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3739                                 status = NT_STATUS_NO_MEMORY;
3740                                 goto done;
3741                         }
3742                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
3743                         set_el->flags = LDB_FLAG_MOD_REPLACE;
3744                 }
3745                 break;
3746
3747         case 20:
3748                 SET_PARAMETERS(msg, info20.parameters,      "userParameters");
3749                 break;
3750
3751         case 21:
3752                 if (r->in.info->info21.fields_present == 0) {
3753                         status = NT_STATUS_INVALID_PARAMETER;
3754                         goto done;
3755                 }
3756
3757 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3758                 IFSET(SAMR_FIELD_LAST_LOGON)
3759                         SET_UINT64(msg, info21.last_logon,     "lastLogon");
3760                 IFSET(SAMR_FIELD_LAST_LOGOFF)
3761                         SET_UINT64(msg, info21.last_logoff,    "lastLogoff");
3762                 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3763                         SET_UINT64(msg, info21.acct_expiry,    "accountExpires");
3764                 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3765                         SET_STRING(msg, info21.account_name,   "samAccountName");
3766                 IFSET(SAMR_FIELD_FULL_NAME)
3767                         SET_STRING(msg, info21.full_name,      "displayName");
3768                 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3769                         SET_STRING(msg, info21.home_directory, "homeDirectory");
3770                 IFSET(SAMR_FIELD_HOME_DRIVE)
3771                         SET_STRING(msg, info21.home_drive,     "homeDrive");
3772                 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3773                         SET_STRING(msg, info21.logon_script,   "scriptPath");
3774                 IFSET(SAMR_FIELD_PROFILE_PATH)
3775                         SET_STRING(msg, info21.profile_path,   "profilePath");
3776                 IFSET(SAMR_FIELD_DESCRIPTION)
3777                         SET_STRING(msg, info21.description,    "description");
3778                 IFSET(SAMR_FIELD_WORKSTATIONS)
3779                         SET_STRING(msg, info21.workstations,   "userWorkstations");
3780                 IFSET(SAMR_FIELD_COMMENT)
3781                         SET_STRING(msg, info21.comment,        "comment");
3782                 IFSET(SAMR_FIELD_PARAMETERS)
3783                         SET_PARAMETERS(msg, info21.parameters, "userParameters");
3784                 IFSET(SAMR_FIELD_PRIMARY_GID)
3785                         SET_UINT(msg, info21.primary_gid,      "primaryGroupID");
3786                 IFSET(SAMR_FIELD_ACCT_FLAGS)
3787                         SET_AFLAGS(msg, info21.acct_flags,     "userAccountControl");
3788                 IFSET(SAMR_FIELD_LOGON_HOURS)
3789                         SET_LHOURS(msg, info21.logon_hours,    "logonHours");
3790                 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3791                         SET_UINT  (msg, info21.bad_password_count, "badPwdCount");
3792                 IFSET(SAMR_FIELD_NUM_LOGONS)
3793                         SET_UINT  (msg, info21.logon_count,    "logonCount");
3794                 IFSET(SAMR_FIELD_COUNTRY_CODE)
3795                         SET_UINT  (msg, info21.country_code,   "countryCode");
3796                 IFSET(SAMR_FIELD_CODE_PAGE)
3797                         SET_UINT  (msg, info21.code_page,      "codePage");
3798
3799                 /* password change fields */
3800                 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
3801                         status = NT_STATUS_ACCESS_DENIED;
3802                         goto done;
3803                 }
3804
3805                 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3806                                         | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3807                         uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3808
3809                         if (r->in.info->info21.lm_password_set) {
3810                                 if ((r->in.info->info21.lm_owf_password.length != 16)
3811                                  || (r->in.info->info21.lm_owf_password.size != 16)) {
3812                                         status = NT_STATUS_INVALID_PARAMETER;
3813                                         goto done;
3814                                 }
3815
3816                                 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3817                         }
3818                         if (r->in.info->info21.nt_password_set) {
3819                                 if ((r->in.info->info21.nt_owf_password.length != 16)
3820                                  || (r->in.info->info21.nt_owf_password.size != 16)) {
3821                                         status = NT_STATUS_INVALID_PARAMETER;
3822                                         goto done;
3823                                 }
3824
3825                                 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3826                         }
3827                         status = samr_set_password_buffers(dce_call,
3828                                                            sam_ctx,
3829                                                            a_state->account_dn,
3830                                                            mem_ctx,
3831                                                            lm_pwd_hash,
3832                                                            nt_pwd_hash);
3833                         if (!NT_STATUS_IS_OK(status)) {
3834                                 goto done;
3835                         }
3836                 }
3837
3838
3839                 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3840                         const char *t = "0";
3841                         struct ldb_message_element *set_el;
3842                         if (r->in.info->info21.password_expired
3843                                         == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3844                                 t = "-1";
3845                         }
3846                         if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3847                                 status = NT_STATUS_NO_MEMORY;
3848                                 goto done;
3849                         }
3850                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
3851                         set_el->flags = LDB_FLAG_MOD_REPLACE;
3852                 }
3853 #undef IFSET
3854                 break;
3855
3856         case 23:
3857                 if (r->in.info->info23.info.fields_present == 0) {
3858                         status = NT_STATUS_INVALID_PARAMETER;
3859                         goto done;
3860                 }
3861
3862 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3863                 IFSET(SAMR_FIELD_LAST_LOGON)
3864                         SET_UINT64(msg, info23.info.last_logon,     "lastLogon");
3865                 IFSET(SAMR_FIELD_LAST_LOGOFF)
3866                         SET_UINT64(msg, info23.info.last_logoff,    "lastLogoff");
3867                 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3868                         SET_UINT64(msg, info23.info.acct_expiry,    "accountExpires");
3869                 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3870                         SET_STRING(msg, info23.info.account_name,   "samAccountName");
3871                 IFSET(SAMR_FIELD_FULL_NAME)
3872                         SET_STRING(msg, info23.info.full_name,      "displayName");
3873                 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3874                         SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3875                 IFSET(SAMR_FIELD_HOME_DRIVE)
3876                         SET_STRING(msg, info23.info.home_drive,     "homeDrive");
3877                 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3878                         SET_STRING(msg, info23.info.logon_script,   "scriptPath");
3879                 IFSET(SAMR_FIELD_PROFILE_PATH)
3880                         SET_STRING(msg, info23.info.profile_path,   "profilePath");
3881                 IFSET(SAMR_FIELD_DESCRIPTION)
3882                         SET_STRING(msg, info23.info.description,    "description");
3883                 IFSET(SAMR_FIELD_WORKSTATIONS)
3884                         SET_STRING(msg, info23.info.workstations,   "userWorkstations");
3885                 IFSET(SAMR_FIELD_COMMENT)
3886                         SET_STRING(msg, info23.info.comment,        "comment");
3887                 IFSET(SAMR_FIELD_PARAMETERS)
3888                         SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3889                 IFSET(SAMR_FIELD_PRIMARY_GID)
3890                         SET_UINT(msg, info23.info.primary_gid,      "primaryGroupID");
3891                 IFSET(SAMR_FIELD_ACCT_FLAGS)
3892                         SET_AFLAGS(msg, info23.info.acct_flags,     "userAccountControl");
3893                 IFSET(SAMR_FIELD_LOGON_HOURS)
3894                         SET_LHOURS(msg, info23.info.logon_hours,    "logonHours");
3895                 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3896                         SET_UINT  (msg, info23.info.bad_password_count, "badPwdCount");
3897                 IFSET(SAMR_FIELD_NUM_LOGONS)
3898                         SET_UINT  (msg, info23.info.logon_count,    "logonCount");
3899
3900                 IFSET(SAMR_FIELD_COUNTRY_CODE)
3901                         SET_UINT  (msg, info23.info.country_code,   "countryCode");
3902                 IFSET(SAMR_FIELD_CODE_PAGE)
3903                         SET_UINT  (msg, info23.info.code_page,      "codePage");
3904
3905                 /* password change fields */
3906                 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
3907                         status = NT_STATUS_ACCESS_DENIED;
3908                         goto done;
3909                 }
3910
3911                 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3912                         status = samr_set_password(dce_call,
3913                                                    sam_ctx,
3914                                                    a_state->account_dn,
3915                                                    mem_ctx,
3916                                                    &r->in.info->info23.password);
3917                 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3918                         status = samr_set_password(dce_call,
3919                                                    sam_ctx,
3920                                                    a_state->account_dn,
3921                                                    mem_ctx,
3922                                                    &r->in.info->info23.password);
3923                 }
3924                 if (!NT_STATUS_IS_OK(status)) {
3925                         goto done;
3926                 }
3927
3928                 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3929                         const char *t = "0";
3930                         struct ldb_message_element *set_el;
3931                         if (r->in.info->info23.info.password_expired
3932                                         == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3933                                 t = "-1";
3934                         }
3935                         if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3936                                 status = NT_STATUS_NO_MEMORY;
3937                                 goto done;
3938                         }
3939                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
3940                         set_el->flags = LDB_FLAG_MOD_REPLACE;
3941                 }
3942 #undef IFSET
3943                 break;
3944
3945                 /* the set password levels are handled separately */
3946         case 24:
3947                 status = samr_set_password(dce_call,
3948                                            sam_ctx,
3949                                            a_state->account_dn,
3950                                            mem_ctx,
3951                                            &r->in.info->info24.password);
3952                 if (!NT_STATUS_IS_OK(status)) {
3953                         goto done;
3954                 }
3955
3956                 if (r->in.info->info24.password_expired > 0) {
3957                         struct ldb_message_element *set_el;
3958                         if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3959                                 status = NT_STATUS_NO_MEMORY;
3960                                 goto done;
3961                         }
3962                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
3963                         set_el->flags = LDB_FLAG_MOD_REPLACE;
3964                 }
3965                 break;
3966
3967         case 25:
3968                 if (r->in.info->info25.info.fields_present == 0) {
3969                         status = NT_STATUS_INVALID_PARAMETER;
3970                         goto done;
3971                 }
3972
3973 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3974                 IFSET(SAMR_FIELD_LAST_LOGON)
3975                         SET_UINT64(msg, info25.info.last_logon,     "lastLogon");
3976                 IFSET(SAMR_FIELD_LAST_LOGOFF)
3977                         SET_UINT64(msg, info25.info.last_logoff,    "lastLogoff");
3978                 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3979                         SET_UINT64(msg, info25.info.acct_expiry,    "accountExpires");
3980                 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3981                         SET_STRING(msg, info25.info.account_name,   "samAccountName");
3982                 IFSET(SAMR_FIELD_FULL_NAME)
3983                         SET_STRING(msg, info25.info.full_name,      "displayName");
3984                 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3985                         SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3986                 IFSET(SAMR_FIELD_HOME_DRIVE)
3987                         SET_STRING(msg, info25.info.home_drive,     "homeDrive");
3988                 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3989                         SET_STRING(msg, info25.info.logon_script,   "scriptPath");
3990                 IFSET(SAMR_FIELD_PROFILE_PATH)
3991                         SET_STRING(msg, info25.info.profile_path,   "profilePath");
3992                 IFSET(SAMR_FIELD_DESCRIPTION)
3993                         SET_STRING(msg, info25.info.description,    "description");
3994                 IFSET(SAMR_FIELD_WORKSTATIONS)
3995                         SET_STRING(msg, info25.info.workstations,   "userWorkstations");
3996                 IFSET(SAMR_FIELD_COMMENT)
3997                         SET_STRING(msg, info25.info.comment,        "comment");
3998                 IFSET(SAMR_FIELD_PARAMETERS)
3999                         SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
4000                 IFSET(SAMR_FIELD_PRIMARY_GID)
4001                         SET_UINT(msg, info25.info.primary_gid,      "primaryGroupID");
4002                 IFSET(SAMR_FIELD_ACCT_FLAGS)
4003                         SET_AFLAGS(msg, info25.info.acct_flags,     "userAccountControl");
4004                 IFSET(SAMR_FIELD_LOGON_HOURS)
4005                         SET_LHOURS(msg, info25.info.logon_hours,    "logonHours");
4006                 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
4007                         SET_UINT  (msg, info25.info.bad_password_count, "badPwdCount");
4008                 IFSET(SAMR_FIELD_NUM_LOGONS)
4009                         SET_UINT  (msg, info25.info.logon_count,    "logonCount");
4010                 IFSET(SAMR_FIELD_COUNTRY_CODE)
4011                         SET_UINT  (msg, info25.info.country_code,   "countryCode");
4012                 IFSET(SAMR_FIELD_CODE_PAGE)
4013                         SET_UINT  (msg, info25.info.code_page,      "codePage");
4014
4015                 /* password change fields */
4016                 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
4017                         status = NT_STATUS_ACCESS_DENIED;
4018                         goto done;
4019                 }
4020
4021                 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
4022                         status = samr_set_password_ex(dce_call,
4023                                                       sam_ctx,
4024                                                       a_state->account_dn,
4025                                                       mem_ctx,
4026                                                       &r->in.info->info25.password);
4027                 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
4028                         status = samr_set_password_ex(dce_call,
4029                                                       sam_ctx,
4030                                                       a_state->account_dn,
4031                                                       mem_ctx,
4032                                                       &r->in.info->info25.password);
4033                 }
4034                 if (!NT_STATUS_IS_OK(status)) {
4035                         goto done;
4036                 }
4037
4038                 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
4039                         const char *t = "0";
4040                         struct ldb_message_element *set_el;
4041                         if (r->in.info->info25.info.password_expired
4042                                         == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4043                                 t = "-1";
4044                         }
4045                         if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4046                                 status = NT_STATUS_NO_MEMORY;
4047                                 goto done;
4048                         }
4049                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
4050                         set_el->flags = LDB_FLAG_MOD_REPLACE;
4051                 }
4052 #undef IFSET
4053                 break;
4054
4055                 /* the set password levels are handled separately */
4056         case 26:
4057                 status = samr_set_password_ex(dce_call,
4058                                               sam_ctx,
4059                                               a_state->account_dn,
4060                                               mem_ctx,
4061                                               &r->in.info->info26.password);
4062                 if (!NT_STATUS_IS_OK(status)) {
4063                         goto done;
4064                 }
4065
4066                 if (r->in.info->info26.password_expired > 0) {
4067                         const char *t = "0";
4068                         struct ldb_message_element *set_el;
4069                         if (r->in.info->info26.password_expired
4070                                         == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4071                                 t = "-1";
4072                         }
4073                         if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4074                                 status = NT_STATUS_NO_MEMORY;
4075                                 goto done;
4076                         }
4077                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
4078                         set_el->flags = LDB_FLAG_MOD_REPLACE;
4079                 }
4080                 break;
4081
4082         case 31:
4083                 status = dcesrv_transport_session_key(dce_call, &session_key);
4084                 if (!NT_STATUS_IS_OK(status)) {
4085                         DBG_NOTICE("samr: failed to get session key: %s\n",
4086                                    nt_errstr(status));
4087                         goto done;
4088                 }
4089
4090                 status = samr_set_password_aes(dce_call,
4091                                                mem_ctx,
4092                                                &session_key,
4093                                                sam_ctx,
4094                                                a_state->account_dn,
4095                                                &r->in.info->info31.password,
4096                                                DSDB_PASSWORD_RESET);
4097                 if (!NT_STATUS_IS_OK(status)) {
4098                         goto done;
4099                 }
4100
4101                 if (r->in.info->info31.password_expired > 0) {
4102                         const char *t = "0";
4103                         struct ldb_message_element *set_el = NULL;
4104
4105                         if (r->in.info->info31.password_expired ==
4106                             PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4107                                 t = "-1";
4108                         }
4109
4110                         ret = ldb_msg_add_string(msg, "pwdLastSet", t);
4111                         if (ret != LDB_SUCCESS) {
4112                                 status = NT_STATUS_NO_MEMORY;
4113                                 goto done;
4114                         }
4115                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
4116                         set_el->flags = LDB_FLAG_MOD_REPLACE;
4117                 }
4118
4119                 break;
4120         case 32:
4121                 status = dcesrv_transport_session_key(dce_call, &session_key);
4122                 if (!NT_STATUS_IS_OK(status)) {
4123                         DBG_NOTICE("samr: failed to get session key: %s\n",
4124                                    nt_errstr(status));
4125                         goto done;
4126                 }
4127
4128                 if (r->in.info->info32.info.fields_present == 0) {
4129                         status = NT_STATUS_INVALID_PARAMETER;
4130                         goto done;
4131                 }
4132
4133 #define IFSET(bit) if (bit & r->in.info->info32.info.fields_present)
4134                 IFSET(SAMR_FIELD_LAST_LOGON)
4135                 {
4136                         SET_UINT64(msg, info32.info.last_logon, "lastLogon");
4137                 }
4138                 IFSET(SAMR_FIELD_LAST_LOGOFF)
4139                 {
4140                         SET_UINT64(msg, info32.info.last_logoff, "lastLogoff");
4141                 }
4142                 IFSET(SAMR_FIELD_ACCT_EXPIRY)
4143                 {
4144                         SET_UINT64(msg,
4145                                    info32.info.acct_expiry,
4146                                    "accountExpires");
4147                 }
4148                 IFSET(SAMR_FIELD_ACCOUNT_NAME)
4149                 {
4150                         SET_STRING(msg,
4151                                    info32.info.account_name,
4152                                    "samAccountName");
4153                 }
4154                 IFSET(SAMR_FIELD_FULL_NAME)
4155                 {
4156                         SET_STRING(msg, info32.info.full_name, "displayName");
4157                 }
4158                 IFSET(SAMR_FIELD_HOME_DIRECTORY)
4159                 {
4160                         SET_STRING(msg,
4161                                    info32.info.home_directory,
4162                                    "homeDirectory");
4163                 }
4164                 IFSET(SAMR_FIELD_HOME_DRIVE)
4165                 {
4166                         SET_STRING(msg, info32.info.home_drive, "homeDrive");
4167                 }
4168                 IFSET(SAMR_FIELD_LOGON_SCRIPT)
4169                 {
4170                         SET_STRING(msg, info32.info.logon_script, "scriptPath");
4171                 }
4172                 IFSET(SAMR_FIELD_PROFILE_PATH)
4173                 {
4174                         SET_STRING(msg,
4175                                    info32.info.profile_path,
4176                                    "profilePath");
4177                 }
4178                 IFSET(SAMR_FIELD_DESCRIPTION)
4179                 {
4180                         SET_STRING(msg, info32.info.description, "description");
4181                 }
4182                 IFSET(SAMR_FIELD_WORKSTATIONS)
4183                 {
4184                         SET_STRING(msg,
4185                                    info32.info.workstations,
4186                                    "userWorkstations");
4187                 }
4188                 IFSET(SAMR_FIELD_COMMENT)
4189                 {
4190                         SET_STRING(msg, info32.info.comment, "comment");
4191                 }
4192                 IFSET(SAMR_FIELD_PARAMETERS)
4193                 {
4194                         SET_PARAMETERS(msg,
4195                                        info32.info.parameters,
4196                                        "userParameters");
4197                 }
4198                 IFSET(SAMR_FIELD_PRIMARY_GID)
4199                 {
4200                         SET_UINT(msg,
4201                                  info32.info.primary_gid,
4202                                  "primaryGroupID");
4203                 }
4204                 IFSET(SAMR_FIELD_ACCT_FLAGS)
4205                 {
4206                         SET_AFLAGS(msg,
4207                                    info32.info.acct_flags,
4208                                    "userAccountControl");
4209                 }
4210                 IFSET(SAMR_FIELD_LOGON_HOURS)
4211                 {
4212                         SET_LHOURS(msg, info32.info.logon_hours, "logonHours");
4213                 }
4214                 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
4215                 {
4216                         SET_UINT(msg,
4217                                  info32.info.bad_password_count,
4218                                  "badPwdCount");
4219                 }
4220                 IFSET(SAMR_FIELD_NUM_LOGONS)
4221                 {
4222                         SET_UINT(msg, info32.info.logon_count, "logonCount");
4223                 }
4224                 IFSET(SAMR_FIELD_COUNTRY_CODE)
4225                 {
4226                         SET_UINT(msg, info32.info.country_code, "countryCode");
4227                 }
4228                 IFSET(SAMR_FIELD_CODE_PAGE)
4229                 {
4230                         SET_UINT(msg, info32.info.code_page, "codePage");
4231                 }
4232
4233                 /* password change fields */
4234                 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
4235                 {
4236                         status = NT_STATUS_ACCESS_DENIED;
4237                         goto done;
4238                 }
4239
4240                 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT)
4241                 {
4242                         status = samr_set_password_aes(
4243                                 dce_call,
4244                                 mem_ctx,
4245                                 &session_key,
4246                                 a_state->sam_ctx,
4247                                 a_state->account_dn,
4248                                 &r->in.info->info32.password,
4249                                 DSDB_PASSWORD_RESET);
4250                 }
4251                 else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT)
4252                 {
4253                         status = samr_set_password_aes(
4254                                 dce_call,
4255                                 mem_ctx,
4256                                 &session_key,
4257                                 a_state->sam_ctx,
4258                                 a_state->account_dn,
4259                                 &r->in.info->info32.password,
4260                                 DSDB_PASSWORD_RESET);
4261                 }
4262                 if (!NT_STATUS_IS_OK(status)) {
4263                         goto done;
4264                 }
4265
4266                 IFSET(SAMR_FIELD_EXPIRED_FLAG)
4267                 {
4268                         const char *t = "0";
4269                         struct ldb_message_element *set_el;
4270                         if (r->in.info->info32.info.password_expired ==
4271                             PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4272                                 t = "-1";
4273                         }
4274                         if (ldb_msg_add_string(msg, "pwdLastSet", t) !=
4275                             LDB_SUCCESS) {
4276                                 status = NT_STATUS_NO_MEMORY;
4277                                 goto done;
4278                         }
4279                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
4280                         set_el->flags = LDB_FLAG_MOD_REPLACE;
4281                 }
4282 #undef IFSET
4283
4284                 break;
4285         default:
4286                 /* many info classes are not valid for SetUserInfo */
4287                 status = NT_STATUS_INVALID_INFO_CLASS;
4288                 goto done;
4289         }
4290
4291         if (!NT_STATUS_IS_OK(status)) {
4292                 goto done;
4293         }
4294
4295         /* modify the samdb record */
4296         if (msg->num_elements > 0) {
4297                 ret = ldb_modify(sam_ctx, msg);
4298                 if (ret != LDB_SUCCESS) {
4299                         DEBUG(1,("Failed to modify record %s: %s\n",
4300                                  ldb_dn_get_linearized(a_state->account_dn),
4301                                  ldb_errstring(sam_ctx)));
4302
4303                         status = dsdb_ldb_err_to_ntstatus(ret);
4304                         goto done;
4305                 }
4306         }
4307
4308         ret = ldb_transaction_commit(sam_ctx);
4309         if (ret != LDB_SUCCESS) {
4310                 DBG_ERR("Failed to commit transaction modifying account record "
4311                         "%s: %s\n",
4312                         ldb_dn_get_linearized(msg->dn),
4313                         ldb_errstring(sam_ctx));
4314                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4315         }
4316
4317         status = NT_STATUS_OK;
4318 done:
4319         if (!NT_STATUS_IS_OK(status)) {
4320                 ldb_transaction_cancel(sam_ctx);
4321         }
4322
4323         return status;
4324 }
4325
4326
4327 /*
4328   samr_GetGroupsForUser
4329 */
4330 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4331                        struct samr_GetGroupsForUser *r)
4332 {
4333         struct dcesrv_handle *h;
4334         struct samr_account_state *a_state;
4335         struct samr_domain_state *d_state;
4336         struct ldb_result *res, *res_memberof;
4337         const char * const attrs[] = { "primaryGroupID",
4338                                        "memberOf",
4339                                        NULL };
4340         const char * const group_attrs[] = { "objectSid",
4341                                              NULL };
4342
4343         struct samr_RidWithAttributeArray *array;
4344         struct ldb_message_element *memberof_el;
4345         int i, ret, count = 0;
4346         uint32_t primary_group_id;
4347         char *filter;
4348
4349         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4350
4351         a_state = h->data;
4352         d_state = a_state->domain_state;
4353
4354         ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
4355                              &res,
4356                              a_state->account_dn,
4357                              attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
4358
4359         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4360                 return NT_STATUS_NO_SUCH_USER;
4361         } else if (ret != LDB_SUCCESS) {
4362                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4363         } else if (res->count != 1) {
4364                 return NT_STATUS_NO_SUCH_USER;
4365         }
4366
4367         primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
4368                                                      0);
4369
4370         filter = talloc_asprintf(mem_ctx,
4371                                  "(&(|(grouptype=%d)(grouptype=%d))"
4372                                  "(objectclass=group)(|",
4373                                  GTYPE_SECURITY_UNIVERSAL_GROUP,
4374                                  GTYPE_SECURITY_GLOBAL_GROUP);
4375         if (filter == NULL) {
4376                 return NT_STATUS_NO_MEMORY;
4377         }
4378
4379         memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
4380         if (memberof_el != NULL) {
4381                 for (i = 0; i < memberof_el->num_values; i++) {
4382                         const struct ldb_val *memberof_sid_binary;
4383                         char *memberof_sid_escaped;
4384                         struct ldb_dn *memberof_dn
4385                                 = ldb_dn_from_ldb_val(mem_ctx,
4386                                                       a_state->sam_ctx,
4387                                                       &memberof_el->values[i]);
4388                         if (memberof_dn == NULL) {
4389                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4390                         }
4391
4392                         memberof_sid_binary
4393                                 = ldb_dn_get_extended_component(memberof_dn,
4394                                                                 "SID");
4395                         if (memberof_sid_binary == NULL) {
4396                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4397                         }
4398
4399                         memberof_sid_escaped = ldb_binary_encode(mem_ctx,
4400                                                                  *memberof_sid_binary);
4401                         if (memberof_sid_escaped == NULL) {
4402                                 return NT_STATUS_NO_MEMORY;
4403                         }
4404                         filter = talloc_asprintf_append(filter, "(objectSID=%s)",
4405                                                         memberof_sid_escaped);
4406                         if (filter == NULL) {
4407                                 return NT_STATUS_NO_MEMORY;
4408                         }
4409                 }
4410
4411                 ret = dsdb_search(a_state->sam_ctx, mem_ctx,
4412                                   &res_memberof,
4413                                   d_state->domain_dn,
4414                                   LDB_SCOPE_SUBTREE,
4415                                   group_attrs, 0,
4416                                   "%s))", filter);
4417
4418                 if (ret != LDB_SUCCESS) {
4419                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
4420                 }
4421                 count = res_memberof->count;
4422         }
4423
4424         array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
4425         if (array == NULL)
4426                 return NT_STATUS_NO_MEMORY;
4427
4428         array->count = 0;
4429         array->rids = NULL;
4430
4431         array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
4432                                    count + 1);
4433         if (array->rids == NULL)
4434                 return NT_STATUS_NO_MEMORY;
4435
4436         /* Adds the primary group */
4437
4438         array->rids[0].rid = primary_group_id;
4439         array->rids[0].attributes = SE_GROUP_DEFAULT_FLAGS;
4440         array->count += 1;
4441
4442         /* Adds the additional groups */
4443         for (i = 0; i < count; i++) {
4444                 struct dom_sid *group_sid;
4445
4446                 group_sid = samdb_result_dom_sid(mem_ctx,
4447                                                  res_memberof->msgs[i],
4448                                                  "objectSid");
4449                 if (group_sid == NULL) {
4450                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
4451                 }
4452
4453                 array->rids[i + 1].rid =
4454                         group_sid->sub_auths[group_sid->num_auths-1];
4455                 array->rids[i + 1].attributes = SE_GROUP_DEFAULT_FLAGS;
4456                 array->count += 1;
4457         }
4458
4459         *r->out.rids = array;
4460
4461         return NT_STATUS_OK;
4462 }
4463
4464 /*
4465  * samr_QueryDisplayInfo
4466  *
4467  * A cache of the GUID's matching the last query is maintained
4468  * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
4469  * n the dcesrv_handle.
4470  */
4471 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4472                        struct samr_QueryDisplayInfo *r)
4473 {
4474         struct dcesrv_handle *h;
4475         struct samr_domain_state *d_state;
4476         struct ldb_result *res;
4477         uint32_t i;
4478         uint32_t results = 0;
4479         uint32_t count = 0;
4480         const char *const cache_attrs[] = {"objectGUID", NULL};
4481         const char *const attrs[] = {
4482             "objectSID", "sAMAccountName", "displayName", "description", NULL};
4483         struct samr_DispEntryFull *entriesFull = NULL;
4484         struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
4485         struct samr_DispEntryAscii *entriesAscii = NULL;
4486         struct samr_DispEntryGeneral *entriesGeneral = NULL;
4487         const char *filter;
4488         int ret;
4489         NTSTATUS status;
4490         struct samr_guid_cache *cache = NULL;
4491
4492         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4493
4494         d_state = h->data;
4495
4496         cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
4497         /*
4498          * Can the cached results be used?
4499          * The cache is discarded if the start index is zero, or the requested
4500          * level is different from that in the cache.
4501          */
4502         if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
4503                 /*
4504                  * The cached results can not be used, so will need to query
4505                  * the database.
4506                  */
4507
4508                 /*
4509                  * Get the search filter for the current level
4510                  */
4511                 switch (r->in.level) {
4512                 case 1:
4513                 case 4:
4514                         filter = talloc_asprintf(mem_ctx,
4515                                                  "(&(objectclass=user)"
4516                                                  "(sAMAccountType=%d))",
4517                                                  ATYPE_NORMAL_ACCOUNT);
4518                         break;
4519                 case 2:
4520                         filter = talloc_asprintf(mem_ctx,
4521                                                  "(&(objectclass=user)"
4522                                                  "(sAMAccountType=%d))",
4523                                                  ATYPE_WORKSTATION_TRUST);
4524                         break;
4525                 case 3:
4526                 case 5:
4527                         filter =
4528                             talloc_asprintf(mem_ctx,
4529                                             "(&(|(groupType=%d)(groupType=%d))"
4530                                             "(objectClass=group))",
4531                                             GTYPE_SECURITY_UNIVERSAL_GROUP,
4532                                             GTYPE_SECURITY_GLOBAL_GROUP);
4533                         break;
4534                 default:
4535                         return NT_STATUS_INVALID_INFO_CLASS;
4536                 }
4537                 clear_guid_cache(cache);
4538
4539                 /*
4540                  * search for all requested objects in all domains.
4541                  */
4542                 ret = dsdb_search(d_state->sam_ctx,
4543                                   mem_ctx,
4544                                   &res,
4545                                   ldb_get_default_basedn(d_state->sam_ctx),
4546                                   LDB_SCOPE_SUBTREE,
4547                                   cache_attrs,
4548                                   0,
4549                                   "%s",
4550                                   filter);
4551                 if (ret != LDB_SUCCESS) {
4552                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
4553                 }
4554                 if ((res->count == 0) || (r->in.max_entries == 0)) {
4555                         return NT_STATUS_OK;
4556                 }
4557
4558                 status = load_guid_cache(cache, d_state, res->count, res->msgs);
4559                 TALLOC_FREE(res);
4560                 if (!NT_STATUS_IS_OK(status)) {
4561                         return status;
4562                 }
4563                 cache->handle = r->in.level;
4564         }
4565         *r->out.total_size = cache->size;
4566
4567         /*
4568          * if there are no entries or the requested start index is greater
4569          * than the number of entries, we return an empty response.
4570          */
4571         if (r->in.start_idx >= cache->size) {
4572                 *r->out.returned_size = 0;
4573                 switch(r->in.level) {
4574                 case 1:
4575                         r->out.info->info1.count = *r->out.returned_size;
4576                         r->out.info->info1.entries = NULL;
4577                         break;
4578                 case 2:
4579                         r->out.info->info2.count = *r->out.returned_size;
4580                         r->out.info->info2.entries = NULL;
4581                         break;
4582                 case 3:
4583                         r->out.info->info3.count = *r->out.returned_size;
4584                         r->out.info->info3.entries = NULL;
4585                         break;
4586                 case 4:
4587                         r->out.info->info4.count = *r->out.returned_size;
4588                         r->out.info->info4.entries = NULL;
4589                         break;
4590                 case 5:
4591                         r->out.info->info5.count = *r->out.returned_size;
4592                         r->out.info->info5.entries = NULL;
4593                         break;
4594                 }
4595                 return NT_STATUS_OK;
4596         }
4597
4598         /*
4599          * Allocate an array of the appropriate result structures for the
4600          * current query level.
4601          *
4602          * r->in.start_idx is always < cache->size due to the check above
4603          */
4604         results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
4605         switch (r->in.level) {
4606         case 1:
4607                 entriesGeneral = talloc_array(
4608                     mem_ctx, struct samr_DispEntryGeneral, results);
4609                 break;
4610         case 2:
4611                 entriesFull =
4612                     talloc_array(mem_ctx, struct samr_DispEntryFull, results);
4613                 break;
4614         case 3:
4615                 entriesFullGroup = talloc_array(
4616                     mem_ctx, struct samr_DispEntryFullGroup, results);
4617                 break;
4618         case 4:
4619         case 5:
4620                 entriesAscii =
4621                     talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
4622                 break;
4623         }
4624
4625         if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
4626             (entriesAscii == NULL) && (entriesFullGroup == NULL))
4627                 return NT_STATUS_NO_MEMORY;
4628
4629         /*
4630          * Process the list of result GUID's.
4631          * Read the details of each object and populate the result structure
4632          * for the current level.
4633          */
4634         count = 0;
4635         for (i = 0; i < results; i++) {
4636                 struct dom_sid *objectsid;
4637                 struct ldb_result *rec;
4638                 const uint32_t idx = r->in.start_idx + i;
4639                 uint32_t rid;
4640
4641                 /*
4642                  * Read an object from disk using the GUID as the key
4643                  *
4644                  * If the object can not be read, or it does not have a SID
4645                  * it is ignored.  In this case the number of entries returned
4646                  * will be less than the requested size, there will also be
4647                  * a gap in the idx numbers in the returned elements e.g. if
4648                  * there are 3 GUIDs a, b, c in the cache and b is deleted from
4649                  * disk then details for a, and c will be returned with
4650                  * idx values of 1 and 3 respectively.
4651                  *
4652                  */
4653                 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
4654                                              mem_ctx,
4655                                              &rec,
4656                                              &cache->entries[idx],
4657                                              attrs,
4658                                              0);
4659                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4660                         struct GUID_txt_buf guid_buf;
4661                         char *guid_str =
4662                                 GUID_buf_string(&cache->entries[idx],
4663                                                 &guid_buf);
4664                         DBG_WARNING("GUID [%s] not found\n", guid_str);
4665                         continue;
4666                 } else if (ret != LDB_SUCCESS) {
4667                         clear_guid_cache(cache);
4668                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
4669                 }
4670                 objectsid = samdb_result_dom_sid(mem_ctx,
4671                                                  rec->msgs[0],
4672                                                  "objectSID");
4673                 if (objectsid == NULL) {
4674                         struct GUID_txt_buf guid_buf;
4675                         DBG_WARNING(
4676                             "objectSID for GUID [%s] not found\n",
4677                             GUID_buf_string(&cache->entries[idx], &guid_buf));
4678                         continue;
4679                 }
4680                 status = dom_sid_split_rid(NULL,
4681                                            objectsid,
4682                                            NULL,
4683                                            &rid);
4684                 if (!NT_STATUS_IS_OK(status)) {
4685                         struct dom_sid_buf sid_buf;
4686                         struct GUID_txt_buf guid_buf;
4687                         DBG_WARNING(
4688                             "objectSID [%s] for GUID [%s] invalid\n",
4689                             dom_sid_str_buf(objectsid, &sid_buf),
4690                             GUID_buf_string(&cache->entries[idx], &guid_buf));
4691                         continue;
4692                 }
4693
4694                 /*
4695                  * Populate the result structure for the current object
4696                  */
4697                 switch(r->in.level) {
4698                 case 1:
4699
4700                         entriesGeneral[count].idx = idx + 1;
4701                         entriesGeneral[count].rid = rid;
4702
4703                         entriesGeneral[count].acct_flags =
4704                             samdb_result_acct_flags(rec->msgs[0], NULL);
4705                         entriesGeneral[count].account_name.string =
4706                             ldb_msg_find_attr_as_string(
4707                                 rec->msgs[0], "sAMAccountName", "");
4708                         entriesGeneral[count].full_name.string =
4709                             ldb_msg_find_attr_as_string(
4710                                 rec->msgs[0], "displayName", "");
4711                         entriesGeneral[count].description.string =
4712                             ldb_msg_find_attr_as_string(
4713                                 rec->msgs[0], "description", "");
4714                         break;
4715                 case 2:
4716                         entriesFull[count].idx = idx + 1;
4717                         entriesFull[count].rid = rid;
4718
4719                         /*
4720                          * No idea why we need to or in ACB_NORMAL here,
4721                          * but this is what Win2k3 seems to do...
4722                          */
4723                         entriesFull[count].acct_flags =
4724                             samdb_result_acct_flags(rec->msgs[0], NULL) |
4725                             ACB_NORMAL;
4726                         entriesFull[count].account_name.string =
4727                             ldb_msg_find_attr_as_string(
4728                                 rec->msgs[0], "sAMAccountName", "");
4729                         entriesFull[count].description.string =
4730                             ldb_msg_find_attr_as_string(
4731                                 rec->msgs[0], "description", "");
4732                         break;
4733                 case 3:
4734                         entriesFullGroup[count].idx = idx + 1;
4735                         entriesFullGroup[count].rid = rid;
4736
4737                         /*
4738                          * We get a "7" here for groups
4739                          */
4740                         entriesFullGroup[count].acct_flags = SE_GROUP_DEFAULT_FLAGS;
4741                         entriesFullGroup[count].account_name.string =
4742                             ldb_msg_find_attr_as_string(
4743                                 rec->msgs[0], "sAMAccountName", "");
4744                         entriesFullGroup[count].description.string =
4745                             ldb_msg_find_attr_as_string(
4746                                 rec->msgs[0], "description", "");
4747                         break;
4748                 case 4:
4749                 case 5:
4750                         entriesAscii[count].idx = idx + 1;
4751                         entriesAscii[count].account_name.string =
4752                             ldb_msg_find_attr_as_string(
4753                                 rec->msgs[0], "sAMAccountName", "");
4754                         break;
4755                 }
4756                 count++;
4757         }
4758
4759         /*
4760          * Build the response based on the request level.
4761          */
4762         *r->out.returned_size = count;
4763         switch(r->in.level) {
4764         case 1:
4765                 r->out.info->info1.count = count;
4766                 r->out.info->info1.entries = entriesGeneral;
4767                 break;
4768         case 2:
4769                 r->out.info->info2.count = count;
4770                 r->out.info->info2.entries = entriesFull;
4771                 break;
4772         case 3:
4773                 r->out.info->info3.count = count;
4774                 r->out.info->info3.entries = entriesFullGroup;
4775                 break;
4776         case 4:
4777                 r->out.info->info4.count = count;
4778                 r->out.info->info4.entries = entriesAscii;
4779                 break;
4780         case 5:
4781                 r->out.info->info5.count = count;
4782                 r->out.info->info5.entries = entriesAscii;
4783                 break;
4784         }
4785
4786         return ((r->in.start_idx + results) < cache->size)
4787                    ? STATUS_MORE_ENTRIES
4788                    : NT_STATUS_OK;
4789 }
4790
4791
4792 /*
4793   samr_GetDisplayEnumerationIndex
4794 */
4795 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4796                        struct samr_GetDisplayEnumerationIndex *r)
4797 {
4798         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4799 }
4800
4801
4802 /*
4803   samr_TestPrivateFunctionsDomain
4804 */
4805 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4806                        struct samr_TestPrivateFunctionsDomain *r)
4807 {
4808         return NT_STATUS_NOT_IMPLEMENTED;
4809 }
4810
4811
4812 /*
4813   samr_TestPrivateFunctionsUser
4814 */
4815 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4816                        struct samr_TestPrivateFunctionsUser *r)
4817 {
4818         return NT_STATUS_NOT_IMPLEMENTED;
4819 }
4820
4821
4822 /*
4823   samr_GetUserPwInfo
4824 */
4825 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4826                                    struct samr_GetUserPwInfo *r)
4827 {
4828         struct dcesrv_handle *h;
4829         struct samr_account_state *a_state;
4830
4831         ZERO_STRUCTP(r->out.info);
4832
4833         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4834
4835         a_state = h->data;
4836
4837         r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
4838                 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
4839                 NULL);
4840         r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
4841                 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
4842
4843         return NT_STATUS_OK;
4844 }
4845
4846
4847 /*
4848   samr_RemoveMemberFromForeignDomain
4849 */
4850 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
4851                                                           TALLOC_CTX *mem_ctx,
4852                                                           struct samr_RemoveMemberFromForeignDomain *r)
4853 {
4854         struct dcesrv_handle *h;
4855         struct samr_domain_state *d_state;
4856         const char *memberdn;
4857         struct ldb_message **res;
4858         const char *no_attrs[] = { NULL };
4859         int i, count;
4860
4861         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4862
4863         d_state = h->data;
4864
4865         memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
4866                                        "distinguishedName", "(objectSid=%s)",
4867                                        ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
4868         /* Nothing to do */
4869         if (memberdn == NULL) {
4870                 return NT_STATUS_OK;
4871         }
4872
4873         count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4874                                     d_state->domain_dn, &res, no_attrs,
4875                                     d_state->domain_sid,
4876                                     "(&(member=%s)(objectClass=group)"
4877                                     "(|(groupType=%d)(groupType=%d)))",
4878                                     memberdn,
4879                                     GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4880                                     GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4881
4882         if (count < 0)
4883                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4884
4885         for (i=0; i<count; i++) {
4886                 struct ldb_message *mod;
4887                 int ret;
4888
4889                 mod = ldb_msg_new(mem_ctx);
4890                 if (mod == NULL) {
4891                         return NT_STATUS_NO_MEMORY;
4892                 }
4893
4894                 mod->dn = res[i]->dn;
4895
4896                 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4897                                          "member", memberdn) != LDB_SUCCESS)
4898                         return NT_STATUS_NO_MEMORY;
4899
4900                 ret = ldb_modify(d_state->sam_ctx, mod);
4901                 talloc_free(mod);
4902                 if (ret != LDB_SUCCESS) {
4903                         return dsdb_ldb_err_to_ntstatus(ret);
4904                 }
4905         }
4906
4907         return NT_STATUS_OK;
4908 }
4909
4910
4911 /*
4912   samr_QueryDomainInfo2
4913
4914   just an alias for samr_QueryDomainInfo
4915 */
4916 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4917                        struct samr_QueryDomainInfo2 *r)
4918 {
4919         struct samr_QueryDomainInfo r1;
4920         NTSTATUS status;
4921
4922         r1 = (struct samr_QueryDomainInfo) {
4923                 .in.domain_handle = r->in.domain_handle,
4924                 .in.level  = r->in.level,
4925                 .out.info  = r->out.info,
4926         };
4927
4928         status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4929
4930         return status;
4931 }
4932
4933
4934 /*
4935   samr_QueryUserInfo2
4936
4937   just an alias for samr_QueryUserInfo
4938 */
4939 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4940                                     struct samr_QueryUserInfo2 *r)
4941 {
4942         struct samr_QueryUserInfo r1;
4943         NTSTATUS status;
4944
4945         r1 = (struct samr_QueryUserInfo) {
4946                 .in.user_handle = r->in.user_handle,
4947                 .in.level  = r->in.level,
4948                 .out.info  = r->out.info
4949         };
4950
4951         status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4952
4953         return status;
4954 }
4955
4956
4957 /*
4958   samr_QueryDisplayInfo2
4959 */
4960 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4961                                        struct samr_QueryDisplayInfo2 *r)
4962 {
4963         struct samr_QueryDisplayInfo q;
4964         NTSTATUS result;
4965
4966         q = (struct samr_QueryDisplayInfo) {
4967                 .in.domain_handle = r->in.domain_handle,
4968                 .in.level = r->in.level,
4969                 .in.start_idx = r->in.start_idx,
4970                 .in.max_entries = r->in.max_entries,
4971                 .in.buf_size = r->in.buf_size,
4972                 .out.total_size = r->out.total_size,
4973                 .out.returned_size = r->out.returned_size,
4974                 .out.info = r->out.info,
4975         };
4976
4977         result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4978
4979         return result;
4980 }
4981
4982
4983 /*
4984   samr_GetDisplayEnumerationIndex2
4985 */
4986 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4987                        struct samr_GetDisplayEnumerationIndex2 *r)
4988 {
4989         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4990 }
4991
4992
4993 /*
4994   samr_QueryDisplayInfo3
4995 */
4996 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4997                        struct samr_QueryDisplayInfo3 *r)
4998 {
4999         struct samr_QueryDisplayInfo q;
5000         NTSTATUS result;
5001
5002         q = (struct samr_QueryDisplayInfo) {
5003                 .in.domain_handle = r->in.domain_handle,
5004                 .in.level = r->in.level,
5005                 .in.start_idx = r->in.start_idx,
5006                 .in.max_entries = r->in.max_entries,
5007                 .in.buf_size = r->in.buf_size,
5008                 .out.total_size = r->out.total_size,
5009                 .out.returned_size = r->out.returned_size,
5010                 .out.info = r->out.info,
5011         };
5012
5013         result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
5014
5015         return result;
5016 }
5017
5018
5019 /*
5020   samr_AddMultipleMembersToAlias
5021 */
5022 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5023                        struct samr_AddMultipleMembersToAlias *r)
5024 {
5025         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5026 }
5027
5028
5029 /*
5030   samr_RemoveMultipleMembersFromAlias
5031 */
5032 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5033                        struct samr_RemoveMultipleMembersFromAlias *r)
5034 {
5035         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5036 }
5037
5038
5039 /*
5040   samr_GetDomPwInfo
5041
5042   this fetches the default password properties for a domain
5043
5044   note that w2k3 completely ignores the domain name in this call, and
5045   always returns the information for the servers primary domain
5046 */
5047 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5048                                   struct samr_GetDomPwInfo *r)
5049 {
5050         struct ldb_message **msgs;
5051         int ret;
5052         const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
5053         struct ldb_context *sam_ctx;
5054
5055         ZERO_STRUCTP(r->out.info);
5056
5057         sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
5058         if (sam_ctx == NULL) {
5059                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
5060         }
5061
5062         /* The domain name in this call is ignored */
5063         ret = gendb_search_dn(sam_ctx,
5064                            mem_ctx, NULL, &msgs, attrs);
5065         if (ret <= 0) {
5066                 talloc_free(sam_ctx);
5067
5068                 return NT_STATUS_NO_SUCH_DOMAIN;
5069         }
5070         if (ret > 1) {
5071                 talloc_free(msgs);
5072                 talloc_free(sam_ctx);
5073
5074                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
5075         }
5076
5077         r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
5078                 "minPwdLength", 0);
5079         r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
5080                 "pwdProperties", 1);
5081
5082         talloc_free(msgs);
5083         talloc_unlink(mem_ctx, sam_ctx);
5084
5085         return NT_STATUS_OK;
5086 }
5087
5088
5089 /*
5090   samr_Connect2
5091 */
5092 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5093                               struct samr_Connect2 *r)
5094 {
5095         struct samr_Connect c;
5096
5097         c = (struct samr_Connect) {
5098                 .in.system_name = NULL,
5099                 .in.access_mask = r->in.access_mask,
5100                 .out.connect_handle = r->out.connect_handle,
5101         };
5102
5103         return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5104 }
5105
5106
5107 /*
5108   samr_SetUserInfo2
5109
5110   just an alias for samr_SetUserInfo
5111 */
5112 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5113                                   struct samr_SetUserInfo2 *r)
5114 {
5115         struct samr_SetUserInfo r2;
5116
5117         r2 = (struct samr_SetUserInfo) {
5118                 .in.user_handle = r->in.user_handle,
5119                 .in.level = r->in.level,
5120                 .in.info = r->in.info,
5121         };
5122
5123         return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
5124 }
5125
5126
5127 /*
5128   samr_SetBootKeyInformation
5129 */
5130 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5131                        struct samr_SetBootKeyInformation *r)
5132 {
5133         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5134 }
5135
5136
5137 /*
5138   samr_GetBootKeyInformation
5139 */
5140 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5141                        struct samr_GetBootKeyInformation *r)
5142 {
5143         /* Windows Server 2008 returns this */
5144         return NT_STATUS_NOT_SUPPORTED;
5145 }
5146
5147
5148 /*
5149   samr_Connect3
5150 */
5151 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5152                        struct samr_Connect3 *r)
5153 {
5154         struct samr_Connect c;
5155
5156         c = (struct samr_Connect) {
5157                 .in.system_name = NULL,
5158                 .in.access_mask = r->in.access_mask,
5159                 .out.connect_handle = r->out.connect_handle,
5160         };
5161
5162         return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5163 }
5164
5165
5166 /*
5167   samr_Connect4
5168 */
5169 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5170                        struct samr_Connect4 *r)
5171 {
5172         struct samr_Connect c;
5173
5174         c = (struct samr_Connect) {
5175                 .in.system_name = NULL,
5176                 .in.access_mask = r->in.access_mask,
5177                 .out.connect_handle = r->out.connect_handle,
5178         };
5179
5180         return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5181 }
5182
5183
5184 /*
5185   samr_Connect5
5186 */
5187 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5188                               struct samr_Connect5 *r)
5189 {
5190         struct samr_Connect c;
5191         NTSTATUS status;
5192
5193         c = (struct samr_Connect) {
5194                 .in.system_name = NULL,
5195                 .in.access_mask = r->in.access_mask,
5196                 .out.connect_handle = r->out.connect_handle,
5197         };
5198
5199         status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5200
5201         r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
5202         r->out.info_out->info1.supported_features = 0;
5203         *r->out.level_out = r->in.level_in;
5204
5205         return status;
5206 }
5207
5208
5209 /*
5210   samr_RidToSid
5211 */
5212 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5213                               struct samr_RidToSid *r)
5214 {
5215         struct samr_domain_state *d_state;
5216         struct dcesrv_handle *h;
5217
5218         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
5219
5220         d_state = h->data;
5221
5222         /* form the users SID */
5223         *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
5224         if (!*r->out.sid) {
5225                 return NT_STATUS_NO_MEMORY;
5226         }
5227
5228         return NT_STATUS_OK;
5229 }
5230
5231
5232 /*
5233   samr_SetDsrmPassword
5234 */
5235 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5236                        struct samr_SetDsrmPassword *r)
5237 {
5238         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5239 }
5240
5241
5242 /*
5243   samr_ValidatePassword
5244
5245   For now the call checks the password complexity (if active) and the minimum
5246   password length on level 2 and 3. Level 1 is ignored for now.
5247 */
5248 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
5249                                              TALLOC_CTX *mem_ctx,
5250                                              struct samr_ValidatePassword *r)
5251 {
5252         struct samr_GetDomPwInfo r2 = {};
5253         struct samr_PwInfo pwInfo = {};
5254         const char *account = NULL;
5255         DATA_BLOB password;
5256         enum samr_ValidationStatus res;
5257         NTSTATUS status;
5258         enum dcerpc_transport_t transport =
5259                 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
5260         enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
5261
5262         if (transport != NCACN_IP_TCP && transport != NCALRPC) {
5263                 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5264         }
5265
5266         dcesrv_call_auth_info(dce_call, NULL, &auth_level);
5267         if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
5268                 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5269         }
5270
5271         (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
5272
5273         r2 = (struct samr_GetDomPwInfo) {
5274                 .in.domain_name = NULL,
5275                 .out.info = &pwInfo,
5276         };
5277
5278         status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
5279         if (!NT_STATUS_IS_OK(status)) {
5280                 return status;
5281         }
5282
5283         switch (r->in.level) {
5284         case NetValidateAuthentication:
5285                 /* we don't support this yet */
5286                 return NT_STATUS_NOT_SUPPORTED;
5287         break;
5288         case NetValidatePasswordChange:
5289                 account = r->in.req->req2.account.string;
5290                 password = data_blob_const(r->in.req->req2.password.string,
5291                                            r->in.req->req2.password.length);
5292                 res = samdb_check_password(mem_ctx,
5293                                            dce_call->conn->dce_ctx->lp_ctx,
5294                                            account,
5295                                            NULL, /* userPrincipalName */
5296                                            NULL, /* displayName/full_name */
5297                                            &password,
5298                                            pwInfo.password_properties,
5299                                            pwInfo.min_password_length);
5300                 (*r->out.rep)->ctr2.status = res;
5301         break;
5302         case NetValidatePasswordReset:
5303                 account = r->in.req->req3.account.string;
5304                 password = data_blob_const(r->in.req->req3.password.string,
5305                                            r->in.req->req3.password.length);
5306                 res = samdb_check_password(mem_ctx,
5307                                            dce_call->conn->dce_ctx->lp_ctx,
5308                                            account,
5309                                            NULL, /* userPrincipalName */
5310                                            NULL, /* displayName/full_name */
5311                                            &password,
5312                                            pwInfo.password_properties,
5313                                            pwInfo.min_password_length);
5314                 (*r->out.rep)->ctr3.status = res;
5315         break;
5316         default:
5317                 return NT_STATUS_INVALID_INFO_CLASS;
5318         break;
5319         }
5320
5321         return NT_STATUS_OK;
5322 }
5323
5324 static void dcesrv_samr_Opnum68NotUsedOnWire(struct dcesrv_call_state *dce_call,
5325                                              TALLOC_CTX *mem_ctx,
5326                                              struct samr_Opnum68NotUsedOnWire *r)
5327 {
5328         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5329 }
5330
5331 static void dcesrv_samr_Opnum69NotUsedOnWire(struct dcesrv_call_state *dce_call,
5332                                              TALLOC_CTX *mem_ctx,
5333                                              struct samr_Opnum69NotUsedOnWire *r)
5334 {
5335         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5336 }
5337
5338 static void dcesrv_samr_Opnum70NotUsedOnWire(struct dcesrv_call_state *dce_call,
5339                                              TALLOC_CTX *mem_ctx,
5340                                              struct samr_Opnum70NotUsedOnWire *r)
5341 {
5342         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5343 }
5344
5345 static void dcesrv_samr_Opnum71NotUsedOnWire(struct dcesrv_call_state *dce_call,
5346                                              TALLOC_CTX *mem_ctx,
5347                                              struct samr_Opnum71NotUsedOnWire *r)
5348 {
5349         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5350 }
5351
5352 static void dcesrv_samr_Opnum72NotUsedOnWire(struct dcesrv_call_state *dce_call,
5353                                              TALLOC_CTX *mem_ctx,
5354                                              struct samr_Opnum72NotUsedOnWire *r)
5355 {
5356         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5357 }
5358
5359 /* include the generated boilerplate */
5360 #include "librpc/gen_ndr/ndr_samr_s.c"