nsswitch/winbindd_group.c nsswitch/winbindd_user.c: formatting fixups.
[ira/wip.git] / source3 / nsswitch / winbindd_user.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4
5    Winbind daemon - user related functions
6
7    Copyright (C) Tim Potter 2000
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 /* Fill a pwent structure with information we have obtained */
27
28 static BOOL winbindd_fill_pwent(char *domain_name, char *name, 
29                                 uint32 user_rid, uint32 group_rid, 
30                                 char *full_name, struct winbindd_pw *pw)
31 {
32         extern userdom_struct current_user_info;
33         fstring name_domain, name_user;
34         pstring homedir;
35         
36         if (!pw || !name)
37                 return False;
38         
39         /* Resolve the uid number */
40         
41         if (!winbindd_idmap_get_uid_from_rid(domain_name, user_rid, 
42                                              &pw->pw_uid)) {
43                 DEBUG(1, ("error getting user id for rid %d\n", user_rid));
44                 return False;
45         }
46         
47         /* Resolve the gid number */   
48         
49         if (!winbindd_idmap_get_gid_from_rid(domain_name, group_rid, 
50                                              &pw->pw_gid)) {
51                 DEBUG(1, ("error getting group id for rid %d\n", group_rid));
52                 return False;
53         }
54
55         /* Username */
56         
57         safe_strcpy(pw->pw_name, name, sizeof(pw->pw_name) - 1);
58         
59         /* Full name (gecos) */
60         
61         safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
62
63         /* Home directory and shell - use template config parameters.  The
64            defaults are /tmp for the home directory and /bin/false for
65            shell. */
66         
67         parse_domain_user(name, name_domain, name_user);
68         
69         /* The substitution of %U and %D in the 'template homedir' is done
70            by lp_string() calling standard_sub_basic(). */
71
72         fstrcpy(current_user_info.smb_name, name_user);
73         fstrcpy(current_user_info.domain, name_domain);
74
75         pstrcpy(homedir, lp_template_homedir());
76         
77         safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
78         
79         safe_strcpy(pw->pw_shell, lp_template_shell(), 
80                     sizeof(pw->pw_shell) - 1);
81         
82         /* Password - set to "x" as we can't generate anything useful here.
83            Authentication can be done using the pam_ntdom module. */
84
85         safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
86         
87         return True;
88 }
89
90 /* Return a password structure from a username.  Specify whether cached data 
91    can be returned. */
92
93 enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state 
94                                                  *state) 
95 {
96         uint32 user_rid, group_rid;
97         SAM_USERINFO_CTR *user_info;
98         DOM_SID user_sid;
99         fstring name_domain, name_user, name, gecos_name;
100         enum SID_NAME_USE name_type;
101         struct winbindd_domain *domain;
102         TALLOC_CTX *mem_ctx;
103         
104         DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
105                   state->request.data.username));
106         
107         /* Parse domain and username */
108
109         parse_domain_user(state->request.data.username, name_domain, 
110                           name_user);
111
112         /* Reject names that don't have a domain - i.e name_domain contains 
113            the entire name. */
114  
115         if (strequal(name_domain, ""))
116                 return WINBINDD_ERROR;
117         
118         if ((domain = find_domain_from_name(name_domain)) == NULL) {
119                 DEBUG(5, ("No such domain: %s\n", name_domain));
120                 return WINBINDD_ERROR;
121         }
122
123         /* Check for cached user entry */
124
125         if (winbindd_fetch_user_cache_entry(domain, name_user,
126                                             &state->response.data.pw))
127                 return WINBINDD_OK;
128         
129         slprintf(name, sizeof(name) - 1, "%s\\%s", name_domain, name_user);
130         
131         /* Get rid and name type from name */
132
133         if (!winbindd_lookup_sid_by_name(name, &user_sid, &name_type)) {
134                 DEBUG(1, ("user '%s' does not exist\n", name_user));
135                 return WINBINDD_ERROR;
136         }
137
138         if (name_type != SID_NAME_USER) {
139                 DEBUG(1, ("name '%s' is not a user name: %d\n", name_user, 
140                           name_type));
141                 return WINBINDD_ERROR;
142         }
143         
144         /* Get some user info.  Split the user rid from the sid obtained
145            from the winbind_lookup_by_name() call and use it in a
146            winbind_lookup_userinfo() */
147     
148         if (!(mem_ctx = talloc_init())) {
149                 DEBUG(1, ("out of memory\n"));
150                 return WINBINDD_ERROR;
151         }
152
153         sid_split_rid(&user_sid, &user_rid);
154         
155         if (!winbindd_lookup_userinfo(domain, mem_ctx, user_rid, &user_info)) {
156                 DEBUG(1, ("pwnam_from_user(): error getting user info for "
157                           "user '%s'\n", name_user));
158                 return WINBINDD_ERROR;
159         }
160     
161         group_rid = user_info->info.id21->group_rid;
162
163         unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
164                          sizeof(gecos_name) - 1);
165
166         talloc_destroy(mem_ctx);
167         user_info = NULL;
168
169         /* Now take all this information and fill in a passwd structure */
170         
171         if (!winbindd_fill_pwent(name_domain, state->request.data.username, 
172                                  user_rid, group_rid, gecos_name,
173                                  &state->response.data.pw)) {
174                 return WINBINDD_ERROR;
175         }
176         
177         winbindd_store_user_cache_entry(domain, name_user, 
178                                         &state->response.data.pw);
179         
180         return WINBINDD_OK;
181 }       
182
183 /* Return a password structure given a uid number */
184
185 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state 
186                                                 *state)
187 {
188         DOM_SID user_sid;
189         struct winbindd_domain *domain;
190         uint32 user_rid, group_rid;
191         fstring user_name, gecos_name;
192         enum SID_NAME_USE name_type;
193         SAM_USERINFO_CTR *user_info;
194         gid_t gid;
195         TALLOC_CTX *mem_ctx;
196         
197         /* Bug out if the uid isn't in the winbind range */
198
199         if ((state->request.data.uid < server_state.uid_low ) ||
200             (state->request.data.uid > server_state.uid_high))
201                 return WINBINDD_ERROR;
202
203         DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid, 
204                   state->request.data.uid));
205         
206         /* Get rid from uid */
207
208         if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid, 
209                                              &user_rid, &domain)) {
210                 DEBUG(1, ("Could not convert uid %d to rid\n", 
211                           state->request.data.uid));
212                 return WINBINDD_ERROR;
213         }
214         
215         /* Check for cached uid entry */
216
217         if (winbindd_fetch_uid_cache_entry(domain, 
218                                            state->request.data.uid,
219                                            &state->response.data.pw))
220                 return WINBINDD_OK;
221         
222         /* Get name and name type from rid */
223
224         sid_copy(&user_sid, &domain->sid);
225         sid_append_rid(&user_sid, user_rid);
226         
227         if (!winbindd_lookup_name_by_sid(&user_sid, user_name, &name_type)) {
228                 fstring temp;
229                 
230                 sid_to_string(temp, &user_sid);
231                 DEBUG(1, ("Could not lookup sid %s\n", temp));
232
233                 return WINBINDD_ERROR;
234         }
235         
236         if (strcmp("\\", lp_winbind_separator()))
237                 string_sub(user_name, "\\", lp_winbind_separator(), 
238                            sizeof(fstring));
239
240         /* Get some user info */
241         
242         if (!(mem_ctx = talloc_init())) {
243                 DEBUG(1, ("out of memory\n"));
244                 return WINBINDD_ERROR;
245         }
246
247         if (!winbindd_lookup_userinfo(domain, mem_ctx, user_rid, &user_info)) {
248                 DEBUG(1, ("pwnam_from_uid(): error getting user info for "
249                           "user '%s'\n", user_name));
250                 return WINBINDD_ERROR;
251         }
252         
253         group_rid = user_info->info.id21->group_rid;
254         unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
255                          sizeof(gecos_name) - 1);
256
257         talloc_destroy(mem_ctx);
258         user_info = NULL;
259
260         /* Resolve gid number */
261
262         if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
263                 DEBUG(1, ("error getting group id for user %s\n", user_name));
264                 return WINBINDD_ERROR;
265         }
266
267         /* Fill in password structure */
268
269         if (!winbindd_fill_pwent(domain->name, user_name, user_rid, group_rid,
270                                  gecos_name, &state->response.data.pw))
271                 return WINBINDD_ERROR;
272         
273         winbindd_store_uid_cache_entry(domain, state->request.data.uid,
274                                        &state->response.data.pw);
275         
276         return WINBINDD_OK;
277 }
278
279 /*
280  * set/get/endpwent functions
281  */
282
283 /* Rewind file pointer for ntdom passwd database */
284
285 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
286 {
287         struct winbindd_domain *tmp;
288         
289         DEBUG(3, ("[%5d]: setpwent\n", state->pid));
290         
291         /* Check user has enabled this */
292         
293         if (!lp_winbind_enum_users())
294                 return WINBINDD_ERROR;
295
296         /* Free old static data if it exists */
297         
298         if (state->getpwent_state != NULL) {
299                 free_getent_state(state->getpwent_state);
300                 state->getpwent_state = NULL;
301         }
302         
303         /* Create sam pipes for each domain we know about */
304         
305         if (domain_list == NULL)
306                 get_domain_info();
307
308         for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
309                 struct getent_state *domain_state;
310                 
311                 /* Skip domains other than WINBINDD_DOMAIN environment
312                    variable */
313                 
314                 if ((strcmp(state->request.domain, "") != 0) &&
315                     !check_domain_env(state->request.domain, tmp->name))
316                         continue;
317
318                 /* Create a state record for this domain */
319                 
320                 if ((domain_state = (struct getent_state *)
321                      malloc(sizeof(struct getent_state))) == NULL)
322                         return WINBINDD_ERROR;
323                 
324                 ZERO_STRUCTP(domain_state);
325
326                 domain_state->domain = tmp;
327
328                 /* Add to list of open domains */
329                 
330                 DLIST_ADD(state->getpwent_state, domain_state);
331         }
332         
333         return WINBINDD_OK;
334 }
335
336 /* Close file pointer to ntdom passwd database */
337
338 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
339 {
340         DEBUG(3, ("[%5d]: endpwent\n", state->pid));
341
342         free_getent_state(state->getpwent_state);    
343         state->getpwent_state = NULL;
344         
345         return WINBINDD_OK;
346 }
347
348 /* Get partial list of domain users for a domain.  We fill in the sam_entries,
349    and num_sam_entries fields with domain user information.  The dispinfo_ndx
350    field is incremented to the index of the next user to fetch.  Return True if
351    some users were returned, False otherwise. */
352
353 #define MAX_FETCH_SAM_ENTRIES 100
354
355 static BOOL get_sam_user_entries(struct getent_state *ent)
356 {
357         NTSTATUS status;
358         uint32 num_entries;
359         SAM_DISPINFO_1 info1;
360         SAM_DISPINFO_CTR ctr;
361         struct getpwent_user *name_list = NULL;
362         uint32 group_rid;
363         BOOL result = False;
364         TALLOC_CTX *mem_ctx;
365
366         if (ent->got_all_sam_entries)
367                 return False;
368
369         if (!(mem_ctx = talloc_init()))
370                 return False;
371
372         ZERO_STRUCT(info1);
373         ZERO_STRUCT(ctr);
374
375         ctr.sam.info1 = &info1;
376
377 #if 0
378         /* Look in cache for entries, else get them direct */
379                     
380         if (winbindd_fetch_user_cache(ent->domain,
381                                       (struct getpwent_user **)
382                                       &ent->sam_entries, 
383                                       &ent->num_sam_entries)) {
384                 return True;
385         }
386 #endif
387
388         /* For the moment we set the primary group for every user to be the
389            Domain Users group.  There are serious problems with determining
390            the actual primary group for large domains.  This should really
391            be made into a 'winbind force group' smb.conf parameter or
392            something like that. */ 
393
394         group_rid = DOMAIN_GROUP_RID_USERS;
395
396         /* Free any existing user info */
397
398         SAFE_FREE(ent->sam_entries);
399         ent->num_sam_entries = 0;
400         
401         /* Call query_dispinfo to get a list of usernames and user rids */
402
403         do {
404                 int i;
405                                         
406                 num_entries = 0;
407
408                 status = winbindd_query_dispinfo(ent->domain, mem_ctx,
409                                                  &ent->dispinfo_ndx, 1,
410                                                  &num_entries, &ctr);
411                 
412                 if (num_entries) {
413                         struct getpwent_user *tnl;
414
415                         tnl = (struct getpwent_user *)Realloc(name_list, 
416                                             sizeof(struct getpwent_user) *
417                                             (ent->num_sam_entries + 
418                                              num_entries));
419
420                         if (!tnl) {
421                                 DEBUG(0,("get_sam_user_entries: Realloc failed.\n"));
422                                 SAFE_FREE(name_list);
423                                 goto done;
424                         } else
425                                 name_list = tnl;
426                 }
427
428                 for (i = 0; i < num_entries; i++) {
429
430                         /* Store account name and gecos */
431
432                         unistr2_to_ascii(
433                                 name_list[ent->num_sam_entries + i].name, 
434                                 &info1.str[i].uni_acct_name, 
435                                 sizeof(fstring));
436
437                         unistr2_to_ascii(
438                                 name_list[ent->num_sam_entries + i].gecos, 
439                                 &info1.str[i].uni_full_name, 
440                                 sizeof(fstring));
441
442                         /* User and group ids */
443
444                         name_list[ent->num_sam_entries + i].user_rid =
445                                 info1.sam[i].rid_user;
446
447                         name_list[ent->num_sam_entries + i].
448                                 group_rid = group_rid;
449                 }
450                 
451                 ent->num_sam_entries += num_entries;
452
453                 if (NT_STATUS_V(status) != NT_STATUS_V(STATUS_MORE_ENTRIES))
454                         break;
455
456         } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES);
457         
458 #if 0
459         /* Fill cache with received entries */
460         
461         winbindd_store_user_cache(ent->domain, ent->sam_entries, 
462                                   ent->num_sam_entries);
463 #endif
464
465         /* Fill in remaining fields */
466         
467         ent->sam_entries = name_list;
468         ent->sam_entry_index = 0;
469         ent->got_all_sam_entries = (NT_STATUS_V(status) != 
470                                     NT_STATUS_V(STATUS_MORE_ENTRIES));
471
472         result = ent->num_sam_entries > 0;
473
474  done:
475         talloc_destroy(mem_ctx);
476
477         return result;
478 }
479
480 /* Fetch next passwd entry from ntdom database */
481
482 #define MAX_GETPWENT_USERS 500
483
484 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
485 {
486         struct getent_state *ent;
487         struct winbindd_pw *user_list;
488         int num_users, user_list_ndx = 0, i;
489         char *sep;
490
491         DEBUG(3, ("[%5d]: getpwent\n", state->pid));
492
493         /* Check user has enabled this */
494
495         if (!lp_winbind_enum_users())
496                 return WINBINDD_ERROR;
497
498         /* Allocate space for returning a chunk of users */
499
500         num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
501         
502         if ((state->response.extra_data = 
503              malloc(num_users * sizeof(struct winbindd_pw))) == NULL)
504                 return WINBINDD_ERROR;
505
506         memset(state->response.extra_data, 0, num_users * 
507                sizeof(struct winbindd_pw));
508
509         user_list = (struct winbindd_pw *)state->response.extra_data;
510         sep = lp_winbind_separator();
511         
512         if (!(ent = state->getpwent_state))
513                 return WINBINDD_ERROR;
514
515         /* Start sending back users */
516
517         for (i = 0; i < num_users; i++) {
518                 struct getpwent_user *name_list = NULL;
519                 fstring domain_user_name;
520                 uint32 result;
521
522                 /* Do we need to fetch another chunk of users? */
523
524                 if (ent->num_sam_entries == ent->sam_entry_index) {
525
526                         while(ent && !get_sam_user_entries(ent)) {
527                                 struct getent_state *next_ent;
528
529                                 /* Free state information for this domain */
530
531                                 SAFE_FREE(ent->sam_entries);
532
533                                 next_ent = ent->next;
534                                 DLIST_REMOVE(state->getpwent_state, ent);
535
536                                 SAFE_FREE(ent);
537                                 ent = next_ent;
538                         }
539  
540                         /* No more domains */
541
542                         if (!ent) 
543                                 break;
544                 }
545
546                 name_list = ent->sam_entries;
547
548                 /* Skip machine accounts */
549
550                 if (name_list[ent->sam_entry_index].
551                     name[strlen(name_list[ent->sam_entry_index].name) - 1] 
552                     == '$') {
553                         ent->sam_entry_index++;
554                         continue;
555                 }
556
557                 /* Lookup user info */
558                 
559                 slprintf(domain_user_name, sizeof(domain_user_name) - 1,
560                          "%s%s%s", ent->domain->name, sep,
561                          name_list[ent->sam_entry_index].name);
562                 
563                 result = winbindd_fill_pwent(
564                         ent->domain->name, 
565                         domain_user_name,
566                         name_list[ent->sam_entry_index].user_rid,
567                         name_list[ent->sam_entry_index].group_rid,
568                         name_list[ent->sam_entry_index].gecos,
569                         &user_list[user_list_ndx]);
570                 
571                 ent->sam_entry_index++;
572                 
573                 /* Add user to return list */
574                 
575                 if (result) {
576                                 
577                         user_list_ndx++;
578                         state->response.data.num_entries++;
579                         state->response.length += 
580                                 sizeof(struct winbindd_pw);
581
582                 } else
583                         DEBUG(1, ("could not lookup domain user %s\n",
584                                   domain_user_name));
585         }
586
587         /* Out of domains */
588
589         return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
590 }
591
592 /* List domain users without mapping to unix ids */
593
594 enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
595 {
596         struct winbindd_domain *domain;
597         SAM_DISPINFO_CTR ctr;
598         SAM_DISPINFO_1 info1;
599         uint32 num_entries = 0, total_entries = 0;
600         char *ted, *extra_data = NULL;
601         int extra_data_len = 0;
602         TALLOC_CTX *mem_ctx;
603         enum winbindd_result rv = WINBINDD_ERROR;
604
605         DEBUG(3, ("[%5d]: list users\n", state->pid));
606
607         if (!(mem_ctx = talloc_init()))
608                 return WINBINDD_ERROR;
609
610         /* Enumerate over trusted domains */
611
612         ctr.sam.info1 = &info1;
613
614         if (domain_list == NULL)
615                 get_domain_info();
616
617         for (domain = domain_list; domain; domain = domain->next) {
618                 NTSTATUS status;
619                 uint32 start_ndx = 0;
620
621                 /* Skip domains other than WINBINDD_DOMAIN environment
622                    variable */ 
623
624                 if ((strcmp(state->request.domain, "") != 0) &&
625                     !check_domain_env(state->request.domain, domain->name))
626                         continue;
627
628                 /* Query display info */
629
630                 do {
631                         int i;
632
633                         status = winbindd_query_dispinfo(
634                                 domain, mem_ctx, &start_ndx, 
635                                 1, &num_entries, &ctr);
636
637                         if (num_entries == 0)
638                                 continue;
639
640                         /* Allocate some memory for extra data */
641
642                         total_entries += num_entries;
643                         
644                         ted = Realloc(extra_data, sizeof(fstring) * 
645                                              total_entries);
646                         
647                         if (!ted) {
648                                 DEBUG(0,("winbindd_list_users: failed to enlarge buffer!\n"));
649                                 SAFE_FREE(extra_data);
650                                 goto done;
651                         } else 
652                                 extra_data = ted;
653                         
654                         /* Pack user list into extra data fields */
655                         
656                         for (i = 0; i < num_entries; i++) {
657                                 UNISTR2 *uni_acct_name;
658                                 fstring acct_name, name;
659
660                                 /* Convert unistring to ascii */
661                                 
662                                 uni_acct_name = &ctr.sam.info1->str[i]. 
663                                         uni_acct_name;
664                                 unistr2_to_ascii(acct_name, uni_acct_name,
665                                                  sizeof(acct_name) - 1);
666                                                  
667                                 slprintf(name, sizeof(name) - 1, "%s%s%s",
668                                          domain->name, lp_winbind_separator(),
669                                          acct_name);
670
671                                 /* Append to extra data */
672                         
673                                 memcpy(&extra_data[extra_data_len], name, 
674                                        strlen(name));
675                                 extra_data_len += strlen(name);
676                                 
677                                 extra_data[extra_data_len++] = ',';
678                         }   
679                 } while (NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES));
680         }
681
682         /* Assign extra_data fields in response structure */
683
684         if (extra_data) {
685                 extra_data[extra_data_len - 1] = '\0';
686                 state->response.extra_data = extra_data;
687                 state->response.length += extra_data_len;
688         }
689
690         /* No domains responded but that's still OK so don't return an
691            error. */
692
693         rv = WINBINDD_OK;
694
695  done:
696
697         talloc_destroy(mem_ctx);
698
699         return rv;
700 }