240b0f524f619f1b7849f1b9b4915176a4acda01
[metze/samba/wip.git] / source3 / winbindd / winbindd_user.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - user related functions
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jeremy Allison 2001.
8    Copyright (C) Gerald (Jerry) Carter 2003.
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "winbindd.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 bool fillup_pw_field(const char *lp_template,
31                             const char *username,
32                             const char *domname,
33                             uid_t uid,
34                             gid_t gid,
35                             const char *in,
36                             fstring out)
37 {
38         char *templ;
39
40         if (out == NULL)
41                 return False;
42
43         /* The substitution of %U and %D in the 'template
44            homedir' is done by talloc_sub_specified() below.
45            If we have an in string (which means the value has already
46            been set in the nss_info backend), then use that.
47            Otherwise use the template value passed in. */
48
49         if ( in && !strequal(in,"") && lp_security() == SEC_ADS ) {
50                 templ = talloc_sub_specified(NULL, in,
51                                              username, domname,
52                                      uid, gid);
53         } else {
54                 templ = talloc_sub_specified(NULL, lp_template,
55                                              username, domname,
56                                              uid, gid);
57         }
58
59         if (!templ)
60                 return False;
61
62         safe_strcpy(out, templ, sizeof(fstring) - 1);
63         TALLOC_FREE(templ);
64
65         return True;
66
67 }
68 /* Fill a pwent structure with information we have obtained */
69
70 static bool winbindd_fill_pwent(TALLOC_CTX *ctx, char *dom_name, char *user_name,
71                                 DOM_SID *user_sid, DOM_SID *group_sid,
72                                 char *full_name, char *homedir, char *shell,
73                                 struct winbindd_pw *pw)
74 {
75         fstring output_username;
76         char *mapped_name = NULL;
77         struct winbindd_domain *domain = NULL;
78         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
79
80         if (!pw || !dom_name || !user_name)
81                 return False;
82
83         domain = find_domain_from_name_noinit(dom_name);
84         if (domain == NULL) {
85                 DEBUG(5,("winbindd_fill_pwent: Failed to find domain for %s.\n",
86                          dom_name));
87                 nt_status = NT_STATUS_NO_SUCH_DOMAIN;
88                 return false;
89         }
90
91         /* Resolve the uid number */
92
93         if (!NT_STATUS_IS_OK(idmap_sid_to_uid(domain->have_idmap_config ?
94                                               dom_name : "", user_sid,
95                                               &pw->pw_uid))) {
96                 DEBUG(1, ("error getting user id for sid %s\n",
97                           sid_string_dbg(user_sid)));
98                 return False;
99         }
100
101         /* Resolve the gid number */
102
103         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config ?
104                                               dom_name : "", group_sid,
105                                               &pw->pw_gid))) {
106                 DEBUG(1, ("error getting group id for sid %s\n",
107                           sid_string_dbg(group_sid)));
108                 return False;
109         }
110
111         /* Username */
112
113         strlower_m(user_name);
114         nt_status = normalize_name_map(ctx, domain, user_name, &mapped_name);
115
116         /* Basic removal of whitespace */
117         if (NT_STATUS_IS_OK(nt_status)) {
118                 fill_domain_username(output_username, dom_name, mapped_name, True);
119         }
120         /* Complete name replacement */
121         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
122                 fstrcpy(output_username, mapped_name);
123         }
124         /* No change at all */
125         else {
126                 fill_domain_username(output_username, dom_name, user_name, True);
127         }
128
129         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
130
131         /* Full name (gecos) */
132
133         safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
134
135         /* Home directory and shell */
136
137         if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name,
138                              pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
139                 return False;
140
141         if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
142                              pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
143                 return False;
144
145         /* Password - set to "*" as we can't generate anything useful here.
146            Authentication can be done using the pam_winbind module. */
147
148         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
149
150         return True;
151 }
152
153 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
154
155 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
156                                             struct winbindd_cli_state *state)
157 {
158         DOM_SID sid;
159         WINBIND_USERINFO user_info;
160         NTSTATUS status;
161
162         /* Ensure null termination */
163         state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
164
165         DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
166                   state->request->data.sid));
167
168         if (!string_to_sid(&sid, state->request->data.sid)) {
169                 DEBUG(5, ("%s not a SID\n", state->request->data.sid));
170                 return WINBINDD_ERROR;
171         }
172
173         status = domain->methods->query_user(domain, state->mem_ctx,
174                                              &sid, &user_info);
175         if (!NT_STATUS_IS_OK(status)) {
176                 DEBUG(1, ("error getting user info for sid %s\n",
177                           sid_string_dbg(&sid)));
178                 return WINBINDD_ERROR;
179         }
180
181         fstrcpy(state->response->data.user_info.acct_name,
182                 user_info.acct_name);
183         fstrcpy(state->response->data.user_info.full_name,
184                 user_info.full_name);
185         fstrcpy(state->response->data.user_info.homedir, user_info.homedir);
186         fstrcpy(state->response->data.user_info.shell, user_info.shell);
187         state->response->data.user_info.primary_gid = user_info.primary_gid;
188         if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
189                                 &state->response->data.user_info.group_rid)) {
190                 DEBUG(1, ("Could not extract group rid out of %s\n",
191                           sid_string_dbg(&sid)));
192                 return WINBINDD_ERROR;
193         }
194
195         return WINBINDD_OK;
196 }
197
198 /*
199  * set/get/endpwent functions
200  */
201
202 /* Rewind file pointer for ntdom passwd database */
203
204 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
205 {
206         struct winbindd_domain *domain;
207
208         DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
209
210         /* Check user has enabled this */
211
212         if (!lp_winbind_enum_users()) {
213                 return False;
214         }
215
216         /* Free old static data if it exists */
217
218         if (state->getpwent_state != NULL) {
219                 free_getent_state(state->getpwent_state);
220                 state->getpwent_state = NULL;
221         }
222
223         /* Create sam pipes for each domain we know about */
224
225         for(domain = domain_list(); domain != NULL; domain = domain->next) {
226                 struct getent_state *domain_state;
227
228
229                 /* don't add our domaina if we are a PDC or if we
230                    are a member of a Samba domain */
231
232                 if ((IS_DC || lp_winbind_trusted_domains_only())
233                         && strequal(domain->name, lp_workgroup())) {
234                         continue;
235                 }
236
237                 /* Create a state record for this domain */
238
239                 domain_state = SMB_MALLOC_P(struct getent_state);
240                 if (!domain_state) {
241                         DEBUG(0, ("malloc failed\n"));
242                         return False;
243                 }
244
245                 ZERO_STRUCTP(domain_state);
246
247                 fstrcpy(domain_state->domain_name, domain->name);
248
249                 /* Add to list of open domains */
250
251                 DLIST_ADD(state->getpwent_state, domain_state);
252         }
253
254         state->getpwent_initialized = True;
255         return True;
256 }
257
258 void winbindd_setpwent(struct winbindd_cli_state *state)
259 {
260         if (winbindd_setpwent_internal(state)) {
261                 request_ok(state);
262         } else {
263                 request_error(state);
264         }
265 }
266
267 /* Close file pointer to ntdom passwd database */
268
269 void winbindd_endpwent(struct winbindd_cli_state *state)
270 {
271         DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
272
273         free_getent_state(state->getpwent_state);
274         state->getpwent_initialized = False;
275         state->getpwent_state = NULL;
276         request_ok(state);
277 }
278
279 /* Get partial list of domain users for a domain.  We fill in the sam_entries,
280    and num_sam_entries fields with domain user information.  The dispinfo_ndx
281    field is incremented to the index of the next user to fetch.  Return True if
282    some users were returned, False otherwise. */
283
284 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
285 {
286         NTSTATUS status;
287         uint32 num_entries;
288         WINBIND_USERINFO *info;
289         struct getpwent_user *name_list = NULL;
290         struct winbindd_domain *domain;
291         struct winbindd_methods *methods;
292         unsigned int i;
293
294         if (ent->num_sam_entries)
295                 return False;
296
297         if (!(domain = find_domain_from_name(ent->domain_name))) {
298                 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
299                           ent->domain_name));
300                 return False;
301         }
302
303         methods = domain->methods;
304
305         /* Free any existing user info */
306
307         SAFE_FREE(ent->sam_entries);
308         ent->num_sam_entries = 0;
309
310         /* Call query_user_list to get a list of usernames and user rids */
311
312         num_entries = 0;
313
314         status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
315
316         if (!NT_STATUS_IS_OK(status)) {
317                 DEBUG(10,("get_sam_user_entries: "
318                           "query_user_list failed with %s\n",
319                           nt_errstr(status)));
320                 return False;
321         }
322
323         if (num_entries) {
324                 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user,
325                                             ent->num_sam_entries + num_entries);
326                 if (!name_list) {
327                         DEBUG(0,("get_sam_user_entries realloc failed.\n"));
328                         return False;
329                 }
330         }
331
332         for (i = 0; i < num_entries; i++) {
333                 /* Store account name and gecos */
334                 if (!info[i].acct_name) {
335                         fstrcpy(name_list[ent->num_sam_entries + i].name, "");
336                 } else {
337                         fstrcpy(name_list[ent->num_sam_entries + i].name,
338                                 info[i].acct_name);
339                 }
340                 if (!info[i].full_name) {
341                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
342                 } else {
343                         fstrcpy(name_list[ent->num_sam_entries + i].gecos,
344                                 info[i].full_name);
345                 }
346                 if (!info[i].homedir) {
347                         fstrcpy(name_list[ent->num_sam_entries + i].homedir,"");
348                 } else {
349                         fstrcpy(name_list[ent->num_sam_entries + i].homedir,
350                                 info[i].homedir);
351                 }
352                 if (!info[i].shell) {
353                         fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
354                 } else {
355                         fstrcpy(name_list[ent->num_sam_entries + i].shell,
356                                 info[i].shell);
357                 }
358
359
360                 /* User and group ids */
361                 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
362                          &info[i].user_sid);
363                 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
364                          &info[i].group_sid);
365         }
366
367         ent->num_sam_entries += num_entries;
368
369         /* Fill in remaining fields */
370
371         ent->sam_entries = name_list;
372         ent->sam_entry_index = 0;
373         return ent->num_sam_entries > 0;
374 }
375
376 /* Fetch next passwd entry from ntdom database */
377
378 #define MAX_GETPWENT_USERS 500
379
380 void winbindd_getpwent(struct winbindd_cli_state *state)
381 {
382         struct getent_state *ent;
383         struct winbindd_pw *user_list;
384         int num_users, user_list_ndx;
385
386         DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
387
388         /* Check user has enabled this */
389
390         if (!lp_winbind_enum_users()) {
391                 request_error(state);
392                 return;
393         }
394
395         /* Allocate space for returning a chunk of users */
396
397         num_users = MIN(MAX_GETPWENT_USERS, state->request->data.num_entries);
398
399         if (num_users == 0) {
400                 request_error(state);
401                 return;
402         }
403
404         user_list = talloc_zero_array(state->mem_ctx, struct winbindd_pw,
405                                       num_users);
406         if (!user_list) {
407                 request_error(state);
408                 return;
409         }
410         state->response->extra_data.data = user_list;
411
412         if (!state->getpwent_initialized)
413                 winbindd_setpwent_internal(state);
414
415         if (!(ent = state->getpwent_state)) {
416                 request_error(state);
417                 return;
418         }
419
420         /* Start sending back users */
421
422         for (user_list_ndx = 0; user_list_ndx < num_users; ) {
423                 struct getpwent_user *name_list = NULL;
424                 uint32 result;
425
426                 /* Do we need to fetch another chunk of users? */
427
428                 if (ent->num_sam_entries == ent->sam_entry_index) {
429
430                         while(ent &&
431                               !get_sam_user_entries(ent, state->mem_ctx)) {
432                                 struct getent_state *next_ent;
433
434                                 /* Free state information for this domain */
435
436                                 SAFE_FREE(ent->sam_entries);
437
438                                 next_ent = ent->next;
439                                 DLIST_REMOVE(state->getpwent_state, ent);
440
441                                 SAFE_FREE(ent);
442                                 ent = next_ent;
443                         }
444
445                         /* No more domains */
446
447                         if (!ent)
448                                 break;
449                 }
450
451                 name_list = (struct getpwent_user *)ent->sam_entries;
452
453                 /* Lookup user info */
454
455                 result = winbindd_fill_pwent(
456                         state->mem_ctx,
457                         ent->domain_name,
458                         name_list[ent->sam_entry_index].name,
459                         &name_list[ent->sam_entry_index].user_sid,
460                         &name_list[ent->sam_entry_index].group_sid,
461                         name_list[ent->sam_entry_index].gecos,
462                         name_list[ent->sam_entry_index].homedir,
463                         name_list[ent->sam_entry_index].shell,
464                         &user_list[user_list_ndx]);
465
466                 /* Add user to return list */
467
468                 if (result) {
469
470                         user_list_ndx++;
471                         state->response->data.num_entries++;
472                         state->response->length += sizeof(struct winbindd_pw);
473
474                 } else
475                         DEBUG(1, ("could not lookup domain user %s\n",
476                                   name_list[ent->sam_entry_index].name));
477
478                 ent->sam_entry_index++;
479
480         }
481
482         /* Out of domains */
483
484         if (user_list_ndx > 0)
485                 request_ok(state);
486         else
487                 request_error(state);
488 }
489
490 /* List domain users without mapping to unix ids */
491 void winbindd_list_users(struct winbindd_cli_state *state)
492 {
493         winbindd_list_ent(state, LIST_USERS);
494 }