s3:winbind: WINBIND_USERINFO -> wbint_userinfo
[Samba/fernandojvsilva.git] / source3 / winbindd / winbindd_user.c
blob10fb1671bdaf9fc844c17d1985154570cfc738bf
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 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(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)
75 fstring output_username;
76 char *mapped_name = NULL;
77 struct winbindd_domain *domain = NULL;
78 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
80 if (!pw || !dom_name || !user_name)
81 return False;
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;
91 /* Resolve the uid number */
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;
101 /* Resolve the gid number */
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;
111 /* Username */
113 strlower_m(user_name);
114 nt_status = normalize_name_map(ctx, domain, user_name, &mapped_name);
116 /* Basic removal of whitespace */
117 if (NT_STATUS_IS_OK(nt_status)) {
118 fill_domain_username(output_username, dom_name, mapped_name, True);
120 /* Complete name replacement */
121 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
122 fstrcpy(output_username, mapped_name);
124 /* No change at all */
125 else {
126 fill_domain_username(output_username, dom_name, user_name, True);
129 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
131 /* Full name (gecos) */
133 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
135 /* Home directory and shell */
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;
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;
145 /* Password - set to "*" as we can't generate anything useful here.
146 Authentication can be done using the pam_winbind module. */
148 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
150 return True;
153 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
155 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
156 struct winbindd_cli_state *state)
158 DOM_SID sid;
159 struct wbint_userinfo user_info;
160 NTSTATUS status;
162 /* Ensure null termination */
163 state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
165 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
166 state->request->data.sid));
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;
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;
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;
195 return WINBINDD_OK;
199 * set/get/endpwent functions
202 /* Rewind file pointer for ntdom passwd database */
204 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
206 struct winbindd_domain *domain;
208 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
210 /* Check user has enabled this */
212 if (!lp_winbind_enum_users()) {
213 return False;
216 /* Free old static data if it exists */
218 if (state->getpwent_state != NULL) {
219 free_getent_state(state->getpwent_state);
220 state->getpwent_state = NULL;
223 /* Create sam pipes for each domain we know about */
225 for(domain = domain_list(); domain != NULL; domain = domain->next) {
226 struct getent_state *domain_state;
229 /* don't add our domaina if we are a PDC or if we
230 are a member of a Samba domain */
232 if ((IS_DC || lp_winbind_trusted_domains_only())
233 && strequal(domain->name, lp_workgroup())) {
234 continue;
237 /* Create a state record for this domain */
239 domain_state = SMB_MALLOC_P(struct getent_state);
240 if (!domain_state) {
241 DEBUG(0, ("malloc failed\n"));
242 return False;
245 ZERO_STRUCTP(domain_state);
247 fstrcpy(domain_state->domain_name, domain->name);
249 /* Add to list of open domains */
251 DLIST_ADD(state->getpwent_state, domain_state);
254 state->getpwent_initialized = True;
255 return True;
258 void winbindd_setpwent(struct winbindd_cli_state *state)
260 if (winbindd_setpwent_internal(state)) {
261 request_ok(state);
262 } else {
263 request_error(state);
267 /* Close file pointer to ntdom passwd database */
269 void winbindd_endpwent(struct winbindd_cli_state *state)
271 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
273 free_getent_state(state->getpwent_state);
274 state->getpwent_initialized = False;
275 state->getpwent_state = NULL;
276 request_ok(state);
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. */
284 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
286 NTSTATUS status;
287 uint32 num_entries;
288 struct wbint_userinfo *info;
289 struct getpwent_user *name_list = NULL;
290 struct winbindd_domain *domain;
291 struct winbindd_methods *methods;
292 unsigned int i;
294 if (ent->num_sam_entries)
295 return False;
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;
303 methods = domain->methods;
305 /* Free any existing user info */
307 SAFE_FREE(ent->sam_entries);
308 ent->num_sam_entries = 0;
310 /* Call query_user_list to get a list of usernames and user rids */
312 num_entries = 0;
314 status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
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;
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;
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);
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);
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);
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);
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);
367 ent->num_sam_entries += num_entries;
369 /* Fill in remaining fields */
371 ent->sam_entries = name_list;
372 ent->sam_entry_index = 0;
373 return ent->num_sam_entries > 0;
376 /* Fetch next passwd entry from ntdom database */
378 #define MAX_GETPWENT_USERS 500
380 void winbindd_getpwent(struct winbindd_cli_state *state)
382 struct getent_state *ent;
383 struct winbindd_pw *user_list;
384 int num_users, user_list_ndx;
386 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
388 /* Check user has enabled this */
390 if (!lp_winbind_enum_users()) {
391 request_error(state);
392 return;
395 /* Allocate space for returning a chunk of users */
397 num_users = MIN(MAX_GETPWENT_USERS, state->request->data.num_entries);
399 if (num_users == 0) {
400 request_error(state);
401 return;
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;
410 state->response->extra_data.data = user_list;
412 if (!state->getpwent_initialized)
413 winbindd_setpwent_internal(state);
415 if (!(ent = state->getpwent_state)) {
416 request_error(state);
417 return;
420 /* Start sending back users */
422 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
423 struct getpwent_user *name_list = NULL;
424 uint32 result;
426 /* Do we need to fetch another chunk of users? */
428 if (ent->num_sam_entries == ent->sam_entry_index) {
430 while(ent &&
431 !get_sam_user_entries(ent, state->mem_ctx)) {
432 struct getent_state *next_ent;
434 /* Free state information for this domain */
436 SAFE_FREE(ent->sam_entries);
438 next_ent = ent->next;
439 DLIST_REMOVE(state->getpwent_state, ent);
441 SAFE_FREE(ent);
442 ent = next_ent;
445 /* No more domains */
447 if (!ent)
448 break;
451 name_list = (struct getpwent_user *)ent->sam_entries;
453 /* Lookup user info */
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]);
466 /* Add user to return list */
468 if (result) {
470 user_list_ndx++;
471 state->response->data.num_entries++;
472 state->response->length += sizeof(struct winbindd_pw);
474 } else
475 DEBUG(1, ("could not lookup domain user %s\n",
476 name_list[ent->sam_entry_index].name));
478 ent->sam_entry_index++;
482 /* Out of domains */
484 if (user_list_ndx > 0)
485 request_ok(state);
486 else
487 request_error(state);
490 /* List domain users without mapping to unix ids */
491 void winbindd_list_users(struct winbindd_cli_state *state)
493 winbindd_list_ent(state, LIST_USERS);