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 struct wbint_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 struct wbint_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
);