Compile fixes for dynamic samr_query_userinfo() stuff.
[Samba/gbeck.git] / source / nsswitch / winbindd_user.c
blob30416e76d775f9f676e7a1ab01ec1163c168a4aa
1 /*
2 Unix SMB/Netbios implementation.
3 Version 2.0
5 Winbind daemon - user related functions
7 Copyright (C) Tim Potter 2000
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "winbindd.h"
26 /* Fill a pwent structure with information we have obtained */
28 static BOOL winbindd_fill_pwent(char *domain_name, char *name,
29 uint32 user_rid, uint32 group_rid,
30 char *full_name, struct winbindd_pw *pw)
32 extern userdom_struct current_user_info;
33 fstring name_domain, name_user;
34 pstring homedir;
36 if (!pw || !name) {
37 return False;
40 /* Resolve the uid number */
42 if (!winbindd_idmap_get_uid_from_rid(domain_name, user_rid,
43 &pw->pw_uid)) {
44 DEBUG(1, ("error getting user id for rid %d\n", user_rid));
45 return False;
48 /* Resolve the gid number */
50 if (!winbindd_idmap_get_gid_from_rid(domain_name, group_rid,
51 &pw->pw_gid)) {
52 DEBUG(1, ("error getting group id for rid %d\n", group_rid));
53 return False;
56 /* Username */
58 safe_strcpy(pw->pw_name, name, sizeof(pw->pw_name) - 1);
60 /* Full name (gecos) */
62 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
64 /* Home directory and shell - use template config parameters. The
65 defaults are /tmp for the home directory and /bin/false for
66 shell. */
68 parse_domain_user(name, name_domain, name_user);
70 /* The substitution of %U and %D in the 'template homedir' is done
71 by lp_string() calling standard_sub_basic(). */
73 fstrcpy(current_user_info.smb_name, name_user);
74 fstrcpy(current_user_info.domain, name_domain);
76 pstrcpy(homedir, lp_template_homedir());
78 safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
80 safe_strcpy(pw->pw_shell, lp_template_shell(),
81 sizeof(pw->pw_shell) - 1);
83 /* Password - set to "x" as we can't generate anything useful here.
84 Authentication can be done using the pam_ntdom module. */
86 safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
88 return True;
91 /* Return a password structure from a username. Specify whether cached data
92 can be returned. */
94 enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state
95 *state)
97 uint32 name_type, user_rid, group_rid;
98 SAM_USERINFO_CTR *user_info;
99 DOM_SID user_sid;
100 fstring name_domain, name_user, name, gecos_name;
101 struct winbindd_domain *domain;
103 DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
104 state->request.data.username));
106 /* Parse domain and username */
108 parse_domain_user(state->request.data.username, name_domain,
109 name_user);
111 /* Reject names that don't have a domain - i.e name_domain contains
112 the entire name. */
114 if (strequal(name_domain, "")) {
115 return WINBINDD_ERROR;
118 /* Get info for the domain */
120 if ((domain = find_domain_from_name(name_domain)) == NULL) {
121 DEBUG(0, ("could not find domain entry for domain %s\n",
122 name_domain));
123 return WINBINDD_ERROR;
126 if (!domain_handles_open(domain)) {
127 return WINBINDD_ERROR;
130 /* Check for cached user entry */
132 if (winbindd_fetch_user_cache_entry(name_domain, name_user,
133 &state->response.data.pw)) {
134 return WINBINDD_OK;
137 slprintf(name, sizeof(name) - 1, "%s\\%s", name_domain, name_user);
139 /* Get rid and name type from name. The following costs 1 packet */
141 if (!winbindd_lookup_sid_by_name(name, &user_sid, &name_type)) {
142 DEBUG(1, ("user '%s' does not exist\n", name_user));
143 return WINBINDD_ERROR;
146 if (name_type != SID_NAME_USER) {
147 DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
148 name_type));
149 return WINBINDD_ERROR;
152 /* Get some user info. Split the user rid from the sid obtained
153 from the winbind_lookup_by_name() call and use it in a
154 winbind_lookup_userinfo() */
156 sid_split_rid(&user_sid, &user_rid);
158 /* The following costs 3 packets */
160 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
161 DEBUG(1, ("pwnam_from_user(): error getting user info for "
162 "user '%s'\n", name_user));
163 return WINBINDD_ERROR;
166 group_rid = user_info->info.id21->group_rid;
167 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
168 sizeof(gecos_name) - 1);
170 /* Now take all this information and fill in a passwd structure */
172 if (!winbindd_fill_pwent(domain->name, state->request.data.username,
173 user_rid, group_rid, gecos_name,
174 &state->response.data.pw)) {
175 return WINBINDD_ERROR;
178 winbindd_store_user_cache_entry(name_domain, name_user,
179 &state->response.data.pw);
181 return WINBINDD_OK;
184 /* Return a password structure given a uid number */
186 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state
187 *state)
189 DOM_SID user_sid;
190 struct winbindd_domain *domain;
191 uint32 user_rid, group_rid;
192 fstring user_name, gecos_name;
193 enum SID_NAME_USE name_type;
194 SAM_USERINFO_CTR *user_info;
195 gid_t gid;
197 /* Bug out if the uid isn't in the winbind range */
199 if ((state->request.data.uid < server_state.uid_low ) ||
200 (state->request.data.uid > server_state.uid_high)) {
201 return WINBINDD_ERROR;
204 DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid,
205 state->request.data.uid));
207 /* Get rid from uid */
209 if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid,
210 &user_rid, &domain)) {
211 DEBUG(1, ("Could not convert uid %d to rid\n",
212 state->request.data.uid));
213 return WINBINDD_ERROR;
216 if (!domain_handles_open(domain)) {
217 return WINBINDD_ERROR;
220 /* Check for cached uid entry */
222 if (winbindd_fetch_uid_cache_entry(domain->name,
223 state->request.data.uid,
224 &state->response.data.pw)) {
225 return WINBINDD_OK;
228 /* Get name and name type from rid */
230 sid_copy(&user_sid, &domain->sid);
231 sid_append_rid(&user_sid, user_rid);
233 if (!winbindd_lookup_name_by_sid(&user_sid, user_name, &name_type)) {
234 fstring temp;
236 sid_to_string(temp, &user_sid);
237 DEBUG(1, ("Could not lookup sid %s\n", temp));
238 return WINBINDD_ERROR;
241 if (strcmp("\\", lp_winbind_separator())) {
242 string_sub(user_name, "\\", lp_winbind_separator(),
243 sizeof(fstring));
246 /* Get some user info */
248 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
249 DEBUG(1, ("pwnam_from_uid(): error getting user info for "
250 "user '%s'\n", user_name));
251 return WINBINDD_ERROR;
254 group_rid = user_info->info.id21->group_rid;
255 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
256 sizeof(gecos_name) - 1);
258 /* Resolve gid number */
260 if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
261 DEBUG(1, ("error getting group id for user %s\n", user_name));
262 return WINBINDD_ERROR;
265 /* Fill in password structure */
267 if (!winbindd_fill_pwent(domain->name, user_name, user_rid, group_rid,
268 gecos_name, &state->response.data.pw)) {
269 return WINBINDD_ERROR;
272 winbindd_store_uid_cache_entry(domain->name, state->request.data.uid,
273 &state->response.data.pw);
275 return WINBINDD_OK;
279 * set/get/endpwent functions
282 /* Rewind file pointer for ntdom passwd database */
284 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
286 struct winbindd_domain *tmp;
288 DEBUG(3, ("[%5d]: setpwent\n", state->pid));
290 if (state == NULL) return WINBINDD_ERROR;
292 /* Check user has enabled this */
294 if (!lp_winbind_enum_users()) {
295 return WINBINDD_ERROR;
298 /* Free old static data if it exists */
300 if (state->getpwent_state != NULL) {
301 free_getent_state(state->getpwent_state);
302 state->getpwent_state = NULL;
305 /* Create sam pipes for each domain we know about */
307 for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
308 struct getent_state *domain_state;
310 /* Skip domains other than WINBINDD_DOMAIN environment variable */
312 if ((strcmp(state->request.domain, "") != 0) &&
313 !check_domain_env(state->request.domain, tmp->name)) {
314 continue;
317 /* Create a state record for this domain */
319 if ((domain_state = (struct getent_state *)
320 malloc(sizeof(struct getent_state))) == NULL) {
322 return WINBINDD_ERROR;
325 ZERO_STRUCTP(domain_state);
326 domain_state->domain = tmp;
328 /* Add to list of open domains */
330 DLIST_ADD(state->getpwent_state, domain_state)
333 return WINBINDD_OK;
336 /* Close file pointer to ntdom passwd database */
338 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
340 DEBUG(3, ("[%5d]: endpwent\n", state->pid));
342 if (state == NULL) return WINBINDD_ERROR;
344 free_getent_state(state->getpwent_state);
345 state->getpwent_state = NULL;
347 return WINBINDD_OK;
350 /* Get partial list of domain users for a domain. We fill in the sam_entries,
351 and num_sam_entries fields with domain user information. The dispinfo_ndx
352 field is incremented to the index of the next user to fetch. Return True if
353 some users were returned, False otherwise. */
355 #define MAX_FETCH_SAM_ENTRIES 100
357 static BOOL get_sam_user_entries(struct getent_state *ent)
359 uint32 status, num_entries;
360 SAM_DISPINFO_1 info1;
361 SAM_DISPINFO_CTR ctr;
362 struct getpwent_user *name_list = NULL;
363 uint32 group_rid;
365 if (ent->got_all_sam_entries) {
366 return False;
369 ZERO_STRUCT(info1);
370 ZERO_STRUCT(ctr);
372 ctr.sam.info1 = &info1;
374 #if 0
375 /* Look in cache for entries, else get them direct */
377 if (winbindd_fetch_user_cache(ent->domain->name,
378 (struct getpwent_user **)
379 &ent->sam_entries,
380 &ent->num_sam_entries)) {
381 return True;
383 #endif
385 /* For the moment we set the primary group for every user to be the
386 Domain Users group. There are serious problems with determining
387 the actual primary group for large domains. This should really
388 be made into a 'winbind force group' smb.conf parameter or
389 something like that. */
391 group_rid = DOMAIN_GROUP_RID_USERS;
393 if (!domain_handles_open(ent->domain)) {
394 return WINBINDD_ERROR;
397 /* Free any existing user info */
399 if (ent->sam_entries) {
400 free(ent->sam_entries);
401 ent->sam_entries = NULL;
402 ent->num_sam_entries = 0;
405 /* Call query_dispinfo to get a list of usernames and user rids */
407 do {
408 int i;
410 num_entries = 0;
412 status = winbindd_query_dispinfo(ent->domain,
413 &ent->dispinfo_ndx, 1,
414 &num_entries, &ctr);
416 if (num_entries) {
417 name_list = Realloc(name_list,
418 sizeof(struct getpwent_user) *
419 (ent->num_sam_entries +
420 num_entries));
423 for (i = 0; i < num_entries; i++) {
425 /* Store account name and gecos */
427 unistr2_to_ascii(
428 name_list[ent->num_sam_entries + i].name,
429 &info1.str[i].uni_acct_name,
430 sizeof(fstring));
432 unistr2_to_ascii(
433 name_list[ent->num_sam_entries + i].gecos,
434 &info1.str[i].uni_full_name,
435 sizeof(fstring));
437 /* User and group ids */
439 name_list[ent->num_sam_entries + i].user_rid =
440 info1.sam[i].rid_user;
442 name_list[ent->num_sam_entries + i].
443 group_rid = group_rid;
446 ent->num_sam_entries += num_entries;
448 if (status != STATUS_MORE_ENTRIES) {
449 break;
452 } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES);
454 #if 0
455 /* Fill cache with received entries */
457 winbindd_store_user_cache(ent->domain->name, ent->sam_entries,
458 ent->num_sam_entries);
459 #endif
461 /* Fill in remaining fields */
463 ent->sam_entries = name_list;
464 ent->sam_entry_index = 0;
465 ent->got_all_sam_entries = (status != STATUS_MORE_ENTRIES);
467 return ent->num_sam_entries > 0;
470 /* Fetch next passwd entry from ntdom database */
472 #define MAX_GETPWENT_USERS 500
474 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
476 struct getent_state *ent;
477 struct winbindd_pw *user_list;
478 int num_users, user_list_ndx = 0, i;
479 char *sep;
481 DEBUG(3, ("[%5d]: getpwent\n", state->pid));
483 if (state == NULL) return WINBINDD_ERROR;
485 /* Check user has enabled this */
487 if (!lp_winbind_enum_users()) {
488 return WINBINDD_ERROR;
491 /* Allocate space for returning a chunk of users */
493 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
495 if ((state->response.extra_data =
496 malloc(num_users * sizeof(struct winbindd_pw))) == NULL) {
497 return WINBINDD_ERROR;
500 memset(state->response.extra_data, 0, num_users *
501 sizeof(struct winbindd_pw));
503 user_list = (struct winbindd_pw *)state->response.extra_data;
504 sep = lp_winbind_separator();
506 if (!(ent = state->getpwent_state)) {
507 return WINBINDD_ERROR;
510 /* Start sending back users */
512 for (i = 0; i < num_users; i++) {
513 struct getpwent_user *name_list = NULL;
514 fstring domain_user_name;
515 uint32 result;
517 /* Do we need to fetch another chunk of users? */
519 if (ent->num_sam_entries == ent->sam_entry_index) {
521 while(ent && !get_sam_user_entries(ent)) {
522 struct getent_state *next_ent;
524 /* Free state information for this domain */
526 safe_free(ent->sam_entries);
527 ent->sam_entries = NULL;
529 next_ent = ent->next;
530 DLIST_REMOVE(state->getpwent_state, ent);
532 free(ent);
533 ent = next_ent;
536 /* No more domains */
538 if (!ent) break;
541 name_list = ent->sam_entries;
543 /* Skip machine accounts */
545 if (name_list[ent->sam_entry_index].
546 name[strlen(name_list[ent->sam_entry_index].name) - 1]
547 == '$') {
548 ent->sam_entry_index++;
549 continue;
552 /* Lookup user info */
554 slprintf(domain_user_name, sizeof(domain_user_name) - 1,
555 "%s%s%s", ent->domain->name, sep,
556 name_list[ent->sam_entry_index].name);
558 result = winbindd_fill_pwent(
559 ent->domain->name,
560 domain_user_name,
561 name_list[ent->sam_entry_index].user_rid,
562 name_list[ent->sam_entry_index].group_rid,
563 name_list[ent->sam_entry_index].gecos,
564 &user_list[user_list_ndx]);
566 ent->sam_entry_index++;
568 /* Add user to return list */
570 if (result) {
572 user_list_ndx++;
573 state->response.data.num_entries++;
574 state->response.length +=
575 sizeof(struct winbindd_pw);
577 } else {
578 DEBUG(1, ("could not lookup domain user %s\n",
579 domain_user_name));
584 /* Out of domains */
586 return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
589 /* List domain users without mapping to unix ids */
591 enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
593 struct winbindd_domain *domain;
594 SAM_DISPINFO_CTR ctr;
595 SAM_DISPINFO_1 info1;
596 uint32 num_entries = 0, total_entries = 0;
597 char *extra_data = NULL;
598 int extra_data_len = 0;
600 DEBUG(3, ("[%5d]: list users\n", state->pid));
602 /* Enumerate over trusted domains */
604 ctr.sam.info1 = &info1;
606 for (domain = domain_list; domain; domain = domain->next) {
607 uint32 status, start_ndx = 0;
609 /* Skip domains other than WINBINDD_DOMAIN environment
610 variable */
612 if ((strcmp(state->request.domain, "") != 0) &&
613 !check_domain_env(state->request.domain, domain->name)) {
614 continue;
617 if (!domain_handles_open(domain)) {
618 continue;
621 /* Query display info */
623 do {
624 int i;
626 status = winbindd_query_dispinfo(domain, &start_ndx,
627 1, &num_entries,
628 &ctr);
630 if (num_entries == 0) {
631 continue;
634 /* Allocate some memory for extra data */
636 total_entries += num_entries;
638 extra_data = Realloc(extra_data, sizeof(fstring) *
639 total_entries);
641 if (!extra_data) {
642 return WINBINDD_ERROR;
645 /* Pack user list into extra data fields */
647 for (i = 0; i < num_entries; i++) {
648 UNISTR2 *uni_acct_name;
649 fstring acct_name, name;
651 /* Convert unistring to ascii */
653 uni_acct_name = &ctr.sam.info1->str[i].
654 uni_acct_name;
655 unistr2_to_ascii(acct_name, uni_acct_name,
656 sizeof(acct_name) - 1);
658 slprintf(name, sizeof(name) - 1, "%s%s%s",
659 domain->name, lp_winbind_separator(),
660 acct_name);
662 /* Append to extra data */
664 memcpy(&extra_data[extra_data_len], name,
665 strlen(name));
666 extra_data_len += strlen(name);
668 extra_data[extra_data_len++] = ',';
670 } while (status == STATUS_MORE_ENTRIES);
673 /* Assign extra_data fields in response structure */
675 if (extra_data) {
676 extra_data[extra_data_len - 1] = '\0';
677 state->response.extra_data = extra_data;
678 state->response.length += extra_data_len;
681 /* No domains responded but that's still OK so don't return an
682 error. */
684 return WINBINDD_OK;