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