2 Unix SMB/Netbios implementation.
5 Winbind daemon for ntdom nss module
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.
27 /* Debug connection state */
29 void debug_conn_state(void)
31 struct winbindd_domain
*domain
;
33 DEBUG(3, ("server: dc=%s, pwdb_init=%d, lsa_hnd=%d\n",
34 server_state
.controller
,
35 server_state
.pwdb_initialised
,
36 server_state
.lsa_handle_open
));
38 for (domain
= domain_list
; domain
; domain
= domain
->next
) {
39 DEBUG(3, ("%s: dc=%s, got_sid=%d, sam_hnd=%d sam_dom_hnd=%d\n",
40 domain
->name
, domain
->controller
,
41 domain
->got_domain_info
, domain
->sam_handle_open
,
42 domain
->sam_dom_handle_open
));
46 /* Add a trusted domain to our list of domains */
48 static struct winbindd_domain
*add_trusted_domain(char *domain_name
)
50 struct winbindd_domain
*domain
, *tmp
;
52 for (tmp
= domain_list
; tmp
!= NULL
; tmp
= tmp
->next
) {
53 if (strcmp(domain_name
, tmp
->name
) == 0) {
54 DEBUG(3, ("domain %s already in trusted list\n",
60 DEBUG(1, ("adding trusted domain %s\n", domain_name
));
62 /* Create new domain entry */
64 if ((domain
= (struct winbindd_domain
*)malloc(sizeof(*domain
))) == NULL
) {
73 fstrcpy(domain
->name
, domain_name
);
76 /* Link to domain list */
78 DLIST_ADD(domain_list
, domain
);
83 /* Look up global info for the winbind daemon */
85 static BOOL
get_trusted_domains(void)
89 char **domains
= NULL
;
94 DEBUG(1, ("getting trusted domain list\n"));
96 /* Add our workgroup - keep handle to look up trusted domains */
97 if (!add_trusted_domain(lp_workgroup())) {
98 DEBUG(0, ("could not add record for domain %s\n",
103 /* Enumerate list of trusted domains */
104 result
= wb_lsa_enum_trust_dom(&server_state
.lsa_handle
, &enum_ctx
,
105 &num_doms
, &domains
, &sids
);
107 if (!result
|| !domains
) return False
;
109 /* Add each domain to the trusted domain list */
110 for(i
= 0; i
< num_doms
; i
++) {
111 if (!add_trusted_domain(domains
[i
])) {
112 DEBUG(0, ("could not add record for domain %s\n",
121 /* Open sam and sam domain handles */
123 static BOOL
open_sam_handles(struct winbindd_domain
*domain
)
125 /* Get domain info (sid and controller name) */
127 if (!domain
->got_domain_info
) {
128 domain
->got_domain_info
= get_domain_info(domain
);
129 if (!domain
->got_domain_info
) return False
;
132 /* Shut down existing sam handles */
134 if (domain
->sam_dom_handle_open
) {
135 wb_samr_close(&domain
->sam_dom_handle
);
136 domain
->sam_dom_handle_open
= False
;
139 if (domain
->sam_handle_open
) {
140 wb_samr_close(&domain
->sam_handle
);
141 domain
->sam_handle_open
= False
;
144 /* Open sam handle */
146 domain
->sam_handle_open
=
147 wb_samr_connect(domain
->controller
,
148 SEC_RIGHTS_MAXIMUM_ALLOWED
,
149 &domain
->sam_handle
);
151 if (!domain
->sam_handle_open
) return False
;
153 /* Open sam domain handle */
155 domain
->sam_dom_handle_open
=
156 wb_samr_open_domain(&domain
->sam_handle
,
157 SEC_RIGHTS_MAXIMUM_ALLOWED
,
159 &domain
->sam_dom_handle
);
161 if (!domain
->sam_dom_handle_open
) return False
;
166 static BOOL
rpc_hnd_ok(CLI_POLICY_HND
*hnd
)
168 return hnd
->cli
->fd
!= -1;
171 /* Return true if the SAM domain handles are open and responding. */
173 BOOL
domain_handles_open(struct winbindd_domain
*domain
)
178 /* Check we haven't checked too recently */
182 if ((t
- domain
->last_check
) < WINBINDD_ESTABLISH_LOOP
) {
183 return domain
->sam_handle_open
&&
184 domain
->sam_dom_handle_open
;
187 DEBUG(3, ("checking domain handles for domain %s\n", domain
->name
));
190 domain
->last_check
= t
;
192 /* Open sam handles if they are marked as closed */
194 if (!domain
->sam_handle_open
|| !domain
->sam_dom_handle_open
) {
196 DEBUG(3, ("opening sam handles\n"));
197 return open_sam_handles(domain
);
200 /* Check sam handles are ok - the domain controller may have failed
201 and we need to move to a BDC. */
203 if (!rpc_hnd_ok(&domain
->sam_handle
) ||
204 !rpc_hnd_ok(&domain
->sam_dom_handle
)) {
206 /* We want to close the current connection but attempt
207 to open a new set, possibly to a new dc. If this
208 doesn't work then return False as we have no dc
211 DEBUG(3, ("sam handles not responding\n"));
213 winbindd_kill_connections(domain
);
217 result
= domain
->sam_handle_open
&& domain
->sam_dom_handle_open
;
222 /* Shut down connections to all domain controllers */
224 void winbindd_kill_connections(struct winbindd_domain
*domain
)
226 BOOL is_server
= False
;
227 struct winbindd_domain
*server_domain
;
229 /* Find pointer to domain of pdc */
231 server_domain
= find_domain_from_name(lp_workgroup());
232 if (!server_domain
) return;
234 /* If NULL passed, use pdc */
237 domain
= server_domain
;
240 if (domain
== server_domain
||
241 strequal(domain
->name
, lp_workgroup())) {
245 /* Log a level 0 message - this is probably a domain controller
248 DEBUG(0, ("killing connections to domain %s with controller %s\n",
249 domain
->name
, domain
->controller
));
254 server_state
.pwdb_initialised
= False
;
255 server_state
.lsa_handle_open
= False
;
256 wb_lsa_close(&server_state
.lsa_handle
);
259 /* Close domain sam handles but don't free them as this
260 severely traumatises the getent state. The connections
261 will be reopened later. */
263 if (domain
->sam_dom_handle_open
) {
264 wb_samr_close(&domain
->sam_dom_handle
);
265 domain
->sam_dom_handle_open
= False
;
268 if (domain
->sam_handle_open
) {
269 wb_samr_close(&domain
->sam_handle
);
270 domain
->sam_handle_open
= False
;
273 /* Re-lookup domain info which includes domain controller name */
275 domain
->got_domain_info
= False
;
278 /* Kill connections to all servers */
280 void winbindd_kill_all_connections(void)
282 struct winbindd_domain
*domain
;
284 /* Iterate over domain list */
286 domain
= domain_list
;
289 struct winbindd_domain
*next
;
291 /* Kill conections */
293 winbindd_kill_connections(domain
);
295 /* Remove domain from list */
298 DLIST_REMOVE(domain_list
, domain
);
305 static BOOL
get_any_dc_name(char *domain
, fstring srv_name
)
307 struct in_addr
*ip_list
, dc_ip
;
308 extern pstring global_myname
;
311 /* Lookup domain controller name */
313 if (!get_dc_list(False
, domain
, &ip_list
, &count
))
316 /* Firstly choose a PDC/BDC who has the same network address as any
317 of our interfaces. */
319 for (i
= 0; i
< count
; i
++) {
320 if(!is_local_net(ip_list
[i
]))
324 i
= (sys_random() % count
);
330 if (!lookup_pdc_name(global_myname
, domain
, &dc_ip
, srv_name
))
336 /* Attempt to connect to all domain controllers we know about */
338 void establish_connections(BOOL force_reestablish
)
343 /* Check we haven't checked too recently */
346 if ((t
- lastt
< WINBINDD_ESTABLISH_LOOP
) && !force_reestablish
) {
351 DEBUG(3, ("establishing connections\n"));
354 /* Maybe the connection died - if so then close up and restart */
356 if (server_state
.pwdb_initialised
&&
357 server_state
.lsa_handle_open
&&
358 !rpc_hnd_ok(&server_state
.lsa_handle
)) {
359 winbindd_kill_connections(NULL
);
362 if (!server_state
.pwdb_initialised
) {
364 /* Lookup domain controller name */
366 if (!get_any_dc_name(lp_workgroup(),
367 server_state
.controller
)) {
368 DEBUG(3, ("could not find any domain controllers "
369 "for domain %s\n", lp_workgroup()));
373 /* Initialise password database and sids */
375 // server_state.pwdb_initialised = pwdb_initialise(False);
376 server_state
.pwdb_initialised
= True
;
378 if (!server_state
.pwdb_initialised
) {
379 DEBUG(3, ("could not initialise pwdb\n"));
384 /* Open lsa handle if it isn't already open */
386 if (!server_state
.lsa_handle_open
) {
388 server_state
.lsa_handle_open
=
389 wb_lsa_open_policy(server_state
.controller
,
390 False
, SEC_RIGHTS_MAXIMUM_ALLOWED
,
391 &server_state
.lsa_handle
);
393 if (!server_state
.lsa_handle_open
) {
394 DEBUG(0, ("error opening lsa handle on dc %s\n",
395 server_state
.controller
));
399 /* Now we can talk to the server we can get some info */
401 get_trusted_domains();
407 /* Connect to a domain controller using get_any_dc_name() to discover
408 the domain name and sid */
410 BOOL
lookup_domain_sid(char *domain_name
, struct winbindd_domain
*domain
)
416 char **domains
= NULL
;
417 DOM_SID
*sids
= NULL
;
419 if (domain
== NULL
) {
423 DEBUG(1, ("looking up sid for domain %s\n", domain_name
));
425 /* Get controller name for domain */
427 if (!get_any_dc_name(domain_name
, domain
->controller
)) {
428 DEBUG(0, ("Could not resolve domain controller for domain %s\n",
433 /* Do a level 5 query info policy if we are looking up our own SID */
435 if (strequal(domain_name
, lp_workgroup())) {
436 return wb_lsa_query_info_pol(&server_state
.lsa_handle
, 0x05,
437 level5_dom
, &domain
->sid
);
440 /* Use lsaenumdomains to get sid for this domain */
442 res
= wb_lsa_enum_trust_dom(&server_state
.lsa_handle
, &enum_ctx
,
443 &num_doms
, &domains
, &sids
);
445 /* Look for domain name */
447 if (res
&& domains
&& sids
) {
451 for(i
= 0; i
< num_doms
; i
++) {
452 if (strequal(domain_name
, domains
[i
])) {
453 sid_copy(&domain
->sid
, &sids
[i
]);
465 /* Lookup domain controller and sid for a domain */
467 BOOL
get_domain_info(struct winbindd_domain
*domain
)
471 DEBUG(1, ("Getting domain info for domain %s\n", domain
->name
));
473 /* Lookup domain sid */
475 if (!lookup_domain_sid(domain
->name
, domain
)) {
476 DEBUG(0, ("could not find sid for domain %s\n", domain
->name
));
478 /* Could be a DC failure - shut down connections to this domain */
480 winbindd_kill_connections(domain
);
487 domain
->got_domain_info
= 1;
489 sid_to_string(sid_str
, &domain
->sid
);
490 DEBUG(1, ("found sid %s for domain %s\n", sid_str
, domain
->name
));
495 /* Lookup a sid in a domain from a name */
497 BOOL
winbindd_lookup_sid_by_name(char *name
, DOM_SID
*sid
,
498 enum SID_NAME_USE
*type
)
500 int num_sids
= 0, num_names
= 1;
501 DOM_SID
*sids
= NULL
;
502 uint32
*types
= NULL
;
505 /* Don't bother with machine accounts */
507 if (name
[strlen(name
) - 1] == '$') {
513 res
= wb_lsa_lookup_names(&server_state
.lsa_handle
, num_names
,
514 (char **)&name
, &sids
, &types
, &num_sids
);
516 /* Return rid and type if lookup successful */
522 if ((sid
!= NULL
) && (sids
!= NULL
)) {
523 sid_copy(sid
, &sids
[0]);
526 /* Return name type */
528 if ((type
!= NULL
) && (types
!= NULL
)) {
535 if (types
!= NULL
) free(types
);
536 if (sids
!= NULL
) free(sids
);
541 /* Lookup a name in a domain from a sid */
543 BOOL
winbindd_lookup_name_by_sid(DOM_SID
*sid
, fstring name
,
544 enum SID_NAME_USE
*type
)
546 int num_sids
= 1, num_names
= 0;
547 uint32
*types
= NULL
;
553 res
= wb_lsa_lookup_sids(&server_state
.lsa_handle
, num_sids
, sid
,
554 &names
, &types
, &num_names
);
556 /* Return name and type if successful */
562 if ((names
!= NULL
) && (name
!= NULL
)) {
563 fstrcpy(name
, names
[0]);
566 /* Return name type */
568 if ((type
!= NULL
) && (types
!= NULL
)) {
576 free_char_array(num_names
, names
);
581 /* Lookup user information from a rid */
583 BOOL
winbindd_lookup_userinfo(struct winbindd_domain
*domain
,
584 uint32 user_rid
, SAM_USERINFO_CTR
**user_info
)
586 return wb_get_samr_query_userinfo(&domain
->sam_dom_handle
, 0x15,
587 user_rid
, user_info
);
590 /* Lookup groups a user is a member of. I wish Unix had a call like this! */
592 BOOL
winbindd_lookup_usergroups(struct winbindd_domain
*domain
,
593 uint32 user_rid
, uint32
*num_groups
,
594 DOM_GID
**user_groups
)
599 if (!wb_samr_open_user(&domain
->sam_dom_handle
,
600 SEC_RIGHTS_MAXIMUM_ALLOWED
,
601 user_rid
, &user_pol
)) {
605 if (cli_samr_query_usergroups(domain
->sam_dom_handle
.cli
,
606 domain
->sam_dom_handle
.mem_ctx
,
607 &user_pol
, num_groups
, user_groups
)
608 != NT_STATUS_NOPROBLEMO
) {
616 cli_samr_close(domain
->sam_dom_handle
.cli
,
617 domain
->sam_dom_handle
.mem_ctx
, &user_pol
);
622 /* Lookup group information from a rid */
624 BOOL
winbindd_lookup_groupinfo(struct winbindd_domain
*domain
,
625 uint32 group_rid
, GROUP_INFO_CTR
*info
)
627 return wb_get_samr_query_groupinfo(&domain
->sam_dom_handle
, 1,
631 /* Lookup group membership given a rid */
633 BOOL
winbindd_lookup_groupmem(struct winbindd_domain
*domain
,
634 uint32 group_rid
, uint32
*num_names
,
635 uint32
**rid_mem
, char ***names
,
636 enum SID_NAME_USE
**name_types
)
638 return wb_sam_query_groupmem(&domain
->sam_dom_handle
, group_rid
,
639 num_names
, rid_mem
, names
, name_types
);
642 /* Globals for domain list stuff */
644 struct winbindd_domain
*domain_list
= NULL
;
646 /* Given a domain name, return the struct winbindd domain info for it
647 if it is actually working. */
649 struct winbindd_domain
*find_domain_from_name(char *domain_name
)
651 struct winbindd_domain
*tmp
;
653 /* Search through list */
655 for (tmp
= domain_list
; tmp
!= NULL
; tmp
= tmp
->next
) {
656 if (strcmp(domain_name
, tmp
->name
) == 0) {
658 if (!tmp
->got_domain_info
) {
659 get_domain_info(tmp
);
662 return tmp
->got_domain_info
? tmp
: NULL
;
671 /* Given a domain name, return the struct winbindd domain info for it */
673 struct winbindd_domain
*find_domain_from_sid(DOM_SID
*sid
)
675 struct winbindd_domain
*tmp
;
677 /* Search through list */
678 for (tmp
= domain_list
; tmp
!= NULL
; tmp
= tmp
->next
) {
679 if (sid_equal(sid
, &tmp
->sid
)) {
680 if (!tmp
->got_domain_info
) return NULL
;
689 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
691 void free_getent_state(struct getent_state
*state
)
693 struct getent_state
*temp
;
695 /* Iterate over state list */
699 while(temp
!= NULL
) {
700 struct getent_state
*next
;
702 /* Free sam entries then list entry */
704 safe_free(state
->sam_entries
);
705 DLIST_REMOVE(state
, state
);
713 /* Parse list of arguments to winbind uid or winbind gid parameters */
715 static BOOL
parse_id_list(char *paramstr
, BOOL is_user
)
717 uid_t id_low
, id_high
= 0;
719 /* Give a nicer error message if no parameters specified */
721 if (strequal(paramstr
, "")) {
722 DEBUG(0, ("winbind %s parameter missing\n", is_user
? "uid" : "gid"));
728 if (sscanf(paramstr
, "%u-%u", &id_low
, &id_high
) != 2) {
729 DEBUG(0, ("winbind %s parameter invalid\n",
730 is_user
? "uid" : "gid"));
737 server_state
.uid_low
= id_low
;
738 server_state
.uid_high
= id_high
;
740 server_state
.gid_low
= id_low
;
741 server_state
.gid_high
= id_high
;
747 /* Initialise trusted domain info */
749 BOOL
winbindd_param_init(void)
751 /* Parse winbind uid and winbind_gid parameters */
753 if (!(parse_id_list(lp_winbind_uid(), True
) &&
754 parse_id_list(lp_winbind_gid(), False
))) {
758 /* Check for reversed uid and gid ranges */
760 if (server_state
.uid_low
> server_state
.uid_high
) {
761 DEBUG(0, ("uid range invalid\n"));
765 if (server_state
.gid_low
> server_state
.gid_high
) {
766 DEBUG(0, ("gid range invalid\n"));
773 /* Convert a enum winbindd_cmd to a string */
775 struct cmdstr_table
{
776 enum winbindd_cmd cmd
;
780 static struct cmdstr_table cmdstr_table
[] = {
784 { WINBINDD_GETPWNAM_FROM_USER
, "getpwnam from user" },
785 { WINBINDD_GETPWNAM_FROM_UID
, "getpwnam from uid" },
786 { WINBINDD_SETPWENT
, "setpwent" },
787 { WINBINDD_ENDPWENT
, "endpwent" },
788 { WINBINDD_GETPWENT
, "getpwent" },
789 { WINBINDD_GETGROUPS
, "getgroups" },
791 /* Group functions */
793 { WINBINDD_GETGRNAM_FROM_GROUP
, "getgrnam from group" },
794 { WINBINDD_GETGRNAM_FROM_GID
, "getgrnam from gid" },
795 { WINBINDD_SETGRENT
, "setgrent" },
796 { WINBINDD_ENDGRENT
, "endgrent" },
797 { WINBINDD_GETGRENT
, "getgrent" },
799 /* PAM auth functions */
801 { WINBINDD_PAM_AUTH
, "pam auth" },
802 { WINBINDD_PAM_CHAUTHTOK
, "pam chauthtok" },
806 { WINBINDD_LIST_USERS
, "list users" },
807 { WINBINDD_LIST_GROUPS
, "list groups" },
808 { WINBINDD_LIST_TRUSTDOM
, "list trusted domains" },
810 /* SID related functions */
812 { WINBINDD_LOOKUPSID
, "lookup sid" },
813 { WINBINDD_LOOKUPNAME
, "lookup name" },
815 /* S*RS related functions */
817 { WINBINDD_SID_TO_UID
, "sid to uid" },
818 { WINBINDD_SID_TO_GID
, "sid to gid " },
819 { WINBINDD_GID_TO_SID
, "gid to sid" },
820 { WINBINDD_UID_TO_SID
, "uid to sid" },
822 /* Miscellaneous other stuff */
824 { WINBINDD_CHECK_MACHACC
, "check machine acct pw" },
828 { WINBINDD_NUM_CMDS
, NULL
}
831 char *winbindd_cmd_to_string(enum winbindd_cmd cmd
)
833 struct cmdstr_table
*table
= cmdstr_table
;
836 for(table
= cmdstr_table
; table
->desc
; table
++) {
837 if (cmd
== table
->cmd
) {
838 result
= table
->desc
;
843 if (result
== NULL
) {
844 result
= "invalid command";
850 /* find the sequence number for a domain */
852 uint32
domain_sequence_number(char *domain_name
)
854 struct winbindd_domain
*domain
;
857 domain
= find_domain_from_name(domain_name
);
858 if (!domain
) return DOM_SEQUENCE_NONE
;
860 if (!wb_samr_query_dom_info(&domain
->sam_dom_handle
, 2, &ctr
)) {
862 /* If this fails, something bad has gone wrong */
864 winbindd_kill_connections(domain
);
866 DEBUG(2,("domain sequence query failed\n"));
867 return DOM_SEQUENCE_NONE
;
870 DEBUG(4,("got domain sequence number for %s of %u\n",
871 domain_name
, (unsigned)ctr
.info
.inf2
.seq_num
));
873 return ctr
.info
.inf2
.seq_num
;
876 /* Query display info for a domain. This returns enough information plus a
877 bit extra to give an overview of domain users for the User Manager
880 uint32
winbindd_query_dispinfo(struct winbindd_domain
*domain
,
881 uint32
*start_ndx
, uint16 info_level
,
882 uint32
*num_entries
, SAM_DISPINFO_CTR
*ctr
)
886 status
= wb_samr_query_dispinfo(&domain
->sam_dom_handle
, start_ndx
,
887 info_level
, num_entries
, ctr
);
892 /* Check if a domain is present in a comma-separated list of domains */
894 BOOL
check_domain_env(char *domain_env
, char *domain
)
897 char *tmp
= domain_env
;
899 while(next_token(&tmp
, name
, ",", sizeof(fstring
))) {
900 if (strequal(name
, domain
)) {
909 /* Parse a string of the form DOMAIN/user into a domain and a user */
911 void parse_domain_user(char *domuser
, fstring domain
, fstring user
)
914 char *sep
= lp_winbind_separator();
915 if (!sep
) sep
= "\\";
916 p
= strchr(domuser
,*sep
);
917 if (!p
) p
= strchr(domuser
,'\\');
920 fstrcpy(user
, domuser
);
925 fstrcpy(domain
, domuser
);
926 domain
[PTR_DIFF(p
, domuser
)] = 0;