reduced memory usage in winbindd with a rpc backend by using a
[tprouty/samba.git] / source / nsswitch / winbindd_rpc.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind rpc backend functions
5
6    Copyright (C) Tim Potter 2000-2001
7    Copyright (C) Andrew Tridgell 2001
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "winbindd.h"
25
26 /* Query display info for a domain.  This returns enough information plus a
27    bit extra to give an overview of domain users for the User Manager
28    application. */
29 static NTSTATUS query_user_list(struct winbindd_domain *domain,
30                                TALLOC_CTX *mem_ctx,
31                                uint32 *num_entries, 
32                                WINBIND_USERINFO **info)
33 {
34         CLI_POLICY_HND *hnd;
35         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
36         POLICY_HND dom_pol;
37         BOOL got_dom_pol = False;
38         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
39         int i;
40
41         *num_entries = 0;
42         *info = NULL;
43
44         /* Get sam handle */
45
46         if (!(hnd = cm_get_sam_handle(domain->name)))
47                 goto done;
48
49         /* Get domain handle */
50
51         result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
52                                         des_access, &domain->sid, &dom_pol);
53
54         if (!NT_STATUS_IS_OK(result))
55                 goto done;
56
57         got_dom_pol = True;
58
59         i = 0;
60         do {
61                 SAM_DISPINFO_CTR ctr;
62                 SAM_DISPINFO_1 info1;
63                 uint32 count = 0, start=i;
64                 int j;
65                 TALLOC_CTX *ctx2;
66
67                 ctr.sam.info1 = &info1;
68
69                 ctx2 = talloc_init_named("winbindd dispinfo");
70                 if (!ctx2) return NT_STATUS_NO_MEMORY;
71                 
72                 /* Query display info level 1 */
73                 result = cli_samr_query_dispinfo(hnd->cli, ctx2,
74                                                  &dom_pol, &start, 1,
75                                                  &count, 0xFFFF, &ctr);
76
77                 if (!NT_STATUS_IS_OK(result) && 
78                     !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) break;
79
80                 (*num_entries) += count;
81
82                 /* now map the result into the WINBIND_USERINFO structure */
83                 (*info) = talloc_realloc(mem_ctx, *info,
84                                          (*num_entries)*sizeof(WINBIND_USERINFO));
85                 if (!(*info)) {
86                         return NT_STATUS_NO_MEMORY;
87                 }
88
89                 for (j=0;j<count;i++, j++) {
90                         (*info)[i].acct_name = unistr2_tdup(mem_ctx, &info1.str[j].uni_acct_name);
91                         (*info)[i].full_name = unistr2_tdup(mem_ctx, &info1.str[j].uni_full_name);
92                         (*info)[i].user_rid = info1.sam[j].rid_user;
93                         /* For the moment we set the primary group for
94                            every user to be the Domain Users group.
95                            There are serious problems with determining
96                            the actual primary group for large domains.
97                            This should really be made into a 'winbind
98                            force group' smb.conf parameter or
99                            something like that. */
100                         (*info)[i].group_rid = DOMAIN_GROUP_RID_USERS;
101                 }
102
103                 talloc_destroy(ctx2);
104         } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
105
106  done:
107
108         if (got_dom_pol)
109                 cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
110
111         return result;
112 }
113
114 /* list all domain groups */
115 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
116                                 TALLOC_CTX *mem_ctx,
117                                 uint32 *num_entries, 
118                                 struct acct_info **info)
119 {
120         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
121         CLI_POLICY_HND *hnd;
122         POLICY_HND dom_pol;
123         NTSTATUS status;
124
125         *num_entries = 0;
126         *info = NULL;
127
128         if (!(hnd = cm_get_sam_handle(domain->name))) {
129                 return NT_STATUS_UNSUCCESSFUL;
130         }
131
132         status = cli_samr_open_domain(hnd->cli, mem_ctx,
133                                       &hnd->pol, des_access, &domain->sid, &dom_pol);
134         if (!NT_STATUS_IS_OK(status)) {
135                 return status;
136         }
137
138         do {
139                 struct acct_info *info2 = NULL;
140                 uint32 count = 0, start = *num_entries;
141                 TALLOC_CTX *mem_ctx2;
142
143                 mem_ctx2 = talloc_init_named("enum_dom_groups[rpc]");
144
145                 status = cli_samr_enum_dom_groups(hnd->cli, mem_ctx2, &dom_pol,
146                                                   &start,
147                                                   0xFFFF, /* buffer size? */
148                                                   &info2, &count);
149
150                 if (!NT_STATUS_IS_OK(status) && 
151                     !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
152                         talloc_destroy(mem_ctx2);
153                         break;
154                 }
155
156                 (*info) = talloc_realloc(mem_ctx, *info, 
157                                          sizeof(**info) * ((*num_entries) + count));
158                 if (! *info) {
159                         talloc_destroy(mem_ctx2);
160                         return NT_STATUS_NO_MEMORY;
161                 }
162
163                 memcpy(&(*info)[*num_entries], info2, count*sizeof(*info2));
164                 (*num_entries) += count;
165                 talloc_destroy(mem_ctx2);
166         } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
167
168         cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
169
170         return status;
171 }
172
173 /* convert a single name to a sid in a domain */
174 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
175                             const char *name,
176                             DOM_SID *sid,
177                             enum SID_NAME_USE *type)
178 {
179         TALLOC_CTX *mem_ctx;
180         CLI_POLICY_HND *hnd;
181         NTSTATUS status;
182         DOM_SID *sids = NULL;
183         uint32 *types = NULL;
184         int num_sids;
185         const char *full_name;
186
187         if (!(mem_ctx = talloc_init_named("name_to_sid[rpc] for [%s]\\[%s]", domain->name, name))) {
188                 DEBUG(0, ("talloc_init failed!\n"));
189                 return NT_STATUS_NO_MEMORY;
190         }
191         
192         if (!(hnd = cm_get_lsa_handle(domain->name))) {
193                 talloc_destroy(mem_ctx);
194                 return NT_STATUS_UNSUCCESSFUL;
195         }
196         
197         full_name = talloc_asprintf(mem_ctx, "%s\\%s", domain->name, name);
198         
199         if (!full_name) {
200                 DEBUG(0, ("talloc_asprintf failed!\n"));
201                 talloc_destroy(mem_ctx);
202                 return NT_STATUS_NO_MEMORY;
203         }
204
205         status = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol, 1, 
206                                       &full_name, &sids, &types, &num_sids);
207         
208         /* Return rid and type if lookup successful */        
209         if (NT_STATUS_IS_OK(status)) {
210                 sid_copy(sid, &sids[0]);
211                 *type = types[0];
212         }
213
214         talloc_destroy(mem_ctx);
215         return status;
216 }
217
218 /*
219   convert a domain SID to a user or group name
220 */
221 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
222                             TALLOC_CTX *mem_ctx,
223                             DOM_SID *sid,
224                             char **name,
225                             enum SID_NAME_USE *type)
226 {
227         CLI_POLICY_HND *hnd;
228         char **domains;
229         char **names;
230         uint32 *types;
231         int num_names;
232         NTSTATUS status;
233
234         if (!(hnd = cm_get_lsa_handle(domain->name)))
235                 return NT_STATUS_UNSUCCESSFUL;
236         
237         status = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol,
238                                      1, sid, &domains, &names, &types, 
239                                      &num_names);
240
241         if (NT_STATUS_IS_OK(status)) {
242                 *type = types[0];
243                 *name = names[0];
244                 DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name));
245
246                 /* Parinoia */
247                 if (strcasecmp(domain->name, domains[0]) != 0) {
248                         DEBUG(1, ("domain name from domain param and PDC lookup return differ! (%s vs %s)\n", domain->name, domains[0]));
249                         return NT_STATUS_UNSUCCESSFUL;
250                 }
251         }
252         return status;
253 }
254
255 /* Lookup user information from a rid or username. */
256 static NTSTATUS query_user(struct winbindd_domain *domain, 
257                            TALLOC_CTX *mem_ctx, 
258                            uint32 user_rid, 
259                            WINBIND_USERINFO *user_info)
260 {
261         CLI_POLICY_HND *hnd;
262         NTSTATUS result;
263         POLICY_HND dom_pol, user_pol;
264         BOOL got_dom_pol = False, got_user_pol = False;
265         SAM_USERINFO_CTR *ctr;
266
267         /* Get sam handle */
268         if (!(hnd = cm_get_sam_handle(domain->name)))
269                 goto done;
270
271         /* Get domain handle */
272
273         result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
274                                       SEC_RIGHTS_MAXIMUM_ALLOWED, 
275                                       &domain->sid, &dom_pol);
276
277         if (!NT_STATUS_IS_OK(result))
278                 goto done;
279
280         got_dom_pol = True;
281
282         /* Get user handle */
283         result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
284                                     SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, &user_pol);
285
286         if (!NT_STATUS_IS_OK(result))
287                 goto done;
288
289         /* Get user info */
290         result = cli_samr_query_userinfo(hnd->cli, mem_ctx, &user_pol, 
291                                          0x15, &ctr);
292
293         cli_samr_close(hnd->cli, mem_ctx, &user_pol);
294
295         user_info->group_rid = ctr->info.id21->group_rid;
296         user_info->acct_name = unistr2_tdup(mem_ctx, 
297                                             &ctr->info.id21->uni_user_name);
298         user_info->full_name = unistr2_tdup(mem_ctx, 
299                                             &ctr->info.id21->uni_full_name);
300
301  done:
302         /* Clean up policy handles */
303         if (got_user_pol)
304                 cli_samr_close(hnd->cli, mem_ctx, &user_pol);
305
306         if (got_dom_pol)
307                 cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
308
309         return result;
310 }                                   
311
312 /* Lookup groups a user is a member of.  I wish Unix had a call like this! */
313 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
314                                   TALLOC_CTX *mem_ctx,
315                                   uint32 user_rid, 
316                                   uint32 *num_groups, uint32 **user_gids)
317 {
318         CLI_POLICY_HND *hnd;
319         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
320         POLICY_HND dom_pol, user_pol;
321         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
322         BOOL got_dom_pol = False, got_user_pol = False;
323         DOM_GID *user_groups;
324         int i;
325
326         *num_groups = 0;
327
328         /* First try cached universal groups from logon */
329         *user_gids = uni_group_cache_fetch(&domain->sid, user_rid, mem_ctx, num_groups);
330         if((*num_groups > 0) && *user_gids) {
331                 return NT_STATUS_OK;
332         } else {
333             *user_gids = NULL;
334             *num_groups = 0;
335         }
336
337         /* Get sam handle */
338         if (!(hnd = cm_get_sam_handle(domain->name)))
339                 goto done;
340
341         /* Get domain handle */
342         result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
343                                         des_access, &domain->sid, &dom_pol);
344
345         if (!NT_STATUS_IS_OK(result))
346                 goto done;
347
348         got_dom_pol = True;
349
350         /* Get user handle */
351         result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol,
352                                         des_access, user_rid, &user_pol);
353
354         if (!NT_STATUS_IS_OK(result))
355                 goto done;
356
357         got_user_pol = True;
358
359         /* Query user rids */
360         result = cli_samr_query_usergroups(hnd->cli, mem_ctx, &user_pol, 
361                                            num_groups, &user_groups);
362
363         if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0)
364                 goto done;
365
366         (*user_gids) = talloc(mem_ctx, sizeof(uint32) * (*num_groups));
367         for (i=0;i<(*num_groups);i++) {
368                 (*user_gids)[i] = user_groups[i].g_rid;
369         }
370         
371  done:
372         /* Clean up policy handles */
373         if (got_user_pol)
374                 cli_samr_close(hnd->cli, mem_ctx, &user_pol);
375
376         if (got_dom_pol)
377                 cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
378
379         return result;
380 }
381
382
383 /* Lookup group membership given a rid.   */
384 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
385                                 TALLOC_CTX *mem_ctx,
386                                 uint32 group_rid, uint32 *num_names, 
387                                 uint32 **rid_mem, char ***names, 
388                                 uint32 **name_types)
389 {
390         CLI_POLICY_HND *hnd;
391         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
392         uint32 i, total_names = 0;
393         POLICY_HND dom_pol, group_pol;
394         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
395         BOOL got_dom_pol = False, got_group_pol = False;
396
397         *num_names = 0;
398
399         /* Get sam handle */
400
401         if (!(hnd = cm_get_sam_handle(domain->name)))
402                 goto done;
403
404         /* Get domain handle */
405
406         result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol,
407                                       des_access, &domain->sid, &dom_pol);
408
409         if (!NT_STATUS_IS_OK(result))
410                 goto done;
411
412         got_dom_pol = True;
413
414         /* Get group handle */
415
416         result = cli_samr_open_group(hnd->cli, mem_ctx, &dom_pol,
417                                      des_access, group_rid, &group_pol);
418
419         if (!NT_STATUS_IS_OK(result))
420                 goto done;
421
422         got_group_pol = True;
423
424         /* Step #1: Get a list of user rids that are the members of the
425            group. */
426
427         result = cli_samr_query_groupmem(hnd->cli, mem_ctx,
428                                          &group_pol, num_names, rid_mem,
429                                          name_types);
430
431         if (!NT_STATUS_IS_OK(result))
432                 goto done;
433
434         /* Step #2: Convert list of rids into list of usernames.  Do this
435            in bunches of ~1000 to avoid crashing NT4.  It looks like there
436            is a buffer overflow or something like that lurking around
437            somewhere. */
438
439 #define MAX_LOOKUP_RIDS 900
440
441         *names = talloc(mem_ctx, *num_names * sizeof(char *));
442         *name_types = talloc(mem_ctx, *num_names * sizeof(uint32));
443
444         for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) {
445                 int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS);
446                 uint32 tmp_num_names = 0;
447                 char **tmp_names = NULL;
448                 uint32 *tmp_types = NULL;
449
450                 /* Lookup a chunk of rids */
451
452                 result = cli_samr_lookup_rids(hnd->cli, mem_ctx,
453                                               &dom_pol, 1000, /* flags */
454                                               num_lookup_rids,
455                                               &(*rid_mem)[i],
456                                               &tmp_num_names,
457                                               &tmp_names, &tmp_types);
458
459                 if (!NT_STATUS_IS_OK(result))
460                         goto done;
461
462                 /* Copy result into array.  The talloc system will take
463                    care of freeing the temporary arrays later on. */
464
465                 memcpy(&(*names)[i], tmp_names, sizeof(char *) * 
466                        tmp_num_names);
467
468                 memcpy(&(*name_types)[i], tmp_types, sizeof(uint32) *
469                        tmp_num_names);
470
471                 total_names += tmp_num_names;
472         }
473
474         *num_names = total_names;
475
476  done:
477         if (got_group_pol)
478                 cli_samr_close(hnd->cli, mem_ctx, &group_pol);
479
480         if (got_dom_pol)
481                 cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
482
483         return result;
484 }
485
486 /* find the sequence number for a domain */
487 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
488 {
489         TALLOC_CTX *mem_ctx;
490         CLI_POLICY_HND *hnd;
491         SAM_UNK_CTR ctr;
492         uint16 switch_value = 2;
493         NTSTATUS result;
494         uint32 seqnum = DOM_SEQUENCE_NONE;
495         POLICY_HND dom_pol;
496         BOOL got_dom_pol = False;
497         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
498
499         *seq = DOM_SEQUENCE_NONE;
500
501         if (!(mem_ctx = talloc_init_named("sequence_number[rpc]")))
502                 return NT_STATUS_NO_MEMORY;
503
504         /* Get sam handle */
505
506         if (!(hnd = cm_get_sam_handle(domain->name)))
507                 goto done;
508
509         /* Get domain handle */
510
511         result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, 
512                                       des_access, &domain->sid, &dom_pol);
513
514         if (!NT_STATUS_IS_OK(result))
515                 goto done;
516
517         got_dom_pol = True;
518
519         /* Query domain info */
520
521         result = cli_samr_query_dom_info(hnd->cli, mem_ctx, &dom_pol,
522                                          switch_value, &ctr);
523
524         if (NT_STATUS_IS_OK(result)) {
525                 seqnum = ctr.info.inf2.seq_num;
526                 DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)seqnum ));
527         } else {
528                 DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n",
529                         (unsigned)seqnum, domain->name ));
530         }
531
532   done:
533
534         if (got_dom_pol)
535                 cli_samr_close(hnd->cli, mem_ctx, &dom_pol);
536
537         talloc_destroy(mem_ctx);
538
539         *seq = seqnum;
540
541         return result;
542 }
543
544 /* get a list of trusted domains */
545 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
546                                 TALLOC_CTX *mem_ctx,
547                                 uint32 *num_domains,
548                                 char ***names,
549                                 DOM_SID **dom_sids)
550 {
551         CLI_POLICY_HND *hnd;
552         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
553         uint32 enum_ctx = 0;
554
555         *num_domains = 0;
556
557         if (!(hnd = cm_get_lsa_handle(lp_workgroup())))
558                 goto done;
559
560         result = cli_lsa_enum_trust_dom(hnd->cli, mem_ctx,
561                                         &hnd->pol, &enum_ctx, num_domains, 
562                                         names, dom_sids);
563 done:
564         return result;
565 }
566
567 /* find the domain sid for a domain */
568 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
569 {
570         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
571         TALLOC_CTX *mem_ctx;
572         CLI_POLICY_HND *hnd;
573         fstring level5_dom;
574
575         if (!(mem_ctx = talloc_init_named("domain_sid[rpc]")))
576                 return NT_STATUS_NO_MEMORY;
577
578         /* Get sam handle */
579         if (!(hnd = cm_get_lsa_handle(domain->name)))
580                 goto done;
581
582         status = cli_lsa_query_info_policy(hnd->cli, mem_ctx,
583                                            &hnd->pol, 0x05, level5_dom, sid);
584
585 done:
586         talloc_destroy(mem_ctx);
587         return status;
588 }
589
590 /* the rpc backend methods are exposed via this structure */
591 struct winbindd_methods msrpc_methods = {
592         False,
593         query_user_list,
594         enum_dom_groups,
595         name_to_sid,
596         sid_to_name,
597         query_user,
598         lookup_usergroups,
599         lookup_groupmem,
600         sequence_number,
601         trusted_domains,
602         domain_sid
603 };