idmap rewrite
[Samba.git] / source / winbindd / winbindd_user.c
blob3b6dfdda1c9db38dc07888e361d252f2e823048b
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - user related functions
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
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.
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.
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/>.
24 #include "includes.h"
25 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 static 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)
38 char *templ;
40 if (out == NULL)
41 return False;
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. */
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);
59 if (!templ)
60 return False;
62 safe_strcpy(out, templ, sizeof(fstring) - 1);
63 TALLOC_FREE(templ);
65 return True;
68 /* Fill a pwent structure with information we have obtained */
70 static bool winbindd_fill_pwent(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)
75 fstring output_username;
77 if (!pw || !dom_name || !user_name)
78 return False;
80 /* Resolve the uid number */
82 if (!NT_STATUS_IS_OK(idmap_sid_to_uid(dom_name, user_sid,
83 &pw->pw_uid))) {
84 DEBUG(1, ("error getting user id for sid %s\n",
85 sid_string_dbg(user_sid)));
86 return False;
89 /* Resolve the gid number */
91 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(dom_name, group_sid,
92 &pw->pw_gid))) {
93 DEBUG(1, ("error getting group id for sid %s\n",
94 sid_string_dbg(group_sid)));
95 return False;
98 strlower_m(user_name);
100 /* Username */
102 fill_domain_username(output_username, dom_name, user_name, True);
104 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
106 /* Full name (gecos) */
108 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
110 /* Home directory and shell */
112 if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name,
113 pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
114 return False;
116 if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
117 pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
118 return False;
120 /* Password - set to "*" as we can't generate anything useful here.
121 Authentication can be done using the pam_winbind module. */
123 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
125 return True;
128 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
130 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
131 struct winbindd_cli_state *state)
133 DOM_SID sid;
134 WINBIND_USERINFO user_info;
135 NTSTATUS status;
137 /* Ensure null termination */
138 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
140 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
141 state->request.data.sid));
143 if (!string_to_sid(&sid, state->request.data.sid)) {
144 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
145 return WINBINDD_ERROR;
148 status = domain->methods->query_user(domain, state->mem_ctx,
149 &sid, &user_info);
150 if (!NT_STATUS_IS_OK(status)) {
151 DEBUG(1, ("error getting user info for sid %s\n",
152 sid_string_dbg(&sid)));
153 return WINBINDD_ERROR;
156 fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
157 fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
158 fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
159 fstrcpy(state->response.data.user_info.shell, user_info.shell);
160 state->response.data.user_info.primary_gid = user_info.primary_gid;
161 if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
162 &state->response.data.user_info.group_rid)) {
163 DEBUG(1, ("Could not extract group rid out of %s\n",
164 sid_string_dbg(&sid)));
165 return WINBINDD_ERROR;
168 return WINBINDD_OK;
171 struct getpwsid_state {
172 struct winbindd_cli_state *state;
173 struct winbindd_domain *domain;
174 char *username;
175 char *fullname;
176 char *homedir;
177 char *shell;
178 DOM_SID user_sid;
179 uid_t uid;
180 DOM_SID group_sid;
181 gid_t gid;
184 static void getpwsid_queryuser_recv(void *private_data, bool success,
185 const char *acct_name,
186 const char *full_name,
187 const char *homedir,
188 const char *shell,
189 gid_t gid,
190 uint32 group_rid);
191 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid);
192 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid);
194 static void winbindd_getpwsid(struct winbindd_cli_state *state,
195 const DOM_SID *sid)
197 struct getpwsid_state *s;
199 s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
200 if (s == NULL) {
201 DEBUG(0, ("talloc failed\n"));
202 goto error;
205 s->state = state;
206 s->domain = find_domain_from_sid_noinit(sid);
207 if (s->domain == NULL) {
208 DEBUG(3, ("Could not find domain for sid %s\n",
209 sid_string_dbg(sid)));
210 goto error;
213 sid_copy(&s->user_sid, sid);
215 query_user_async(s->state->mem_ctx, s->domain, sid,
216 getpwsid_queryuser_recv, s);
217 return;
219 error:
220 request_error(state);
223 static void getpwsid_queryuser_recv(void *private_data, bool success,
224 const char *acct_name,
225 const char *full_name,
226 const char *homedir,
227 const char *shell,
228 gid_t gid,
229 uint32 group_rid)
231 fstring username;
232 struct getpwsid_state *s =
233 talloc_get_type_abort(private_data, struct getpwsid_state);
235 if (!success) {
236 DEBUG(5, ("Could not query domain %s SID %s\n",
237 s->domain->name, sid_string_dbg(&s->user_sid)));
238 request_error(s->state);
239 return;
242 if ( acct_name && *acct_name ) {
243 fstrcpy( username, acct_name );
244 } else {
245 char *domain_name = NULL;
246 enum lsa_SidType type;
247 char *user_name = NULL;
248 struct winbindd_domain *domain = NULL;
250 domain = find_lookup_domain_from_sid(&s->user_sid);
251 if (domain == NULL) {
252 DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
253 sid_string_dbg(&s->user_sid)));
254 request_error(s->state);
255 return;
257 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
258 &s->user_sid, &domain_name,
259 &user_name, &type );
261 /* If this still fails we ar4e done. Just error out */
262 if ( !user_name ) {
263 DEBUG(5,("Could not obtain a name for SID %s\n",
264 sid_string_dbg(&s->user_sid)));
265 request_error(s->state);
266 return;
269 fstrcpy( username, user_name );
272 strlower_m( username );
273 s->username = talloc_strdup(s->state->mem_ctx, username);
275 ws_name_replace( s->username, WB_REPLACE_CHAR );
277 s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
278 s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
279 s->shell = talloc_strdup(s->state->mem_ctx, shell);
280 s->gid = gid;
281 sid_copy(&s->group_sid, &s->domain->sid);
282 sid_append_rid(&s->group_sid, group_rid);
284 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
285 getpwsid_sid2uid_recv, s);
288 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
290 struct getpwsid_state *s =
291 talloc_get_type_abort(private_data, struct getpwsid_state);
293 if (!success) {
294 DEBUG(5, ("Could not query uid for user %s\\%s\n",
295 s->domain->name, s->username));
296 request_error(s->state);
297 return;
300 s->uid = uid;
301 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
302 getpwsid_sid2gid_recv, s);
305 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
307 struct getpwsid_state *s =
308 talloc_get_type_abort(private_data, struct getpwsid_state);
309 struct winbindd_pw *pw;
310 fstring output_username;
312 /* allow the nss backend to override the primary group ID.
313 If the gid has already been set, then keep it.
314 This makes me feel dirty. If the nss backend already
315 gave us a gid, we don't really care whether the sid2gid()
316 call worked or not. --jerry */
318 if ( s->gid == (gid_t)-1 ) {
320 if (!success) {
321 DEBUG(5, ("Could not query gid for user %s\\%s\n",
322 s->domain->name, s->username));
323 goto failed;
326 /* take what the sid2gid() call gave us */
327 s->gid = gid;
330 pw = &s->state->response.data.pw;
331 pw->pw_uid = s->uid;
332 pw->pw_gid = s->gid;
333 fill_domain_username(output_username, s->domain->name,
334 s->username, True);
335 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
336 safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
338 if (!fillup_pw_field(lp_template_homedir(), s->username,
339 s->domain->name, pw->pw_uid, pw->pw_gid,
340 s->homedir, pw->pw_dir)) {
341 DEBUG(5, ("Could not compose homedir\n"));
342 goto failed;
345 if (!fillup_pw_field(lp_template_shell(), s->username,
346 s->domain->name, pw->pw_uid, pw->pw_gid,
347 s->shell, pw->pw_shell)) {
348 DEBUG(5, ("Could not compose shell\n"));
349 goto failed;
352 /* Password - set to "*" as we can't generate anything useful here.
353 Authentication can be done using the pam_winbind module. */
355 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
357 request_ok(s->state);
358 return;
360 failed:
361 request_error(s->state);
364 /* Return a password structure from a username. */
366 static void getpwnam_name2sid_recv(void *private_data, bool success,
367 const DOM_SID *sid, enum lsa_SidType type);
369 void winbindd_getpwnam(struct winbindd_cli_state *state)
371 struct winbindd_domain *domain;
372 fstring domname, username;
373 char *domuser;
374 size_t dusize;
376 domuser = state->request.data.username;
377 dusize = sizeof(state->request.data.username);
379 /* Ensure null termination (it's an fstring) */
380 domuser[dusize-1] = '\0';
382 DEBUG(3, ("[%5lu]: getpwnam %s\n",
383 (unsigned long)state->pid,
384 domuser));
386 ws_name_return(domuser, WB_REPLACE_CHAR);
388 if (!parse_domain_user(domuser, domname, username)) {
389 DEBUG(5, ("Could not parse domain user: %s\n", domuser));
390 request_error(state);
391 return;
394 /* Get info for the domain */
396 domain = find_domain_from_name(domname);
398 if (domain == NULL) {
399 DEBUG(7, ("could not find domain entry for domain %s. "
400 "Using primary domain\n", domname));
401 if ( (domain = find_our_domain()) == NULL ) {
402 DEBUG(0,("Cannot find my primary domain structure!\n"));
403 request_error(state);
404 return;
408 if (strequal(domname, lp_workgroup()) &&
409 lp_winbind_trusted_domains_only() ) {
410 DEBUG(7,("winbindd_getpwnam: My domain -- "
411 "rejecting getpwnam() for %s\\%s.\n",
412 domname, username));
413 request_error(state);
414 return;
417 /* Get rid and name type from name. The following costs 1 packet */
419 winbindd_lookupname_async(state->mem_ctx, domname, username,
420 getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
421 state);
424 static void getpwnam_name2sid_recv(void *private_data, bool success,
425 const DOM_SID *sid, enum lsa_SidType type)
427 struct winbindd_cli_state *state =
428 (struct winbindd_cli_state *)private_data;
429 fstring domname, username;
430 char *domuser = state->request.data.username;
432 if (!success) {
433 DEBUG(5, ("Could not lookup name for user %s\n", domuser));
434 request_error(state);
435 return;
438 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
439 DEBUG(5, ("%s is not a user\n", domuser));
440 request_error(state);
441 return;
444 if (parse_domain_user(domuser, domname, username)) {
445 check_domain_trusted(domname, sid);
448 winbindd_getpwsid(state, sid);
451 static void getpwuid_recv(void *private_data, bool success, const char *sid)
453 struct winbindd_cli_state *state =
454 (struct winbindd_cli_state *)private_data;
455 DOM_SID user_sid;
457 if (!success) {
458 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
459 (unsigned long)(state->request.data.uid)));
460 request_error(state);
461 return;
464 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
465 (unsigned long)(state->request.data.uid), sid));
467 string_to_sid(&user_sid, sid);
468 winbindd_getpwsid(state, &user_sid);
471 /* Return a password structure given a uid number */
472 void winbindd_getpwuid(struct winbindd_cli_state *state)
474 uid_t uid = state->request.data.uid;
476 DEBUG(3, ("[%5lu]: getpwuid %lu\n",
477 (unsigned long)state->pid,
478 (unsigned long)uid));
480 /* always query idmap via the async interface */
481 /* if this turns to be too slow we will add here
482 * a direct query to the cache */
483 winbindd_uid2sid_async(state->mem_ctx, uid, getpwuid_recv, state);
487 * set/get/endpwent functions
490 /* Rewind file pointer for ntdom passwd database */
492 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
494 struct winbindd_domain *domain;
496 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
498 /* Check user has enabled this */
500 if (!lp_winbind_enum_users()) {
501 return False;
504 /* Free old static data if it exists */
506 if (state->getpwent_state != NULL) {
507 free_getent_state(state->getpwent_state);
508 state->getpwent_state = NULL;
511 /* Create sam pipes for each domain we know about */
513 for(domain = domain_list(); domain != NULL; domain = domain->next) {
514 struct getent_state *domain_state;
517 /* don't add our domaina if we are a PDC or if we
518 are a member of a Samba domain */
520 if ((IS_DC || lp_winbind_trusted_domains_only())
521 && strequal(domain->name, lp_workgroup())) {
522 continue;
525 /* Create a state record for this domain */
527 domain_state = SMB_MALLOC_P(struct getent_state);
528 if (!domain_state) {
529 DEBUG(0, ("malloc failed\n"));
530 return False;
533 ZERO_STRUCTP(domain_state);
535 fstrcpy(domain_state->domain_name, domain->name);
537 /* Add to list of open domains */
539 DLIST_ADD(state->getpwent_state, domain_state);
542 state->getpwent_initialized = True;
543 return True;
546 void winbindd_setpwent(struct winbindd_cli_state *state)
548 if (winbindd_setpwent_internal(state)) {
549 request_ok(state);
550 } else {
551 request_error(state);
555 /* Close file pointer to ntdom passwd database */
557 void winbindd_endpwent(struct winbindd_cli_state *state)
559 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
561 free_getent_state(state->getpwent_state);
562 state->getpwent_initialized = False;
563 state->getpwent_state = NULL;
564 request_ok(state);
567 /* Get partial list of domain users for a domain. We fill in the sam_entries,
568 and num_sam_entries fields with domain user information. The dispinfo_ndx
569 field is incremented to the index of the next user to fetch. Return True if
570 some users were returned, False otherwise. */
572 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
574 NTSTATUS status;
575 uint32 num_entries;
576 WINBIND_USERINFO *info;
577 struct getpwent_user *name_list = NULL;
578 struct winbindd_domain *domain;
579 struct winbindd_methods *methods;
580 unsigned int i;
582 if (ent->num_sam_entries)
583 return False;
585 if (!(domain = find_domain_from_name(ent->domain_name))) {
586 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
587 ent->domain_name));
588 return False;
591 methods = domain->methods;
593 /* Free any existing user info */
595 SAFE_FREE(ent->sam_entries);
596 ent->num_sam_entries = 0;
598 /* Call query_user_list to get a list of usernames and user rids */
600 num_entries = 0;
602 status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
604 if (!NT_STATUS_IS_OK(status)) {
605 DEBUG(10,("get_sam_user_entries: "
606 "query_user_list failed with %s\n",
607 nt_errstr(status)));
608 return False;
611 if (num_entries) {
612 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user,
613 ent->num_sam_entries + num_entries);
614 if (!name_list) {
615 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
616 return False;
620 for (i = 0; i < num_entries; i++) {
621 /* Store account name and gecos */
622 if (!info[i].acct_name) {
623 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
624 } else {
625 fstrcpy(name_list[ent->num_sam_entries + i].name,
626 info[i].acct_name);
628 if (!info[i].full_name) {
629 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
630 } else {
631 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
632 info[i].full_name);
634 if (!info[i].homedir) {
635 fstrcpy(name_list[ent->num_sam_entries + i].homedir,"");
636 } else {
637 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
638 info[i].homedir);
640 if (!info[i].shell) {
641 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
642 } else {
643 fstrcpy(name_list[ent->num_sam_entries + i].shell,
644 info[i].shell);
648 /* User and group ids */
649 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
650 &info[i].user_sid);
651 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
652 &info[i].group_sid);
655 ent->num_sam_entries += num_entries;
657 /* Fill in remaining fields */
659 ent->sam_entries = name_list;
660 ent->sam_entry_index = 0;
661 return ent->num_sam_entries > 0;
664 /* Fetch next passwd entry from ntdom database */
666 #define MAX_GETPWENT_USERS 500
668 void winbindd_getpwent(struct winbindd_cli_state *state)
670 struct getent_state *ent;
671 struct winbindd_pw *user_list;
672 int num_users, user_list_ndx;
674 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
676 /* Check user has enabled this */
678 if (!lp_winbind_enum_users()) {
679 request_error(state);
680 return;
683 /* Allocate space for returning a chunk of users */
685 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
687 if (num_users == 0) {
688 request_error(state);
689 return;
692 user_list = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users);
693 if (!user_list) {
694 request_error(state);
695 return;
697 /* will be freed by process_request() */
698 state->response.extra_data.data = user_list;
700 memset(user_list, 0, num_users * sizeof(struct winbindd_pw));
702 if (!state->getpwent_initialized)
703 winbindd_setpwent_internal(state);
705 if (!(ent = state->getpwent_state)) {
706 request_error(state);
707 return;
710 /* Start sending back users */
712 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
713 struct getpwent_user *name_list = NULL;
714 uint32 result;
716 /* Do we need to fetch another chunk of users? */
718 if (ent->num_sam_entries == ent->sam_entry_index) {
720 while(ent &&
721 !get_sam_user_entries(ent, state->mem_ctx)) {
722 struct getent_state *next_ent;
724 /* Free state information for this domain */
726 SAFE_FREE(ent->sam_entries);
728 next_ent = ent->next;
729 DLIST_REMOVE(state->getpwent_state, ent);
731 SAFE_FREE(ent);
732 ent = next_ent;
735 /* No more domains */
737 if (!ent)
738 break;
741 name_list = (struct getpwent_user *)ent->sam_entries;
743 /* Lookup user info */
745 result = winbindd_fill_pwent(
746 ent->domain_name,
747 name_list[ent->sam_entry_index].name,
748 &name_list[ent->sam_entry_index].user_sid,
749 &name_list[ent->sam_entry_index].group_sid,
750 name_list[ent->sam_entry_index].gecos,
751 name_list[ent->sam_entry_index].homedir,
752 name_list[ent->sam_entry_index].shell,
753 &user_list[user_list_ndx]);
755 /* Add user to return list */
757 if (result) {
759 user_list_ndx++;
760 state->response.data.num_entries++;
761 state->response.length += sizeof(struct winbindd_pw);
763 } else
764 DEBUG(1, ("could not lookup domain user %s\n",
765 name_list[ent->sam_entry_index].name));
767 ent->sam_entry_index++;
771 /* Out of domains */
773 if (user_list_ndx > 0)
774 request_ok(state);
775 else
776 request_error(state);
779 /* List domain users without mapping to unix ids */
780 void winbindd_list_users(struct winbindd_cli_state *state)
782 winbindd_list_ent(state, LIST_USERS);