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