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