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