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