s4-samr: Use dom_sid_split_rid() to get the RID in dcesrv_samr_QueryDisplayInfo
[amitay/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 #define DCESRV_INTERFACE_SAMR_BIND(call, iface) \
45        dcesrv_interface_samr_bind(call, iface)
46 static NTSTATUS dcesrv_interface_samr_bind(struct dcesrv_call_state *dce_call,
47                                              const struct dcesrv_interface *iface)
48 {
49         return dcesrv_interface_bind_reject_connect(dce_call, iface);
50 }
51
52 /* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
53
54 #define QUERY_STRING(msg, field, attr) \
55         info->field.string = ldb_msg_find_attr_as_string(msg, attr, "");
56 #define QUERY_UINT(msg, field, attr) \
57         info->field = ldb_msg_find_attr_as_uint(msg, attr, 0);
58 #define QUERY_RID(msg, field, attr) \
59         info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
60 #define QUERY_UINT64(msg, field, attr) \
61         info->field = ldb_msg_find_attr_as_uint64(msg, attr, 0);
62 #define QUERY_APASSC(msg, field, attr) \
63         info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
64                                                          a_state->domain_state->domain_dn, msg, attr);
65 #define QUERY_BPWDCT(msg, field, attr) \
66         info->field = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, \
67                                                          a_state->domain_state->domain_dn, msg);
68 #define QUERY_LHOURS(msg, field, attr) \
69         info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
70 #define QUERY_AFLAGS(msg, field, attr) \
71         info->field = samdb_result_acct_flags(msg, attr);
72
73
74 /* these are used to make the Set[User|Group]Info code easier to follow */
75
76 #define SET_STRING(msg, field, attr) do {                               \
77         struct ldb_message_element *set_el;                             \
78         if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
79         if (r->in.info->field.string[0] == '\0') {                      \
80                 if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL) != LDB_SUCCESS) { \
81                         return NT_STATUS_NO_MEMORY;                     \
82                 }                                                       \
83         }                                                               \
84         if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != LDB_SUCCESS) { \
85                 return NT_STATUS_NO_MEMORY;                             \
86         }                                                               \
87         set_el = ldb_msg_find_element(msg, attr);                       \
88         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
89 } while (0)
90
91 #define SET_UINT(msg, field, attr) do {                                 \
92         struct ldb_message_element *set_el;                             \
93         if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
94                 return NT_STATUS_NO_MEMORY;                             \
95         }                                                               \
96         set_el = ldb_msg_find_element(msg, attr);                       \
97         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
98 } while (0)
99
100 #define SET_INT64(msg, field, attr) do {                                \
101         struct ldb_message_element *set_el;                             \
102         if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
103                 return NT_STATUS_NO_MEMORY;                             \
104         }                                                               \
105         set_el = ldb_msg_find_element(msg, attr);                       \
106         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
107 } while (0)
108
109 #define SET_UINT64(msg, field, attr) do {                               \
110         struct ldb_message_element *set_el;                             \
111         if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != LDB_SUCCESS) { \
112                 return NT_STATUS_NO_MEMORY;                             \
113         }                                                               \
114         set_el = ldb_msg_find_element(msg, attr);                       \
115         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
116 } while (0)
117
118 /* Set account flags, discarding flags that cannot be set with SAMR */
119 #define SET_AFLAGS(msg, field, attr) do {                               \
120         struct ldb_message_element *set_el;                             \
121         if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
122                 return NT_STATUS_NO_MEMORY;                             \
123         }                                                               \
124         set_el = ldb_msg_find_element(msg, attr);                       \
125         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
126 } while (0)
127
128 #define SET_LHOURS(msg, field, attr) do {                               \
129         struct ldb_message_element *set_el;                             \
130         if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
131                 return NT_STATUS_NO_MEMORY;                             \
132         }                                                               \
133         set_el = ldb_msg_find_element(msg, attr);                       \
134         set_el->flags = LDB_FLAG_MOD_REPLACE;                           \
135 } while (0)
136
137 #define SET_PARAMETERS(msg, field, attr) do {                           \
138         struct ldb_message_element *set_el;                             \
139         if (r->in.info->field.length != 0) {                            \
140                 if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != LDB_SUCCESS) { \
141                         return NT_STATUS_NO_MEMORY;                     \
142                 }                                                       \
143                 set_el = ldb_msg_find_element(msg, attr);               \
144                 set_el->flags = LDB_FLAG_MOD_REPLACE;                   \
145         }                                                               \
146 } while (0)
147
148 /*
149  * Clear a GUID cache
150  */
151 static void clear_guid_cache(struct samr_guid_cache *cache)
152 {
153         cache->handle = 0;
154         cache->size = 0;
155         TALLOC_FREE(cache->entries);
156 }
157
158 /*
159  * initialize a GUID cache
160  */
161 static void initialize_guid_cache(struct samr_guid_cache *cache)
162 {
163         cache->handle = 0;
164         cache->size = 0;
165         cache->entries = NULL;
166 }
167
168 static NTSTATUS load_guid_cache(
169         struct samr_guid_cache *cache,
170         struct samr_domain_state *d_state,
171         unsigned int ldb_cnt,
172         struct ldb_message **res)
173 {
174         NTSTATUS status = NT_STATUS_OK;
175         unsigned int i;
176         TALLOC_CTX *frame = talloc_stackframe();
177
178         clear_guid_cache(cache);
179
180         /*
181          * Store the GUID's in the cache.
182          */
183         cache->handle = 0;
184         cache->size = ldb_cnt;
185         cache->entries = talloc_array(d_state, struct GUID, ldb_cnt);
186         if (cache->entries == NULL) {
187                 clear_guid_cache(cache);
188                 status = NT_STATUS_NO_MEMORY;
189                 goto exit;
190         }
191
192         /*
193          * Extract a list of the GUIDs for all the matching objects
194          * we cache just the GUIDS to reduce the memory overhead of
195          * the result cache.
196          */
197         for (i = 0; i < ldb_cnt; i++) {
198                 cache->entries[i] = samdb_result_guid(res[i], "objectGUID");
199         }
200 exit:
201         TALLOC_FREE(frame);
202         return status;
203 }
204
205 /*
206   samr_Connect
207
208   create a connection to the SAM database
209 */
210 static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
211                              struct samr_Connect *r)
212 {
213         struct samr_connect_state *c_state;
214         struct dcesrv_handle *handle;
215
216         ZERO_STRUCTP(r->out.connect_handle);
217
218         c_state = talloc(mem_ctx, struct samr_connect_state);
219         if (!c_state) {
220                 return NT_STATUS_NO_MEMORY;
221         }
222
223         /* make sure the sam database is accessible */
224         c_state->sam_ctx = samdb_connect(c_state,
225                                          dce_call->event_ctx,
226                                          dce_call->conn->dce_ctx->lp_ctx,
227                                          dce_call->conn->auth_state.session_info,
228                                          dce_call->conn->remote_address,
229                                          0);
230         if (c_state->sam_ctx == NULL) {
231                 talloc_free(c_state);
232                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
233         }
234
235
236         handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
237         if (!handle) {
238                 talloc_free(c_state);
239                 return NT_STATUS_NO_MEMORY;
240         }
241
242         handle->data = talloc_steal(handle, c_state);
243
244         c_state->access_mask = r->in.access_mask;
245         *r->out.connect_handle = handle->wire_handle;
246
247         return NT_STATUS_OK;
248 }
249
250
251 /*
252   samr_Close
253 */
254 static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
255                            struct samr_Close *r)
256 {
257         struct dcesrv_handle *h;
258
259         *r->out.handle = *r->in.handle;
260
261         DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
262
263         talloc_free(h);
264
265         ZERO_STRUCTP(r->out.handle);
266
267         return NT_STATUS_OK;
268 }
269
270
271 /*
272   samr_SetSecurity
273 */
274 static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
275                                  struct samr_SetSecurity *r)
276 {
277         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
278 }
279
280
281 /*
282   samr_QuerySecurity
283 */
284 static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
285                                    struct samr_QuerySecurity *r)
286 {
287         struct dcesrv_handle *h;
288         struct sec_desc_buf *sd;
289
290         *r->out.sdbuf = NULL;
291
292         DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
293
294         sd = talloc(mem_ctx, struct sec_desc_buf);
295         if (sd == NULL) {
296                 return NT_STATUS_NO_MEMORY;
297         }
298
299         sd->sd = samdb_default_security_descriptor(mem_ctx);
300
301         *r->out.sdbuf = sd;
302
303         return NT_STATUS_OK;
304 }
305
306
307 /*
308   samr_Shutdown
309
310   we refuse this operation completely. If a admin wants to shutdown samr
311   in Samba then they should use the samba admin tools to disable the samr pipe
312 */
313 static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
314                               struct samr_Shutdown *r)
315 {
316         return NT_STATUS_ACCESS_DENIED;
317 }
318
319
320 /*
321   samr_LookupDomain
322
323   this maps from a domain name to a SID
324 */
325 static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
326                                   struct samr_LookupDomain *r)
327 {
328         struct samr_connect_state *c_state;
329         struct dcesrv_handle *h;
330         struct dom_sid *sid;
331         const char * const dom_attrs[] = { "objectSid", NULL};
332         struct ldb_message **dom_msgs;
333         int ret;
334
335         *r->out.sid = NULL;
336
337         DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
338
339         c_state = h->data;
340
341         if (r->in.domain_name->string == NULL) {
342                 return NT_STATUS_INVALID_PARAMETER;
343         }
344
345         if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
346                 ret = gendb_search(c_state->sam_ctx,
347                                    mem_ctx, NULL, &dom_msgs, dom_attrs,
348                                    "(objectClass=builtinDomain)");
349         } else if (strcasecmp_m(r->in.domain_name->string, lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
350                 ret = gendb_search_dn(c_state->sam_ctx,
351                                       mem_ctx, ldb_get_default_basedn(c_state->sam_ctx),
352                                       &dom_msgs, dom_attrs);
353         } else {
354                 return NT_STATUS_NO_SUCH_DOMAIN;
355         }
356         if (ret != 1) {
357                 return NT_STATUS_NO_SUCH_DOMAIN;
358         }
359
360         sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
361                                    "objectSid");
362
363         if (sid == NULL) {
364                 return NT_STATUS_NO_SUCH_DOMAIN;
365         }
366
367         *r->out.sid = sid;
368
369         return NT_STATUS_OK;
370 }
371
372
373 /*
374   samr_EnumDomains
375
376   list the domains in the SAM
377 */
378 static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
379                                  struct samr_EnumDomains *r)
380 {
381         struct dcesrv_handle *h;
382         struct samr_SamArray *array;
383         uint32_t i, start_i;
384
385         *r->out.resume_handle = 0;
386         *r->out.sam = NULL;
387         *r->out.num_entries = 0;
388
389         DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
390
391         *r->out.resume_handle = 2;
392
393         start_i = *r->in.resume_handle;
394
395         if (start_i >= 2) {
396                 /* search past end of list is not an error for this call */
397                 return NT_STATUS_OK;
398         }
399
400         array = talloc(mem_ctx, struct samr_SamArray);
401         if (array == NULL) {
402                 return NT_STATUS_NO_MEMORY;
403         }
404
405         array->count = 0;
406         array->entries = NULL;
407
408         array->entries = talloc_array(mem_ctx, struct samr_SamEntry, 2 - start_i);
409         if (array->entries == NULL) {
410                 return NT_STATUS_NO_MEMORY;
411         }
412
413         for (i=0;i<2-start_i;i++) {
414                 array->entries[i].idx = start_i + i;
415                 if (i == 0) {
416                         array->entries[i].name.string = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
417                 } else {
418                         array->entries[i].name.string = "BUILTIN";
419                 }
420         }
421
422         *r->out.sam = array;
423         *r->out.num_entries = i;
424         array->count = *r->out.num_entries;
425
426         return NT_STATUS_OK;
427 }
428
429
430 /*
431   samr_OpenDomain
432 */
433 static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
434                                 struct samr_OpenDomain *r)
435 {
436         struct dcesrv_handle *h_conn, *h_domain;
437         struct samr_connect_state *c_state;
438         struct samr_domain_state *d_state;
439         const char * const dom_attrs[] = { "cn", NULL};
440         struct ldb_message **dom_msgs;
441         int ret;
442         unsigned int i;
443
444         ZERO_STRUCTP(r->out.domain_handle);
445
446         DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
447
448         c_state = h_conn->data;
449
450         if (r->in.sid == NULL) {
451                 return NT_STATUS_INVALID_PARAMETER;
452         }
453
454         d_state = talloc(mem_ctx, struct samr_domain_state);
455         if (!d_state) {
456                 return NT_STATUS_NO_MEMORY;
457         }
458
459         d_state->domain_sid = talloc_steal(d_state, r->in.sid);
460
461         if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
462                 d_state->builtin = true;
463                 d_state->domain_name = "BUILTIN";
464         } else {
465                 d_state->builtin = false;
466                 d_state->domain_name = lpcfg_sam_name(dce_call->conn->dce_ctx->lp_ctx);
467         }
468
469         ret = gendb_search(c_state->sam_ctx,
470                            mem_ctx, ldb_get_default_basedn(c_state->sam_ctx), &dom_msgs, dom_attrs,
471                            "(objectSid=%s)",
472                            ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
473
474         if (ret == 0) {
475                 talloc_free(d_state);
476                 return NT_STATUS_NO_SUCH_DOMAIN;
477         } else if (ret > 1) {
478                 talloc_free(d_state);
479                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
480         } else if (ret == -1) {
481                 talloc_free(d_state);
482                 DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
483                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
484         }
485
486         d_state->domain_dn = talloc_steal(d_state, dom_msgs[0]->dn);
487         d_state->role = lpcfg_server_role(dce_call->conn->dce_ctx->lp_ctx);
488         d_state->connect_state = talloc_reference(d_state, c_state);
489         d_state->sam_ctx = c_state->sam_ctx;
490         d_state->access_mask = r->in.access_mask;
491
492         d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
493
494         for (i = 0; i < SAMR_LAST_CACHE; i++) {
495                 initialize_guid_cache(&d_state->guid_caches[i]);
496         }
497
498         h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
499         if (!h_domain) {
500                 talloc_free(d_state);
501                 return NT_STATUS_NO_MEMORY;
502         }
503
504         h_domain->data = talloc_steal(h_domain, d_state);
505
506         *r->out.domain_handle = h_domain->wire_handle;
507
508         return NT_STATUS_OK;
509 }
510
511 /*
512   return DomInfo1
513 */
514 static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
515                                           TALLOC_CTX *mem_ctx,
516                                           struct ldb_message **dom_msgs,
517                                           struct samr_DomInfo1 *info)
518 {
519         info->min_password_length =
520                 ldb_msg_find_attr_as_uint(dom_msgs[0], "minPwdLength", 0);
521         info->password_history_length =
522                 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdHistoryLength", 0);
523         info->password_properties =
524                 ldb_msg_find_attr_as_uint(dom_msgs[0], "pwdProperties", 0);
525         info->max_password_age =
526                 ldb_msg_find_attr_as_int64(dom_msgs[0], "maxPwdAge", 0);
527         info->min_password_age =
528                 ldb_msg_find_attr_as_int64(dom_msgs[0], "minPwdAge", 0);
529
530         return NT_STATUS_OK;
531 }
532
533 /*
534   return DomInfo2
535 */
536 static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
537                                                        TALLOC_CTX *mem_ctx,
538                                                        struct ldb_message **dom_msgs,
539                                                        struct samr_DomGeneralInformation *info)
540 {
541         /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
542         info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
543                                                            "domainReplica",
544                                                            "");
545
546         info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
547                                                             0x8000000000000000LL);
548
549         info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
550                                                                    "oEMInformation",
551                                                                    "");
552         info->domain_name.string  = state->domain_name;
553
554         info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
555                                                  0);
556         switch (state->role) {
557         case ROLE_ACTIVE_DIRECTORY_DC:
558                 /* This pulls the NetBIOS name from the
559                    cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
560                    string */
561                 if (samdb_is_pdc(state->sam_ctx)) {
562                         info->role = SAMR_ROLE_DOMAIN_PDC;
563                 } else {
564                         info->role = SAMR_ROLE_DOMAIN_BDC;
565                 }
566                 break;
567         case ROLE_DOMAIN_PDC:
568         case ROLE_DOMAIN_BDC:
569         case ROLE_AUTO:
570                 return NT_STATUS_INTERNAL_ERROR;
571         case ROLE_DOMAIN_MEMBER:
572                 info->role = SAMR_ROLE_DOMAIN_MEMBER;
573                 break;
574         case ROLE_STANDALONE:
575                 info->role = SAMR_ROLE_STANDALONE;
576                 break;
577         }
578
579         info->num_users = samdb_search_count(state->sam_ctx, mem_ctx,
580                                              state->domain_dn,
581                                              "(objectClass=user)");
582         info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx,
583                                               state->domain_dn,
584                                               "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
585                                               GTYPE_SECURITY_UNIVERSAL_GROUP,
586                                               GTYPE_SECURITY_GLOBAL_GROUP);
587         info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx,
588                                                state->domain_dn,
589                                                "(&(objectClass=group)(|(groupType=%d)(groupType=%d)))",
590                                                GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
591                                                GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
592
593         return NT_STATUS_OK;
594 }
595
596 /*
597   return DomInfo3
598 */
599 static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
600                                           TALLOC_CTX *mem_ctx,
601                                           struct ldb_message **dom_msgs,
602                                           struct samr_DomInfo3 *info)
603 {
604         info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
605                                                       0x8000000000000000LL);
606
607         return NT_STATUS_OK;
608 }
609
610 /*
611   return DomInfo4
612 */
613 static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
614                                    TALLOC_CTX *mem_ctx,
615                                     struct ldb_message **dom_msgs,
616                                    struct samr_DomOEMInformation *info)
617 {
618         info->oem_information.string = ldb_msg_find_attr_as_string(dom_msgs[0],
619                                                                    "oEMInformation",
620                                                                    "");
621
622         return NT_STATUS_OK;
623 }
624
625 /*
626   return DomInfo5
627 */
628 static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
629                                           TALLOC_CTX *mem_ctx,
630                                           struct ldb_message **dom_msgs,
631                                           struct samr_DomInfo5 *info)
632 {
633         info->domain_name.string  = state->domain_name;
634
635         return NT_STATUS_OK;
636 }
637
638 /*
639   return DomInfo6
640 */
641 static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
642                                           TALLOC_CTX *mem_ctx,
643                                           struct ldb_message **dom_msgs,
644                                           struct samr_DomInfo6 *info)
645 {
646         /* MS-SAMR 2.2.4.1 - ReplicaSourceNodeName: "domainReplica" attribute */
647         info->primary.string = ldb_msg_find_attr_as_string(dom_msgs[0],
648                                                            "domainReplica",
649                                                            "");
650
651         return NT_STATUS_OK;
652 }
653
654 /*
655   return DomInfo7
656 */
657 static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
658                                           TALLOC_CTX *mem_ctx,
659                                           struct ldb_message **dom_msgs,
660                                           struct samr_DomInfo7 *info)
661 {
662
663         switch (state->role) {
664         case ROLE_ACTIVE_DIRECTORY_DC:
665                 /* This pulls the NetBIOS name from the
666                    cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
667                    string */
668                 if (samdb_is_pdc(state->sam_ctx)) {
669                         info->role = SAMR_ROLE_DOMAIN_PDC;
670                 } else {
671                         info->role = SAMR_ROLE_DOMAIN_BDC;
672                 }
673                 break;
674         case ROLE_DOMAIN_PDC:
675         case ROLE_DOMAIN_BDC:
676         case ROLE_AUTO:
677                 return NT_STATUS_INTERNAL_ERROR;
678         case ROLE_DOMAIN_MEMBER:
679                 info->role = SAMR_ROLE_DOMAIN_MEMBER;
680                 break;
681         case ROLE_STANDALONE:
682                 info->role = SAMR_ROLE_STANDALONE;
683                 break;
684         }
685
686         return NT_STATUS_OK;
687 }
688
689 /*
690   return DomInfo8
691 */
692 static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
693                                           TALLOC_CTX *mem_ctx,
694                                           struct ldb_message **dom_msgs,
695                                           struct samr_DomInfo8 *info)
696 {
697         info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
698                                                time(NULL));
699
700         info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
701                                                      0x0LL);
702
703         return NT_STATUS_OK;
704 }
705
706 /*
707   return DomInfo9
708 */
709 static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
710                                           TALLOC_CTX *mem_ctx,
711                                           struct ldb_message **dom_msgs,
712                                           struct samr_DomInfo9 *info)
713 {
714         info->domain_server_state = DOMAIN_SERVER_ENABLED;
715
716         return NT_STATUS_OK;
717 }
718
719 /*
720   return DomInfo11
721 */
722 static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
723                                                         TALLOC_CTX *mem_ctx,
724                                                         struct ldb_message **dom_msgs,
725                                                         struct samr_DomGeneralInformation2 *info)
726 {
727         NTSTATUS status;
728         status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
729         if (!NT_STATUS_IS_OK(status)) {
730                 return status;
731         }
732
733         info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
734                                                     -18000000000LL);
735         info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
736                                                     -18000000000LL);
737         info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
738
739         return NT_STATUS_OK;
740 }
741
742 /*
743   return DomInfo12
744 */
745 static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
746                                            TALLOC_CTX *mem_ctx,
747                                            struct ldb_message **dom_msgs,
748                                            struct samr_DomInfo12 *info)
749 {
750         info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
751                                                     -18000000000LL);
752         info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
753                                                     -18000000000LL);
754         info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
755
756         return NT_STATUS_OK;
757 }
758
759 /*
760   return DomInfo13
761 */
762 static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
763                                            TALLOC_CTX *mem_ctx,
764                                            struct ldb_message **dom_msgs,
765                                            struct samr_DomInfo13 *info)
766 {
767         info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
768                                                time(NULL));
769
770         info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
771                                                      0x0LL);
772
773         info->modified_count_at_last_promotion = 0;
774
775         return NT_STATUS_OK;
776 }
777
778 /*
779   samr_QueryDomainInfo
780 */
781 static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call,
782                                             TALLOC_CTX *mem_ctx,
783                                             struct samr_QueryDomainInfo *r)
784 {
785         struct dcesrv_handle *h;
786         struct samr_domain_state *d_state;
787         union samr_DomainInfo *info;
788
789         struct ldb_message **dom_msgs;
790         const char * const *attrs = NULL;
791
792         *r->out.info = NULL;
793
794         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
795
796         d_state = h->data;
797
798         switch (r->in.level) {
799         case 1:
800         {
801                 static const char * const attrs2[] = { "minPwdLength",
802                                                        "pwdHistoryLength",
803                                                        "pwdProperties",
804                                                        "maxPwdAge",
805                                                        "minPwdAge",
806                                                        NULL };
807                 attrs = attrs2;
808                 break;
809         }
810         case 2:
811         {
812                 static const char * const attrs2[] = {"forceLogoff",
813                                                       "oEMInformation",
814                                                       "modifiedCount",
815                                                       "domainReplica",
816                                                       NULL};
817                 attrs = attrs2;
818                 break;
819         }
820         case 3:
821         {
822                 static const char * const attrs2[] = {"forceLogoff",
823                                                       NULL};
824                 attrs = attrs2;
825                 break;
826         }
827         case 4:
828         {
829                 static const char * const attrs2[] = {"oEMInformation",
830                                                       NULL};
831                 attrs = attrs2;
832                 break;
833         }
834         case 5:
835         {
836                 attrs = NULL;
837                 break;
838         }
839         case 6:
840         {
841                 static const char * const attrs2[] = { "domainReplica",
842                                                        NULL };
843                 attrs = attrs2;
844                 break;
845         }
846         case 7:
847         {
848                 attrs = NULL;
849                 break;
850         }
851         case 8:
852         {
853                 static const char * const attrs2[] = { "modifiedCount",
854                                                        "creationTime",
855                                                        NULL };
856                 attrs = attrs2;
857                 break;
858         }
859         case 9:
860         {
861                 attrs = NULL;
862                 break;
863         }
864         case 11:
865         {
866                 static const char * const attrs2[] = { "oEMInformation",
867                                                        "forceLogoff",
868                                                        "modifiedCount",
869                                                        "lockoutDuration",
870                                                        "lockOutObservationWindow",
871                                                        "lockoutThreshold",
872                                                        NULL};
873                 attrs = attrs2;
874                 break;
875         }
876         case 12:
877         {
878                 static const char * const attrs2[] = { "lockoutDuration",
879                                                        "lockOutObservationWindow",
880                                                        "lockoutThreshold",
881                                                        NULL};
882                 attrs = attrs2;
883                 break;
884         }
885         case 13:
886         {
887                 static const char * const attrs2[] = { "modifiedCount",
888                                                        "creationTime",
889                                                        NULL };
890                 attrs = attrs2;
891                 break;
892         }
893         default:
894         {
895                 return NT_STATUS_INVALID_INFO_CLASS;
896         }
897         }
898
899         /* some levels don't need a search */
900         if (attrs) {
901                 int ret;
902                 ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
903                                       d_state->domain_dn, &dom_msgs, attrs);
904                 if (ret == 0) {
905                         return NT_STATUS_NO_SUCH_DOMAIN;
906                 }
907                 if (ret != 1) {
908                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
909                 }
910         }
911
912         /* allocate the info structure */
913         info = talloc_zero(mem_ctx, union samr_DomainInfo);
914         if (info == NULL) {
915                 return NT_STATUS_NO_MEMORY;
916         }
917
918         *r->out.info = info;
919
920         switch (r->in.level) {
921         case 1:
922                 return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
923                                                  &info->info1);
924         case 2:
925                 return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
926                                                               &info->general);
927         case 3:
928                 return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
929                                                  &info->info3);
930         case 4:
931                 return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
932                                                           &info->oem);
933         case 5:
934                 return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
935                                                  &info->info5);
936         case 6:
937                 return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
938                                                  &info->info6);
939         case 7:
940                 return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
941                                                  &info->info7);
942         case 8:
943                 return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
944                                                  &info->info8);
945         case 9:
946                 return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
947                                                  &info->info9);
948         case 11:
949                 return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
950                                                                &info->general2);
951         case 12:
952                 return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
953                                                   &info->info12);
954         case 13:
955                 return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
956                                                   &info->info13);
957         default:
958                 return NT_STATUS_INVALID_INFO_CLASS;
959         }
960 }
961
962
963 /*
964   samr_SetDomainInfo
965 */
966 static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
967                        struct samr_SetDomainInfo *r)
968 {
969         struct dcesrv_handle *h;
970         struct samr_domain_state *d_state;
971         struct ldb_message *msg;
972         int ret;
973         struct ldb_context *sam_ctx;
974
975         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
976
977         d_state = h->data;
978         sam_ctx = d_state->sam_ctx;
979
980         msg = ldb_msg_new(mem_ctx);
981         if (msg == NULL) {
982                 return NT_STATUS_NO_MEMORY;
983         }
984
985         msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
986         if (!msg->dn) {
987                 return NT_STATUS_NO_MEMORY;
988         }
989
990         switch (r->in.level) {
991         case 1:
992                 SET_UINT  (msg, info1.min_password_length,     "minPwdLength");
993                 SET_UINT  (msg, info1.password_history_length, "pwdHistoryLength");
994                 SET_UINT  (msg, info1.password_properties,     "pwdProperties");
995                 SET_INT64  (msg, info1.max_password_age,       "maxPwdAge");
996                 SET_INT64  (msg, info1.min_password_age,       "minPwdAge");
997                 break;
998         case 3:
999                 SET_UINT64  (msg, info3.force_logoff_time,     "forceLogoff");
1000                 break;
1001         case 4:
1002                 SET_STRING(msg, oem.oem_information,           "oEMInformation");
1003                 break;
1004
1005         case 6:
1006         case 7:
1007         case 9:
1008                 /* No op, we don't know where to set these */
1009                 return NT_STATUS_OK;
1010
1011         case 12:
1012                 /*
1013                  * It is not possible to set lockout_duration < lockout_window.
1014                  * (The test is the other way around since the negative numbers
1015                  *  are stored...)
1016                  *
1017                  * TODO:
1018                  *   This check should be moved to the backend, i.e. to some
1019                  *   ldb module under dsdb/samdb/ldb_modules/ .
1020                  *
1021                  * This constraint is documented here for the samr rpc service:
1022                  * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
1023                  * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
1024                  *
1025                  * And here for the ldap backend:
1026                  * MS-ADTS 3.1.1.5.3.2 Constraints
1027                  * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
1028                  */
1029                 if (r->in.info->info12.lockout_duration >
1030                     r->in.info->info12.lockout_window)
1031                 {
1032                         return NT_STATUS_INVALID_PARAMETER;
1033                 }
1034                 SET_INT64  (msg, info12.lockout_duration,      "lockoutDuration");
1035                 SET_INT64  (msg, info12.lockout_window,        "lockOutObservationWindow");
1036                 SET_INT64  (msg, info12.lockout_threshold,     "lockoutThreshold");
1037                 break;
1038
1039         default:
1040                 /* many info classes are not valid for SetDomainInfo */
1041                 return NT_STATUS_INVALID_INFO_CLASS;
1042         }
1043
1044         /* modify the samdb record */
1045         ret = ldb_modify(sam_ctx, msg);
1046         if (ret != LDB_SUCCESS) {
1047                 DEBUG(1,("Failed to modify record %s: %s\n",
1048                          ldb_dn_get_linearized(d_state->domain_dn),
1049                          ldb_errstring(sam_ctx)));
1050                 return dsdb_ldb_err_to_ntstatus(ret);
1051         }
1052
1053         return NT_STATUS_OK;
1054 }
1055
1056 /*
1057   samr_CreateDomainGroup
1058 */
1059 static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1060                                        struct samr_CreateDomainGroup *r)
1061 {
1062         NTSTATUS status;
1063         struct samr_domain_state *d_state;
1064         struct samr_account_state *a_state;
1065         struct dcesrv_handle *h;
1066         const char *groupname;
1067         struct dom_sid *group_sid;
1068         struct ldb_dn *group_dn;
1069         struct dcesrv_handle *g_handle;
1070
1071         ZERO_STRUCTP(r->out.group_handle);
1072         *r->out.rid = 0;
1073
1074         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1075
1076         d_state = h->data;
1077
1078         if (d_state->builtin) {
1079                 DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
1080                 return NT_STATUS_ACCESS_DENIED;
1081         }
1082
1083         groupname = r->in.name->string;
1084
1085         if (groupname == NULL) {
1086                 return NT_STATUS_INVALID_PARAMETER;
1087         }
1088
1089         status = dsdb_add_domain_group(d_state->sam_ctx, mem_ctx, groupname, &group_sid, &group_dn);
1090         if (!NT_STATUS_IS_OK(status)) {
1091                 return status;
1092         }
1093
1094         a_state = talloc(mem_ctx, struct samr_account_state);
1095         if (!a_state) {
1096                 return NT_STATUS_NO_MEMORY;
1097         }
1098         a_state->sam_ctx = d_state->sam_ctx;
1099         a_state->access_mask = r->in.access_mask;
1100         a_state->domain_state = talloc_reference(a_state, d_state);
1101         a_state->account_dn = talloc_steal(a_state, group_dn);
1102
1103         a_state->account_name = talloc_steal(a_state, groupname);
1104
1105         /* create the policy handle */
1106         g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1107         if (!g_handle) {
1108                 return NT_STATUS_NO_MEMORY;
1109         }
1110
1111         g_handle->data = talloc_steal(g_handle, a_state);
1112
1113         *r->out.group_handle = g_handle->wire_handle;
1114         *r->out.rid = group_sid->sub_auths[group_sid->num_auths-1];
1115
1116         return NT_STATUS_OK;
1117 }
1118
1119
1120 /*
1121   comparison function for sorting SamEntry array
1122 */
1123 static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
1124 {
1125         return e1->idx - e2->idx;
1126 }
1127
1128 /*
1129   samr_EnumDomainGroups
1130 */
1131 static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1132                                       struct samr_EnumDomainGroups *r)
1133 {
1134         struct dcesrv_handle *h;
1135         struct samr_domain_state *d_state;
1136         struct ldb_message **res;
1137         int i, ldb_cnt;
1138         uint32_t first, count;
1139         struct samr_SamEntry *entries;
1140         const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1141         struct samr_SamArray *sam;
1142
1143         *r->out.resume_handle = 0;
1144         *r->out.sam = NULL;
1145         *r->out.num_entries = 0;
1146
1147         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1148
1149         d_state = h->data;
1150
1151         /* search for all domain groups in this domain. This could possibly be
1152            cached and resumed based on resume_key */
1153         ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1154                                       d_state->domain_dn, &res, attrs,
1155                                       d_state->domain_sid,
1156                                       "(&(|(groupType=%d)(groupType=%d))(objectClass=group))",
1157                                       GTYPE_SECURITY_UNIVERSAL_GROUP,
1158                                       GTYPE_SECURITY_GLOBAL_GROUP);
1159         if (ldb_cnt < 0) {
1160                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1161         }
1162
1163         /* convert to SamEntry format */
1164         entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1165         if (!entries) {
1166                 return NT_STATUS_NO_MEMORY;
1167         }
1168
1169         count = 0;
1170
1171         for (i=0;i<ldb_cnt;i++) {
1172                 struct dom_sid *group_sid;
1173
1174                 group_sid = samdb_result_dom_sid(mem_ctx, res[i],
1175                                                  "objectSid");
1176                 if (group_sid == NULL) {
1177                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1178                 }
1179
1180                 entries[count].idx =
1181                         group_sid->sub_auths[group_sid->num_auths-1];
1182                 entries[count].name.string =
1183                         ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1184                 count += 1;
1185         }
1186
1187         /* sort the results by rid */
1188         TYPESAFE_QSORT(entries, count, compare_SamEntry);
1189
1190         /* find the first entry to return */
1191         for (first=0;
1192              first<count && entries[first].idx <= *r->in.resume_handle;
1193              first++) ;
1194
1195         /* return the rest, limit by max_size. Note that we
1196            use the w2k3 element size value of 54 */
1197         *r->out.num_entries = count - first;
1198         *r->out.num_entries = MIN(*r->out.num_entries,
1199                                  1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1200
1201         sam = talloc(mem_ctx, struct samr_SamArray);
1202         if (!sam) {
1203                 return NT_STATUS_NO_MEMORY;
1204         }
1205
1206         sam->entries = entries+first;
1207         sam->count = *r->out.num_entries;
1208
1209         *r->out.sam = sam;
1210
1211         if (first == count) {
1212                 return NT_STATUS_OK;
1213         }
1214
1215         if (*r->out.num_entries < count - first) {
1216                 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1217                 return STATUS_MORE_ENTRIES;
1218         }
1219
1220         return NT_STATUS_OK;
1221 }
1222
1223
1224 /*
1225   samr_CreateUser2
1226
1227   This call uses transactions to ensure we don't get a new conflicting
1228   user while we are processing this, and to ensure the user either
1229   completly exists, or does not.
1230 */
1231 static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1232                                  struct samr_CreateUser2 *r)
1233 {
1234         NTSTATUS status;
1235         struct samr_domain_state *d_state;
1236         struct samr_account_state *a_state;
1237         struct dcesrv_handle *h;
1238         struct ldb_dn *dn;
1239         struct dom_sid *sid;
1240         struct dcesrv_handle *u_handle;
1241         const char *account_name;
1242
1243         ZERO_STRUCTP(r->out.user_handle);
1244         *r->out.access_granted = 0;
1245         *r->out.rid = 0;
1246
1247         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1248
1249         d_state = h->data;
1250
1251         if (d_state->builtin) {
1252                 DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
1253                 return NT_STATUS_ACCESS_DENIED;
1254         } else if (r->in.acct_flags == ACB_DOMTRUST) {
1255                 /* Domain trust accounts must be created by the LSA calls */
1256                 return NT_STATUS_ACCESS_DENIED;
1257         }
1258         account_name = r->in.account_name->string;
1259
1260         if (account_name == NULL) {
1261                 return NT_STATUS_INVALID_PARAMETER;
1262         }
1263
1264         status = dsdb_add_user(d_state->sam_ctx, mem_ctx, account_name, r->in.acct_flags, NULL,
1265                                &sid, &dn);
1266         if (!NT_STATUS_IS_OK(status)) {
1267                 return status;
1268         }
1269         a_state = talloc(mem_ctx, struct samr_account_state);
1270         if (!a_state) {
1271                 return NT_STATUS_NO_MEMORY;
1272         }
1273         a_state->sam_ctx = d_state->sam_ctx;
1274         a_state->access_mask = r->in.access_mask;
1275         a_state->domain_state = talloc_reference(a_state, d_state);
1276         a_state->account_dn = talloc_steal(a_state, dn);
1277
1278         a_state->account_name = talloc_steal(a_state, account_name);
1279         if (!a_state->account_name) {
1280                 return NT_STATUS_NO_MEMORY;
1281         }
1282
1283         /* create the policy handle */
1284         u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
1285         if (!u_handle) {
1286                 return NT_STATUS_NO_MEMORY;
1287         }
1288
1289         u_handle->data = talloc_steal(u_handle, a_state);
1290
1291         *r->out.user_handle = u_handle->wire_handle;
1292         *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
1293
1294         *r->out.rid = sid->sub_auths[sid->num_auths-1];
1295
1296         return NT_STATUS_OK;
1297 }
1298
1299
1300 /*
1301   samr_CreateUser
1302 */
1303 static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1304                                 struct samr_CreateUser *r)
1305 {
1306         struct samr_CreateUser2 r2;
1307         uint32_t access_granted = 0;
1308
1309
1310         /* a simple wrapper around samr_CreateUser2 works nicely */
1311
1312         r2 = (struct samr_CreateUser2) {
1313                 .in.domain_handle = r->in.domain_handle,
1314                 .in.account_name = r->in.account_name,
1315                 .in.acct_flags = ACB_NORMAL,
1316                 .in.access_mask = r->in.access_mask,
1317                 .out.user_handle = r->out.user_handle,
1318                 .out.access_granted = &access_granted,
1319                 .out.rid = r->out.rid
1320         };
1321
1322         return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
1323 }
1324
1325 /*
1326   samr_EnumDomainUsers
1327 */
1328 static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1329                                      struct samr_EnumDomainUsers *r)
1330 {
1331         struct dcesrv_handle *h;
1332         struct samr_domain_state *d_state;
1333         struct ldb_message **res;
1334         int i, ldb_cnt;
1335         uint32_t first, count;
1336         struct samr_SamEntry *entries;
1337         const char * const attrs[] = { "objectSid", "sAMAccountName",
1338                 "userAccountControl", NULL };
1339         struct samr_SamArray *sam;
1340
1341         *r->out.resume_handle = 0;
1342         *r->out.sam = NULL;
1343         *r->out.num_entries = 0;
1344
1345         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1346
1347         d_state = h->data;
1348
1349         /* search for all domain users in this domain. This could possibly be
1350            cached and resumed on resume_key */
1351         ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
1352                                       d_state->domain_dn,
1353                                       &res, attrs,
1354                                       d_state->domain_sid,
1355                                       "(objectClass=user)");
1356         if (ldb_cnt < 0) {
1357                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1358         }
1359
1360         /* convert to SamEntry format */
1361         entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1362         if (!entries) {
1363                 return NT_STATUS_NO_MEMORY;
1364         }
1365
1366         count = 0;
1367
1368         for (i=0;i<ldb_cnt;i++) {
1369                 /* Check if a mask has been requested */
1370                 if (r->in.acct_flags
1371                     && ((samdb_result_acct_flags(res[i], NULL) & r->in.acct_flags) == 0)) {
1372                         continue;
1373                 }
1374                 entries[count].idx = samdb_result_rid_from_sid(mem_ctx, res[i],
1375                                                                "objectSid", 0);
1376                 entries[count].name.string = ldb_msg_find_attr_as_string(res[i],
1377                                                                  "sAMAccountName", "");
1378                 count += 1;
1379         }
1380
1381         /* sort the results by rid */
1382         TYPESAFE_QSORT(entries, count, compare_SamEntry);
1383
1384         /* find the first entry to return */
1385         for (first=0;
1386              first<count && entries[first].idx <= *r->in.resume_handle;
1387              first++) ;
1388
1389         /* return the rest, limit by max_size. Note that we
1390            use the w2k3 element size value of 54 */
1391         *r->out.num_entries = count - first;
1392         *r->out.num_entries = MIN(*r->out.num_entries,
1393                                  1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1394
1395         sam = talloc(mem_ctx, struct samr_SamArray);
1396         if (!sam) {
1397                 return NT_STATUS_NO_MEMORY;
1398         }
1399
1400         sam->entries = entries+first;
1401         sam->count = *r->out.num_entries;
1402
1403         *r->out.sam = sam;
1404
1405         if (first == count) {
1406                 return NT_STATUS_OK;
1407         }
1408
1409         if (*r->out.num_entries < count - first) {
1410                 *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
1411                 return STATUS_MORE_ENTRIES;
1412         }
1413
1414         return NT_STATUS_OK;
1415 }
1416
1417
1418 /*
1419   samr_CreateDomAlias
1420 */
1421 static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1422                        struct samr_CreateDomAlias *r)
1423 {
1424         struct samr_domain_state *d_state;
1425         struct samr_account_state *a_state;
1426         struct dcesrv_handle *h;
1427         const char *alias_name;
1428         struct dom_sid *sid;
1429         struct dcesrv_handle *a_handle;
1430         struct ldb_dn *dn;
1431         NTSTATUS status;
1432
1433         ZERO_STRUCTP(r->out.alias_handle);
1434         *r->out.rid = 0;
1435
1436         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1437
1438         d_state = h->data;
1439
1440         if (d_state->builtin) {
1441                 DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
1442                 return NT_STATUS_ACCESS_DENIED;
1443         }
1444
1445         alias_name = r->in.alias_name->string;
1446
1447         if (alias_name == NULL) {
1448                 return NT_STATUS_INVALID_PARAMETER;
1449         }
1450
1451         status = dsdb_add_domain_alias(d_state->sam_ctx, mem_ctx, alias_name, &sid, &dn);
1452         if (!NT_STATUS_IS_OK(status)) {
1453                 return status;
1454         }
1455
1456         a_state = talloc(mem_ctx, struct samr_account_state);
1457         if (!a_state) {
1458                 return NT_STATUS_NO_MEMORY;
1459         }
1460
1461         a_state->sam_ctx = d_state->sam_ctx;
1462         a_state->access_mask = r->in.access_mask;
1463         a_state->domain_state = talloc_reference(a_state, d_state);
1464         a_state->account_dn = talloc_steal(a_state, dn);
1465
1466         a_state->account_name = talloc_steal(a_state, alias_name);
1467
1468         /* create the policy handle */
1469         a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
1470         if (a_handle == NULL)
1471                 return NT_STATUS_NO_MEMORY;
1472
1473         a_handle->data = talloc_steal(a_handle, a_state);
1474
1475         *r->out.alias_handle = a_handle->wire_handle;
1476
1477         *r->out.rid = sid->sub_auths[sid->num_auths-1];
1478
1479         return NT_STATUS_OK;
1480 }
1481
1482
1483 /*
1484   samr_EnumDomainAliases
1485 */
1486 static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1487                        struct samr_EnumDomainAliases *r)
1488 {
1489         struct dcesrv_handle *h;
1490         struct samr_domain_state *d_state;
1491         struct ldb_message **res;
1492         int i, ldb_cnt;
1493         uint32_t first, count;
1494         struct samr_SamEntry *entries;
1495         const char * const attrs[] = { "objectSid", "sAMAccountName", NULL };
1496         struct samr_SamArray *sam;
1497
1498         *r->out.resume_handle = 0;
1499         *r->out.sam = NULL;
1500         *r->out.num_entries = 0;
1501
1502         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1503
1504         d_state = h->data;
1505
1506         /* search for all domain aliases in this domain. This could possibly be
1507            cached and resumed based on resume_key */
1508         ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1509                                       &res, attrs,
1510                                       d_state->domain_sid,
1511                                       "(&(|(grouptype=%d)(grouptype=%d)))"
1512                                       "(objectclass=group))",
1513                                       GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1514                                       GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1515         if (ldb_cnt < 0) {
1516                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1517         }
1518
1519         /* convert to SamEntry format */
1520         entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
1521         if (!entries) {
1522                 return NT_STATUS_NO_MEMORY;
1523         }
1524
1525         count = 0;
1526
1527         for (i=0;i<ldb_cnt;i++) {
1528                 struct dom_sid *alias_sid;
1529
1530                 alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
1531                                                  "objectSid");
1532
1533                 if (alias_sid == NULL) {
1534                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1535                 }
1536
1537                 entries[count].idx =
1538                         alias_sid->sub_auths[alias_sid->num_auths-1];
1539                 entries[count].name.string =
1540                         ldb_msg_find_attr_as_string(res[i], "sAMAccountName", "");
1541                 count += 1;
1542         }
1543
1544         /* sort the results by rid */
1545         TYPESAFE_QSORT(entries, count, compare_SamEntry);
1546
1547         /* find the first entry to return */
1548         for (first=0;
1549              first<count && entries[first].idx <= *r->in.resume_handle;
1550              first++) ;
1551
1552         /* return the rest, limit by max_size. Note that we
1553            use the w2k3 element size value of 54 */
1554         *r->out.num_entries = count - first;
1555         *r->out.num_entries = MIN(*r->out.num_entries,
1556                                   1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
1557
1558         sam = talloc(mem_ctx, struct samr_SamArray);
1559         if (!sam) {
1560                 return NT_STATUS_NO_MEMORY;
1561         }
1562
1563         sam->entries = entries+first;
1564         sam->count = *r->out.num_entries;
1565
1566         *r->out.sam = sam;
1567
1568         if (first == count) {
1569                 return NT_STATUS_OK;
1570         }
1571
1572         if (*r->out.num_entries < count - first) {
1573                 *r->out.resume_handle =
1574                         entries[first+*r->out.num_entries-1].idx;
1575                 return STATUS_MORE_ENTRIES;
1576         }
1577
1578         return NT_STATUS_OK;
1579 }
1580
1581
1582 /*
1583   samr_GetAliasMembership
1584 */
1585 static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1586                        struct samr_GetAliasMembership *r)
1587 {
1588         struct dcesrv_handle *h;
1589         struct samr_domain_state *d_state;
1590         char *filter;
1591         const char * const attrs[] = { "objectSid", NULL };
1592         struct ldb_message **res;
1593         uint32_t i;
1594         int count = 0;
1595
1596         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1597
1598         d_state = h->data;
1599
1600         filter = talloc_asprintf(mem_ctx,
1601                                  "(&(|(grouptype=%d)(grouptype=%d))"
1602                                  "(objectclass=group)(|",
1603                                  GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
1604                                  GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
1605         if (filter == NULL) {
1606                 return NT_STATUS_NO_MEMORY;
1607         }
1608
1609         for (i=0; i<r->in.sids->num_sids; i++) {
1610                 struct dom_sid_buf buf;
1611
1612                 filter = talloc_asprintf_append(
1613                         filter,
1614                         "(member=<SID=%s>)",
1615                         dom_sid_str_buf(r->in.sids->sids[i].sid, &buf));
1616
1617                 if (filter == NULL) {
1618                         return NT_STATUS_NO_MEMORY;
1619                 }
1620         }
1621
1622         /* Find out if we had at least one valid member SID passed - otherwise
1623          * just skip the search. */
1624         if (strstr(filter, "member") != NULL) {
1625                 count = samdb_search_domain(d_state->sam_ctx, mem_ctx, NULL,
1626                                             &res, attrs, d_state->domain_sid,
1627                                             "%s))", filter);
1628                 if (count < 0) {
1629                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1630                 }
1631         }
1632
1633         r->out.rids->count = 0;
1634         r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
1635         if (r->out.rids->ids == NULL)
1636                 return NT_STATUS_NO_MEMORY;
1637
1638         for (i=0; i<count; i++) {
1639                 struct dom_sid *alias_sid;
1640
1641                 alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
1642                 if (alias_sid == NULL) {
1643                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
1644                 }
1645
1646                 r->out.rids->ids[r->out.rids->count] =
1647                         alias_sid->sub_auths[alias_sid->num_auths-1];
1648                 r->out.rids->count += 1;
1649         }
1650
1651         return NT_STATUS_OK;
1652 }
1653
1654
1655 /*
1656   samr_LookupNames
1657 */
1658 static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1659                                  struct samr_LookupNames *r)
1660 {
1661         struct dcesrv_handle *h;
1662         struct samr_domain_state *d_state;
1663         uint32_t i, num_mapped;
1664         NTSTATUS status = NT_STATUS_OK;
1665         const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
1666         int count;
1667
1668         ZERO_STRUCTP(r->out.rids);
1669         ZERO_STRUCTP(r->out.types);
1670
1671         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1672
1673         d_state = h->data;
1674
1675         if (r->in.num_names == 0) {
1676                 return NT_STATUS_OK;
1677         }
1678
1679         r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1680         r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
1681         if (!r->out.rids->ids || !r->out.types->ids) {
1682                 return NT_STATUS_NO_MEMORY;
1683         }
1684         r->out.rids->count = r->in.num_names;
1685         r->out.types->count = r->in.num_names;
1686
1687         num_mapped = 0;
1688
1689         for (i=0;i<r->in.num_names;i++) {
1690                 struct ldb_message **res;
1691                 struct dom_sid *sid;
1692                 uint32_t atype, rtype;
1693
1694                 r->out.rids->ids[i] = 0;
1695                 r->out.types->ids[i] = SID_NAME_UNKNOWN;
1696
1697                 count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
1698                                      "sAMAccountName=%s",
1699                                      ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
1700                 if (count != 1) {
1701                         status = STATUS_SOME_UNMAPPED;
1702                         continue;
1703                 }
1704
1705                 sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
1706                 if (sid == NULL) {
1707                         status = STATUS_SOME_UNMAPPED;
1708                         continue;
1709                 }
1710
1711                 atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
1712                 if (atype == 0) {
1713                         status = STATUS_SOME_UNMAPPED;
1714                         continue;
1715                 }
1716
1717                 rtype = ds_atype_map(atype);
1718
1719                 if (rtype == SID_NAME_UNKNOWN) {
1720                         status = STATUS_SOME_UNMAPPED;
1721                         continue;
1722                 }
1723
1724                 r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
1725                 r->out.types->ids[i] = rtype;
1726                 num_mapped++;
1727         }
1728
1729         if (num_mapped == 0) {
1730                 return NT_STATUS_NONE_MAPPED;
1731         }
1732         return status;
1733 }
1734
1735
1736 /*
1737   samr_LookupRids
1738 */
1739 static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1740                        struct samr_LookupRids *r)
1741 {
1742         NTSTATUS status;
1743         struct dcesrv_handle *h;
1744         struct samr_domain_state *d_state;
1745         const char **names;
1746         struct lsa_String *lsa_names;
1747         enum lsa_SidType *ids;
1748
1749         ZERO_STRUCTP(r->out.names);
1750         ZERO_STRUCTP(r->out.types);
1751
1752         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1753
1754         d_state = h->data;
1755
1756         if (r->in.num_rids == 0)
1757                 return NT_STATUS_OK;
1758
1759         lsa_names = talloc_zero_array(mem_ctx, struct lsa_String, r->in.num_rids);
1760         names = talloc_zero_array(mem_ctx, const char *, r->in.num_rids);
1761         ids = talloc_zero_array(mem_ctx, enum lsa_SidType, r->in.num_rids);
1762
1763         if ((lsa_names == NULL) || (names == NULL) || (ids == NULL))
1764                 return NT_STATUS_NO_MEMORY;
1765
1766         r->out.names->names = lsa_names;
1767         r->out.names->count = r->in.num_rids;
1768
1769         r->out.types->ids = (uint32_t *) ids;
1770         r->out.types->count = r->in.num_rids;
1771
1772         status = dsdb_lookup_rids(d_state->sam_ctx, mem_ctx, d_state->domain_sid,
1773                                   r->in.num_rids, r->in.rids, names, ids);
1774         if (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) || NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
1775                 uint32_t i;
1776                 for (i = 0; i < r->in.num_rids; i++) {
1777                         lsa_names[i].string = names[i];
1778                 }
1779         }
1780         return status;
1781 }
1782
1783
1784 /*
1785   samr_OpenGroup
1786 */
1787 static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1788                        struct samr_OpenGroup *r)
1789 {
1790         struct samr_domain_state *d_state;
1791         struct samr_account_state *a_state;
1792         struct dcesrv_handle *h;
1793         const char *groupname;
1794         struct dom_sid *sid;
1795         struct ldb_message **msgs;
1796         struct dcesrv_handle *g_handle;
1797         const char * const attrs[2] = { "sAMAccountName", NULL };
1798         int ret;
1799
1800         ZERO_STRUCTP(r->out.group_handle);
1801
1802         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
1803
1804         d_state = h->data;
1805
1806         /* form the group SID */
1807         sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
1808         if (!sid) {
1809                 return NT_STATUS_NO_MEMORY;
1810         }
1811
1812         /* search for the group record */
1813         if (d_state->builtin) {
1814                 ret = gendb_search(d_state->sam_ctx,
1815                                    mem_ctx, d_state->domain_dn, &msgs, attrs,
1816                                    "(&(objectSid=%s)(objectClass=group)"
1817                                    "(groupType=%d))",
1818                                    ldap_encode_ndr_dom_sid(mem_ctx, sid),
1819                                    GTYPE_SECURITY_BUILTIN_LOCAL_GROUP);
1820         } else {
1821                 ret = gendb_search(d_state->sam_ctx,
1822                                    mem_ctx, d_state->domain_dn, &msgs, attrs,
1823                                    "(&(objectSid=%s)(objectClass=group)"
1824                                    "(|(groupType=%d)(groupType=%d)))",
1825                                    ldap_encode_ndr_dom_sid(mem_ctx, sid),
1826                                    GTYPE_SECURITY_UNIVERSAL_GROUP,
1827                                    GTYPE_SECURITY_GLOBAL_GROUP);
1828         }
1829         if (ret == 0) {
1830                 return NT_STATUS_NO_SUCH_GROUP;
1831         }
1832         if (ret != 1) {
1833                 DEBUG(0,("Found %d records matching sid %s\n",
1834                          ret, dom_sid_string(mem_ctx, sid)));
1835                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1836         }
1837
1838         groupname = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
1839         if (groupname == NULL) {
1840                 DEBUG(0,("sAMAccountName field missing for sid %s\n",
1841                          dom_sid_string(mem_ctx, sid)));
1842                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1843         }
1844
1845         a_state = talloc(mem_ctx, struct samr_account_state);
1846         if (!a_state) {
1847                 return NT_STATUS_NO_MEMORY;
1848         }
1849         a_state->sam_ctx = d_state->sam_ctx;
1850         a_state->access_mask = r->in.access_mask;
1851         a_state->domain_state = talloc_reference(a_state, d_state);
1852         a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
1853         a_state->account_sid = talloc_steal(a_state, sid);
1854         a_state->account_name = talloc_strdup(a_state, groupname);
1855         if (!a_state->account_name) {
1856                 return NT_STATUS_NO_MEMORY;
1857         }
1858
1859         /* create the policy handle */
1860         g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
1861         if (!g_handle) {
1862                 return NT_STATUS_NO_MEMORY;
1863         }
1864
1865         g_handle->data = talloc_steal(g_handle, a_state);
1866
1867         *r->out.group_handle = g_handle->wire_handle;
1868
1869         return NT_STATUS_OK;
1870 }
1871
1872 /*
1873   samr_QueryGroupInfo
1874 */
1875 static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1876                        struct samr_QueryGroupInfo *r)
1877 {
1878         struct dcesrv_handle *h;
1879         struct samr_account_state *a_state;
1880         struct ldb_message *msg, **res;
1881         const char * const attrs[4] = { "sAMAccountName", "description",
1882                                         "numMembers", NULL };
1883         int ret;
1884         union samr_GroupInfo *info;
1885
1886         *r->out.info = NULL;
1887
1888         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1889
1890         a_state = h->data;
1891
1892         /* pull all the group attributes */
1893         ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
1894                               a_state->account_dn, &res, attrs);
1895         if (ret == 0) {
1896                 return NT_STATUS_NO_SUCH_GROUP;
1897         }
1898         if (ret != 1) {
1899                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1900         }
1901         msg = res[0];
1902
1903         /* allocate the info structure */
1904         info = talloc_zero(mem_ctx, union samr_GroupInfo);
1905         if (info == NULL) {
1906                 return NT_STATUS_NO_MEMORY;
1907         }
1908
1909         /* Fill in the level */
1910         switch (r->in.level) {
1911         case GROUPINFOALL:
1912                 QUERY_STRING(msg, all.name,        "sAMAccountName");
1913                 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1914                 QUERY_UINT  (msg, all.num_members,      "numMembers")
1915                 QUERY_STRING(msg, all.description, "description");
1916                 break;
1917         case GROUPINFONAME:
1918                 QUERY_STRING(msg, name,            "sAMAccountName");
1919                 break;
1920         case GROUPINFOATTRIBUTES:
1921                 info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1922                 break;
1923         case GROUPINFODESCRIPTION:
1924                 QUERY_STRING(msg, description, "description");
1925                 break;
1926         case GROUPINFOALL2:
1927                 QUERY_STRING(msg, all2.name,        "sAMAccountName");
1928                 info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
1929                 QUERY_UINT  (msg, all2.num_members,      "numMembers")
1930                 QUERY_STRING(msg, all2.description, "description");
1931                 break;
1932         default:
1933                 talloc_free(info);
1934                 return NT_STATUS_INVALID_INFO_CLASS;
1935         }
1936
1937         *r->out.info = info;
1938
1939         return NT_STATUS_OK;
1940 }
1941
1942
1943 /*
1944   samr_SetGroupInfo
1945 */
1946 static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1947                                   struct samr_SetGroupInfo *r)
1948 {
1949         struct dcesrv_handle *h;
1950         struct samr_account_state *g_state;
1951         struct ldb_message *msg;
1952         int ret;
1953
1954         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
1955
1956         g_state = h->data;
1957
1958         msg = ldb_msg_new(mem_ctx);
1959         if (msg == NULL) {
1960                 return NT_STATUS_NO_MEMORY;
1961         }
1962
1963         msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
1964         if (!msg->dn) {
1965                 return NT_STATUS_NO_MEMORY;
1966         }
1967
1968         switch (r->in.level) {
1969         case GROUPINFODESCRIPTION:
1970                 SET_STRING(msg, description,         "description");
1971                 break;
1972         case GROUPINFONAME:
1973                 /* On W2k3 this does not change the name, it changes the
1974                  * sAMAccountName attribute */
1975                 SET_STRING(msg, name,                "sAMAccountName");
1976                 break;
1977         case GROUPINFOATTRIBUTES:
1978                 /* This does not do anything obviously visible in W2k3 LDAP */
1979                 return NT_STATUS_OK;
1980         default:
1981                 return NT_STATUS_INVALID_INFO_CLASS;
1982         }
1983
1984         /* modify the samdb record */
1985         ret = ldb_modify(g_state->sam_ctx, msg);
1986         if (ret != LDB_SUCCESS) {
1987                 return dsdb_ldb_err_to_ntstatus(ret);
1988         }
1989
1990         return NT_STATUS_OK;
1991 }
1992
1993
1994 /*
1995   samr_AddGroupMember
1996 */
1997 static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
1998                        struct samr_AddGroupMember *r)
1999 {
2000         struct dcesrv_handle *h;
2001         struct samr_account_state *a_state;
2002         struct samr_domain_state *d_state;
2003         struct ldb_message *mod;
2004         struct dom_sid *membersid;
2005         const char *memberdn;
2006         struct ldb_result *res;
2007         const char * const attrs[] = { NULL };
2008         int ret;
2009
2010         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2011
2012         a_state = h->data;
2013         d_state = a_state->domain_state;
2014
2015         membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2016         if (membersid == NULL) {
2017                 return NT_STATUS_NO_MEMORY;
2018         }
2019
2020         /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2021         ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2022                          d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2023                          "(objectSid=%s)",
2024                          ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2025
2026         if (ret != LDB_SUCCESS) {
2027                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2028         }
2029
2030         if (res->count == 0) {
2031                 return NT_STATUS_NO_SUCH_USER;
2032         }
2033
2034         if (res->count > 1) {
2035                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2036         }
2037
2038         memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2039
2040         if (memberdn == NULL)
2041                 return NT_STATUS_NO_MEMORY;
2042
2043         mod = ldb_msg_new(mem_ctx);
2044         if (mod == NULL) {
2045                 return NT_STATUS_NO_MEMORY;
2046         }
2047
2048         mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2049
2050         ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2051                                                                 memberdn);
2052         if (ret != LDB_SUCCESS) {
2053                 return dsdb_ldb_err_to_ntstatus(ret);
2054         }
2055
2056         ret = ldb_modify(a_state->sam_ctx, mod);
2057         switch (ret) {
2058         case LDB_SUCCESS:
2059                 return NT_STATUS_OK;
2060         case LDB_ERR_ENTRY_ALREADY_EXISTS:
2061                 return NT_STATUS_MEMBER_IN_GROUP;
2062         case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2063                 return NT_STATUS_ACCESS_DENIED;
2064         default:
2065                 return dsdb_ldb_err_to_ntstatus(ret);
2066         }
2067 }
2068
2069
2070 /*
2071   samr_DeleteDomainGroup
2072 */
2073 static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2074                        struct samr_DeleteDomainGroup *r)
2075 {
2076         struct dcesrv_handle *h;
2077         struct samr_account_state *a_state;
2078         int ret;
2079
2080         *r->out.group_handle = *r->in.group_handle;
2081
2082         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2083
2084         a_state = h->data;
2085
2086         ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2087         if (ret != LDB_SUCCESS) {
2088                 return dsdb_ldb_err_to_ntstatus(ret);
2089         }
2090
2091         talloc_free(h);
2092         ZERO_STRUCTP(r->out.group_handle);
2093
2094         return NT_STATUS_OK;
2095 }
2096
2097
2098 /*
2099   samr_DeleteGroupMember
2100 */
2101 static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2102                        struct samr_DeleteGroupMember *r)
2103 {
2104         struct dcesrv_handle *h;
2105         struct samr_account_state *a_state;
2106         struct samr_domain_state *d_state;
2107         struct ldb_message *mod;
2108         struct dom_sid *membersid;
2109         const char *memberdn;
2110         struct ldb_result *res;
2111         const char * const attrs[] = { NULL };
2112         int ret;
2113
2114         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2115
2116         a_state = h->data;
2117         d_state = a_state->domain_state;
2118
2119         membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2120         if (membersid == NULL) {
2121                 return NT_STATUS_NO_MEMORY;
2122         }
2123
2124         /* according to MS-SAMR 3.1.5.8.2 all type of accounts are accepted */
2125         ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
2126                          d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
2127                          "(objectSid=%s)",
2128                          ldap_encode_ndr_dom_sid(mem_ctx, membersid));
2129
2130         if (ret != LDB_SUCCESS) {
2131                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2132         }
2133
2134         if (res->count == 0) {
2135                 return NT_STATUS_NO_SUCH_USER;
2136         }
2137
2138         if (res->count > 1) {
2139                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2140         }
2141
2142         memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
2143
2144         if (memberdn == NULL)
2145                 return NT_STATUS_NO_MEMORY;
2146
2147         mod = ldb_msg_new(mem_ctx);
2148         if (mod == NULL) {
2149                 return NT_STATUS_NO_MEMORY;
2150         }
2151
2152         mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2153
2154         ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2155                                                                 memberdn);
2156         if (ret != LDB_SUCCESS) {
2157                 return NT_STATUS_NO_MEMORY;
2158         }
2159
2160         ret = ldb_modify(a_state->sam_ctx, mod);
2161         switch (ret) {
2162         case LDB_SUCCESS:
2163                 return NT_STATUS_OK;
2164         case LDB_ERR_UNWILLING_TO_PERFORM:
2165                 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2166         case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2167                 return NT_STATUS_ACCESS_DENIED;
2168         default:
2169                 return dsdb_ldb_err_to_ntstatus(ret);
2170         }
2171 }
2172
2173
2174 /*
2175   samr_QueryGroupMember
2176 */
2177 static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2178                                       struct samr_QueryGroupMember *r)
2179 {
2180         struct dcesrv_handle *h;
2181         struct samr_account_state *a_state;
2182         struct samr_domain_state *d_state;
2183         struct samr_RidAttrArray *array;
2184         unsigned int i, num_members;
2185         struct dom_sid *members;
2186         NTSTATUS status;
2187
2188         DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
2189
2190         a_state = h->data;
2191         d_state = a_state->domain_state;
2192
2193         status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2194                                      a_state->account_dn, &members,
2195                                      &num_members);
2196         if (!NT_STATUS_IS_OK(status)) {
2197                 return status;
2198         }
2199
2200         array = talloc_zero(mem_ctx, struct samr_RidAttrArray);
2201         if (array == NULL) {
2202                 return NT_STATUS_NO_MEMORY;
2203         }
2204
2205         if (num_members == 0) {
2206                 *r->out.rids = array;
2207
2208                 return NT_STATUS_OK;
2209         }
2210
2211         array->rids = talloc_array(array, uint32_t, num_members);
2212         if (array->rids == NULL) {
2213                 return NT_STATUS_NO_MEMORY;
2214         }
2215
2216         array->attributes = talloc_array(array, uint32_t, num_members);
2217         if (array->attributes == NULL) {
2218                 return NT_STATUS_NO_MEMORY;
2219         }
2220
2221         array->count = 0;
2222         for (i=0; i<num_members; i++) {
2223                 if (!dom_sid_in_domain(d_state->domain_sid, &members[i])) {
2224                         continue;
2225                 }
2226
2227                 status = dom_sid_split_rid(NULL, &members[i], NULL,
2228                                            &array->rids[array->count]);
2229                 if (!NT_STATUS_IS_OK(status)) {
2230                         return status;
2231                 }
2232
2233                 array->attributes[array->count] = SE_GROUP_MANDATORY |
2234                                                   SE_GROUP_ENABLED_BY_DEFAULT |
2235                                                   SE_GROUP_ENABLED;
2236                 array->count++;
2237         }
2238
2239         *r->out.rids = array;
2240
2241         return NT_STATUS_OK;
2242 }
2243
2244
2245 /*
2246   samr_SetMemberAttributesOfGroup
2247 */
2248 static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2249                        struct samr_SetMemberAttributesOfGroup *r)
2250 {
2251         DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
2252 }
2253
2254
2255 /*
2256   samr_OpenAlias
2257 */
2258 static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2259                        struct samr_OpenAlias *r)
2260 {
2261         struct samr_domain_state *d_state;
2262         struct samr_account_state *a_state;
2263         struct dcesrv_handle *h;
2264         const char *alias_name;
2265         struct dom_sid *sid;
2266         struct ldb_message **msgs;
2267         struct dcesrv_handle *g_handle;
2268         const char * const attrs[2] = { "sAMAccountName", NULL };
2269         int ret;
2270
2271         ZERO_STRUCTP(r->out.alias_handle);
2272
2273         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2274
2275         d_state = h->data;
2276
2277         /* form the alias SID */
2278         sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2279         if (sid == NULL)
2280                 return NT_STATUS_NO_MEMORY;
2281
2282         /* search for the group record */
2283         ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL, &msgs, attrs,
2284                            "(&(objectSid=%s)(objectclass=group)"
2285                            "(|(grouptype=%d)(grouptype=%d)))",
2286                            ldap_encode_ndr_dom_sid(mem_ctx, sid),
2287                            GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
2288                            GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
2289         if (ret == 0) {
2290                 return NT_STATUS_NO_SUCH_ALIAS;
2291         }
2292         if (ret != 1) {
2293                 DEBUG(0,("Found %d records matching sid %s\n",
2294                          ret, dom_sid_string(mem_ctx, sid)));
2295                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2296         }
2297
2298         alias_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2299         if (alias_name == NULL) {
2300                 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2301                          dom_sid_string(mem_ctx, sid)));
2302                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2303         }
2304
2305         a_state = talloc(mem_ctx, struct samr_account_state);
2306         if (!a_state) {
2307                 return NT_STATUS_NO_MEMORY;
2308         }
2309         a_state->sam_ctx = d_state->sam_ctx;
2310         a_state->access_mask = r->in.access_mask;
2311         a_state->domain_state = talloc_reference(a_state, d_state);
2312         a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2313         a_state->account_sid = talloc_steal(a_state, sid);
2314         a_state->account_name = talloc_strdup(a_state, alias_name);
2315         if (!a_state->account_name) {
2316                 return NT_STATUS_NO_MEMORY;
2317         }
2318
2319         /* create the policy handle */
2320         g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
2321         if (!g_handle) {
2322                 return NT_STATUS_NO_MEMORY;
2323         }
2324
2325         g_handle->data = talloc_steal(g_handle, a_state);
2326
2327         *r->out.alias_handle = g_handle->wire_handle;
2328
2329         return NT_STATUS_OK;
2330 }
2331
2332
2333 /*
2334   samr_QueryAliasInfo
2335 */
2336 static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2337                        struct samr_QueryAliasInfo *r)
2338 {
2339         struct dcesrv_handle *h;
2340         struct samr_account_state *a_state;
2341         struct ldb_message *msg, **res;
2342         const char * const attrs[4] = { "sAMAccountName", "description",
2343                                         "numMembers", NULL };
2344         int ret;
2345         union samr_AliasInfo *info;
2346
2347         *r->out.info = NULL;
2348
2349         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2350
2351         a_state = h->data;
2352
2353         /* pull all the alias attributes */
2354         ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2355                               a_state->account_dn, &res, attrs);
2356         if (ret == 0) {
2357                 return NT_STATUS_NO_SUCH_ALIAS;
2358         }
2359         if (ret != 1) {
2360                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2361         }
2362         msg = res[0];
2363
2364         /* allocate the info structure */
2365         info = talloc_zero(mem_ctx, union samr_AliasInfo);
2366         if (info == NULL) {
2367                 return NT_STATUS_NO_MEMORY;
2368         }
2369
2370         switch(r->in.level) {
2371         case ALIASINFOALL:
2372                 QUERY_STRING(msg, all.name, "sAMAccountName");
2373                 QUERY_UINT  (msg, all.num_members, "numMembers");
2374                 QUERY_STRING(msg, all.description, "description");
2375                 break;
2376         case ALIASINFONAME:
2377                 QUERY_STRING(msg, name, "sAMAccountName");
2378                 break;
2379         case ALIASINFODESCRIPTION:
2380                 QUERY_STRING(msg, description, "description");
2381                 break;
2382         default:
2383                 talloc_free(info);
2384                 return NT_STATUS_INVALID_INFO_CLASS;
2385         }
2386
2387         *r->out.info = info;
2388
2389         return NT_STATUS_OK;
2390 }
2391
2392
2393 /*
2394   samr_SetAliasInfo
2395 */
2396 static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2397                        struct samr_SetAliasInfo *r)
2398 {
2399         struct dcesrv_handle *h;
2400         struct samr_account_state *a_state;
2401         struct ldb_message *msg;
2402         int ret;
2403
2404         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2405
2406         a_state = h->data;
2407
2408         msg = ldb_msg_new(mem_ctx);
2409         if (msg == NULL) {
2410                 return NT_STATUS_NO_MEMORY;
2411         }
2412
2413         msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
2414         if (!msg->dn) {
2415                 return NT_STATUS_NO_MEMORY;
2416         }
2417
2418         switch (r->in.level) {
2419         case ALIASINFODESCRIPTION:
2420                 SET_STRING(msg, description,         "description");
2421                 break;
2422         case ALIASINFONAME:
2423                 /* On W2k3 this does not change the name, it changes the
2424                  * sAMAccountName attribute */
2425                 SET_STRING(msg, name,                "sAMAccountName");
2426                 break;
2427         default:
2428                 return NT_STATUS_INVALID_INFO_CLASS;
2429         }
2430
2431         /* modify the samdb record */
2432         ret = ldb_modify(a_state->sam_ctx, msg);
2433         if (ret != LDB_SUCCESS) {
2434                 return dsdb_ldb_err_to_ntstatus(ret);
2435         }
2436
2437         return NT_STATUS_OK;
2438 }
2439
2440
2441 /*
2442   samr_DeleteDomAlias
2443 */
2444 static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2445                        struct samr_DeleteDomAlias *r)
2446 {
2447         struct dcesrv_handle *h;
2448         struct samr_account_state *a_state;
2449         int ret;
2450
2451         *r->out.alias_handle = *r->in.alias_handle;
2452
2453         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2454
2455         a_state = h->data;
2456
2457         ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2458         if (ret != LDB_SUCCESS) {
2459                 return dsdb_ldb_err_to_ntstatus(ret);
2460         }
2461
2462         talloc_free(h);
2463         ZERO_STRUCTP(r->out.alias_handle);
2464
2465         return NT_STATUS_OK;
2466 }
2467
2468
2469 /*
2470   samr_AddAliasMember
2471 */
2472 static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2473                        struct samr_AddAliasMember *r)
2474 {
2475         struct dcesrv_handle *h;
2476         struct samr_account_state *a_state;
2477         struct samr_domain_state *d_state;
2478         struct ldb_message *mod;
2479         struct ldb_message **msgs;
2480         const char * const attrs[] = { NULL };
2481         struct ldb_dn *memberdn = NULL;
2482         int ret;
2483         NTSTATUS status;
2484
2485         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2486
2487         a_state = h->data;
2488         d_state = a_state->domain_state;
2489
2490         ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
2491                            &msgs, attrs, "(objectsid=%s)",
2492                            ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2493
2494         if (ret == 1) {
2495                 memberdn = msgs[0]->dn;
2496         } else if (ret == 0) {
2497                 status = samdb_create_foreign_security_principal(
2498                         d_state->sam_ctx, mem_ctx, r->in.sid, &memberdn);
2499                 if (!NT_STATUS_IS_OK(status)) {
2500                         return status;
2501                 }
2502         } else {
2503                 DEBUG(0,("Found %d records matching sid %s\n",
2504                          ret, dom_sid_string(mem_ctx, r->in.sid)));
2505                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2506         }
2507
2508         if (memberdn == NULL) {
2509                 DEBUG(0, ("Could not find memberdn\n"));
2510                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2511         }
2512
2513         mod = ldb_msg_new(mem_ctx);
2514         if (mod == NULL) {
2515                 return NT_STATUS_NO_MEMORY;
2516         }
2517
2518         mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2519
2520         ret = samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
2521                                  ldb_dn_alloc_linearized(mem_ctx, memberdn));
2522         if (ret != LDB_SUCCESS) {
2523                 return dsdb_ldb_err_to_ntstatus(ret);
2524         }
2525
2526         ret = ldb_modify(a_state->sam_ctx, mod);
2527         switch (ret) {
2528         case LDB_SUCCESS:
2529                 return NT_STATUS_OK;
2530         case LDB_ERR_ENTRY_ALREADY_EXISTS:
2531                 return NT_STATUS_MEMBER_IN_GROUP;
2532         case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2533                 return NT_STATUS_ACCESS_DENIED;
2534         default:
2535                 return dsdb_ldb_err_to_ntstatus(ret);
2536         }
2537 }
2538
2539
2540 /*
2541   samr_DeleteAliasMember
2542 */
2543 static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2544                        struct samr_DeleteAliasMember *r)
2545 {
2546         struct dcesrv_handle *h;
2547         struct samr_account_state *a_state;
2548         struct samr_domain_state *d_state;
2549         struct ldb_message *mod;
2550         const char *memberdn;
2551         int ret;
2552
2553         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2554
2555         a_state = h->data;
2556         d_state = a_state->domain_state;
2557
2558         memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
2559                                        "distinguishedName", "(objectSid=%s)",
2560                                        ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
2561         if (memberdn == NULL) {
2562                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2563         }
2564
2565         mod = ldb_msg_new(mem_ctx);
2566         if (mod == NULL) {
2567                 return NT_STATUS_NO_MEMORY;
2568         }
2569
2570         mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
2571
2572         ret = samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
2573                                                                  memberdn);
2574         if (ret != LDB_SUCCESS) {
2575                 return dsdb_ldb_err_to_ntstatus(ret);
2576         }
2577
2578         ret = ldb_modify(a_state->sam_ctx, mod);
2579         switch (ret) {
2580         case LDB_SUCCESS:
2581                 return NT_STATUS_OK;
2582         case LDB_ERR_UNWILLING_TO_PERFORM:
2583                 return NT_STATUS_MEMBER_NOT_IN_GROUP;
2584         case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
2585                 return NT_STATUS_ACCESS_DENIED;
2586         default:
2587                 return dsdb_ldb_err_to_ntstatus(ret);
2588         }
2589 }
2590
2591
2592 /*
2593   samr_GetMembersInAlias
2594 */
2595 static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2596                        struct samr_GetMembersInAlias *r)
2597 {
2598         struct dcesrv_handle *h;
2599         struct samr_account_state *a_state;
2600         struct samr_domain_state *d_state;
2601         struct lsa_SidPtr *array;
2602         unsigned int i, num_members;
2603         struct dom_sid *members;
2604         NTSTATUS status;
2605
2606         DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
2607
2608         a_state = h->data;
2609         d_state = a_state->domain_state;
2610
2611         status = dsdb_enum_group_mem(d_state->sam_ctx, mem_ctx,
2612                                      a_state->account_dn, &members,
2613                                      &num_members);
2614         if (!NT_STATUS_IS_OK(status)) {
2615                 return status;
2616         }
2617
2618         if (num_members == 0) {
2619                 r->out.sids->sids = NULL;
2620
2621                 return NT_STATUS_OK;
2622         }
2623
2624         array = talloc_array(mem_ctx, struct lsa_SidPtr, num_members);
2625         if (array == NULL) {
2626                 return NT_STATUS_NO_MEMORY;
2627         }
2628
2629         for (i=0; i<num_members; i++) {
2630                 array[i].sid = &members[i];
2631         }
2632
2633         r->out.sids->num_sids = num_members;
2634         r->out.sids->sids = array;
2635
2636         return NT_STATUS_OK;
2637 }
2638
2639 /*
2640   samr_OpenUser
2641 */
2642 static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2643                               struct samr_OpenUser *r)
2644 {
2645         struct samr_domain_state *d_state;
2646         struct samr_account_state *a_state;
2647         struct dcesrv_handle *h;
2648         const char *account_name;
2649         struct dom_sid *sid;
2650         struct ldb_message **msgs;
2651         struct dcesrv_handle *u_handle;
2652         const char * const attrs[2] = { "sAMAccountName", NULL };
2653         int ret;
2654
2655         ZERO_STRUCTP(r->out.user_handle);
2656
2657         DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
2658
2659         d_state = h->data;
2660
2661         /* form the users SID */
2662         sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
2663         if (!sid) {
2664                 return NT_STATUS_NO_MEMORY;
2665         }
2666
2667         /* search for the user record */
2668         ret = gendb_search(d_state->sam_ctx,
2669                            mem_ctx, d_state->domain_dn, &msgs, attrs,
2670                            "(&(objectSid=%s)(objectclass=user))",
2671                            ldap_encode_ndr_dom_sid(mem_ctx, sid));
2672         if (ret == 0) {
2673                 return NT_STATUS_NO_SUCH_USER;
2674         }
2675         if (ret != 1) {
2676                 DEBUG(0,("Found %d records matching sid %s\n", ret,
2677                          dom_sid_string(mem_ctx, sid)));
2678                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2679         }
2680
2681         account_name = ldb_msg_find_attr_as_string(msgs[0], "sAMAccountName", NULL);
2682         if (account_name == NULL) {
2683                 DEBUG(0,("sAMAccountName field missing for sid %s\n",
2684                          dom_sid_string(mem_ctx, sid)));
2685                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
2686         }
2687
2688         a_state = talloc(mem_ctx, struct samr_account_state);
2689         if (!a_state) {
2690                 return NT_STATUS_NO_MEMORY;
2691         }
2692         a_state->sam_ctx = d_state->sam_ctx;
2693         a_state->access_mask = r->in.access_mask;
2694         a_state->domain_state = talloc_reference(a_state, d_state);
2695         a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
2696         a_state->account_sid = talloc_steal(a_state, sid);
2697         a_state->account_name = talloc_strdup(a_state, account_name);
2698         if (!a_state->account_name) {
2699                 return NT_STATUS_NO_MEMORY;
2700         }
2701
2702         /* create the policy handle */
2703         u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
2704         if (!u_handle) {
2705                 return NT_STATUS_NO_MEMORY;
2706         }
2707
2708         u_handle->data = talloc_steal(u_handle, a_state);
2709
2710         *r->out.user_handle = u_handle->wire_handle;
2711
2712         return NT_STATUS_OK;
2713
2714 }
2715
2716
2717 /*
2718   samr_DeleteUser
2719 */
2720 static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2721                                 struct samr_DeleteUser *r)
2722 {
2723         struct dcesrv_handle *h;
2724         struct samr_account_state *a_state;
2725         int ret;
2726
2727         *r->out.user_handle = *r->in.user_handle;
2728
2729         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2730
2731         a_state = h->data;
2732
2733         ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
2734         if (ret != LDB_SUCCESS) {
2735                 DEBUG(1, ("Failed to delete user: %s: %s\n",
2736                           ldb_dn_get_linearized(a_state->account_dn),
2737                           ldb_errstring(a_state->sam_ctx)));
2738                 return dsdb_ldb_err_to_ntstatus(ret);
2739         }
2740
2741         talloc_free(h);
2742         ZERO_STRUCTP(r->out.user_handle);
2743
2744         return NT_STATUS_OK;
2745 }
2746
2747
2748 /*
2749   samr_QueryUserInfo
2750 */
2751 static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
2752                                    struct samr_QueryUserInfo *r)
2753 {
2754         struct dcesrv_handle *h;
2755         struct samr_account_state *a_state;
2756         struct ldb_message *msg, **res;
2757         int ret;
2758         struct ldb_context *sam_ctx;
2759
2760         const char * const *attrs = NULL;
2761         union samr_UserInfo *info;
2762
2763         NTSTATUS status;
2764
2765         *r->out.info = NULL;
2766
2767         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
2768
2769         a_state = h->data;
2770         sam_ctx = a_state->sam_ctx;
2771
2772         /* fill in the reply */
2773         switch (r->in.level) {
2774         case 1:
2775         {
2776                 static const char * const attrs2[] = {"sAMAccountName",
2777                                                       "displayName",
2778                                                       "primaryGroupID",
2779                                                       "description",
2780                                                       "comment",
2781                                                       NULL};
2782                 attrs = attrs2;
2783                 break;
2784         }
2785         case 2:
2786         {
2787                 static const char * const attrs2[] = {"comment",
2788                                                       "countryCode",
2789                                                       "codePage",
2790                                                       NULL};
2791                 attrs = attrs2;
2792                 break;
2793         }
2794         case 3:
2795         {
2796                 static const char * const attrs2[] = {"sAMAccountName",
2797                                                       "displayName",
2798                                                       "objectSid",
2799                                                       "primaryGroupID",
2800                                                       "homeDirectory",
2801                                                       "homeDrive",
2802                                                       "scriptPath",
2803                                                       "profilePath",
2804                                                       "userWorkstations",
2805                                                       "lastLogon",
2806                                                       "lastLogoff",
2807                                                       "pwdLastSet",
2808                                                       "msDS-UserPasswordExpiryTimeComputed",
2809                                                       "logonHours",
2810                                                       "badPwdCount",
2811                                                       "badPasswordTime",
2812                                                       "logonCount",
2813                                                       "userAccountControl",
2814                                                       "msDS-User-Account-Control-Computed",
2815                                                       NULL};
2816                 attrs = attrs2;
2817                 break;
2818         }
2819         case 4:
2820         {
2821                 static const char * const attrs2[] = {"logonHours",
2822                                                       NULL};
2823                 attrs = attrs2;
2824                 break;
2825         }
2826         case 5:
2827         {
2828                 static const char * const attrs2[] = {"sAMAccountName",
2829                                                       "displayName",
2830                                                       "objectSid",
2831                                                       "primaryGroupID",
2832                                                       "homeDirectory",
2833                                                       "homeDrive",
2834                                                       "scriptPath",
2835                                                       "profilePath",
2836                                                       "description",
2837                                                       "userWorkstations",
2838                                                       "lastLogon",
2839                                                       "lastLogoff",
2840                                                       "logonHours",
2841                                                       "badPwdCount",
2842                                                       "badPasswordTime",
2843                                                       "logonCount",
2844                                                       "pwdLastSet",
2845                                                       "msDS-ResultantPSO",
2846                                                       "msDS-UserPasswordExpiryTimeComputed",
2847                                                       "accountExpires",
2848                                                       "userAccountControl",
2849                                                       "msDS-User-Account-Control-Computed",
2850                                                       NULL};
2851                 attrs = attrs2;
2852                 break;
2853         }
2854         case 6:
2855         {
2856                 static const char * const attrs2[] = {"sAMAccountName",
2857                                                       "displayName",
2858                                                       NULL};
2859                 attrs = attrs2;
2860                 break;
2861         }
2862         case 7:
2863         {
2864                 static const char * const attrs2[] = {"sAMAccountName",
2865                                                       NULL};
2866                 attrs = attrs2;
2867                 break;
2868         }
2869         case 8:
2870         {
2871                 static const char * const attrs2[] = {"displayName",
2872                                                       NULL};
2873                 attrs = attrs2;
2874                 break;
2875         }
2876         case 9:
2877         {
2878                 static const char * const attrs2[] = {"primaryGroupID",
2879                                                       NULL};
2880                 attrs = attrs2;
2881                 break;
2882         }
2883         case 10:
2884         {
2885                 static const char * const attrs2[] = {"homeDirectory",
2886                                                       "homeDrive",
2887                                                       NULL};
2888                 attrs = attrs2;
2889                 break;
2890         }
2891         case 11:
2892         {
2893                 static const char * const attrs2[] = {"scriptPath",
2894                                                       NULL};
2895                 attrs = attrs2;
2896                 break;
2897         }
2898         case 12:
2899         {
2900                 static const char * const attrs2[] = {"profilePath",
2901                                                       NULL};
2902                 attrs = attrs2;
2903                 break;
2904         }
2905         case 13:
2906         {
2907                 static const char * const attrs2[] = {"description",
2908                                                       NULL};
2909                 attrs = attrs2;
2910                 break;
2911         }
2912         case 14:
2913         {
2914                 static const char * const attrs2[] = {"userWorkstations",
2915                                                       NULL};
2916                 attrs = attrs2;
2917                 break;
2918         }
2919         case 16:
2920         {
2921                 static const char * const attrs2[] = {"userAccountControl",
2922                                                       "msDS-User-Account-Control-Computed",
2923                                                       "pwdLastSet",
2924                                                       "msDS-UserPasswordExpiryTimeComputed",
2925                                                       NULL};
2926                 attrs = attrs2;
2927                 break;
2928         }
2929         case 17:
2930         {
2931                 static const char * const attrs2[] = {"accountExpires",
2932                                                       NULL};
2933                 attrs = attrs2;
2934                 break;
2935         }
2936         case 18:
2937         {
2938                 return NT_STATUS_NOT_SUPPORTED;
2939         }
2940         case 20:
2941         {
2942                 static const char * const attrs2[] = {"userParameters",
2943                                                       NULL};
2944                 attrs = attrs2;
2945                 break;
2946         }
2947         case 21:
2948         {
2949                 static const char * const attrs2[] = {"lastLogon",
2950                                                       "lastLogoff",
2951                                                       "pwdLastSet",
2952                                                       "msDS-ResultantPSO",
2953                                                       "msDS-UserPasswordExpiryTimeComputed",
2954                                                       "accountExpires",
2955                                                       "sAMAccountName",
2956                                                       "displayName",
2957                                                       "homeDirectory",
2958                                                       "homeDrive",
2959                                                       "scriptPath",
2960                                                       "profilePath",
2961                                                       "description",
2962                                                       "userWorkstations",
2963                                                       "comment",
2964                                                       "userParameters",
2965                                                       "objectSid",
2966                                                       "primaryGroupID",
2967                                                       "userAccountControl",
2968                                                       "msDS-User-Account-Control-Computed",
2969                                                       "logonHours",
2970                                                       "badPwdCount",
2971                                                       "badPasswordTime",
2972                                                       "logonCount",
2973                                                       "countryCode",
2974                                                       "codePage",
2975                                                       NULL};
2976                 attrs = attrs2;
2977                 break;
2978         }
2979         case 23:
2980         case 24:
2981         case 25:
2982         case 26:
2983         {
2984                 return NT_STATUS_NOT_SUPPORTED;
2985         }
2986         default:
2987         {
2988                 return NT_STATUS_INVALID_INFO_CLASS;
2989         }
2990         }
2991
2992         /* pull all the user attributes */
2993         ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
2994                               a_state->account_dn, &res, attrs);
2995         if (ret == 0) {
2996                 return NT_STATUS_NO_SUCH_USER;
2997         }
2998         if (ret != 1) {
2999                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
3000         }
3001         msg = res[0];
3002
3003         /* allocate the info structure */
3004         info = talloc_zero(mem_ctx, union samr_UserInfo);
3005         if (info == NULL) {
3006                 return NT_STATUS_NO_MEMORY;
3007         }
3008
3009         /* fill in the reply */
3010         switch (r->in.level) {
3011         case 1:
3012                 QUERY_STRING(msg, info1.account_name,          "sAMAccountName");
3013                 QUERY_STRING(msg, info1.full_name,             "displayName");
3014                 QUERY_UINT  (msg, info1.primary_gid,           "primaryGroupID");
3015                 QUERY_STRING(msg, info1.description,           "description");
3016                 QUERY_STRING(msg, info1.comment,               "comment");
3017                 break;
3018
3019         case 2:
3020                 QUERY_STRING(msg, info2.comment,               "comment");
3021                 QUERY_UINT  (msg, info2.country_code,          "countryCode");
3022                 QUERY_UINT  (msg, info2.code_page,             "codePage");
3023                 break;
3024
3025         case 3:
3026                 QUERY_STRING(msg, info3.account_name,          "sAMAccountName");
3027                 QUERY_STRING(msg, info3.full_name,             "displayName");
3028                 QUERY_RID   (msg, info3.rid,                   "objectSid");
3029                 QUERY_UINT  (msg, info3.primary_gid,           "primaryGroupID");
3030                 QUERY_STRING(msg, info3.home_directory,        "homeDirectory");
3031                 QUERY_STRING(msg, info3.home_drive,            "homeDrive");
3032                 QUERY_STRING(msg, info3.logon_script,          "scriptPath");
3033                 QUERY_STRING(msg, info3.profile_path,          "profilePath");
3034                 QUERY_STRING(msg, info3.workstations,          "userWorkstations");
3035                 QUERY_UINT64(msg, info3.last_logon,            "lastLogon");
3036                 QUERY_UINT64(msg, info3.last_logoff,           "lastLogoff");
3037                 QUERY_UINT64(msg, info3.last_password_change,  "pwdLastSet");