Back-port of Volkers fix.
[samba.git] / source / nsswitch / winbindd_passdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind rpc backend functions
5
6    Copyright (C) Tim Potter 2000-2001,2003
7    Copyright (C) Simo Sorce 2003
8    Copyright (C) Volker Lendecke 2004
9    Copyright (C) Jeremy Allison 2008
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 static NTSTATUS enum_groups_internal(struct winbindd_domain *domain,
33                                 TALLOC_CTX *mem_ctx,
34                                 uint32 *num_entries, 
35                                 struct acct_info **info,
36                                 enum lsa_SidType sidtype)
37 {
38         struct pdb_search *search;
39         struct samr_displayentry *entries;
40         int i;
41         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
42
43         if (sidtype == SID_NAME_ALIAS) {
44                 search = pdb_search_aliases(&domain->sid);
45         } else {
46                 search = pdb_search_groups();
47         }
48
49         if (search == NULL) goto done;
50
51         *num_entries = pdb_search_entries(search, 0, 0xffffffff, &entries);
52         if (*num_entries == 0) {
53                 /* Zero entries isn't an error */
54                 result = NT_STATUS_OK;
55                 goto done;
56         }
57
58         *info = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
59         if (*info == NULL) {
60                 result = NT_STATUS_NO_MEMORY;
61                 goto done;
62         }
63
64         for (i=0; i<*num_entries; i++) {
65                 fstrcpy((*info)[i].acct_name, entries[i].account_name);
66                 fstrcpy((*info)[i].acct_desc, entries[i].description);
67                 (*info)[i].rid = entries[i].rid;
68         }
69
70         result = NT_STATUS_OK;
71  done:
72         pdb_search_destroy(search);
73         return result;
74 }
75
76 /* List all local groups (aliases) */
77 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
78                                 TALLOC_CTX *mem_ctx,
79                                 uint32 *num_entries, 
80                                 struct acct_info **info)
81 {
82         return enum_groups_internal(domain,
83                                 mem_ctx,
84                                 num_entries,
85                                 info,
86                                 SID_NAME_ALIAS);
87 }
88
89 /* convert a single name to a sid in a domain */
90 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
91                             TALLOC_CTX *mem_ctx,
92                             const char *domain_name,
93                             const char *name,
94                             DOM_SID *sid,
95                             enum lsa_SidType *type)
96 {
97         DEBUG(10, ("Finding name %s\n", name));
98
99         if ( !lookup_name( mem_ctx, name, LOOKUP_NAME_ALL, 
100                 NULL, NULL, sid, type ) )
101         {
102                 return NT_STATUS_NONE_MAPPED;
103         }
104
105         return NT_STATUS_OK;
106 }
107
108 /*
109   convert a domain SID to a user or group name
110 */
111 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
112                             TALLOC_CTX *mem_ctx,
113                             const DOM_SID *sid,
114                             char **domain_name,
115                             char **name,
116                             enum lsa_SidType *type)
117 {
118         const char *dom, *nam;
119
120         DEBUG(10, ("Converting SID %s\n", sid_string_static(sid)));
121
122         /* Paranoia check */
123         if (!sid_check_is_in_builtin(sid) &&
124             !sid_check_is_in_our_domain(sid)) {
125                 DEBUG(0, ("Possible deadlock: Trying to lookup SID %s with "
126                           "passdb backend\n", sid_string_static(sid)));
127                 return NT_STATUS_NONE_MAPPED;
128         }
129
130         if (!lookup_sid(mem_ctx, sid, &dom, &nam, type)) {
131                 return NT_STATUS_NONE_MAPPED;
132         }
133
134         *domain_name = talloc_strdup(mem_ctx, dom);
135         *name = talloc_strdup(mem_ctx, nam);
136
137         return NT_STATUS_OK;
138 }
139
140 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
141                               TALLOC_CTX *mem_ctx,
142                               const DOM_SID *domain_sid,
143                               uint32 *rids,
144                               size_t num_rids,
145                               char **domain_name,
146                               char ***names,
147                               enum lsa_SidType **types)
148 {
149         size_t i;
150         bool have_mapped;
151         bool have_unmapped;
152
153         *domain_name = NULL;
154         *names = NULL;
155         *types = NULL;
156
157         if (!num_rids) {
158                 return NT_STATUS_OK;
159         }
160
161         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
162         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
163
164         if ((*names == NULL) || (*types == NULL)) {
165                 return NT_STATUS_NO_MEMORY;
166         }
167
168         have_mapped = have_unmapped = false;
169
170         for (i=0; i<num_rids; i++) {
171                 DOM_SID sid;
172                 const char *dom = NULL, *nam = NULL;
173                 enum lsa_SidType type = SID_NAME_UNKNOWN;
174
175                 if (!sid_compose(&sid, domain_sid, rids[i])) {
176                         return NT_STATUS_INTERNAL_ERROR;
177                 }
178
179                 if (!lookup_sid(mem_ctx, &sid, &dom, &nam, &type)) {
180                         have_unmapped = true;
181                         (*types)[i] = SID_NAME_UNKNOWN;
182                         (*names)[i] = talloc_strdup(mem_ctx, "");
183                 } else {
184                         have_mapped = true;
185                         (*types)[i] = type;
186                         (*names)[i] = CONST_DISCARD(char *, nam);
187                 }
188
189                 if (domain_name == NULL) {
190                         *domain_name = CONST_DISCARD(char *, dom);
191                 } else {
192                         char *dname = CONST_DISCARD(char *, dom);
193                         TALLOC_FREE(dname);
194                 }
195         }
196
197         if (!have_mapped) {
198                 return NT_STATUS_NONE_MAPPED;
199         }
200         if (!have_unmapped) {
201                 return NT_STATUS_OK;
202         }
203         return STATUS_SOME_UNMAPPED;
204 }
205
206 /* Lookup groups a user is a member of.  I wish Unix had a call like this! */
207 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
208                                   TALLOC_CTX *mem_ctx,
209                                   const DOM_SID *user_sid,
210                                   uint32 *num_groups, DOM_SID **user_gids)
211 {
212         NTSTATUS result;
213         DOM_SID *groups = NULL;
214         gid_t *gids = NULL;
215         size_t ngroups = 0;
216         struct samu *user;
217
218         if ( (user = samu_new(mem_ctx)) == NULL ) {
219                 return NT_STATUS_NO_MEMORY;
220         }
221
222         if (!pdb_getsampwsid(user, user_sid ) ) {
223                 return NT_STATUS_NO_SUCH_USER;
224         }
225
226         result = pdb_enum_group_memberships( mem_ctx, user, &groups, &gids, &ngroups );
227
228         TALLOC_FREE( user );
229
230         *num_groups = (uint32)ngroups;
231         *user_gids = groups;
232
233         return result;
234 }
235
236 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
237                                    TALLOC_CTX *mem_ctx,
238                                    uint32 num_sids, const DOM_SID *sids,
239                                    uint32 *p_num_aliases, uint32 **rids)
240 {
241         NTSTATUS result;
242         size_t num_aliases = 0;
243
244         result = pdb_enum_alias_memberships(mem_ctx, &domain->sid,
245                                             sids, num_sids, rids, &num_aliases);
246
247         *p_num_aliases = num_aliases;
248         return result;
249 }
250
251 /* find the sequence number for a domain */
252 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
253 {
254         BOOL result;
255         time_t seq_num;
256
257         result = pdb_get_seq_num(&seq_num);
258         if (!result) {
259                 *seq = 1;
260         }
261
262         *seq = (int) seq_num;
263         /* *seq = 1; */
264         return NT_STATUS_OK;
265 }
266
267 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
268                                TALLOC_CTX *mem_ctx,
269                                SAM_UNK_INFO_12 *policy)
270 {
271         /* actually we have that */
272         return NT_STATUS_NOT_IMPLEMENTED;
273 }
274
275 static NTSTATUS password_policy(struct winbindd_domain *domain,
276                                 TALLOC_CTX *mem_ctx,
277                                 SAM_UNK_INFO_1 *policy)
278 {
279         uint32 min_pass_len,pass_hist,password_properties;
280         time_t u_expire, u_min_age;
281         NTTIME nt_expire, nt_min_age;
282         uint32 account_policy_temp;
283
284         if ((policy = TALLOC_ZERO_P(mem_ctx, SAM_UNK_INFO_1)) == NULL) {
285                 return NT_STATUS_NO_MEMORY;
286         }
287
288         if (!pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &account_policy_temp)) {
289                 return NT_STATUS_ACCESS_DENIED;
290         }
291         min_pass_len = account_policy_temp;
292
293         if (!pdb_get_account_policy(AP_PASSWORD_HISTORY, &account_policy_temp)) {
294                 return NT_STATUS_ACCESS_DENIED;
295         }
296         pass_hist = account_policy_temp;
297
298         if (!pdb_get_account_policy(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp)) {
299                 return NT_STATUS_ACCESS_DENIED;
300         }
301         password_properties = account_policy_temp;
302
303         if (!pdb_get_account_policy(AP_MAX_PASSWORD_AGE, &account_policy_temp)) {
304                 return NT_STATUS_ACCESS_DENIED;
305         }
306         u_expire = account_policy_temp;
307
308         if (!pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &account_policy_temp)) {
309                 return NT_STATUS_ACCESS_DENIED;
310         }
311         u_min_age = account_policy_temp;
312
313         unix_to_nt_time_abs(&nt_expire, u_expire);
314         unix_to_nt_time_abs(&nt_min_age, u_min_age);
315
316         init_unk_info1(policy, (uint16)min_pass_len, (uint16)pass_hist, 
317                        password_properties, nt_expire, nt_min_age);
318
319         return NT_STATUS_OK;
320 }
321
322 /*********************************************************************
323  BUILTIN specific functions.
324 *********************************************************************/
325
326 /* list all domain groups */
327 static NTSTATUS builtin_enum_dom_groups(struct winbindd_domain *domain,
328                                 TALLOC_CTX *mem_ctx,
329                                 uint32 *num_entries, 
330                                 struct acct_info **info)
331 {
332         /* BUILTIN doesn't have domain groups */
333         *num_entries = 0;
334         *info = NULL;
335         return NT_STATUS_OK;
336 }
337
338 /* Query display info for a domain.  This returns enough information plus a
339    bit extra to give an overview of domain users for the User Manager
340    application. */
341 static NTSTATUS builtin_query_user_list(struct winbindd_domain *domain,
342                                TALLOC_CTX *mem_ctx,
343                                uint32 *num_entries, 
344                                WINBIND_USERINFO **info)
345 {
346         /* We don't have users */
347         *num_entries = 0;
348         *info = NULL;
349         return NT_STATUS_OK;
350 }
351
352 /* Lookup user information from a rid or username. */
353 static NTSTATUS builtin_query_user(struct winbindd_domain *domain, 
354                            TALLOC_CTX *mem_ctx, 
355                            const DOM_SID *user_sid,
356                            WINBIND_USERINFO *user_info)
357 {
358         return NT_STATUS_NO_SUCH_USER;
359 }
360
361 static NTSTATUS builtin_lookup_groupmem(struct winbindd_domain *domain,
362                                 TALLOC_CTX *mem_ctx,
363                                 const DOM_SID *group_sid, uint32 *num_names, 
364                                 DOM_SID **sid_mem, char ***names, 
365                                 uint32 **name_types)
366 {
367         *num_names = 0;
368         *sid_mem = NULL;
369         *names = NULL;
370         *name_types = 0;
371         return NT_STATUS_NO_SUCH_GROUP;
372 }
373
374 /* get a list of trusted domains - builtin domain */
375 static NTSTATUS builtin_trusted_domains(struct winbindd_domain *domain,
376                                 TALLOC_CTX *mem_ctx,
377                                 uint32 *num_domains,
378                                 char ***names,
379                                 char ***alt_names,
380                                 DOM_SID **dom_sids)
381 {
382         *num_domains = 0;
383         *names = NULL;
384         *alt_names = NULL;
385         *dom_sids = NULL;
386         return NT_STATUS_OK;
387 }
388
389 /*********************************************************************
390  SAM specific functions.
391 *********************************************************************/
392
393 /* list all domain groups */
394 static NTSTATUS sam_enum_dom_groups(struct winbindd_domain *domain,
395                                 TALLOC_CTX *mem_ctx,
396                                 uint32 *num_entries, 
397                                 struct acct_info **info)
398 {
399         return enum_groups_internal(domain,
400                                 mem_ctx,
401                                 num_entries,
402                                 info,
403                                 SID_NAME_DOM_GRP);
404 }
405
406 static NTSTATUS sam_query_user_list(struct winbindd_domain *domain,
407                                TALLOC_CTX *mem_ctx,
408                                uint32 *num_entries, 
409                                WINBIND_USERINFO **info)
410 {
411         struct pdb_search *ps = pdb_search_users(ACB_NORMAL);
412         struct samr_displayentry *entries = NULL;
413         uint32 i;
414
415         *num_entries = 0;
416         *info = NULL;
417
418         if (!ps) {
419                 return NT_STATUS_NO_MEMORY;
420         }
421
422         *num_entries = pdb_search_entries(ps,
423                                         1, 0xffffffff,
424                                         &entries);
425
426         *info = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
427         if (!(*info)) {
428                 pdb_search_destroy(ps);
429                 return NT_STATUS_NO_MEMORY;
430         }
431
432         for (i = 0; i < *num_entries; i++) {
433                 struct samr_displayentry *e = &entries[i];
434
435                 (*info)[i].acct_name = talloc_strdup(mem_ctx, e->account_name );
436                 (*info)[i].full_name = talloc_strdup(mem_ctx, e->fullname );
437                 (*info)[i].homedir = NULL;
438                 (*info)[i].shell = NULL;
439                 sid_compose(&(*info)[i].user_sid, &domain->sid, e->rid);
440
441                 /* For the moment we set the primary group for
442                 every user to be the Domain Users group.
443                 There are serious problems with determining
444                 the actual primary group for large domains.
445                 This should really be made into a 'winbind
446                 force group' smb.conf parameter or
447                 something like that. */
448
449                 sid_compose(&(*info)[i].group_sid, &domain->sid,
450                                 DOMAIN_GROUP_RID_USERS);
451         }
452
453         pdb_search_destroy(ps);
454         return NT_STATUS_OK;
455 }
456
457 /* Lookup user information from a rid or username. */
458 static NTSTATUS sam_query_user(struct winbindd_domain *domain,
459                            TALLOC_CTX *mem_ctx,
460                            const DOM_SID *user_sid,
461                            WINBIND_USERINFO *user_info)
462 {
463         struct samu *sampass = NULL;
464         fstring sidstr;
465
466         ZERO_STRUCTP(user_info);
467
468         if (!sid_check_is_in_our_domain(user_sid)) {
469                 return NT_STATUS_NO_SUCH_USER;
470         }
471
472         sid_to_string(sidstr, user_sid);
473         DEBUG(10,("sam_query_user: getting samu info for sid %s\n",
474                 sidstr ));
475
476         if (!(sampass = samu_new(mem_ctx))) {
477                 return NT_STATUS_NO_MEMORY;
478         }
479
480         if (!pdb_getsampwsid(sampass, user_sid)) {
481                 TALLOC_FREE(sampass);
482                 return NT_STATUS_NO_SUCH_USER;
483         }
484
485         if (pdb_get_group_sid(sampass) == NULL) {
486                 TALLOC_FREE(sampass);
487                 return NT_STATUS_NO_SUCH_GROUP;
488         }
489
490         sid_to_string(sidstr, sampass->group_sid);
491         DEBUG(10,("sam_query_user: group sid %s\n", sidstr ));
492
493         sid_copy(&user_info->user_sid, user_sid);
494         sid_copy(&user_info->group_sid, sampass->group_sid);
495
496         user_info->acct_name = talloc_strdup(mem_ctx, sampass->username ?
497                                         sampass->username : "");
498         user_info->full_name = talloc_strdup(mem_ctx, sampass->full_name ?
499                                         sampass->full_name : "");
500         user_info->homedir = talloc_strdup(mem_ctx, sampass->home_dir ?
501                                         sampass->home_dir : "");
502         if (sampass->unix_pw && sampass->unix_pw->pw_shell) {
503                 user_info->shell = talloc_strdup(mem_ctx, sampass->unix_pw->pw_shell);
504         } else {
505                 user_info->shell = talloc_strdup(mem_ctx, "");
506         }
507         user_info->primary_gid = sampass->unix_pw ? sampass->unix_pw->pw_gid : (gid_t)-1;
508
509         TALLOC_FREE(sampass);
510         return NT_STATUS_OK;
511 }
512
513 /* Lookup group membership given a rid.   */
514 static NTSTATUS sam_lookup_groupmem(struct winbindd_domain *domain,
515                                 TALLOC_CTX *mem_ctx,
516                                 const DOM_SID *group_sid, uint32 *num_names,
517                                 DOM_SID **sid_mem, char ***names,
518                                 uint32 **name_types)
519 {
520         size_t i, num_members, num_mapped;
521         uint32 *rids;
522         NTSTATUS result;
523         const DOM_SID **sids;
524         struct lsa_dom_info *lsa_domains;
525         struct lsa_name_info *lsa_names;
526         TALLOC_CTX *tmp_ctx;
527
528         if (!sid_check_is_in_our_domain(group_sid)) {
529                 /* There's no groups, only aliases in BUILTIN */
530                 return NT_STATUS_NO_SUCH_GROUP;
531         }
532
533         if (!(tmp_ctx = talloc_init("lookup_groupmem"))) {
534                 return NT_STATUS_NO_MEMORY;
535         }
536
537         result = pdb_enum_group_members(tmp_ctx, group_sid, &rids,
538                                         &num_members);
539         if (!NT_STATUS_IS_OK(result)) {
540                 TALLOC_FREE(tmp_ctx);
541                 return result;
542         }
543
544         if (num_members == 0) {
545                 *num_names = 0;
546                 *sid_mem = NULL;
547                 *names = NULL;
548                 *name_types = NULL;
549                 TALLOC_FREE(tmp_ctx);
550                 return NT_STATUS_OK;
551         }
552
553         *sid_mem = TALLOC_ARRAY(mem_ctx, DOM_SID, num_members);
554         *names = TALLOC_ARRAY(mem_ctx, char *, num_members);
555         *name_types = TALLOC_ARRAY(mem_ctx, uint32, num_members);
556         sids = TALLOC_ARRAY(tmp_ctx, const DOM_SID *, num_members);
557
558         if (((*sid_mem) == NULL) || ((*names) == NULL) ||
559             ((*name_types) == NULL) || (sids == NULL)) {
560                 TALLOC_FREE(tmp_ctx);
561                 return NT_STATUS_NO_MEMORY;
562         }
563
564         /*
565          * Prepare an array of sid pointers for the lookup_sids calling
566          * convention.
567          */
568
569         for (i=0; i<num_members; i++) {
570                 DOM_SID *sid = &((*sid_mem)[i]);
571                 if (!sid_compose(sid, &domain->sid, rids[i])) {
572                         TALLOC_FREE(tmp_ctx);
573                         return NT_STATUS_INTERNAL_ERROR;
574                 }
575                 sids[i] = sid;
576         }
577
578         result = lookup_sids(tmp_ctx, num_members, sids, 1,
579                              &lsa_domains, &lsa_names);
580         if (!NT_STATUS_IS_OK(result)) {
581                 TALLOC_FREE(tmp_ctx);
582                 return result;
583         }
584
585         num_mapped = 0;
586         for (i=0; i<num_members; i++) {
587                 if (lsa_names[i].type != SID_NAME_USER) {
588                         DEBUG(2, ("Got %s as group member -- ignoring\n",
589                                   sid_type_lookup(lsa_names[i].type)));
590                         continue;
591                 }
592                 if (!((*names)[i] = talloc_strdup((*names),
593                                                   lsa_names[i].name))) {
594                         TALLOC_FREE(tmp_ctx);
595                         return NT_STATUS_NO_MEMORY;
596                 }
597
598                 (*name_types)[i] = lsa_names[i].type;
599
600                 num_mapped += 1;
601         }
602
603         *num_names = num_mapped;
604
605         TALLOC_FREE(tmp_ctx);
606         return NT_STATUS_OK;
607 }
608
609 /* get a list of trusted domains - sam */
610 static NTSTATUS sam_trusted_domains(struct winbindd_domain *domain,
611                                 TALLOC_CTX *mem_ctx,
612                                 uint32 *num_domains,
613                                 char ***names,
614                                 char ***alt_names,
615                                 DOM_SID **dom_sids)
616 {
617         NTSTATUS nt_status;
618         struct trustdom_info **domains;
619         int i;
620         TALLOC_CTX *tmp_ctx;
621
622         *num_domains = 0;
623         *names = NULL;
624         *alt_names = NULL;
625         *dom_sids = NULL;
626
627         if (!(tmp_ctx = talloc_init("trusted_domains"))) {
628                 return NT_STATUS_NO_MEMORY;
629         }
630
631         nt_status = secrets_trusted_domains(tmp_ctx, num_domains,
632                                             &domains);
633         if (!NT_STATUS_IS_OK(nt_status)) {
634                 TALLOC_FREE(tmp_ctx);
635                 return nt_status;
636         }
637
638         if (*num_domains) {
639                 *names = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
640                 *alt_names = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
641                 *dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
642
643                 if ((*alt_names == NULL) || (*names == NULL) || (*dom_sids == NULL)) {
644                         TALLOC_FREE(tmp_ctx);
645                         return NT_STATUS_NO_MEMORY;
646                 }
647         } else {
648                 *names = NULL;
649                 *alt_names = NULL;
650                 *dom_sids = NULL;
651         }
652
653         for (i=0; i<*num_domains; i++) {
654                 (*alt_names)[i] = NULL;
655                 if (!((*names)[i] = talloc_strdup((*names),
656                                                   domains[i]->name))) {
657                         TALLOC_FREE(tmp_ctx);
658                         return NT_STATUS_NO_MEMORY;
659                 }
660                 sid_copy(&(*dom_sids)[i], &domains[i]->sid);
661         }
662
663         TALLOC_FREE(tmp_ctx);
664         return NT_STATUS_OK;
665 }
666
667 /* the rpc backend methods are exposed via this structure */
668 struct winbindd_methods builtin_passdb_methods = {
669         false,
670         builtin_query_user_list,
671         builtin_enum_dom_groups,
672         enum_local_groups,
673         name_to_sid,
674         sid_to_name,
675         rids_to_names,
676         builtin_query_user,
677         lookup_usergroups,
678         lookup_useraliases,
679         builtin_lookup_groupmem,
680         sequence_number,
681         lockout_policy,
682         password_policy,
683         builtin_trusted_domains,
684 };
685
686 /* the rpc backend methods are exposed via this structure */
687 struct winbindd_methods sam_passdb_methods = {
688         false,
689         sam_query_user_list,
690         sam_enum_dom_groups,
691         enum_local_groups,
692         name_to_sid,
693         sid_to_name,
694         rids_to_names,
695         sam_query_user,
696         lookup_usergroups,
697         lookup_useraliases,
698         sam_lookup_groupmem,
699         sequence_number,
700         lockout_policy,
701         password_policy,
702         sam_trusted_domains,
703 };