DEBUG(0, -> DEBUG(10, Caught by Andrew Bartlett (thanks !).
[Samba.git] / source / nsswitch / winbindd_user.c
blob420a12b547361ffc6562299380254fad7c888c82
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - user related functions
6 Copyright (C) Tim Potter 2000,2002
7 Copyright (C) Jeremy Allison 2001.
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.
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.
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.
24 #include "winbindd.h"
26 /* Fill a pwent structure with information we have obtained */
28 static BOOL winbindd_fill_pwent(char *dom_name, char *user_name,
29 uint32 user_rid, uint32 group_rid,
30 char *full_name, struct winbindd_pw *pw)
32 extern userdom_struct current_user_info;
33 fstring output_username;
34 pstring homedir;
36 if (!pw || !dom_name || !user_name)
37 return False;
39 /* Resolve the uid number */
41 if (!winbindd_idmap_get_uid_from_rid(dom_name, user_rid,
42 &pw->pw_uid)) {
43 DEBUG(1, ("error getting user id for rid %d\n", user_rid));
44 return False;
47 /* Resolve the gid number */
49 if (!winbindd_idmap_get_gid_from_rid(dom_name, group_rid,
50 &pw->pw_gid)) {
51 DEBUG(1, ("error getting group id for rid %d\n", group_rid));
52 return False;
55 /* Username */
57 fill_domain_username(output_username, dom_name, user_name);
59 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
61 /* Full name (gecos) */
63 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
65 /* Home directory and shell - use template config parameters. The
66 defaults are /tmp for the home directory and /bin/false for
67 shell. */
69 /* The substitution of %U and %D in the 'template homedir' is done
70 by lp_string() calling standard_sub_basic(). */
72 fstrcpy(current_user_info.smb_name, user_name);
73 fstrcpy(current_user_info.domain, dom_name);
75 pstrcpy(homedir, lp_template_homedir());
77 safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
79 safe_strcpy(pw->pw_shell, lp_template_shell(),
80 sizeof(pw->pw_shell) - 1);
82 /* Password - set to "x" as we can't generate anything useful here.
83 Authentication can be done using the pam_winbind module. */
85 safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
87 return True;
90 /* Return a password structure from a username. */
92 enum winbindd_result winbindd_getpwnam(struct winbindd_cli_state *state)
94 uint32 user_rid;
95 WINBIND_USERINFO user_info;
96 DOM_SID user_sid;
97 NTSTATUS status;
98 fstring name_domain, name_user;
99 enum SID_NAME_USE name_type;
100 struct winbindd_domain *domain;
101 TALLOC_CTX *mem_ctx;
103 DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
104 state->request.data.username));
106 /* Parse domain and username */
108 if (!parse_domain_user(state->request.data.username, name_domain,
109 name_user))
110 return WINBINDD_ERROR;
112 if ((domain = find_domain_from_name(name_domain)) == NULL) {
113 DEBUG(5, ("no such domain: %s\n", name_domain));
114 return WINBINDD_ERROR;
117 /* Get rid and name type from name */
119 if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, &name_type)) {
120 DEBUG(1, ("user '%s' does not exist\n", name_user));
121 return WINBINDD_ERROR;
124 if (name_type != SID_NAME_USER) {
125 DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
126 name_type));
127 return WINBINDD_ERROR;
130 /* Get some user info. Split the user rid from the sid obtained
131 from the winbind_lookup_by_name() call and use it in a
132 winbind_lookup_userinfo() */
134 if (!(mem_ctx = talloc_init_named("winbindd_getpwnam([%s]\\[%s])",
135 name_domain, name_user))) {
136 DEBUG(1, ("out of memory\n"));
137 return WINBINDD_ERROR;
140 sid_split_rid(&user_sid, &user_rid);
142 status = domain->methods->query_user(domain, mem_ctx, user_rid,
143 &user_info);
145 if (!NT_STATUS_IS_OK(status)) {
146 DEBUG(1, ("error getting user info for user '[%s]\\[%s]'\n",
147 name_domain, name_user));
148 talloc_destroy(mem_ctx);
149 return WINBINDD_ERROR;
152 /* Now take all this information and fill in a passwd structure */
153 if (!winbindd_fill_pwent(name_domain, name_user,
154 user_rid, user_info.group_rid,
155 user_info.full_name,
156 &state->response.data.pw)) {
157 talloc_destroy(mem_ctx);
158 return WINBINDD_ERROR;
161 talloc_destroy(mem_ctx);
163 return WINBINDD_OK;
166 /* Return a password structure given a uid number */
168 enum winbindd_result winbindd_getpwuid(struct winbindd_cli_state *state)
170 DOM_SID user_sid;
171 struct winbindd_domain *domain;
172 uint32 user_rid;
173 fstring dom_name;
174 fstring user_name;
175 enum SID_NAME_USE name_type;
176 WINBIND_USERINFO user_info;
177 gid_t gid;
178 TALLOC_CTX *mem_ctx;
179 NTSTATUS status;
181 /* Bug out if the uid isn't in the winbind range */
183 if ((state->request.data.uid < server_state.uid_low ) ||
184 (state->request.data.uid > server_state.uid_high))
185 return WINBINDD_ERROR;
187 DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid,
188 state->request.data.uid));
190 /* Get rid from uid */
192 if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid,
193 &user_rid, &domain)) {
194 DEBUG(1, ("could not convert uid %d to rid\n",
195 state->request.data.uid));
196 return WINBINDD_ERROR;
199 /* Get name and name type from rid */
201 sid_copy(&user_sid, &domain->sid);
202 sid_append_rid(&user_sid, user_rid);
204 if (!winbindd_lookup_name_by_sid(&user_sid, dom_name, user_name, &name_type)) {
205 fstring temp;
207 sid_to_string(temp, &user_sid);
208 DEBUG(1, ("could not lookup sid %s\n", temp));
209 return WINBINDD_ERROR;
212 /* Get some user info */
214 if (!(mem_ctx = talloc_init_named("winbind_getpwuid(%d)",
215 state->request.data.uid))) {
217 DEBUG(1, ("out of memory\n"));
218 return WINBINDD_ERROR;
221 status = domain->methods->query_user(domain, mem_ctx, user_rid,
222 &user_info);
224 if (!NT_STATUS_IS_OK(status)) {
225 DEBUG(1, ("error getting user info for user '%s'\n",
226 user_name));
227 talloc_destroy(mem_ctx);
228 return WINBINDD_ERROR;
231 /* Resolve gid number */
233 if (!winbindd_idmap_get_gid_from_rid(domain->name, user_info.group_rid, &gid)) {
234 DEBUG(1, ("error getting group id for user %s\n", user_name));
235 talloc_destroy(mem_ctx);
236 return WINBINDD_ERROR;
239 /* Fill in password structure */
241 if (!winbindd_fill_pwent(domain->name, user_name, user_rid, user_info.group_rid,
242 user_info.full_name, &state->response.data.pw)) {
243 talloc_destroy(mem_ctx);
244 return WINBINDD_ERROR;
247 talloc_destroy(mem_ctx);
249 return WINBINDD_OK;
253 * set/get/endpwent functions
256 /* Rewind file pointer for ntdom passwd database */
258 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
260 struct winbindd_domain *domain;
262 DEBUG(3, ("[%5d]: setpwent\n", state->pid));
264 /* Check user has enabled this */
266 if (!lp_winbind_enum_users())
267 return WINBINDD_ERROR;
269 /* Free old static data if it exists */
271 if (state->getpwent_state != NULL) {
272 free_getent_state(state->getpwent_state);
273 state->getpwent_state = NULL;
276 /* Create sam pipes for each domain we know about */
278 for(domain = domain_list(); domain != NULL; domain = domain->next) {
279 struct getent_state *domain_state;
282 * Skip domains other than WINBINDD_DOMAIN environment
283 * variable.
286 if ((strcmp(state->request.domain, "") != 0) &&
287 !check_domain_env(state->request.domain,
288 domain->name))
289 continue;
291 /* Create a state record for this domain */
293 if ((domain_state = (struct getent_state *)
294 malloc(sizeof(struct getent_state))) == NULL)
295 return WINBINDD_ERROR;
297 ZERO_STRUCTP(domain_state);
299 fstrcpy(domain_state->domain_name, domain->name);
301 /* Add to list of open domains */
303 DLIST_ADD(state->getpwent_state, domain_state);
306 return WINBINDD_OK;
309 /* Close file pointer to ntdom passwd database */
311 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
313 DEBUG(3, ("[%5d]: endpwent\n", state->pid));
315 free_getent_state(state->getpwent_state);
316 state->getpwent_state = NULL;
318 return WINBINDD_OK;
321 /* Get partial list of domain users for a domain. We fill in the sam_entries,
322 and num_sam_entries fields with domain user information. The dispinfo_ndx
323 field is incremented to the index of the next user to fetch. Return True if
324 some users were returned, False otherwise. */
326 #define MAX_FETCH_SAM_ENTRIES 100
328 static BOOL get_sam_user_entries(struct getent_state *ent)
330 NTSTATUS status;
331 uint32 num_entries;
332 WINBIND_USERINFO *info;
333 struct getpwent_user *name_list = NULL;
334 BOOL result = False;
335 TALLOC_CTX *mem_ctx;
336 struct winbindd_domain *domain;
337 struct winbindd_methods *methods;
338 int i;
340 if (ent->num_sam_entries)
341 return False;
343 if (!(mem_ctx = talloc_init_named("get_sam_user_entries(%s)",
344 ent->domain_name)))
345 return False;
347 if (!(domain = find_domain_from_name(ent->domain_name))) {
348 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
349 ent->domain_name));
350 return False;
353 methods = domain->methods;
355 /* Free any existing user info */
357 SAFE_FREE(ent->sam_entries);
358 ent->num_sam_entries = 0;
360 /* Call query_user_list to get a list of usernames and user rids */
362 num_entries = 0;
364 status = methods->query_user_list(domain, mem_ctx, &num_entries,
365 &info);
367 if (num_entries) {
368 struct getpwent_user *tnl;
370 tnl = (struct getpwent_user *)Realloc(name_list,
371 sizeof(struct getpwent_user) *
372 (ent->num_sam_entries +
373 num_entries));
375 if (!tnl) {
376 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
377 SAFE_FREE(name_list);
378 goto done;
379 } else
380 name_list = tnl;
383 for (i = 0; i < num_entries; i++) {
384 /* Store account name and gecos */
385 if (!info[i].acct_name) {
386 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
387 } else {
388 fstrcpy(name_list[ent->num_sam_entries + i].name,
389 info[i].acct_name);
391 if (!info[i].full_name) {
392 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
393 } else {
394 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
395 info[i].full_name);
398 /* User and group ids */
399 name_list[ent->num_sam_entries+i].user_rid = info[i].user_rid;
400 name_list[ent->num_sam_entries+i].group_rid = info[i].group_rid;
403 ent->num_sam_entries += num_entries;
405 /* Fill in remaining fields */
407 ent->sam_entries = name_list;
408 ent->sam_entry_index = 0;
409 result = ent->num_sam_entries > 0;
411 done:
413 talloc_destroy(mem_ctx);
415 return result;
418 /* Fetch next passwd entry from ntdom database */
420 #define MAX_GETPWENT_USERS 500
422 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
424 struct getent_state *ent;
425 struct winbindd_pw *user_list;
426 int num_users, user_list_ndx = 0, i;
428 DEBUG(3, ("[%5d]: getpwent\n", state->pid));
430 /* Check user has enabled this */
432 if (!lp_winbind_enum_users())
433 return WINBINDD_ERROR;
435 /* Allocate space for returning a chunk of users */
437 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
439 if ((state->response.extra_data =
440 malloc(num_users * sizeof(struct winbindd_pw))) == NULL)
441 return WINBINDD_ERROR;
443 memset(state->response.extra_data, 0, num_users *
444 sizeof(struct winbindd_pw));
446 user_list = (struct winbindd_pw *)state->response.extra_data;
448 if (!(ent = state->getpwent_state))
449 return WINBINDD_ERROR;
451 /* Start sending back users */
453 for (i = 0; i < num_users; i++) {
454 struct getpwent_user *name_list = NULL;
455 fstring domain_user_name;
456 uint32 result;
458 /* Do we need to fetch another chunk of users? */
460 if (ent->num_sam_entries == ent->sam_entry_index) {
462 while(ent && !get_sam_user_entries(ent)) {
463 struct getent_state *next_ent;
465 /* Free state information for this domain */
467 SAFE_FREE(ent->sam_entries);
469 next_ent = ent->next;
470 DLIST_REMOVE(state->getpwent_state, ent);
472 SAFE_FREE(ent);
473 ent = next_ent;
476 /* No more domains */
478 if (!ent)
479 break;
482 name_list = ent->sam_entries;
484 /* Skip machine accounts */
486 if (name_list[ent->sam_entry_index].
487 name[strlen(name_list[ent->sam_entry_index].name) - 1]
488 == '$') {
489 ent->sam_entry_index++;
490 continue;
493 /* Lookup user info */
495 result = winbindd_fill_pwent(
496 ent->domain_name,
497 name_list[ent->sam_entry_index].name,
498 name_list[ent->sam_entry_index].user_rid,
499 name_list[ent->sam_entry_index].group_rid,
500 name_list[ent->sam_entry_index].gecos,
501 &user_list[user_list_ndx]);
503 ent->sam_entry_index++;
505 /* Add user to return list */
507 if (result) {
509 user_list_ndx++;
510 state->response.data.num_entries++;
511 state->response.length +=
512 sizeof(struct winbindd_pw);
514 } else
515 DEBUG(1, ("could not lookup domain user %s\n",
516 domain_user_name));
519 /* Out of domains */
521 return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
524 /* List domain users without mapping to unix ids */
526 enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
528 struct winbindd_domain *domain;
529 WINBIND_USERINFO *info;
530 uint32 num_entries = 0, total_entries = 0;
531 char *ted, *extra_data = NULL;
532 int extra_data_len = 0;
533 TALLOC_CTX *mem_ctx;
534 enum winbindd_result rv = WINBINDD_ERROR;
536 DEBUG(3, ("[%5d]: list users\n", state->pid));
538 if (!(mem_ctx = talloc_init_named("winbindd_list_users")))
539 return WINBINDD_ERROR;
541 /* Enumerate over trusted domains */
543 for (domain = domain_list(); domain; domain = domain->next) {
544 NTSTATUS status;
545 struct winbindd_methods *methods;
546 int i;
548 /* Skip domains other than WINBINDD_DOMAIN environment
549 variable */
551 if ((strcmp(state->request.domain, "") != 0) &&
552 !check_domain_env(state->request.domain, domain->name))
553 continue;
555 methods = domain->methods;
557 /* Query display info */
559 status = methods->query_user_list(
560 domain, mem_ctx, &num_entries, &info);
562 /* If an error occured on this domain, set the extended error
563 info and continue to the next domain. If we receive
564 NT_STATUS_MORE_PROCESSING_REQUIRED then cached data was
565 returned but we couldn't contact the DC for the sequence
566 number. */
568 if (!NT_STATUS_IS_OK(status)) {
569 state->response.nt_status = NT_STATUS_V(status);
570 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
571 continue;
574 /* No entries for this domain */
576 if (num_entries == 0)
577 continue;
579 /* Allocate some memory for extra data */
581 total_entries += num_entries;
583 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
585 if (!ted) {
586 DEBUG(0,("failed to enlarge buffer!\n"));
587 SAFE_FREE(extra_data);
588 goto done;
589 } else
590 extra_data = ted;
592 /* Pack user list into extra data fields */
594 for (i = 0; i < num_entries; i++) {
595 fstring acct_name, name;
597 if (!info[i].acct_name) {
598 fstrcpy(acct_name, "");
599 } else {
600 fstrcpy(acct_name, info[i].acct_name);
603 fill_domain_username(name, domain->name, acct_name);
605 /* Append to extra data */
606 memcpy(&extra_data[extra_data_len], name,
607 strlen(name));
608 extra_data_len += strlen(name);
609 extra_data[extra_data_len++] = ',';
613 /* Assign extra_data fields in response structure */
615 if (extra_data) {
616 extra_data[extra_data_len - 1] = '\0';
617 state->response.extra_data = extra_data;
618 state->response.length += extra_data_len;
621 /* No domains responded but that's still OK so don't return an
622 error. */
624 rv = WINBINDD_OK;
626 done:
628 talloc_destroy(mem_ctx);
630 return rv;