9fd391870b486b0994f588740a1005c3faf69b32
[jra/samba/.git] / source3 / nsswitch / winbindd_ads.c
1 /* 
2    Unix SMB/CIFS 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 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 /* the realm of our primary LDAP server */
31 static char *primary_realm;
32
33
34 /*
35   return our ads connections structure for a domain. We keep the connection
36   open to make things faster
37 */
38 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
39 {
40         ADS_STRUCT *ads;
41         ADS_STATUS status;
42
43         if (domain->private) {
44                 return (ADS_STRUCT *)domain->private;
45         }
46
47         /* we don't want this to affect the users ccache */
48         setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
49
50         ads = ads_init(domain->alt_name, domain->name, NULL);
51         if (!ads) {
52                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
53                 return NULL;
54         }
55
56         /* the machine acct password might have change - fetch it every time */
57         SAFE_FREE(ads->auth.password);
58         ads->auth.password = secrets_fetch_machine_password();
59
60         if (primary_realm) {
61                 SAFE_FREE(ads->auth.realm);
62                 ads->auth.realm = strdup(primary_realm);
63         }
64
65         status = ads_connect(ads);
66         if (!ADS_ERR_OK(status) || !ads->config.realm) {
67                 extern struct winbindd_methods msrpc_methods;
68                 DEBUG(1,("ads_connect for domain %s failed: %s\n", 
69                          domain->name, ads_errstr(status)));
70                 ads_destroy(&ads);
71
72                 /* if we get ECONNREFUSED then it might be a NT4
73                    server, fall back to MSRPC */
74                 if (status.error_type == ADS_ERROR_SYSTEM &&
75                     status.err.rc == ECONNREFUSED) {
76                         DEBUG(1,("Trying MSRPC methods\n"));
77                         domain->methods = &msrpc_methods;
78                 }
79                 return NULL;
80         }
81
82         /* remember our primary realm for trusted domain support */
83         if (!primary_realm) {
84                 primary_realm = strdup(ads->config.realm);
85         }
86
87         domain->private = (void *)ads;
88         return ads;
89 }
90
91 /* useful utility */
92 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
93 {
94         sid_copy(sid, &domain->sid);
95         sid_append_rid(sid, rid);
96 }
97
98
99 /* Query display info for a realm. This is the basic user list fn */
100 static NTSTATUS query_user_list(struct winbindd_domain *domain,
101                                TALLOC_CTX *mem_ctx,
102                                uint32 *num_entries, 
103                                WINBIND_USERINFO **info)
104 {
105         ADS_STRUCT *ads = NULL;
106         const char *attrs[] = {"userPrincipalName",
107                                "sAMAccountName",
108                                "name", "objectSid", "primaryGroupID", 
109                                "sAMAccountType", NULL};
110         int i, count;
111         ADS_STATUS rc;
112         void *res = NULL;
113         void *msg = NULL;
114         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
115
116         *num_entries = 0;
117
118         DEBUG(3,("ads: query_user_list\n"));
119
120         ads = ads_cached_connection(domain);
121         if (!ads) goto done;
122
123         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
124         if (!ADS_ERR_OK(rc)) {
125                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
126                 goto done;
127         }
128
129         count = ads_count_replies(ads, res);
130         if (count == 0) {
131                 DEBUG(1,("query_user_list: No users found\n"));
132                 goto done;
133         }
134
135         (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
136         if (!*info) {
137                 status = NT_STATUS_NO_MEMORY;
138                 goto done;
139         }
140
141         i = 0;
142
143         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
144                 char *name, *gecos;
145                 DOM_SID sid;
146                 uint32 rid, group;
147                 uint32 atype;
148
149                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
150                     ads_atype_map(atype) != SID_NAME_USER) {
151                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
152                         continue;
153                 }
154
155                 name = ads_pull_username(ads, mem_ctx, msg);
156                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
157                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
158                         DEBUG(1,("No sid for %s !?\n", name));
159                         continue;
160                 }
161                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
162                         DEBUG(1,("No primary group for %s !?\n", name));
163                         continue;
164                 }
165
166                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
167                         DEBUG(1,("No rid for %s !?\n", name));
168                         continue;
169                 }
170
171                 (*info)[i].acct_name = name;
172                 (*info)[i].full_name = gecos;
173                 (*info)[i].user_rid = rid;
174                 (*info)[i].group_rid = group;
175                 i++;
176         }
177
178         (*num_entries) = i;
179         status = NT_STATUS_OK;
180
181         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
182
183 done:
184         if (res) ads_msgfree(ads, res);
185
186         return status;
187 }
188
189 /* list all domain groups */
190 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
191                                 TALLOC_CTX *mem_ctx,
192                                 uint32 *num_entries, 
193                                 struct acct_info **info)
194 {
195         ADS_STRUCT *ads = NULL;
196         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
197                                "name", "objectSid", 
198                                "sAMAccountType", NULL};
199         int i, count;
200         ADS_STATUS rc;
201         void *res = NULL;
202         void *msg = NULL;
203         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
204         uint32 group_flags;
205
206         *num_entries = 0;
207
208         DEBUG(3,("ads: enum_dom_groups\n"));
209
210         ads = ads_cached_connection(domain);
211         if (!ads) goto done;
212
213         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
214         if (!ADS_ERR_OK(rc)) {
215                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
216                 goto done;
217         }
218
219         count = ads_count_replies(ads, res);
220         if (count == 0) {
221                 DEBUG(1,("enum_dom_groups: No groups found\n"));
222                 goto done;
223         }
224
225         (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
226         if (!*info) {
227                 status = NT_STATUS_NO_MEMORY;
228                 goto done;
229         }
230
231         i = 0;
232         
233         group_flags = ATYPE_GLOBAL_GROUP;
234         if ( domain->native_mode )
235                 group_flags |= ATYPE_LOCAL_GROUP;
236
237         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
238                 char *name, *gecos;
239                 DOM_SID sid;
240                 uint32 rid;
241                 uint32 account_type;
242
243                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) ) 
244                         continue; 
245                         
246                 name = ads_pull_username(ads, mem_ctx, msg);
247                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
248                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
249                         DEBUG(1,("No sid for %s !?\n", name));
250                         continue;
251                 }
252
253                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
254                         DEBUG(1,("No rid for %s !?\n", name));
255                         continue;
256                 }
257
258                 fstrcpy((*info)[i].acct_name, name);
259                 fstrcpy((*info)[i].acct_desc, gecos);
260                 (*info)[i].rid = rid;
261                 i++;
262         }
263
264         (*num_entries) = i;
265
266         status = NT_STATUS_OK;
267
268         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
269
270 done:
271         if (res) ads_msgfree(ads, res);
272
273         return status;
274 }
275
276 /* list all domain local groups */
277 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
278                                 TALLOC_CTX *mem_ctx,
279                                 uint32 *num_entries, 
280                                 struct acct_info **info)
281 {
282         /*
283          * This is a stub function only as we returned the domain 
284          * ocal groups in enum_dom_groups() if the domain->native field
285          * was true.  This is a simple performance optimization when
286          * using LDAP.
287          *
288          * if we ever need to enumerate domain local groups separately, 
289          * then this the optimization in enum_dom_groups() will need 
290          * to be split out
291          */
292         *num_entries = 0;
293         
294         return NT_STATUS_OK;
295 }
296
297 /* convert a single name to a sid in a domain */
298 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
299                             const char *name,
300                             DOM_SID *sid,
301                             enum SID_NAME_USE *type)
302 {
303         ADS_STRUCT *ads;
304
305         DEBUG(3,("ads: name_to_sid\n"));
306
307         ads = ads_cached_connection(domain);
308         if (!ads) 
309                 return NT_STATUS_UNSUCCESSFUL;
310
311         return ads_name_to_sid(ads, name, sid, type);
312 }
313
314 /* convert a sid to a user or group name */
315 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
316                             TALLOC_CTX *mem_ctx,
317                             DOM_SID *sid,
318                             char **name,
319                             enum SID_NAME_USE *type)
320 {
321         ADS_STRUCT *ads = NULL;
322         DEBUG(3,("ads: sid_to_name\n"));
323         ads = ads_cached_connection(domain);
324         if (!ads) 
325                 return NT_STATUS_UNSUCCESSFUL;
326
327         return ads_sid_to_name(ads, mem_ctx, sid, name, type);
328 }
329
330
331 /* convert a DN to a name, rid and name type 
332    this might become a major speed bottleneck if groups have
333    lots of users, in which case we could cache the results
334 */
335 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
336                       const char *dn,
337                       char **name, uint32 *name_type, uint32 *rid)
338 {
339         char *exp;
340         void *res = NULL;
341         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
342                                "objectSid", "sAMAccountType", NULL};
343         ADS_STATUS rc;
344         uint32 atype;
345         DOM_SID sid;
346         char *escaped_dn = escape_ldap_string_alloc(dn);
347
348         if (!escaped_dn) {
349                 return False;
350         }
351
352         asprintf(&exp, "(distinguishedName=%s)", dn);
353         rc = ads_search_retry(ads, &res, exp, attrs);
354         SAFE_FREE(exp);
355         SAFE_FREE(escaped_dn);
356
357         if (!ADS_ERR_OK(rc)) {
358                 goto failed;
359         }
360
361         (*name) = ads_pull_username(ads, mem_ctx, res);
362
363         if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
364                 goto failed;
365         }
366         (*name_type) = ads_atype_map(atype);
367
368         if (!ads_pull_sid(ads, res, "objectSid", &sid) || 
369             !sid_peek_rid(&sid, rid)) {
370                 goto failed;
371         }
372
373         if (res) ads_msgfree(ads, res);
374         return True;
375
376 failed:
377         if (res) ads_msgfree(ads, res);
378         return False;
379 }
380
381 /* Lookup user information from a rid */
382 static NTSTATUS query_user(struct winbindd_domain *domain, 
383                            TALLOC_CTX *mem_ctx, 
384                            uint32 user_rid, 
385                            WINBIND_USERINFO *info)
386 {
387         ADS_STRUCT *ads = NULL;
388         const char *attrs[] = {"userPrincipalName", 
389                                "sAMAccountName",
390                                "name", "objectSid", 
391                                "primaryGroupID", NULL};
392         ADS_STATUS rc;
393         int count;
394         void *msg = NULL;
395         char *exp;
396         DOM_SID sid;
397         char *sidstr;
398         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
399
400         DEBUG(3,("ads: query_user\n"));
401
402         sid_from_rid(domain, user_rid, &sid);
403
404         ads = ads_cached_connection(domain);
405         if (!ads) goto done;
406
407         sidstr = sid_binstring(&sid);
408         asprintf(&exp, "(objectSid=%s)", sidstr);
409         rc = ads_search_retry(ads, &msg, exp, attrs);
410         free(exp);
411         free(sidstr);
412         if (!ADS_ERR_OK(rc)) {
413                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
414                 goto done;
415         }
416
417         count = ads_count_replies(ads, msg);
418         if (count != 1) {
419                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
420                 goto done;
421         }
422
423         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
424         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
425         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
426                 DEBUG(1,("No sid for %d !?\n", user_rid));
427                 goto done;
428         }
429         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
430                 DEBUG(1,("No primary group for %d !?\n", user_rid));
431                 goto done;
432         }
433         
434         if (!sid_peek_check_rid(&domain->sid,&sid, &info->user_rid)) {
435                 DEBUG(1,("No rid for %d !?\n", user_rid));
436                 goto done;
437         }
438
439         status = NT_STATUS_OK;
440
441         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
442 done:
443         if (msg) ads_msgfree(ads, msg);
444
445         return status;
446 }
447
448
449 /* Lookup groups a user is a member of. */
450 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
451                                   TALLOC_CTX *mem_ctx,
452                                   uint32 user_rid, 
453                                   uint32 *num_groups, uint32 **user_gids)
454 {
455         ADS_STRUCT *ads = NULL;
456         const char *attrs[] = {"distinguishedName", NULL};
457         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
458         ADS_STATUS rc;
459         int count;
460         void *msg = NULL;
461         char *exp;
462         char *user_dn;
463         DOM_SID *sids;
464         int i;
465         uint32 primary_group;
466         DOM_SID sid;
467         char *sidstr;
468         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
469
470         *num_groups = 0;
471
472         DEBUG(3,("ads: lookup_usergroups\n"));
473
474         (*num_groups) = 0;
475
476         sid_from_rid(domain, user_rid, &sid);
477
478         ads = ads_cached_connection(domain);
479         if (!ads) goto done;
480
481         sidstr = sid_binstring(&sid);
482         asprintf(&exp, "(objectSid=%s)", sidstr);
483         rc = ads_search_retry(ads, &msg, exp, attrs);
484         free(exp);
485         free(sidstr);
486         if (!ADS_ERR_OK(rc)) {
487                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
488                 goto done;
489         }
490
491         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
492         if (!user_dn) {
493                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search did not return a a distinguishedName!\n", user_rid));
494                 goto done;
495         }
496
497         if (msg) ads_msgfree(ads, msg);
498
499         rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
500         if (!ADS_ERR_OK(rc)) {
501                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
502                 goto done;
503         }
504
505         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
506                 DEBUG(1,("%s: No primary group for rid=%d !?\n", domain->name, user_rid));
507                 goto done;
508         }
509
510         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
511         (*user_gids) = (uint32 *)talloc_zero(mem_ctx, sizeof(uint32) * count);
512         (*user_gids)[(*num_groups)++] = primary_group;
513
514         for (i=1;i<count;i++) {
515                 uint32 rid;
516                 if (!sid_peek_check_rid(&domain->sid, &sids[i-1], &rid)) continue;
517                 (*user_gids)[*num_groups] = rid;
518                 (*num_groups)++;
519         }
520
521         status = NT_STATUS_OK;
522         DEBUG(3,("ads lookup_usergroups for rid=%d\n", user_rid));
523 done:
524         if (msg) ads_msgfree(ads, msg);
525
526         return status;
527 }
528
529 /*
530   find the members of a group, given a group rid and domain
531  */
532 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
533                                 TALLOC_CTX *mem_ctx,
534                                 uint32 group_rid, uint32 *num_names, 
535                                 uint32 **rid_mem, char ***names, 
536                                 uint32 **name_types)
537 {
538         DOM_SID group_sid;
539         ADS_STATUS rc;
540         int count;
541         void *res=NULL;
542         ADS_STRUCT *ads = NULL;
543         char *exp;
544         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
545         char *sidstr;
546         const char *attrs[] = {"member", NULL};
547         char **members;
548         int i, num_members;
549
550         *num_names = 0;
551
552         ads = ads_cached_connection(domain);
553         if (!ads) goto done;
554
555         sid_from_rid(domain, group_rid, &group_sid);
556         sidstr = sid_binstring(&group_sid);
557
558         /* search for all members of the group */
559         asprintf(&exp, "(objectSid=%s)",sidstr);
560         rc = ads_search_retry(ads, &res, exp, attrs);
561         free(exp);
562         free(sidstr);
563
564         if (!ADS_ERR_OK(rc)) {
565                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
566                 goto done;
567         }
568
569         count = ads_count_replies(ads, res);
570         if (count == 0) {
571                 status = NT_STATUS_OK;
572                 goto done;
573         }
574
575         members = ads_pull_strings(ads, mem_ctx, res, "member");
576         if (!members) {
577                 /* no members? ok ... */
578                 status = NT_STATUS_OK;
579                 goto done;
580         }
581
582         /* now we need to turn a list of members into rids, names and name types 
583            the problem is that the members are in the form of distinguised names
584         */
585         for (i=0;members[i];i++) /* noop */ ;
586         num_members = i;
587
588         (*rid_mem) = talloc_zero(mem_ctx, sizeof(uint32) * num_members);
589         (*name_types) = talloc_zero(mem_ctx, sizeof(uint32) * num_members);
590         (*names) = talloc_zero(mem_ctx, sizeof(char *) * num_members);
591
592         for (i=0;i<num_members;i++) {
593                 uint32 name_type, rid;
594                 char *name;
595
596                 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &rid)) {
597                     (*names)[*num_names] = name;
598                     (*name_types)[*num_names] = name_type;
599                     (*rid_mem)[*num_names] = rid;
600                     (*num_names)++;
601                 }
602         }       
603
604         status = NT_STATUS_OK;
605         DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid));
606 done:
607         if (res) ads_msgfree(ads, res);
608
609         return status;
610 }
611
612
613 /* find the sequence number for a domain */
614 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
615 {
616         ADS_STRUCT *ads = NULL;
617         ADS_STATUS rc;
618
619         *seq = DOM_SEQUENCE_NONE;
620
621         ads = ads_cached_connection(domain);
622         if (!ads) return NT_STATUS_UNSUCCESSFUL;
623
624         rc = ads_USN(ads, seq);
625         if (!ADS_ERR_OK(rc)) {
626                 /* its a dead connection */
627                 ads_destroy(&ads);
628                 domain->private = NULL;
629         }
630         return ads_ntstatus(rc);
631 }
632
633 /* get a list of trusted domains */
634 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
635                                 TALLOC_CTX *mem_ctx,
636                                 uint32 *num_domains,
637                                 char ***names,
638                                 char ***alt_names,
639                                 DOM_SID **dom_sids)
640 {
641         ADS_STRUCT *ads;
642         ADS_STATUS rc;
643
644         *num_domains = 0;
645         *names = NULL;
646
647         ads = ads_cached_connection(domain);
648         if (!ads) return NT_STATUS_UNSUCCESSFUL;
649
650         rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids);
651
652         return ads_ntstatus(rc);
653 }
654
655 /* find the domain sid for a domain */
656 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
657 {
658         ADS_STRUCT *ads;
659         ADS_STATUS rc;
660
661         ads = ads_cached_connection(domain);
662         if (!ads) return NT_STATUS_UNSUCCESSFUL;
663
664         rc = ads_domain_sid(ads, sid);
665
666         if (!ADS_ERR_OK(rc)) {
667                 /* its a dead connection */
668                 ads_destroy(&ads);
669                 domain->private = NULL;
670         }
671
672         return ads_ntstatus(rc);
673 }
674
675
676 /* find alternate names list for the domain - for ADS this is the
677    netbios name */
678 static NTSTATUS alternate_name(struct winbindd_domain *domain)
679 {
680         ADS_STRUCT *ads;
681         ADS_STATUS rc;
682         TALLOC_CTX *ctx;
683         char *workgroup;
684
685         ads = ads_cached_connection(domain);
686         if (!ads) return NT_STATUS_UNSUCCESSFUL;
687
688         if (!(ctx = talloc_init("alternate_name"))) {
689                 return NT_STATUS_NO_MEMORY;
690         }
691
692         rc = ads_workgroup_name(ads, ctx, &workgroup);
693
694         if (ADS_ERR_OK(rc)) {
695                 fstrcpy(domain->name, workgroup);
696                 fstrcpy(domain->alt_name, ads->config.realm);
697                 strupper(domain->alt_name);
698                 strupper(domain->name);
699         }
700
701         talloc_destroy(ctx);
702
703         return ads_ntstatus(rc);        
704 }
705
706 /* the ADS backend methods are exposed via this structure */
707 struct winbindd_methods ads_methods = {
708         True,
709         query_user_list,
710         enum_dom_groups,
711         enum_local_groups,
712         name_to_sid,
713         sid_to_name,
714         query_user,
715         lookup_usergroups,
716         lookup_groupmem,
717         sequence_number,
718         trusted_domains,
719         domain_sid,
720         alternate_name
721 };
722
723 #endif