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