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