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/>.
28 #define DBGC_CLASS DBGC_WINBIND
30 bool fillup_pw_field(const char *lp_template,
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,
54 templ = talloc_sub_specified(NULL, lp_template,
62 safe_strcpy(out, templ, sizeof(fstring) - 1);
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)
83 domain = find_domain_from_name_noinit(dom_name);
85 DEBUG(5,("winbindd_fill_pwent: Failed to find domain for %s.\n",
87 nt_status = NT_STATUS_NO_SUCH_DOMAIN;
91 /* Resolve the uid number */
93 if (!NT_STATUS_IS_OK(idmap_sid_to_uid(domain->have_idmap_config ?
94 dom_name : "", user_sid,
96 DEBUG(1, ("error getting user id for sid %s\n",
97 sid_string_dbg(user_sid)));
101 /* Resolve the gid number */
103 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config ?
104 dom_name : "", group_sid,
106 DEBUG(1, ("error getting group id for sid %s\n",
107 sid_string_dbg(group_sid)));
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 */
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))
141 if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
142 pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
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);
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)
159 WINBIND_USERINFO user_info;
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,
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;
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()) {
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())) {
237 /* Create a state record for this domain */
239 domain_state = SMB_MALLOC_P(struct getent_state);
241 DEBUG(0, ("malloc failed\n"));
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;
258 void winbindd_setpwent(struct winbindd_cli_state *state)
260 if (winbindd_setpwent_internal(state)) {
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;
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)
288 WINBIND_USERINFO *info;
289 struct getpwent_user *name_list = NULL;
290 struct winbindd_domain *domain;
291 struct winbindd_methods *methods;
294 if (ent->num_sam_entries)
297 if (!(domain = find_domain_from_name(ent->domain_name))) {
298 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
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 */
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",
324 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user,
325 ent->num_sam_entries + num_entries);
327 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
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, "");
337 fstrcpy(name_list[ent->num_sam_entries + i].name,
340 if (!info[i].full_name) {
341 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
343 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
346 if (!info[i].homedir) {
347 fstrcpy(name_list[ent->num_sam_entries + i].homedir,"");
349 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
352 if (!info[i].shell) {
353 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
355 fstrcpy(name_list[ent->num_sam_entries + i].shell,
360 /* User and group ids */
361 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
363 sid_copy(&name_list[ent->num_sam_entries+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);
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);
404 user_list = talloc_zero_array(state->mem_ctx, struct winbindd_pw,
407 request_error(state);
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);
420 /* Start sending back users */
422 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
423 struct getpwent_user *name_list = NULL;
426 /* Do we need to fetch another chunk of users? */
428 if (ent->num_sam_entries == ent->sam_entry_index) {
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);
445 /* No more domains */
451 name_list = (struct getpwent_user *)ent->sam_entries;
453 /* Lookup user info */
455 result = winbindd_fill_pwent(
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 */
471 state->response->data.num_entries++;
472 state->response->length += sizeof(struct winbindd_pw);
475 DEBUG(1, ("could not lookup domain user %s\n",
476 name_list[ent->sam_entry_index].name));
478 ent->sam_entry_index++;
484 if (user_list_ndx > 0)
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);