Fix an incompatible pointer warning
[Samba.git] / source / winbindd / winbindd_user.c
blob00e53eff74795d2c1b83e67f7795c07f1524c2ca
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(user_sid, &pw->pw_uid))) {
83 DEBUG(1, ("error getting user id for sid %s\n",
84 sid_string_dbg(user_sid)));
85 return False;
88 /* Resolve the gid number */
90 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid))) {
91 DEBUG(1, ("error getting group id for sid %s\n",
92 sid_string_dbg(group_sid)));
93 return False;
96 strlower_m(user_name);
98 /* Username */
100 fill_domain_username(output_username, dom_name, user_name, True);
102 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
104 /* Full name (gecos) */
106 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
108 /* Home directory and shell */
110 if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name,
111 pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
112 return False;
114 if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
115 pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
116 return False;
118 /* Password - set to "*" as we can't generate anything useful here.
119 Authentication can be done using the pam_winbind module. */
121 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
123 return True;
126 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
128 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
129 struct winbindd_cli_state *state)
131 DOM_SID sid;
132 WINBIND_USERINFO user_info;
133 NTSTATUS status;
135 /* Ensure null termination */
136 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
138 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
139 state->request.data.sid));
141 if (!string_to_sid(&sid, state->request.data.sid)) {
142 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
143 return WINBINDD_ERROR;
146 status = domain->methods->query_user(domain, state->mem_ctx,
147 &sid, &user_info);
148 if (!NT_STATUS_IS_OK(status)) {
149 DEBUG(1, ("error getting user info for sid %s\n",
150 sid_string_dbg(&sid)));
151 return WINBINDD_ERROR;
154 fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
155 fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
156 fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
157 fstrcpy(state->response.data.user_info.shell, user_info.shell);
158 state->response.data.user_info.primary_gid = user_info.primary_gid;
159 if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
160 &state->response.data.user_info.group_rid)) {
161 DEBUG(1, ("Could not extract group rid out of %s\n",
162 sid_string_dbg(&sid)));
163 return WINBINDD_ERROR;
166 return WINBINDD_OK;
169 struct getpwsid_state {
170 struct winbindd_cli_state *state;
171 struct winbindd_domain *domain;
172 char *username;
173 char *fullname;
174 char *homedir;
175 char *shell;
176 DOM_SID user_sid;
177 uid_t uid;
178 DOM_SID group_sid;
179 gid_t gid;
182 static void getpwsid_queryuser_recv(void *private_data, bool success,
183 const char *acct_name,
184 const char *full_name,
185 const char *homedir,
186 const char *shell,
187 gid_t gid,
188 uint32 group_rid);
189 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid);
190 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid);
192 static void winbindd_getpwsid(struct winbindd_cli_state *state,
193 const DOM_SID *sid)
195 struct getpwsid_state *s;
197 s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
198 if (s == NULL) {
199 DEBUG(0, ("talloc failed\n"));
200 goto error;
203 s->state = state;
204 s->domain = find_domain_from_sid_noinit(sid);
205 if (s->domain == NULL) {
206 DEBUG(3, ("Could not find domain for sid %s\n",
207 sid_string_dbg(sid)));
208 goto error;
211 sid_copy(&s->user_sid, sid);
213 query_user_async(s->state->mem_ctx, s->domain, sid,
214 getpwsid_queryuser_recv, s);
215 return;
217 error:
218 request_error(state);
221 static void getpwsid_queryuser_recv(void *private_data, bool success,
222 const char *acct_name,
223 const char *full_name,
224 const char *homedir,
225 const char *shell,
226 gid_t gid,
227 uint32 group_rid)
229 fstring username;
230 struct getpwsid_state *s =
231 talloc_get_type_abort(private_data, struct getpwsid_state);
233 if (!success) {
234 DEBUG(5, ("Could not query domain %s SID %s\n",
235 s->domain->name, sid_string_dbg(&s->user_sid)));
236 request_error(s->state);
237 return;
240 if ( acct_name && *acct_name ) {
241 fstrcpy( username, acct_name );
242 } else {
243 char *domain_name = NULL;
244 enum lsa_SidType type;
245 char *user_name = NULL;
246 struct winbindd_domain *domain = NULL;
248 domain = find_lookup_domain_from_sid(&s->user_sid);
249 if (domain == NULL) {
250 DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
251 sid_string_dbg(&s->user_sid)));
252 request_error(s->state);
253 return;
255 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
256 &s->user_sid, &domain_name,
257 &user_name, &type );
259 /* If this still fails we ar4e done. Just error out */
260 if ( !user_name ) {
261 DEBUG(5,("Could not obtain a name for SID %s\n",
262 sid_string_dbg(&s->user_sid)));
263 request_error(s->state);
264 return;
267 fstrcpy( username, user_name );
270 strlower_m( username );
271 s->username = talloc_strdup(s->state->mem_ctx, username);
273 ws_name_replace( s->username, WB_REPLACE_CHAR );
275 s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
276 s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
277 s->shell = talloc_strdup(s->state->mem_ctx, shell);
278 s->gid = gid;
279 sid_copy(&s->group_sid, &s->domain->sid);
280 sid_append_rid(&s->group_sid, group_rid);
282 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
283 getpwsid_sid2uid_recv, s);
286 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
288 struct getpwsid_state *s =
289 talloc_get_type_abort(private_data, struct getpwsid_state);
291 if (!success) {
292 DEBUG(5, ("Could not query uid for user %s\\%s\n",
293 s->domain->name, s->username));
294 request_error(s->state);
295 return;
298 s->uid = uid;
299 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
300 getpwsid_sid2gid_recv, s);
303 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
305 struct getpwsid_state *s =
306 talloc_get_type_abort(private_data, struct getpwsid_state);
307 struct winbindd_pw *pw;
308 fstring output_username;
310 /* allow the nss backend to override the primary group ID.
311 If the gid has already been set, then keep it.
312 This makes me feel dirty. If the nss backend already
313 gave us a gid, we don't really care whether the sid2gid()
314 call worked or not. --jerry */
316 if ( s->gid == (gid_t)-1 ) {
318 if (!success) {
319 DEBUG(5, ("Could not query gid for user %s\\%s\n",
320 s->domain->name, s->username));
321 goto failed;
324 /* take what the sid2gid() call gave us */
325 s->gid = gid;
328 pw = &s->state->response.data.pw;
329 pw->pw_uid = s->uid;
330 pw->pw_gid = s->gid;
331 fill_domain_username(output_username, s->domain->name,
332 s->username, True);
333 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
334 safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
336 if (!fillup_pw_field(lp_template_homedir(), s->username,
337 s->domain->name, pw->pw_uid, pw->pw_gid,
338 s->homedir, pw->pw_dir)) {
339 DEBUG(5, ("Could not compose homedir\n"));
340 goto failed;
343 if (!fillup_pw_field(lp_template_shell(), s->username,
344 s->domain->name, pw->pw_uid, pw->pw_gid,
345 s->shell, pw->pw_shell)) {
346 DEBUG(5, ("Could not compose shell\n"));
347 goto failed;
350 /* Password - set to "*" as we can't generate anything useful here.
351 Authentication can be done using the pam_winbind module. */
353 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
355 request_ok(s->state);
356 return;
358 failed:
359 request_error(s->state);
362 /* Return a password structure from a username. */
364 static void getpwnam_name2sid_recv(void *private_data, bool success,
365 const DOM_SID *sid, enum lsa_SidType type);
367 void winbindd_getpwnam(struct winbindd_cli_state *state)
369 struct winbindd_domain *domain;
370 fstring domname, username;
371 char *domuser;
372 size_t dusize;
374 domuser = state->request.data.username;
375 dusize = sizeof(state->request.data.username);
377 /* Ensure null termination (it's an fstring) */
378 domuser[dusize-1] = '\0';
380 DEBUG(3, ("[%5lu]: getpwnam %s\n",
381 (unsigned long)state->pid,
382 domuser));
384 ws_name_return(domuser, WB_REPLACE_CHAR);
386 if (!parse_domain_user(domuser, domname, username)) {
387 DEBUG(5, ("Could not parse domain user: %s\n", domuser));
388 request_error(state);
389 return;
392 /* Get info for the domain */
394 domain = find_domain_from_name(domname);
396 if (domain == NULL) {
397 DEBUG(7, ("could not find domain entry for domain %s. "
398 "Using primary domain\n", domname));
399 if ( (domain = find_our_domain()) == NULL ) {
400 DEBUG(0,("Cannot find my primary domain structure!\n"));
401 request_error(state);
402 return;
406 if (strequal(domname, lp_workgroup()) &&
407 lp_winbind_trusted_domains_only() ) {
408 DEBUG(7,("winbindd_getpwnam: My domain -- "
409 "rejecting getpwnam() for %s\\%s.\n",
410 domname, username));
411 request_error(state);
412 return;
415 /* Get rid and name type from name. The following costs 1 packet */
417 winbindd_lookupname_async(state->mem_ctx, domname, username,
418 getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
419 state);
422 static void getpwnam_name2sid_recv(void *private_data, bool success,
423 const DOM_SID *sid, enum lsa_SidType type)
425 struct winbindd_cli_state *state =
426 (struct winbindd_cli_state *)private_data;
427 fstring domname, username;
428 char *domuser = state->request.data.username;
430 if (!success) {
431 DEBUG(5, ("Could not lookup name for user %s\n", domuser));
432 request_error(state);
433 return;
436 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
437 DEBUG(5, ("%s is not a user\n", domuser));
438 request_error(state);
439 return;
442 if (parse_domain_user(domuser, domname, username)) {
443 check_domain_trusted(domname, sid);
446 winbindd_getpwsid(state, sid);
449 static void getpwuid_recv(void *private_data, bool success, const char *sid)
451 struct winbindd_cli_state *state =
452 (struct winbindd_cli_state *)private_data;
453 DOM_SID user_sid;
455 if (!success) {
456 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
457 (unsigned long)(state->request.data.uid)));
458 request_error(state);
459 return;
462 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
463 (unsigned long)(state->request.data.uid), sid));
465 string_to_sid(&user_sid, sid);
466 winbindd_getpwsid(state, &user_sid);
469 /* Return a password structure given a uid number */
470 void winbindd_getpwuid(struct winbindd_cli_state *state)
472 uid_t uid = state->request.data.uid;
474 DEBUG(3, ("[%5lu]: getpwuid %lu\n",
475 (unsigned long)state->pid,
476 (unsigned long)uid));
478 /* always query idmap via the async interface */
479 /* if this turns to be too slow we will add here
480 * a direct query to the cache */
481 winbindd_uid2sid_async(state->mem_ctx, uid, getpwuid_recv, state);
485 * set/get/endpwent functions
488 /* Rewind file pointer for ntdom passwd database */
490 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
492 struct winbindd_domain *domain;
494 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
496 /* Check user has enabled this */
498 if (!lp_winbind_enum_users()) {
499 return False;
502 /* Free old static data if it exists */
504 if (state->getpwent_state != NULL) {
505 free_getent_state(state->getpwent_state);
506 state->getpwent_state = NULL;
509 /* Create sam pipes for each domain we know about */
511 for(domain = domain_list(); domain != NULL; domain = domain->next) {
512 struct getent_state *domain_state;
515 /* don't add our domaina if we are a PDC or if we
516 are a member of a Samba domain */
518 if ((IS_DC || lp_winbind_trusted_domains_only())
519 && strequal(domain->name, lp_workgroup())) {
520 continue;
523 /* Create a state record for this domain */
525 domain_state = SMB_MALLOC_P(struct getent_state);
526 if (!domain_state) {
527 DEBUG(0, ("malloc failed\n"));
528 return False;
531 ZERO_STRUCTP(domain_state);
533 fstrcpy(domain_state->domain_name, domain->name);
535 /* Add to list of open domains */
537 DLIST_ADD(state->getpwent_state, domain_state);
540 state->getpwent_initialized = True;
541 return True;
544 void winbindd_setpwent(struct winbindd_cli_state *state)
546 if (winbindd_setpwent_internal(state)) {
547 request_ok(state);
548 } else {
549 request_error(state);
553 /* Close file pointer to ntdom passwd database */
555 void winbindd_endpwent(struct winbindd_cli_state *state)
557 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
559 free_getent_state(state->getpwent_state);
560 state->getpwent_initialized = False;
561 state->getpwent_state = NULL;
562 request_ok(state);
565 /* Get partial list of domain users for a domain. We fill in the sam_entries,
566 and num_sam_entries fields with domain user information. The dispinfo_ndx
567 field is incremented to the index of the next user to fetch. Return True if
568 some users were returned, False otherwise. */
570 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
572 NTSTATUS status;
573 uint32 num_entries;
574 WINBIND_USERINFO *info;
575 struct getpwent_user *name_list = NULL;
576 struct winbindd_domain *domain;
577 struct winbindd_methods *methods;
578 unsigned int i;
580 if (ent->num_sam_entries)
581 return False;
583 if (!(domain = find_domain_from_name(ent->domain_name))) {
584 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
585 ent->domain_name));
586 return False;
589 methods = domain->methods;
591 /* Free any existing user info */
593 SAFE_FREE(ent->sam_entries);
594 ent->num_sam_entries = 0;
596 /* Call query_user_list to get a list of usernames and user rids */
598 num_entries = 0;
600 status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
602 if (!NT_STATUS_IS_OK(status)) {
603 DEBUG(10,("get_sam_user_entries: "
604 "query_user_list failed with %s\n",
605 nt_errstr(status)));
606 return False;
609 if (num_entries) {
610 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user,
611 ent->num_sam_entries + num_entries);
612 if (!name_list) {
613 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
614 return False;
618 for (i = 0; i < num_entries; i++) {
619 /* Store account name and gecos */
620 if (!info[i].acct_name) {
621 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
622 } else {
623 fstrcpy(name_list[ent->num_sam_entries + i].name,
624 info[i].acct_name);
626 if (!info[i].full_name) {
627 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
628 } else {
629 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
630 info[i].full_name);
632 if (!info[i].homedir) {
633 fstrcpy(name_list[ent->num_sam_entries + i].homedir,"");
634 } else {
635 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
636 info[i].homedir);
638 if (!info[i].shell) {
639 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
640 } else {
641 fstrcpy(name_list[ent->num_sam_entries + i].shell,
642 info[i].shell);
646 /* User and group ids */
647 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
648 &info[i].user_sid);
649 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
650 &info[i].group_sid);
653 ent->num_sam_entries += num_entries;
655 /* Fill in remaining fields */
657 ent->sam_entries = name_list;
658 ent->sam_entry_index = 0;
659 return ent->num_sam_entries > 0;
662 /* Fetch next passwd entry from ntdom database */
664 #define MAX_GETPWENT_USERS 500
666 void winbindd_getpwent(struct winbindd_cli_state *state)
668 struct getent_state *ent;
669 struct winbindd_pw *user_list;
670 int num_users, user_list_ndx;
672 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
674 /* Check user has enabled this */
676 if (!lp_winbind_enum_users()) {
677 request_error(state);
678 return;
681 /* Allocate space for returning a chunk of users */
683 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
685 if (num_users == 0) {
686 request_error(state);
687 return;
690 user_list = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users);
691 if (!user_list) {
692 request_error(state);
693 return;
695 /* will be freed by process_request() */
696 state->response.extra_data.data = user_list;
698 memset(user_list, 0, num_users * sizeof(struct winbindd_pw));
700 if (!state->getpwent_initialized)
701 winbindd_setpwent_internal(state);
703 if (!(ent = state->getpwent_state)) {
704 request_error(state);
705 return;
708 /* Start sending back users */
710 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
711 struct getpwent_user *name_list = NULL;
712 uint32 result;
714 /* Do we need to fetch another chunk of users? */
716 if (ent->num_sam_entries == ent->sam_entry_index) {
718 while(ent &&
719 !get_sam_user_entries(ent, state->mem_ctx)) {
720 struct getent_state *next_ent;
722 /* Free state information for this domain */
724 SAFE_FREE(ent->sam_entries);
726 next_ent = ent->next;
727 DLIST_REMOVE(state->getpwent_state, ent);
729 SAFE_FREE(ent);
730 ent = next_ent;
733 /* No more domains */
735 if (!ent)
736 break;
739 name_list = (struct getpwent_user *)ent->sam_entries;
741 /* Lookup user info */
743 result = winbindd_fill_pwent(
744 ent->domain_name,
745 name_list[ent->sam_entry_index].name,
746 &name_list[ent->sam_entry_index].user_sid,
747 &name_list[ent->sam_entry_index].group_sid,
748 name_list[ent->sam_entry_index].gecos,
749 name_list[ent->sam_entry_index].homedir,
750 name_list[ent->sam_entry_index].shell,
751 &user_list[user_list_ndx]);
753 /* Add user to return list */
755 if (result) {
757 user_list_ndx++;
758 state->response.data.num_entries++;
759 state->response.length += sizeof(struct winbindd_pw);
761 } else
762 DEBUG(1, ("could not lookup domain user %s\n",
763 name_list[ent->sam_entry_index].name));
765 ent->sam_entry_index++;
769 /* Out of domains */
771 if (user_list_ndx > 0)
772 request_ok(state);
773 else
774 request_error(state);
777 /* List domain users without mapping to unix ids */
778 void winbindd_list_users(struct winbindd_cli_state *state)
780 winbindd_list_ent(state, LIST_USERS);