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