removed the start_ndx parameter from group enumeration
[jra/samba/.git] / source3 / nsswitch / winbindd_ads.c
1 /* 
2    Unix SMB/Netbios implementation.
3
4    Winbind ADS backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "winbindd.h"
24
25 #ifdef HAVE_ADS
26
27
28 /*
29   a wrapper around ldap_search_s that retries depending on the error code
30   this is supposed to catch dropped connections and auto-reconnect
31 */
32 int ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, 
33                         const char *exp,
34                         const char **attrs, void **res)
35 {
36         int rc = -1, rc2;
37         int count = 3;
38
39         if (!ads->ld &&
40             time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
41                 return LDAP_SERVER_DOWN;
42         }
43
44         while (count--) {
45                 rc = ads_do_search(ads, bind_path, scope, exp, attrs, res);
46                 if (rc == 0) {
47                         DEBUG(5,("Search for %s gave %d replies\n",
48                                  exp, ads_count_replies(ads, *res)));
49                         return rc;
50                 }
51
52                 if (*res) ads_msgfree(ads, *res);
53                 *res = NULL;
54                 DEBUG(1,("Reopening ads connection after error %s\n", ads_errstr(rc)));
55                 if (ads->ld) {
56                         /* we should unbind here, but that seems to trigger openldap bugs :(
57                            ldap_unbind(ads->ld); 
58                         */
59                 }
60                 ads->ld = NULL;
61                 rc2 = ads_connect(ads);
62                 if (rc2) {
63                         DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n", ads_errstr(rc)));
64                         return rc2;
65                 }
66         }
67         DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(rc)));
68         return rc;
69 }
70
71
72 int ads_search_retry(ADS_STRUCT *ads, void **res, 
73                      const char *exp, 
74                      const char **attrs)
75 {
76         return ads_do_search_retry(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
77                                    exp, attrs, res);
78 }
79
80 int ads_search_retry_dn(ADS_STRUCT *ads, void **res, 
81                         const char *dn, 
82                         const char **attrs)
83 {
84         return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
85                                    "(objectclass=*)", attrs, res);
86 }
87
88 /*
89   return our ads connections structure for a domain. We keep the connection
90   open to make things faster
91 */
92 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
93 {
94         ADS_STRUCT *ads;
95         int rc;
96         char *ccache;
97
98         if (domain->private) {
99                 return (ADS_STRUCT *)domain->private;
100         }
101
102         /* we don't want this to affect the users ccache */
103         ccache = lock_path("winbindd_ccache");
104         setenv("KRB5CCNAME", ccache, 1);
105         unlink(ccache);
106
107         ads = ads_init(NULL, NULL, NULL, NULL);
108         if (!ads) {
109                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
110                 return NULL;
111         }
112
113         /* the machine acct password might have change - fetch it every time */
114         SAFE_FREE(ads->password);
115         ads->password = secrets_fetch_machine_password();
116
117         rc = ads_connect(ads);
118         if (rc) {
119                 DEBUG(1,("ads_connect for domain %s failed: %s\n", domain->name, ads_errstr(rc)));
120                 ads_destroy(&ads);
121                 return NULL;
122         }
123
124         domain->private = (void *)ads;
125         return ads;
126 }
127
128 /* useful utility */
129 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
130 {
131         sid_copy(sid, &domain->sid);
132         sid_append_rid(sid, rid);
133 }
134
135 /* turn a sAMAccountType into a SID_NAME_USE */
136 static enum SID_NAME_USE ads_atype_map(uint32 atype)
137 {
138         switch (atype & 0xF0000000) {
139         case ATYPE_GROUP:
140                 return SID_NAME_DOM_GRP;
141         case ATYPE_USER:
142                 return SID_NAME_USER;
143         default:
144                 DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
145         }
146         return SID_NAME_UNKNOWN;
147 }
148
149 /* Query display info for a realm. This is the basic user list fn */
150 static NTSTATUS query_user_list(struct winbindd_domain *domain,
151                                TALLOC_CTX *mem_ctx,
152                                uint32 *num_entries, 
153                                WINBIND_USERINFO **info)
154 {
155         ADS_STRUCT *ads = NULL;
156         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
157                                "sAMAccountType", NULL};
158         int rc, i, count;
159         void *res = NULL;
160         void *msg = NULL;
161         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
162
163         *num_entries = 0;
164
165         DEBUG(3,("ads: query_user_list\n"));
166
167         ads = ads_cached_connection(domain);
168         if (!ads) goto done;
169
170         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
171         if (rc) {
172                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
173                 goto done;
174         }
175
176         count = ads_count_replies(ads, res);
177         if (count == 0) {
178                 DEBUG(1,("query_user_list: No users found\n"));
179                 goto done;
180         }
181
182         (*info) = talloc(mem_ctx, count * sizeof(**info));
183         if (!*info) {
184                 status = NT_STATUS_NO_MEMORY;
185                 goto done;
186         }
187
188         i = 0;
189
190         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
191                 char *name, *gecos;
192                 DOM_SID sid;
193                 uint32 rid, group;
194                 uint32 atype;
195
196                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
197                     ads_atype_map(atype) != SID_NAME_USER) {
198                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
199                         continue;
200                 }
201
202                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
203                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
204                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
205                         DEBUG(1,("No sid for %s !?\n", name));
206                         continue;
207                 }
208                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
209                         DEBUG(1,("No primary group for %s !?\n", name));
210                         continue;
211                 }
212
213                 if (!sid_peek_rid(&sid, &rid)) {
214                         DEBUG(1,("No rid for %s !?\n", name));
215                         continue;
216                 }
217
218                 (*info)[i].acct_name = name;
219                 (*info)[i].full_name = gecos;
220                 (*info)[i].user_rid = rid;
221                 (*info)[i].group_rid = group;
222                 i++;
223         }
224
225         (*num_entries) = i;
226         status = NT_STATUS_OK;
227
228         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
229
230 done:
231         if (res) ads_msgfree(ads, res);
232
233         return status;
234 }
235
236 /* list all domain groups */
237 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
238                                 TALLOC_CTX *mem_ctx,
239                                 uint32 *num_entries, 
240                                 struct acct_info **info)
241 {
242         ADS_STRUCT *ads = NULL;
243         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
244                                "sAMAccountType", NULL};
245         int rc, i, count;
246         void *res = NULL;
247         void *msg = NULL;
248         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
249
250         *num_entries = 0;
251
252         DEBUG(3,("ads: enum_dom_groups\n"));
253
254         ads = ads_cached_connection(domain);
255         if (!ads) goto done;
256
257         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
258         if (rc) {
259                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
260                 goto done;
261         }
262
263         count = ads_count_replies(ads, res);
264         if (count == 0) {
265                 DEBUG(1,("query_user_list: No users found\n"));
266                 goto done;
267         }
268
269         (*info) = talloc(mem_ctx, count * sizeof(**info));
270         if (!*info) {
271                 status = NT_STATUS_NO_MEMORY;
272                 goto done;
273         }
274
275         i = 0;
276
277         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
278                 char *name, *gecos;
279                 DOM_SID sid;
280                 uint32 rid;
281                 uint32 account_type;
282
283                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", 
284                                      &account_type) ||
285                     !(account_type & ATYPE_GROUP)) continue;
286
287                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
288                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
289                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
290                         DEBUG(1,("No sid for %s !?\n", name));
291                         continue;
292                 }
293
294                 if (!sid_peek_rid(&sid, &rid)) {
295                         DEBUG(1,("No rid for %s !?\n", name));
296                         continue;
297                 }
298
299                 fstrcpy((*info)[i].acct_name, name);
300                 fstrcpy((*info)[i].acct_desc, gecos);
301                 (*info)[i].rid = rid;
302                 i++;
303         }
304
305         (*num_entries) = i;
306
307         status = NT_STATUS_OK;
308
309         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
310
311 done:
312         if (res) ads_msgfree(ads, res);
313
314         return status;
315 }
316
317
318 /* convert a single name to a sid in a domain */
319 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
320                             const char *name,
321                             DOM_SID *sid,
322                             enum SID_NAME_USE *type)
323 {
324         ADS_STRUCT *ads = NULL;
325         const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
326         int rc, count;
327         void *res = NULL;
328         char *exp;
329         uint32 t;
330         fstring name2, dom2;
331         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
332
333         /* sigh. Need to fix interface to give us a raw name */
334         if (!parse_domain_user(name, dom2, name2)) {
335                 goto done;
336         }
337
338         DEBUG(3,("ads: name_to_sid\n"));
339
340         ads = ads_cached_connection(domain);
341         if (!ads) goto done;
342
343         asprintf(&exp, "(sAMAccountName=%s)", name2);
344         rc = ads_search_retry(ads, &res, exp, attrs);
345         free(exp);
346         if (rc) {
347                 DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
348                 goto done;
349         }
350
351         count = ads_count_replies(ads, res);
352         if (count != 1) {
353                 DEBUG(1,("name_to_sid: %s not found\n", name));
354                 goto done;
355         }
356
357         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
358                 DEBUG(1,("No sid for %s !?\n", name));
359                 goto done;
360         }
361
362         if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
363                 DEBUG(1,("No sAMAccountType for %s !?\n", name));
364                 goto done;
365         }
366
367         *type = ads_atype_map(t);
368
369         status = NT_STATUS_OK;
370
371         DEBUG(3,("ads name_to_sid mapped %s\n", name));
372
373 done:
374         if (res) ads_msgfree(ads, res);
375
376         return status;
377 }
378
379 /* convert a sid to a user or group name */
380 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
381                             TALLOC_CTX *mem_ctx,
382                             DOM_SID *sid,
383                             char **name,
384                             enum SID_NAME_USE *type)
385 {
386         ADS_STRUCT *ads = NULL;
387         const char *attrs[] = {"sAMAccountName", "sAMAccountType", NULL};
388         int rc;
389         void *msg = NULL;
390         char *exp;
391         char *sidstr;
392         uint32 atype;
393         char *s;
394         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
395
396         DEBUG(3,("ads: sid_to_name\n"));
397
398         ads = ads_cached_connection(domain);
399         if (!ads) goto done;
400
401         sidstr = sid_binstring(sid);
402         asprintf(&exp, "(objectSid=%s)", sidstr);
403         rc = ads_search_retry(ads, &msg, exp, attrs);
404         free(exp);
405         free(sidstr);
406         if (rc) {
407                 DEBUG(1,("sid_to_name ads_search: %s\n", ads_errstr(rc)));
408                 goto done;
409         }
410
411         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
412                 goto done;
413         }
414
415         s = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
416         *name = talloc_asprintf(mem_ctx, "%s%s%s", domain->name, lp_winbind_separator(), s);
417         *type = ads_atype_map(atype);
418
419         status = NT_STATUS_OK;
420
421         DEBUG(3,("ads sid_to_name mapped %s\n", *name));
422
423 done:
424         if (msg) ads_msgfree(ads, msg);
425
426         return status;
427 }
428
429
430 /* Lookup user information from a rid */
431 static NTSTATUS query_user(struct winbindd_domain *domain, 
432                            TALLOC_CTX *mem_ctx, 
433                            uint32 user_rid, 
434                            WINBIND_USERINFO *info)
435 {
436         ADS_STRUCT *ads = NULL;
437         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
438                                "primaryGroupID", NULL};
439         int rc, count;
440         void *msg = NULL;
441         char *exp;
442         DOM_SID sid;
443         char *sidstr;
444         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
445
446         DEBUG(3,("ads: query_user\n"));
447
448         sid_from_rid(domain, user_rid, &sid);
449
450         ads = ads_cached_connection(domain);
451         if (!ads) goto done;
452
453         sidstr = sid_binstring(&sid);
454         asprintf(&exp, "(objectSid=%s)", sidstr);
455         rc = ads_search_retry(ads, &msg, exp, attrs);
456         free(exp);
457         free(sidstr);
458         if (rc) {
459                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
460                 goto done;
461         }
462
463         count = ads_count_replies(ads, msg);
464         if (count != 1) {
465                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
466                 goto done;
467         }
468
469         info->acct_name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
470         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
471         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
472                 DEBUG(1,("No sid for %d !?\n", user_rid));
473                 goto done;
474         }
475         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
476                 DEBUG(1,("No primary group for %d !?\n", user_rid));
477                 goto done;
478         }
479         
480         if (!sid_peek_rid(&sid, &info->user_rid)) {
481                 DEBUG(1,("No rid for %d !?\n", user_rid));
482                 goto done;
483         }
484
485         status = NT_STATUS_OK;
486
487         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
488 done:
489         if (msg) ads_msgfree(ads, msg);
490
491         return status;
492 }
493
494
495 /* Lookup groups a user is a member of. */
496 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
497                                   TALLOC_CTX *mem_ctx,
498                                   uint32 user_rid, 
499                                   uint32 *num_groups, uint32 **user_gids)
500 {
501         ADS_STRUCT *ads = NULL;
502         const char *attrs[] = {"distinguishedName", NULL};
503         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
504         int rc, count;
505         void *msg = NULL;
506         char *exp;
507         char *user_dn;
508         DOM_SID *sids;
509         int i;
510         uint32 primary_group;
511         DOM_SID sid;
512         char *sidstr;
513         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
514
515         *num_groups = 0;
516
517         DEBUG(3,("ads: lookup_usergroups\n"));
518
519         (*num_groups) = 0;
520
521         sid_from_rid(domain, user_rid, &sid);
522
523         ads = ads_cached_connection(domain);
524         if (!ads) goto done;
525
526         sidstr = sid_binstring(&sid);
527         asprintf(&exp, "(objectSid=%s)", sidstr);
528         rc = ads_search_retry(ads, &msg, exp, attrs);
529         free(exp);
530         free(sidstr);
531         if (rc) {
532                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
533                 goto done;
534         }
535
536         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
537
538         if (msg) ads_msgfree(ads, msg);
539
540         rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
541         if (rc) {
542                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
543                 goto done;
544         }
545
546         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
547                 DEBUG(1,("No primary group for rid=%d !?\n", user_rid));
548                 goto done;
549         }
550
551         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
552         (*user_gids) = (uint32 *)talloc(mem_ctx, sizeof(uint32) * count);
553         (*user_gids)[(*num_groups)++] = primary_group;
554
555         for (i=1;i<count;i++) {
556                 uint32 rid;
557                 if (!sid_peek_rid(&sids[i-1], &rid)) continue;
558                 (*user_gids)[*num_groups] = rid;
559                 (*num_groups)++;
560         }
561
562         status = NT_STATUS_OK;
563         DEBUG(3,("ads lookup_usergroups for rid=%d\n", user_rid));
564 done:
565         if (msg) ads_msgfree(ads, msg);
566
567         return status;
568 }
569
570
571 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
572                                 TALLOC_CTX *mem_ctx,
573                                 uint32 group_rid, uint32 *num_names, 
574                                 uint32 **rid_mem, char ***names, 
575                                 uint32 **name_types)
576 {
577         DOM_SID group_sid;
578         char *sidstr;
579         const char *attrs[] = {"sAMAccountName", "objectSid", "sAMAccountType", NULL};
580         int rc, count;
581         void *res=NULL, *msg=NULL;
582         ADS_STRUCT *ads = NULL;
583         char *exp;
584         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
585
586         *num_names = 0;
587
588         ads = ads_cached_connection(domain);
589         if (!ads) goto done;
590
591         sid_from_rid(domain, group_rid, &group_sid);
592         sidstr = sid_binstring(&group_sid);
593         /* search for all users who have that group sid as primary group or as member */
594         asprintf(&exp, "(&(objectCategory=user)(|(primaryGroupID=%d)(memberOf=%s)))",
595                  group_rid, sidstr);
596         rc = ads_search_retry(ads, &res, exp, attrs);
597         free(exp);
598         free(sidstr);
599         if (rc) {
600                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
601                 goto done;
602         }
603
604         count = ads_count_replies(ads, res);
605         if (count == 0) {
606                 status = NT_STATUS_OK;
607                 goto done;
608         }
609
610         (*rid_mem) = talloc(mem_ctx, sizeof(uint32) * count);
611         (*name_types) = talloc(mem_ctx, sizeof(uint32) * count);
612         (*names) = talloc(mem_ctx, sizeof(char *) * count);
613
614         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
615                 uint32 atype, rid;
616                 DOM_SID sid;
617
618                 (*names)[*num_names] = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
619                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
620                         continue;
621                 }
622                 (*name_types)[*num_names] = ads_atype_map(atype);
623                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
624                         DEBUG(1,("No sid for %s !?\n", (*names)[*num_names]));
625                         continue;
626                 }
627                 if (!sid_peek_rid(&sid, &rid)) {
628                         DEBUG(1,("No rid for %s !?\n", (*names)[*num_names]));
629                         continue;
630                 }
631                 (*rid_mem)[*num_names] = rid;
632                 (*num_names)++;
633         }       
634
635         status = NT_STATUS_OK;
636         DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid));
637 done:
638         if (res) ads_msgfree(ads, res);
639
640         return status;
641 }
642
643 /* find the sequence number for a domain */
644 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
645 {
646         ADS_STRUCT *ads = NULL;
647
648         *seq = DOM_SEQUENCE_NONE;
649
650         ads = ads_cached_connection(domain);
651         if (!ads) return NT_STATUS_UNSUCCESSFUL;
652
653         if (!ads_USN(ads, seq)) {
654                 return NT_STATUS_UNSUCCESSFUL;
655         }
656
657         return NT_STATUS_OK;
658 }
659
660 /* get a list of trusted domains */
661 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
662                                 TALLOC_CTX *mem_ctx,
663                                 uint32 *num_domains,
664                                 char ***names,
665                                 DOM_SID **dom_sids)
666 {
667         *num_domains = 0;
668         return NT_STATUS_NOT_IMPLEMENTED;
669 }
670
671 /* find the domain sid for a domain */
672 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
673 {
674         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
675         const char *attrs[] = {"objectSid", NULL};
676         ADS_STRUCT *ads = NULL;
677         void *res;
678         int rc;
679
680         ads = ads_cached_connection(domain);
681         if (!ads) goto done;
682
683         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
684                            attrs, &res);
685         if (rc) goto done;
686         if (ads_pull_sid(ads, res, "objectSid", sid)) {
687                 status = NT_STATUS_OK;
688         }
689         ads_msgfree(ads, res);
690
691 done:
692         return status;
693 }
694
695 /* the ADS backend methods are exposed via this structure */
696 struct winbindd_methods ads_methods = {
697         True,
698         query_user_list,
699         enum_dom_groups,
700         name_to_sid,
701         sid_to_name,
702         query_user,
703         lookup_usergroups,
704         lookup_groupmem,
705         sequence_number,
706         trusted_domains,
707         domain_sid
708 };
709
710 #endif