s4:rpc_server: Fix code spelling
[samba.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, dom_sid_parse_talloc(mem_ctx, 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                                                    a_state->domain_state->domain_dn,
3730                                                    mem_ctx,
3731                                                    r->in.info->info18.lm_pwd_active ? r->in.info->info18.lm_pwd.hash : NULL,
3732                                                    r->in.info->info18.nt_pwd_active ? r->in.info->info18.nt_pwd.hash : NULL);
3733                 if (!NT_STATUS_IS_OK(status)) {
3734                         goto done;
3735                 }
3736
3737                 if (r->in.info->info18.password_expired > 0) {
3738                         struct ldb_message_element *set_el;
3739                         if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3740                                 status = NT_STATUS_NO_MEMORY;
3741                                 goto done;
3742                         }
3743                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
3744                         set_el->flags = LDB_FLAG_MOD_REPLACE;
3745                 }
3746                 break;
3747
3748         case 20:
3749                 SET_PARAMETERS(msg, info20.parameters,      "userParameters");
3750                 break;
3751
3752         case 21:
3753                 if (r->in.info->info21.fields_present == 0) {
3754                         status = NT_STATUS_INVALID_PARAMETER;
3755                         goto done;
3756                 }
3757
3758 #define IFSET(bit) if (bit & r->in.info->info21.fields_present)
3759                 IFSET(SAMR_FIELD_LAST_LOGON)
3760                         SET_UINT64(msg, info21.last_logon,     "lastLogon");
3761                 IFSET(SAMR_FIELD_LAST_LOGOFF)
3762                         SET_UINT64(msg, info21.last_logoff,    "lastLogoff");
3763                 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3764                         SET_UINT64(msg, info21.acct_expiry,    "accountExpires");
3765                 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3766                         SET_STRING(msg, info21.account_name,   "samAccountName");
3767                 IFSET(SAMR_FIELD_FULL_NAME)
3768                         SET_STRING(msg, info21.full_name,      "displayName");
3769                 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3770                         SET_STRING(msg, info21.home_directory, "homeDirectory");
3771                 IFSET(SAMR_FIELD_HOME_DRIVE)
3772                         SET_STRING(msg, info21.home_drive,     "homeDrive");
3773                 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3774                         SET_STRING(msg, info21.logon_script,   "scriptPath");
3775                 IFSET(SAMR_FIELD_PROFILE_PATH)
3776                         SET_STRING(msg, info21.profile_path,   "profilePath");
3777                 IFSET(SAMR_FIELD_DESCRIPTION)
3778                         SET_STRING(msg, info21.description,    "description");
3779                 IFSET(SAMR_FIELD_WORKSTATIONS)
3780                         SET_STRING(msg, info21.workstations,   "userWorkstations");
3781                 IFSET(SAMR_FIELD_COMMENT)
3782                         SET_STRING(msg, info21.comment,        "comment");
3783                 IFSET(SAMR_FIELD_PARAMETERS)
3784                         SET_PARAMETERS(msg, info21.parameters, "userParameters");
3785                 IFSET(SAMR_FIELD_PRIMARY_GID)
3786                         SET_UINT(msg, info21.primary_gid,      "primaryGroupID");
3787                 IFSET(SAMR_FIELD_ACCT_FLAGS)
3788                         SET_AFLAGS(msg, info21.acct_flags,     "userAccountControl");
3789                 IFSET(SAMR_FIELD_LOGON_HOURS)
3790                         SET_LHOURS(msg, info21.logon_hours,    "logonHours");
3791                 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3792                         SET_UINT  (msg, info21.bad_password_count, "badPwdCount");
3793                 IFSET(SAMR_FIELD_NUM_LOGONS)
3794                         SET_UINT  (msg, info21.logon_count,    "logonCount");
3795                 IFSET(SAMR_FIELD_COUNTRY_CODE)
3796                         SET_UINT  (msg, info21.country_code,   "countryCode");
3797                 IFSET(SAMR_FIELD_CODE_PAGE)
3798                         SET_UINT  (msg, info21.code_page,      "codePage");
3799
3800                 /* password change fields */
3801                 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
3802                         status = NT_STATUS_ACCESS_DENIED;
3803                         goto done;
3804                 }
3805
3806                 IFSET((SAMR_FIELD_LM_PASSWORD_PRESENT
3807                                         | SAMR_FIELD_NT_PASSWORD_PRESENT)) {
3808                         uint8_t *lm_pwd_hash = NULL, *nt_pwd_hash = NULL;
3809
3810                         if (r->in.info->info21.lm_password_set) {
3811                                 if ((r->in.info->info21.lm_owf_password.length != 16)
3812                                  || (r->in.info->info21.lm_owf_password.size != 16)) {
3813                                         status = NT_STATUS_INVALID_PARAMETER;
3814                                         goto done;
3815                                 }
3816
3817                                 lm_pwd_hash = (uint8_t *) r->in.info->info21.lm_owf_password.array;
3818                         }
3819                         if (r->in.info->info21.nt_password_set) {
3820                                 if ((r->in.info->info21.nt_owf_password.length != 16)
3821                                  || (r->in.info->info21.nt_owf_password.size != 16)) {
3822                                         status = NT_STATUS_INVALID_PARAMETER;
3823                                         goto done;
3824                                 }
3825
3826                                 nt_pwd_hash = (uint8_t *) r->in.info->info21.nt_owf_password.array;
3827                         }
3828                         status = samr_set_password_buffers(dce_call,
3829                                                            sam_ctx,
3830                                                            a_state->account_dn,
3831                                                            a_state->domain_state->domain_dn,
3832                                                            mem_ctx,
3833                                                            lm_pwd_hash,
3834                                                            nt_pwd_hash);
3835                         if (!NT_STATUS_IS_OK(status)) {
3836                                 goto done;
3837                         }
3838                 }
3839
3840
3841                 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3842                         const char *t = "0";
3843                         struct ldb_message_element *set_el;
3844                         if (r->in.info->info21.password_expired
3845                                         == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3846                                 t = "-1";
3847                         }
3848                         if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3849                                 status = NT_STATUS_NO_MEMORY;
3850                                 goto done;
3851                         }
3852                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
3853                         set_el->flags = LDB_FLAG_MOD_REPLACE;
3854                 }
3855 #undef IFSET
3856                 break;
3857
3858         case 23:
3859                 if (r->in.info->info23.info.fields_present == 0) {
3860                         status = NT_STATUS_INVALID_PARAMETER;
3861                         goto done;
3862                 }
3863
3864 #define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
3865                 IFSET(SAMR_FIELD_LAST_LOGON)
3866                         SET_UINT64(msg, info23.info.last_logon,     "lastLogon");
3867                 IFSET(SAMR_FIELD_LAST_LOGOFF)
3868                         SET_UINT64(msg, info23.info.last_logoff,    "lastLogoff");
3869                 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3870                         SET_UINT64(msg, info23.info.acct_expiry,    "accountExpires");
3871                 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3872                         SET_STRING(msg, info23.info.account_name,   "samAccountName");
3873                 IFSET(SAMR_FIELD_FULL_NAME)
3874                         SET_STRING(msg, info23.info.full_name,      "displayName");
3875                 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3876                         SET_STRING(msg, info23.info.home_directory, "homeDirectory");
3877                 IFSET(SAMR_FIELD_HOME_DRIVE)
3878                         SET_STRING(msg, info23.info.home_drive,     "homeDrive");
3879                 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3880                         SET_STRING(msg, info23.info.logon_script,   "scriptPath");
3881                 IFSET(SAMR_FIELD_PROFILE_PATH)
3882                         SET_STRING(msg, info23.info.profile_path,   "profilePath");
3883                 IFSET(SAMR_FIELD_DESCRIPTION)
3884                         SET_STRING(msg, info23.info.description,    "description");
3885                 IFSET(SAMR_FIELD_WORKSTATIONS)
3886                         SET_STRING(msg, info23.info.workstations,   "userWorkstations");
3887                 IFSET(SAMR_FIELD_COMMENT)
3888                         SET_STRING(msg, info23.info.comment,        "comment");
3889                 IFSET(SAMR_FIELD_PARAMETERS)
3890                         SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
3891                 IFSET(SAMR_FIELD_PRIMARY_GID)
3892                         SET_UINT(msg, info23.info.primary_gid,      "primaryGroupID");
3893                 IFSET(SAMR_FIELD_ACCT_FLAGS)
3894                         SET_AFLAGS(msg, info23.info.acct_flags,     "userAccountControl");
3895                 IFSET(SAMR_FIELD_LOGON_HOURS)
3896                         SET_LHOURS(msg, info23.info.logon_hours,    "logonHours");
3897                 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
3898                         SET_UINT  (msg, info23.info.bad_password_count, "badPwdCount");
3899                 IFSET(SAMR_FIELD_NUM_LOGONS)
3900                         SET_UINT  (msg, info23.info.logon_count,    "logonCount");
3901
3902                 IFSET(SAMR_FIELD_COUNTRY_CODE)
3903                         SET_UINT  (msg, info23.info.country_code,   "countryCode");
3904                 IFSET(SAMR_FIELD_CODE_PAGE)
3905                         SET_UINT  (msg, info23.info.code_page,      "codePage");
3906
3907                 /* password change fields */
3908                 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
3909                         status = NT_STATUS_ACCESS_DENIED;
3910                         goto done;
3911                 }
3912
3913                 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
3914                         status = samr_set_password(dce_call,
3915                                                    sam_ctx,
3916                                                    a_state->account_dn,
3917                                                    a_state->domain_state->domain_dn,
3918                                                    mem_ctx,
3919                                                    &r->in.info->info23.password);
3920                 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
3921                         status = samr_set_password(dce_call,
3922                                                    sam_ctx,
3923                                                    a_state->account_dn,
3924                                                    a_state->domain_state->domain_dn,
3925                                                    mem_ctx,
3926                                                    &r->in.info->info23.password);
3927                 }
3928                 if (!NT_STATUS_IS_OK(status)) {
3929                         goto done;
3930                 }
3931
3932                 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
3933                         const char *t = "0";
3934                         struct ldb_message_element *set_el;
3935                         if (r->in.info->info23.info.password_expired
3936                                         == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
3937                                 t = "-1";
3938                         }
3939                         if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
3940                                 status = NT_STATUS_NO_MEMORY;
3941                                 goto done;
3942                         }
3943                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
3944                         set_el->flags = LDB_FLAG_MOD_REPLACE;
3945                 }
3946 #undef IFSET
3947                 break;
3948
3949                 /* the set password levels are handled separately */
3950         case 24:
3951                 status = samr_set_password(dce_call,
3952                                            sam_ctx,
3953                                            a_state->account_dn,
3954                                            a_state->domain_state->domain_dn,
3955                                            mem_ctx,
3956                                            &r->in.info->info24.password);
3957                 if (!NT_STATUS_IS_OK(status)) {
3958                         goto done;
3959                 }
3960
3961                 if (r->in.info->info24.password_expired > 0) {
3962                         struct ldb_message_element *set_el;
3963                         if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, "pwdLastSet", 0) != LDB_SUCCESS) {
3964                                 status = NT_STATUS_NO_MEMORY;
3965                                 goto done;
3966                         }
3967                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
3968                         set_el->flags = LDB_FLAG_MOD_REPLACE;
3969                 }
3970                 break;
3971
3972         case 25:
3973                 if (r->in.info->info25.info.fields_present == 0) {
3974                         status = NT_STATUS_INVALID_PARAMETER;
3975                         goto done;
3976                 }
3977
3978 #define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
3979                 IFSET(SAMR_FIELD_LAST_LOGON)
3980                         SET_UINT64(msg, info25.info.last_logon,     "lastLogon");
3981                 IFSET(SAMR_FIELD_LAST_LOGOFF)
3982                         SET_UINT64(msg, info25.info.last_logoff,    "lastLogoff");
3983                 IFSET(SAMR_FIELD_ACCT_EXPIRY)
3984                         SET_UINT64(msg, info25.info.acct_expiry,    "accountExpires");
3985                 IFSET(SAMR_FIELD_ACCOUNT_NAME)
3986                         SET_STRING(msg, info25.info.account_name,   "samAccountName");
3987                 IFSET(SAMR_FIELD_FULL_NAME)
3988                         SET_STRING(msg, info25.info.full_name,      "displayName");
3989                 IFSET(SAMR_FIELD_HOME_DIRECTORY)
3990                         SET_STRING(msg, info25.info.home_directory, "homeDirectory");
3991                 IFSET(SAMR_FIELD_HOME_DRIVE)
3992                         SET_STRING(msg, info25.info.home_drive,     "homeDrive");
3993                 IFSET(SAMR_FIELD_LOGON_SCRIPT)
3994                         SET_STRING(msg, info25.info.logon_script,   "scriptPath");
3995                 IFSET(SAMR_FIELD_PROFILE_PATH)
3996                         SET_STRING(msg, info25.info.profile_path,   "profilePath");
3997                 IFSET(SAMR_FIELD_DESCRIPTION)
3998                         SET_STRING(msg, info25.info.description,    "description");
3999                 IFSET(SAMR_FIELD_WORKSTATIONS)
4000                         SET_STRING(msg, info25.info.workstations,   "userWorkstations");
4001                 IFSET(SAMR_FIELD_COMMENT)
4002                         SET_STRING(msg, info25.info.comment,        "comment");
4003                 IFSET(SAMR_FIELD_PARAMETERS)
4004                         SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
4005                 IFSET(SAMR_FIELD_PRIMARY_GID)
4006                         SET_UINT(msg, info25.info.primary_gid,      "primaryGroupID");
4007                 IFSET(SAMR_FIELD_ACCT_FLAGS)
4008                         SET_AFLAGS(msg, info25.info.acct_flags,     "userAccountControl");
4009                 IFSET(SAMR_FIELD_LOGON_HOURS)
4010                         SET_LHOURS(msg, info25.info.logon_hours,    "logonHours");
4011                 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
4012                         SET_UINT  (msg, info25.info.bad_password_count, "badPwdCount");
4013                 IFSET(SAMR_FIELD_NUM_LOGONS)
4014                         SET_UINT  (msg, info25.info.logon_count,    "logonCount");
4015                 IFSET(SAMR_FIELD_COUNTRY_CODE)
4016                         SET_UINT  (msg, info25.info.country_code,   "countryCode");
4017                 IFSET(SAMR_FIELD_CODE_PAGE)
4018                         SET_UINT  (msg, info25.info.code_page,      "codePage");
4019
4020                 /* password change fields */
4021                 IFSET(SAMR_FIELD_LAST_PWD_CHANGE) {
4022                         status = NT_STATUS_ACCESS_DENIED;
4023                         goto done;
4024                 }
4025
4026                 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
4027                         status = samr_set_password_ex(dce_call,
4028                                                       sam_ctx,
4029                                                       a_state->account_dn,
4030                                                       a_state->domain_state->domain_dn,
4031                                                       mem_ctx,
4032                                                       &r->in.info->info25.password);
4033                 } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
4034                         status = samr_set_password_ex(dce_call,
4035                                                       sam_ctx,
4036                                                       a_state->account_dn,
4037                                                       a_state->domain_state->domain_dn,
4038                                                       mem_ctx,
4039                                                       &r->in.info->info25.password);
4040                 }
4041                 if (!NT_STATUS_IS_OK(status)) {
4042                         goto done;
4043                 }
4044
4045                 IFSET(SAMR_FIELD_EXPIRED_FLAG) {
4046                         const char *t = "0";
4047                         struct ldb_message_element *set_el;
4048                         if (r->in.info->info25.info.password_expired
4049                                         == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4050                                 t = "-1";
4051                         }
4052                         if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4053                                 status = NT_STATUS_NO_MEMORY;
4054                                 goto done;
4055                         }
4056                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
4057                         set_el->flags = LDB_FLAG_MOD_REPLACE;
4058                 }
4059 #undef IFSET
4060                 break;
4061
4062                 /* the set password levels are handled separately */
4063         case 26:
4064                 status = samr_set_password_ex(dce_call,
4065                                               sam_ctx,
4066                                               a_state->account_dn,
4067                                               a_state->domain_state->domain_dn,
4068                                               mem_ctx,
4069                                               &r->in.info->info26.password);
4070                 if (!NT_STATUS_IS_OK(status)) {
4071                         goto done;
4072                 }
4073
4074                 if (r->in.info->info26.password_expired > 0) {
4075                         const char *t = "0";
4076                         struct ldb_message_element *set_el;
4077                         if (r->in.info->info26.password_expired
4078                                         == PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4079                                 t = "-1";
4080                         }
4081                         if (ldb_msg_add_string(msg, "pwdLastSet", t) != LDB_SUCCESS) {
4082                                 status = NT_STATUS_NO_MEMORY;
4083                                 goto done;
4084                         }
4085                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
4086                         set_el->flags = LDB_FLAG_MOD_REPLACE;
4087                 }
4088                 break;
4089
4090         case 31:
4091                 status = dcesrv_transport_session_key(dce_call, &session_key);
4092                 if (!NT_STATUS_IS_OK(status)) {
4093                         DBG_NOTICE("samr: failed to get session key: %s\n",
4094                                    nt_errstr(status));
4095                         goto done;
4096                 }
4097
4098                 status = samr_set_password_aes(dce_call,
4099                                                mem_ctx,
4100                                                &session_key,
4101                                                sam_ctx,
4102                                                a_state->account_dn,
4103                                                a_state->domain_state->domain_dn,
4104                                                &r->in.info->info31.password,
4105                                                DSDB_PASSWORD_RESET);
4106                 if (!NT_STATUS_IS_OK(status)) {
4107                         goto done;
4108                 }
4109
4110                 if (r->in.info->info31.password_expired > 0) {
4111                         const char *t = "0";
4112                         struct ldb_message_element *set_el = NULL;
4113
4114                         if (r->in.info->info31.password_expired ==
4115                             PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4116                                 t = "-1";
4117                         }
4118
4119                         ret = ldb_msg_add_string(msg, "pwdLastSet", t);
4120                         if (ret != LDB_SUCCESS) {
4121                                 status = NT_STATUS_NO_MEMORY;
4122                                 goto done;
4123                         }
4124                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
4125                         set_el->flags = LDB_FLAG_MOD_REPLACE;
4126                 }
4127
4128                 break;
4129         case 32:
4130                 status = dcesrv_transport_session_key(dce_call, &session_key);
4131                 if (!NT_STATUS_IS_OK(status)) {
4132                         DBG_NOTICE("samr: failed to get session key: %s\n",
4133                                    nt_errstr(status));
4134                         goto done;
4135                 }
4136
4137                 if (r->in.info->info32.info.fields_present == 0) {
4138                         status = NT_STATUS_INVALID_PARAMETER;
4139                         goto done;
4140                 }
4141
4142 #define IFSET(bit) if (bit & r->in.info->info32.info.fields_present)
4143                 IFSET(SAMR_FIELD_LAST_LOGON)
4144                 {
4145                         SET_UINT64(msg, info32.info.last_logon, "lastLogon");
4146                 }
4147                 IFSET(SAMR_FIELD_LAST_LOGOFF)
4148                 {
4149                         SET_UINT64(msg, info32.info.last_logoff, "lastLogoff");
4150                 }
4151                 IFSET(SAMR_FIELD_ACCT_EXPIRY)
4152                 {
4153                         SET_UINT64(msg,
4154                                    info32.info.acct_expiry,
4155                                    "accountExpires");
4156                 }
4157                 IFSET(SAMR_FIELD_ACCOUNT_NAME)
4158                 {
4159                         SET_STRING(msg,
4160                                    info32.info.account_name,
4161                                    "samAccountName");
4162                 }
4163                 IFSET(SAMR_FIELD_FULL_NAME)
4164                 {
4165                         SET_STRING(msg, info32.info.full_name, "displayName");
4166                 }
4167                 IFSET(SAMR_FIELD_HOME_DIRECTORY)
4168                 {
4169                         SET_STRING(msg,
4170                                    info32.info.home_directory,
4171                                    "homeDirectory");
4172                 }
4173                 IFSET(SAMR_FIELD_HOME_DRIVE)
4174                 {
4175                         SET_STRING(msg, info32.info.home_drive, "homeDrive");
4176                 }
4177                 IFSET(SAMR_FIELD_LOGON_SCRIPT)
4178                 {
4179                         SET_STRING(msg, info32.info.logon_script, "scriptPath");
4180                 }
4181                 IFSET(SAMR_FIELD_PROFILE_PATH)
4182                 {
4183                         SET_STRING(msg,
4184                                    info32.info.profile_path,
4185                                    "profilePath");
4186                 }
4187                 IFSET(SAMR_FIELD_DESCRIPTION)
4188                 {
4189                         SET_STRING(msg, info32.info.description, "description");
4190                 }
4191                 IFSET(SAMR_FIELD_WORKSTATIONS)
4192                 {
4193                         SET_STRING(msg,
4194                                    info32.info.workstations,
4195                                    "userWorkstations");
4196                 }
4197                 IFSET(SAMR_FIELD_COMMENT)
4198                 {
4199                         SET_STRING(msg, info32.info.comment, "comment");
4200                 }
4201                 IFSET(SAMR_FIELD_PARAMETERS)
4202                 {
4203                         SET_PARAMETERS(msg,
4204                                        info32.info.parameters,
4205                                        "userParameters");
4206                 }
4207                 IFSET(SAMR_FIELD_PRIMARY_GID)
4208                 {
4209                         SET_UINT(msg,
4210                                  info32.info.primary_gid,
4211                                  "primaryGroupID");
4212                 }
4213                 IFSET(SAMR_FIELD_ACCT_FLAGS)
4214                 {
4215                         SET_AFLAGS(msg,
4216                                    info32.info.acct_flags,
4217                                    "userAccountControl");
4218                 }
4219                 IFSET(SAMR_FIELD_LOGON_HOURS)
4220                 {
4221                         SET_LHOURS(msg, info32.info.logon_hours, "logonHours");
4222                 }
4223                 IFSET(SAMR_FIELD_BAD_PWD_COUNT)
4224                 {
4225                         SET_UINT(msg,
4226                                  info32.info.bad_password_count,
4227                                  "badPwdCount");
4228                 }
4229                 IFSET(SAMR_FIELD_NUM_LOGONS)
4230                 {
4231                         SET_UINT(msg, info32.info.logon_count, "logonCount");
4232                 }
4233                 IFSET(SAMR_FIELD_COUNTRY_CODE)
4234                 {
4235                         SET_UINT(msg, info32.info.country_code, "countryCode");
4236                 }
4237                 IFSET(SAMR_FIELD_CODE_PAGE)
4238                 {
4239                         SET_UINT(msg, info32.info.code_page, "codePage");
4240                 }
4241
4242                 /* password change fields */
4243                 IFSET(SAMR_FIELD_LAST_PWD_CHANGE)
4244                 {
4245                         status = NT_STATUS_ACCESS_DENIED;
4246                         goto done;
4247                 }
4248
4249                 IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT)
4250                 {
4251                         status = samr_set_password_aes(
4252                                 dce_call,
4253                                 mem_ctx,
4254                                 &session_key,
4255                                 a_state->sam_ctx,
4256                                 a_state->account_dn,
4257                                 a_state->domain_state->domain_dn,
4258                                 &r->in.info->info32.password,
4259                                 DSDB_PASSWORD_RESET);
4260                 }
4261                 else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT)
4262                 {
4263                         status = samr_set_password_aes(
4264                                 dce_call,
4265                                 mem_ctx,
4266                                 &session_key,
4267                                 a_state->sam_ctx,
4268                                 a_state->account_dn,
4269                                 a_state->domain_state->domain_dn,
4270                                 &r->in.info->info32.password,
4271                                 DSDB_PASSWORD_RESET);
4272                 }
4273                 if (!NT_STATUS_IS_OK(status)) {
4274                         goto done;
4275                 }
4276
4277                 IFSET(SAMR_FIELD_EXPIRED_FLAG)
4278                 {
4279                         const char *t = "0";
4280                         struct ldb_message_element *set_el;
4281                         if (r->in.info->info32.info.password_expired ==
4282                             PASS_DONT_CHANGE_AT_NEXT_LOGON) {
4283                                 t = "-1";
4284                         }
4285                         if (ldb_msg_add_string(msg, "pwdLastSet", t) !=
4286                             LDB_SUCCESS) {
4287                                 status = NT_STATUS_NO_MEMORY;
4288                                 goto done;
4289                         }
4290                         set_el = ldb_msg_find_element(msg, "pwdLastSet");
4291                         set_el->flags = LDB_FLAG_MOD_REPLACE;
4292                 }
4293 #undef IFSET
4294
4295                 break;
4296         default:
4297                 /* many info classes are not valid for SetUserInfo */
4298                 status = NT_STATUS_INVALID_INFO_CLASS;
4299                 goto done;
4300         }
4301
4302         if (!NT_STATUS_IS_OK(status)) {
4303                 goto done;
4304         }
4305
4306         /* modify the samdb record */
4307         if (msg->num_elements > 0) {
4308                 ret = ldb_modify(sam_ctx, msg);
4309                 if (ret != LDB_SUCCESS) {
4310                         DEBUG(1,("Failed to modify record %s: %s\n",
4311                                  ldb_dn_get_linearized(a_state->account_dn),
4312                                  ldb_errstring(sam_ctx)));
4313
4314                         status = dsdb_ldb_err_to_ntstatus(ret);
4315                         goto done;
4316                 }
4317         }
4318
4319         ret = ldb_transaction_commit(sam_ctx);
4320         if (ret != LDB_SUCCESS) {
4321                 DBG_ERR("Failed to commit transaction modifying account record "
4322                         "%s: %s\n",
4323                         ldb_dn_get_linearized(msg->dn),
4324                         ldb_errstring(sam_ctx));
4325                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4326         }
4327
4328         status = NT_STATUS_OK;
4329 done:
4330         if (!NT_STATUS_IS_OK(status)) {
4331                 ldb_transaction_cancel(sam_ctx);
4332         }
4333
4334         return status;
4335 }
4336
4337
4338 /*
4339   samr_GetGroupsForUser
4340 */
4341 static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4342                        struct samr_GetGroupsForUser *r)
4343 {
4344         struct dcesrv_handle *h;
4345         struct samr_account_state *a_state;
4346         struct samr_domain_state *d_state;
4347         struct ldb_result *res, *res_memberof;
4348         const char * const attrs[] = { "primaryGroupID",
4349                                        "memberOf",
4350                                        NULL };
4351         const char * const group_attrs[] = { "objectSid",
4352                                              NULL };
4353
4354         struct samr_RidWithAttributeArray *array;
4355         struct ldb_message_element *memberof_el;
4356         int i, ret, count = 0;
4357         uint32_t primary_group_id;
4358         char *filter;
4359
4360         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4361
4362         a_state = h->data;
4363         d_state = a_state->domain_state;
4364
4365         ret = dsdb_search_dn(a_state->sam_ctx, mem_ctx,
4366                              &res,
4367                              a_state->account_dn,
4368                              attrs, DSDB_SEARCH_SHOW_EXTENDED_DN);
4369
4370         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4371                 return NT_STATUS_NO_SUCH_USER;
4372         } else if (ret != LDB_SUCCESS) {
4373                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4374         } else if (res->count != 1) {
4375                 return NT_STATUS_NO_SUCH_USER;
4376         }
4377
4378         primary_group_id = ldb_msg_find_attr_as_uint(res->msgs[0], "primaryGroupID",
4379                                                      0);
4380
4381         filter = talloc_asprintf(mem_ctx,
4382                                  "(&(|(grouptype=%d)(grouptype=%d))"
4383                                  "(objectclass=group)(|",
4384                                  GTYPE_SECURITY_UNIVERSAL_GROUP,
4385                                  GTYPE_SECURITY_GLOBAL_GROUP);
4386         if (filter == NULL) {
4387                 return NT_STATUS_NO_MEMORY;
4388         }
4389
4390         memberof_el = ldb_msg_find_element(res->msgs[0], "memberOf");
4391         if (memberof_el != NULL) {
4392                 for (i = 0; i < memberof_el->num_values; i++) {
4393                         const struct ldb_val *memberof_sid_binary;
4394                         char *memberof_sid_escaped;
4395                         struct ldb_dn *memberof_dn
4396                                 = ldb_dn_from_ldb_val(mem_ctx,
4397                                                       a_state->sam_ctx,
4398                                                       &memberof_el->values[i]);
4399                         if (memberof_dn == NULL) {
4400                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4401                         }
4402
4403                         memberof_sid_binary
4404                                 = ldb_dn_get_extended_component(memberof_dn,
4405                                                                 "SID");
4406                         if (memberof_sid_binary == NULL) {
4407                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4408                         }
4409
4410                         memberof_sid_escaped = ldb_binary_encode(mem_ctx,
4411                                                                  *memberof_sid_binary);
4412                         if (memberof_sid_escaped == NULL) {
4413                                 return NT_STATUS_NO_MEMORY;
4414                         }
4415                         filter = talloc_asprintf_append(filter, "(objectSID=%s)",
4416                                                         memberof_sid_escaped);
4417                         if (filter == NULL) {
4418                                 return NT_STATUS_NO_MEMORY;
4419                         }
4420                 }
4421
4422                 ret = dsdb_search(a_state->sam_ctx, mem_ctx,
4423                                   &res_memberof,
4424                                   d_state->domain_dn,
4425                                   LDB_SCOPE_SUBTREE,
4426                                   group_attrs, 0,
4427                                   "%s))", filter);
4428
4429                 if (ret != LDB_SUCCESS) {
4430                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
4431                 }
4432                 count = res_memberof->count;
4433         }
4434
4435         array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
4436         if (array == NULL)
4437                 return NT_STATUS_NO_MEMORY;
4438
4439         array->count = 0;
4440         array->rids = NULL;
4441
4442         array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
4443                                    count + 1);
4444         if (array->rids == NULL)
4445                 return NT_STATUS_NO_MEMORY;
4446
4447         /* Adds the primary group */
4448
4449         array->rids[0].rid = primary_group_id;
4450         array->rids[0].attributes = SE_GROUP_DEFAULT_FLAGS;
4451         array->count += 1;
4452
4453         /* Adds the additional groups */
4454         for (i = 0; i < count; i++) {
4455                 struct dom_sid *group_sid;
4456
4457                 group_sid = samdb_result_dom_sid(mem_ctx,
4458                                                  res_memberof->msgs[i],
4459                                                  "objectSid");
4460                 if (group_sid == NULL) {
4461                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
4462                 }
4463
4464                 array->rids[i + 1].rid =
4465                         group_sid->sub_auths[group_sid->num_auths-1];
4466                 array->rids[i + 1].attributes = SE_GROUP_DEFAULT_FLAGS;
4467                 array->count += 1;
4468         }
4469
4470         *r->out.rids = array;
4471
4472         return NT_STATUS_OK;
4473 }
4474
4475 /*
4476  * samr_QueryDisplayInfo
4477  *
4478  * A cache of the GUID's matching the last query is maintained
4479  * in the SAMR_QUERY_DISPLAY_INFO_CACHE guid_cache maintained o
4480  * n the dcesrv_handle.
4481  */
4482 static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4483                        struct samr_QueryDisplayInfo *r)
4484 {
4485         struct dcesrv_handle *h;
4486         struct samr_domain_state *d_state;
4487         struct ldb_result *res;
4488         uint32_t i;
4489         uint32_t results = 0;
4490         uint32_t count = 0;
4491         const char *const cache_attrs[] = {"objectGUID", NULL};
4492         const char *const attrs[] = {
4493             "objectSID", "sAMAccountName", "displayName", "description", NULL};
4494         struct samr_DispEntryFull *entriesFull = NULL;
4495         struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
4496         struct samr_DispEntryAscii *entriesAscii = NULL;
4497         struct samr_DispEntryGeneral *entriesGeneral = NULL;
4498         const char *filter;
4499         int ret;
4500         NTSTATUS status;
4501         struct samr_guid_cache *cache = NULL;
4502
4503         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4504
4505         d_state = h->data;
4506
4507         cache = &d_state->guid_caches[SAMR_QUERY_DISPLAY_INFO_CACHE];
4508         /*
4509          * Can the cached results be used?
4510          * The cache is discarded if the start index is zero, or the requested
4511          * level is different from that in the cache.
4512          */
4513         if ((r->in.start_idx == 0) || (r->in.level != cache->handle)) {
4514                 /*
4515                  * The cached results can not be used, so will need to query
4516                  * the database.
4517                  */
4518
4519                 /*
4520                  * Get the search filter for the current level
4521                  */
4522                 switch (r->in.level) {
4523                 case 1:
4524                 case 4:
4525                         filter = talloc_asprintf(mem_ctx,
4526                                                  "(&(objectclass=user)"
4527                                                  "(sAMAccountType=%d))",
4528                                                  ATYPE_NORMAL_ACCOUNT);
4529                         break;
4530                 case 2:
4531                         filter = talloc_asprintf(mem_ctx,
4532                                                  "(&(objectclass=user)"
4533                                                  "(sAMAccountType=%d))",
4534                                                  ATYPE_WORKSTATION_TRUST);
4535                         break;
4536                 case 3:
4537                 case 5:
4538                         filter =
4539                             talloc_asprintf(mem_ctx,
4540                                             "(&(|(groupType=%d)(groupType=%d))"
4541                                             "(objectClass=group))",
4542                                             GTYPE_SECURITY_UNIVERSAL_GROUP,
4543                                             GTYPE_SECURITY_GLOBAL_GROUP);
4544                         break;
4545                 default:
4546                         return NT_STATUS_INVALID_INFO_CLASS;
4547                 }
4548                 clear_guid_cache(cache);
4549
4550                 /*
4551                  * search for all requested objects in all domains.
4552                  */
4553                 ret = dsdb_search(d_state->sam_ctx,
4554                                   mem_ctx,
4555                                   &res,
4556                                   ldb_get_default_basedn(d_state->sam_ctx),
4557                                   LDB_SCOPE_SUBTREE,
4558                                   cache_attrs,
4559                                   0,
4560                                   "%s",
4561                                   filter);
4562                 if (ret != LDB_SUCCESS) {
4563                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
4564                 }
4565                 if ((res->count == 0) || (r->in.max_entries == 0)) {
4566                         return NT_STATUS_OK;
4567                 }
4568
4569                 status = load_guid_cache(cache, d_state, res->count, res->msgs);
4570                 TALLOC_FREE(res);
4571                 if (!NT_STATUS_IS_OK(status)) {
4572                         return status;
4573                 }
4574                 cache->handle = r->in.level;
4575         }
4576         *r->out.total_size = cache->size;
4577
4578         /*
4579          * if there are no entries or the requested start index is greater
4580          * than the number of entries, we return an empty response.
4581          */
4582         if (r->in.start_idx >= cache->size) {
4583                 *r->out.returned_size = 0;
4584                 switch(r->in.level) {
4585                 case 1:
4586                         r->out.info->info1.count = *r->out.returned_size;
4587                         r->out.info->info1.entries = NULL;
4588                         break;
4589                 case 2:
4590                         r->out.info->info2.count = *r->out.returned_size;
4591                         r->out.info->info2.entries = NULL;
4592                         break;
4593                 case 3:
4594                         r->out.info->info3.count = *r->out.returned_size;
4595                         r->out.info->info3.entries = NULL;
4596                         break;
4597                 case 4:
4598                         r->out.info->info4.count = *r->out.returned_size;
4599                         r->out.info->info4.entries = NULL;
4600                         break;
4601                 case 5:
4602                         r->out.info->info5.count = *r->out.returned_size;
4603                         r->out.info->info5.entries = NULL;
4604                         break;
4605                 }
4606                 return NT_STATUS_OK;
4607         }
4608
4609         /*
4610          * Allocate an array of the appropriate result structures for the
4611          * current query level.
4612          *
4613          * r->in.start_idx is always < cache->size due to the check above
4614          */
4615         results = MIN((cache->size - r->in.start_idx), r->in.max_entries);
4616         switch (r->in.level) {
4617         case 1:
4618                 entriesGeneral = talloc_array(
4619                     mem_ctx, struct samr_DispEntryGeneral, results);
4620                 break;
4621         case 2:
4622                 entriesFull =
4623                     talloc_array(mem_ctx, struct samr_DispEntryFull, results);
4624                 break;
4625         case 3:
4626                 entriesFullGroup = talloc_array(
4627                     mem_ctx, struct samr_DispEntryFullGroup, results);
4628                 break;
4629         case 4:
4630         case 5:
4631                 entriesAscii =
4632                     talloc_array(mem_ctx, struct samr_DispEntryAscii, results);
4633                 break;
4634         }
4635
4636         if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
4637             (entriesAscii == NULL) && (entriesFullGroup == NULL))
4638                 return NT_STATUS_NO_MEMORY;
4639
4640         /*
4641          * Process the list of result GUID's.
4642          * Read the details of each object and populate the result structure
4643          * for the current level.
4644          */
4645         count = 0;
4646         for (i = 0; i < results; i++) {
4647                 struct dom_sid *objectsid;
4648                 struct ldb_result *rec;
4649                 const uint32_t idx = r->in.start_idx + i;
4650                 uint32_t rid;
4651
4652                 /*
4653                  * Read an object from disk using the GUID as the key
4654                  *
4655                  * If the object can not be read, or it does not have a SID
4656                  * it is ignored.  In this case the number of entries returned
4657                  * will be less than the requested size, there will also be
4658                  * a gap in the idx numbers in the returned elements e.g. if
4659                  * there are 3 GUIDs a, b, c in the cache and b is deleted from
4660                  * disk then details for a, and c will be returned with
4661                  * idx values of 1 and 3 respectively.
4662                  *
4663                  */
4664                 ret = dsdb_search_by_dn_guid(d_state->sam_ctx,
4665                                              mem_ctx,
4666                                              &rec,
4667                                              &cache->entries[idx],
4668                                              attrs,
4669                                              0);
4670                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
4671                         struct GUID_txt_buf guid_buf;
4672                         char *guid_str =
4673                                 GUID_buf_string(&cache->entries[idx],
4674                                                 &guid_buf);
4675                         DBG_WARNING("GUID [%s] not found\n", guid_str);
4676                         continue;
4677                 } else if (ret != LDB_SUCCESS) {
4678                         clear_guid_cache(cache);
4679                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
4680                 }
4681                 objectsid = samdb_result_dom_sid(mem_ctx,
4682                                                  rec->msgs[0],
4683                                                  "objectSID");
4684                 if (objectsid == NULL) {
4685                         struct GUID_txt_buf guid_buf;
4686                         DBG_WARNING(
4687                             "objectSID for GUID [%s] not found\n",
4688                             GUID_buf_string(&cache->entries[idx], &guid_buf));
4689                         continue;
4690                 }
4691                 status = dom_sid_split_rid(NULL,
4692                                            objectsid,
4693                                            NULL,
4694                                            &rid);
4695                 if (!NT_STATUS_IS_OK(status)) {
4696                         struct dom_sid_buf sid_buf;
4697                         struct GUID_txt_buf guid_buf;
4698                         DBG_WARNING(
4699                             "objectSID [%s] for GUID [%s] invalid\n",
4700                             dom_sid_str_buf(objectsid, &sid_buf),
4701                             GUID_buf_string(&cache->entries[idx], &guid_buf));
4702                         continue;
4703                 }
4704
4705                 /*
4706                  * Populate the result structure for the current object
4707                  */
4708                 switch(r->in.level) {
4709                 case 1:
4710
4711                         entriesGeneral[count].idx = idx + 1;
4712                         entriesGeneral[count].rid = rid;
4713
4714                         entriesGeneral[count].acct_flags =
4715                             samdb_result_acct_flags(rec->msgs[0], NULL);
4716                         entriesGeneral[count].account_name.string =
4717                             ldb_msg_find_attr_as_string(
4718                                 rec->msgs[0], "sAMAccountName", "");
4719                         entriesGeneral[count].full_name.string =
4720                             ldb_msg_find_attr_as_string(
4721                                 rec->msgs[0], "displayName", "");
4722                         entriesGeneral[count].description.string =
4723                             ldb_msg_find_attr_as_string(
4724                                 rec->msgs[0], "description", "");
4725                         break;
4726                 case 2:
4727                         entriesFull[count].idx = idx + 1;
4728                         entriesFull[count].rid = rid;
4729
4730                         /*
4731                          * No idea why we need to or in ACB_NORMAL here,
4732                          * but this is what Win2k3 seems to do...
4733                          */
4734                         entriesFull[count].acct_flags =
4735                             samdb_result_acct_flags(rec->msgs[0], NULL) |
4736                             ACB_NORMAL;
4737                         entriesFull[count].account_name.string =
4738                             ldb_msg_find_attr_as_string(
4739                                 rec->msgs[0], "sAMAccountName", "");
4740                         entriesFull[count].description.string =
4741                             ldb_msg_find_attr_as_string(
4742                                 rec->msgs[0], "description", "");
4743                         break;
4744                 case 3:
4745                         entriesFullGroup[count].idx = idx + 1;
4746                         entriesFullGroup[count].rid = rid;
4747
4748                         /*
4749                          * We get a "7" here for groups
4750                          */
4751                         entriesFullGroup[count].acct_flags = SE_GROUP_DEFAULT_FLAGS;
4752                         entriesFullGroup[count].account_name.string =
4753                             ldb_msg_find_attr_as_string(
4754                                 rec->msgs[0], "sAMAccountName", "");
4755                         entriesFullGroup[count].description.string =
4756                             ldb_msg_find_attr_as_string(
4757                                 rec->msgs[0], "description", "");
4758                         break;
4759                 case 4:
4760                 case 5:
4761                         entriesAscii[count].idx = idx + 1;
4762                         entriesAscii[count].account_name.string =
4763                             ldb_msg_find_attr_as_string(
4764                                 rec->msgs[0], "sAMAccountName", "");
4765                         break;
4766                 }
4767                 count++;
4768         }
4769
4770         /*
4771          * Build the response based on the request level.
4772          */
4773         *r->out.returned_size = count;
4774         switch(r->in.level) {
4775         case 1:
4776                 r->out.info->info1.count = count;
4777                 r->out.info->info1.entries = entriesGeneral;
4778                 break;
4779         case 2:
4780                 r->out.info->info2.count = count;
4781                 r->out.info->info2.entries = entriesFull;
4782                 break;
4783         case 3:
4784                 r->out.info->info3.count = count;
4785                 r->out.info->info3.entries = entriesFullGroup;
4786                 break;
4787         case 4:
4788                 r->out.info->info4.count = count;
4789                 r->out.info->info4.entries = entriesAscii;
4790                 break;
4791         case 5:
4792                 r->out.info->info5.count = count;
4793                 r->out.info->info5.entries = entriesAscii;
4794                 break;
4795         }
4796
4797         return ((r->in.start_idx + results) < cache->size)
4798                    ? STATUS_MORE_ENTRIES
4799                    : NT_STATUS_OK;
4800 }
4801
4802
4803 /*
4804   samr_GetDisplayEnumerationIndex
4805 */
4806 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4807                        struct samr_GetDisplayEnumerationIndex *r)
4808 {
4809         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
4810 }
4811
4812
4813 /*
4814   samr_TestPrivateFunctionsDomain
4815 */
4816 static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4817                        struct samr_TestPrivateFunctionsDomain *r)
4818 {
4819         return NT_STATUS_NOT_IMPLEMENTED;
4820 }
4821
4822
4823 /*
4824   samr_TestPrivateFunctionsUser
4825 */
4826 static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4827                        struct samr_TestPrivateFunctionsUser *r)
4828 {
4829         return NT_STATUS_NOT_IMPLEMENTED;
4830 }
4831
4832
4833 /*
4834   samr_GetUserPwInfo
4835 */
4836 static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4837                                    struct samr_GetUserPwInfo *r)
4838 {
4839         struct dcesrv_handle *h;
4840         struct samr_account_state *a_state;
4841
4842         ZERO_STRUCTP(r->out.info);
4843
4844         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
4845
4846         a_state = h->data;
4847
4848         r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx,
4849                 mem_ctx, 0, a_state->domain_state->domain_dn, "minPwdLength",
4850                 NULL);
4851         r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx,
4852                 mem_ctx, 0, a_state->account_dn, "pwdProperties", NULL);
4853
4854         return NT_STATUS_OK;
4855 }
4856
4857
4858 /*
4859   samr_RemoveMemberFromForeignDomain
4860 */
4861 static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call,
4862                                                           TALLOC_CTX *mem_ctx,
4863                                                           struct samr_RemoveMemberFromForeignDomain *r)
4864 {
4865         struct dcesrv_handle *h;
4866         struct samr_domain_state *d_state;
4867         const char *memberdn;
4868         struct ldb_message **res;
4869         const char *no_attrs[] = { NULL };
4870         int i, count;
4871
4872         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
4873
4874         d_state = h->data;
4875
4876         memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
4877                                        "distinguishedName", "(objectSid=%s)",
4878                                        ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
4879         /* Nothing to do */
4880         if (memberdn == NULL) {
4881                 return NT_STATUS_OK;
4882         }
4883
4884         count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
4885                                     d_state->domain_dn, &res, no_attrs,
4886                                     d_state->domain_sid,
4887                                     "(&(member=%s)(objectClass=group)"
4888                                     "(|(groupType=%d)(groupType=%d)))",
4889                                     memberdn,
4890                                     GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
4891                                     GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
4892
4893         if (count < 0)
4894                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
4895
4896         for (i=0; i<count; i++) {
4897                 struct ldb_message *mod;
4898                 int ret;
4899
4900                 mod = ldb_msg_new(mem_ctx);
4901                 if (mod == NULL) {
4902                         return NT_STATUS_NO_MEMORY;
4903                 }
4904
4905                 mod->dn = res[i]->dn;
4906
4907                 if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
4908                                          "member", memberdn) != LDB_SUCCESS)
4909                         return NT_STATUS_NO_MEMORY;
4910
4911                 ret = ldb_modify(d_state->sam_ctx, mod);
4912                 talloc_free(mod);
4913                 if (ret != LDB_SUCCESS) {
4914                         return dsdb_ldb_err_to_ntstatus(ret);
4915                 }
4916         }
4917
4918         return NT_STATUS_OK;
4919 }
4920
4921
4922 /*
4923   samr_QueryDomainInfo2
4924
4925   just an alias for samr_QueryDomainInfo
4926 */
4927 static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4928                        struct samr_QueryDomainInfo2 *r)
4929 {
4930         struct samr_QueryDomainInfo r1;
4931         NTSTATUS status;
4932
4933         r1 = (struct samr_QueryDomainInfo) {
4934                 .in.domain_handle = r->in.domain_handle,
4935                 .in.level  = r->in.level,
4936                 .out.info  = r->out.info,
4937         };
4938
4939         status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
4940
4941         return status;
4942 }
4943
4944
4945 /*
4946   samr_QueryUserInfo2
4947
4948   just an alias for samr_QueryUserInfo
4949 */
4950 static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4951                                     struct samr_QueryUserInfo2 *r)
4952 {
4953         struct samr_QueryUserInfo r1;
4954         NTSTATUS status;
4955
4956         r1 = (struct samr_QueryUserInfo) {
4957                 .in.user_handle = r->in.user_handle,
4958                 .in.level  = r->in.level,
4959                 .out.info  = r->out.info
4960         };
4961
4962         status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
4963
4964         return status;
4965 }
4966
4967
4968 /*
4969   samr_QueryDisplayInfo2
4970 */
4971 static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4972                                        struct samr_QueryDisplayInfo2 *r)
4973 {
4974         struct samr_QueryDisplayInfo q;
4975         NTSTATUS result;
4976
4977         q = (struct samr_QueryDisplayInfo) {
4978                 .in.domain_handle = r->in.domain_handle,
4979                 .in.level = r->in.level,
4980                 .in.start_idx = r->in.start_idx,
4981                 .in.max_entries = r->in.max_entries,
4982                 .in.buf_size = r->in.buf_size,
4983                 .out.total_size = r->out.total_size,
4984                 .out.returned_size = r->out.returned_size,
4985                 .out.info = r->out.info,
4986         };
4987
4988         result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
4989
4990         return result;
4991 }
4992
4993
4994 /*
4995   samr_GetDisplayEnumerationIndex2
4996 */
4997 static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
4998                        struct samr_GetDisplayEnumerationIndex2 *r)
4999 {
5000         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5001 }
5002
5003
5004 /*
5005   samr_QueryDisplayInfo3
5006 */
5007 static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5008                        struct samr_QueryDisplayInfo3 *r)
5009 {
5010         struct samr_QueryDisplayInfo q;
5011         NTSTATUS result;
5012
5013         q = (struct samr_QueryDisplayInfo) {
5014                 .in.domain_handle = r->in.domain_handle,
5015                 .in.level = r->in.level,
5016                 .in.start_idx = r->in.start_idx,
5017                 .in.max_entries = r->in.max_entries,
5018                 .in.buf_size = r->in.buf_size,
5019                 .out.total_size = r->out.total_size,
5020                 .out.returned_size = r->out.returned_size,
5021                 .out.info = r->out.info,
5022         };
5023
5024         result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
5025
5026         return result;
5027 }
5028
5029
5030 /*
5031   samr_AddMultipleMembersToAlias
5032 */
5033 static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5034                        struct samr_AddMultipleMembersToAlias *r)
5035 {
5036         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5037 }
5038
5039
5040 /*
5041   samr_RemoveMultipleMembersFromAlias
5042 */
5043 static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5044                        struct samr_RemoveMultipleMembersFromAlias *r)
5045 {
5046         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5047 }
5048
5049
5050 /*
5051   samr_GetDomPwInfo
5052
5053   this fetches the default password properties for a domain
5054
5055   note that w2k3 completely ignores the domain name in this call, and
5056   always returns the information for the servers primary domain
5057 */
5058 static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5059                                   struct samr_GetDomPwInfo *r)
5060 {
5061         struct ldb_message **msgs;
5062         int ret;
5063         const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
5064         struct ldb_context *sam_ctx;
5065
5066         ZERO_STRUCTP(r->out.info);
5067
5068         sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
5069         if (sam_ctx == NULL) {
5070                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
5071         }
5072
5073         /* The domain name in this call is ignored */
5074         ret = gendb_search_dn(sam_ctx,
5075                            mem_ctx, NULL, &msgs, attrs);
5076         if (ret <= 0) {
5077                 talloc_free(sam_ctx);
5078
5079                 return NT_STATUS_NO_SUCH_DOMAIN;
5080         }
5081         if (ret > 1) {
5082                 talloc_free(msgs);
5083                 talloc_free(sam_ctx);
5084
5085                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
5086         }
5087
5088         r->out.info->min_password_length = ldb_msg_find_attr_as_uint(msgs[0],
5089                 "minPwdLength", 0);
5090         r->out.info->password_properties = ldb_msg_find_attr_as_uint(msgs[0],
5091                 "pwdProperties", 1);
5092
5093         talloc_free(msgs);
5094         talloc_unlink(mem_ctx, sam_ctx);
5095
5096         return NT_STATUS_OK;
5097 }
5098
5099
5100 /*
5101   samr_Connect2
5102 */
5103 static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5104                               struct samr_Connect2 *r)
5105 {
5106         struct samr_Connect c;
5107
5108         c = (struct samr_Connect) {
5109                 .in.system_name = NULL,
5110                 .in.access_mask = r->in.access_mask,
5111                 .out.connect_handle = r->out.connect_handle,
5112         };
5113
5114         return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5115 }
5116
5117
5118 /*
5119   samr_SetUserInfo2
5120
5121   just an alias for samr_SetUserInfo
5122 */
5123 static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5124                                   struct samr_SetUserInfo2 *r)
5125 {
5126         struct samr_SetUserInfo r2;
5127
5128         r2 = (struct samr_SetUserInfo) {
5129                 .in.user_handle = r->in.user_handle,
5130                 .in.level = r->in.level,
5131                 .in.info = r->in.info,
5132         };
5133
5134         return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
5135 }
5136
5137
5138 /*
5139   samr_SetBootKeyInformation
5140 */
5141 static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5142                        struct samr_SetBootKeyInformation *r)
5143 {
5144         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5145 }
5146
5147
5148 /*
5149   samr_GetBootKeyInformation
5150 */
5151 static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5152                        struct samr_GetBootKeyInformation *r)
5153 {
5154         /* Windows Server 2008 returns this */
5155         return NT_STATUS_NOT_SUPPORTED;
5156 }
5157
5158
5159 /*
5160   samr_Connect3
5161 */
5162 static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5163                        struct samr_Connect3 *r)
5164 {
5165         struct samr_Connect c;
5166
5167         c = (struct samr_Connect) {
5168                 .in.system_name = NULL,
5169                 .in.access_mask = r->in.access_mask,
5170                 .out.connect_handle = r->out.connect_handle,
5171         };
5172
5173         return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5174 }
5175
5176
5177 /*
5178   samr_Connect4
5179 */
5180 static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5181                        struct samr_Connect4 *r)
5182 {
5183         struct samr_Connect c;
5184
5185         c = (struct samr_Connect) {
5186                 .in.system_name = NULL,
5187                 .in.access_mask = r->in.access_mask,
5188                 .out.connect_handle = r->out.connect_handle,
5189         };
5190
5191         return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5192 }
5193
5194
5195 /*
5196   samr_Connect5
5197 */
5198 static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5199                               struct samr_Connect5 *r)
5200 {
5201         struct samr_Connect c;
5202         NTSTATUS status;
5203
5204         c = (struct samr_Connect) {
5205                 .in.system_name = NULL,
5206                 .in.access_mask = r->in.access_mask,
5207                 .out.connect_handle = r->out.connect_handle,
5208         };
5209
5210         status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
5211
5212         r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
5213         r->out.info_out->info1.supported_features = 0;
5214         *r->out.level_out = r->in.level_in;
5215
5216         return status;
5217 }
5218
5219
5220 /*
5221   samr_RidToSid
5222 */
5223 static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5224                               struct samr_RidToSid *r)
5225 {
5226         struct samr_domain_state *d_state;
5227         struct dcesrv_handle *h;
5228
5229         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
5230
5231         d_state = h->data;
5232
5233         /* form the users SID */
5234         *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
5235         if (!*r->out.sid) {
5236                 return NT_STATUS_NO_MEMORY;
5237         }
5238
5239         return NT_STATUS_OK;
5240 }
5241
5242
5243 /*
5244   samr_SetDsrmPassword
5245 */
5246 static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
5247                        struct samr_SetDsrmPassword *r)
5248 {
5249         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
5250 }
5251
5252
5253 /*
5254   samr_ValidatePassword
5255
5256   For now the call checks the password complexity (if active) and the minimum
5257   password length on level 2 and 3. Level 1 is ignored for now.
5258 */
5259 static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call,
5260                                              TALLOC_CTX *mem_ctx,
5261                                              struct samr_ValidatePassword *r)
5262 {
5263         struct samr_GetDomPwInfo r2;
5264         struct samr_PwInfo pwInfo;
5265         const char *account = NULL;
5266         DATA_BLOB password;
5267         enum samr_ValidationStatus res;
5268         NTSTATUS status;
5269         enum dcerpc_transport_t transport =
5270                 dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
5271         enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
5272
5273         if (transport != NCACN_IP_TCP && transport != NCALRPC) {
5274                 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5275         }
5276
5277         dcesrv_call_auth_info(dce_call, NULL, &auth_level);
5278         if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
5279                 DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
5280         }
5281
5282         (*r->out.rep) = talloc_zero(mem_ctx, union samr_ValidatePasswordRep);
5283
5284         r2 = (struct samr_GetDomPwInfo) {
5285                 .in.domain_name = NULL,
5286                 .out.info = &pwInfo,
5287         };
5288
5289         status = dcesrv_samr_GetDomPwInfo(dce_call, mem_ctx, &r2);
5290         if (!NT_STATUS_IS_OK(status)) {
5291                 return status;
5292         }
5293
5294         switch (r->in.level) {
5295         case NetValidateAuthentication:
5296                 /* we don't support this yet */
5297                 return NT_STATUS_NOT_SUPPORTED;
5298         break;
5299         case NetValidatePasswordChange:
5300                 account = r->in.req->req2.account.string;
5301                 password = data_blob_const(r->in.req->req2.password.string,
5302                                            r->in.req->req2.password.length);
5303                 res = samdb_check_password(mem_ctx,
5304                                            dce_call->conn->dce_ctx->lp_ctx,
5305                                            account,
5306                                            NULL, /* userPrincipalName */
5307                                            NULL, /* displayName/full_name */
5308                                            &password,
5309                                            pwInfo.password_properties,
5310                                            pwInfo.min_password_length);
5311                 (*r->out.rep)->ctr2.status = res;
5312         break;
5313         case NetValidatePasswordReset:
5314                 account = r->in.req->req3.account.string;
5315                 password = data_blob_const(r->in.req->req3.password.string,
5316                                            r->in.req->req3.password.length);
5317                 res = samdb_check_password(mem_ctx,
5318                                            dce_call->conn->dce_ctx->lp_ctx,
5319                                            account,
5320                                            NULL, /* userPrincipalName */
5321                                            NULL, /* displayName/full_name */
5322                                            &password,
5323                                            pwInfo.password_properties,
5324                                            pwInfo.min_password_length);
5325                 (*r->out.rep)->ctr3.status = res;
5326         break;
5327         default:
5328                 return NT_STATUS_INVALID_INFO_CLASS;
5329         break;
5330         }
5331
5332         return NT_STATUS_OK;
5333 }
5334
5335 static void dcesrv_samr_Opnum68NotUsedOnWire(struct dcesrv_call_state *dce_call,
5336                                              TALLOC_CTX *mem_ctx,
5337                                              struct samr_Opnum68NotUsedOnWire *r)
5338 {
5339         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5340 }
5341
5342 static void dcesrv_samr_Opnum69NotUsedOnWire(struct dcesrv_call_state *dce_call,
5343                                              TALLOC_CTX *mem_ctx,
5344                                              struct samr_Opnum69NotUsedOnWire *r)
5345 {
5346         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5347 }
5348
5349 static void dcesrv_samr_Opnum70NotUsedOnWire(struct dcesrv_call_state *dce_call,
5350                                              TALLOC_CTX *mem_ctx,
5351                                              struct samr_Opnum70NotUsedOnWire *r)
5352 {
5353         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5354 }
5355
5356 static void dcesrv_samr_Opnum71NotUsedOnWire(struct dcesrv_call_state *dce_call,
5357                                              TALLOC_CTX *mem_ctx,
5358                                              struct samr_Opnum71NotUsedOnWire *r)
5359 {
5360         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5361 }
5362
5363 static void dcesrv_samr_Opnum72NotUsedOnWire(struct dcesrv_call_state *dce_call,
5364                                              TALLOC_CTX *mem_ctx,
5365                                              struct samr_Opnum72NotUsedOnWire *r)
5366 {
5367         DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
5368 }
5369
5370 /* include the generated boilerplate */
5371 #include "librpc/gen_ndr/ndr_samr_s.c"