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