Compile fixes for dynamic samr_query_userinfo() stuff.
[Samba/gbeck.git] / source / nsswitch / winbindd_util.c
blob4343f859ed315c19538e5ac6c090c1fc854a8c42
1 /*
2 Unix SMB/Netbios implementation.
3 Version 2.0
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.
24 #include "winbindd.h"
25 #include "sids.h"
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",
55 domain_name));
56 return tmp;
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) {
65 return NULL;
68 /* Fill in fields */
70 ZERO_STRUCTP(domain);
72 if (domain_name) {
73 fstrcpy(domain->name, domain_name);
76 /* Link to domain list */
78 DLIST_ADD(domain_list, domain);
80 return domain;
83 /* Look up global info for the winbind daemon */
85 static BOOL get_trusted_domains(void)
87 uint32 enum_ctx = 0;
88 uint32 num_doms = 0;
89 char **domains = NULL;
90 DOM_SID *sids = NULL;
91 BOOL result;
92 int i;
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",
99 lp_workgroup()));
100 return False;
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",
113 domains[i]));
114 result = False;
118 return True;
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,
158 &domain->sid,
159 &domain->sam_dom_handle);
161 if (!domain->sam_dom_handle_open) return False;
163 return True;
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)
175 time_t t;
176 BOOL result;
178 /* Check we haven't checked too recently */
180 t = time(NULL);
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));
188 debug_conn_state();
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) {
195 reopen:
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
209 to talk to. */
211 DEBUG(3, ("sam handles not responding\n"));
213 winbindd_kill_connections(domain);
214 goto reopen;
217 result = domain->sam_handle_open && domain->sam_dom_handle_open;
219 return result;
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 */
236 if (!domain) {
237 domain = server_domain;
240 if (domain == server_domain ||
241 strequal(domain->name, lp_workgroup())) {
242 is_server = True;
245 /* Log a level 0 message - this is probably a domain controller
246 failure */
248 DEBUG(0, ("killing connections to domain %s with controller %s\n",
249 domain->name, domain->controller));
251 debug_conn_state();
253 if (is_server) {
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;
288 while (domain) {
289 struct winbindd_domain *next;
291 /* Kill conections */
293 winbindd_kill_connections(domain);
295 /* Remove domain from list */
297 next = domain->next;
298 DLIST_REMOVE(domain_list, domain);
299 free(domain);
301 domain = next;
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;
309 int count, i;
311 /* Lookup domain controller name */
313 if (!get_dc_list(False, domain, &ip_list, &count))
314 return False;
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]))
321 goto got_ip;
324 i = (sys_random() % count);
326 got_ip:
327 dc_ip = ip_list[i];
328 free(ip_list);
330 if (!lookup_pdc_name(global_myname, domain, &dc_ip, srv_name))
331 return False;
333 return True;
336 /* Attempt to connect to all domain controllers we know about */
338 void establish_connections(BOOL force_reestablish)
340 static time_t lastt;
341 time_t t;
343 /* Check we haven't checked too recently */
345 t = time(NULL);
346 if ((t - lastt < WINBINDD_ESTABLISH_LOOP) && !force_reestablish) {
347 return;
349 lastt = t;
351 DEBUG(3, ("establishing connections\n"));
352 debug_conn_state();
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()));
370 return;
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"));
380 return;
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));
396 return;
399 /* Now we can talk to the server we can get some info */
401 get_trusted_domains();
404 debug_conn_state();
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)
412 fstring level5_dom;
413 BOOL res;
414 uint32 enum_ctx = 0;
415 uint32 num_doms = 0;
416 char **domains = NULL;
417 DOM_SID *sids = NULL;
419 if (domain == NULL) {
420 return False;
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",
429 domain_name));
430 return False;
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) {
448 int found = False;
449 int i;
451 for(i = 0; i < num_doms; i++) {
452 if (strequal(domain_name, domains[i])) {
453 sid_copy(&domain->sid, &sids[i]);
454 found = True;
455 break;
459 res = found;
462 return res;
465 /* Lookup domain controller and sid for a domain */
467 BOOL get_domain_info(struct winbindd_domain *domain)
469 fstring sid_str;
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);
482 return False;
485 /* Lookup OK */
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));
492 return True;
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;
503 BOOL res;
505 /* Don't bother with machine accounts */
507 if (name[strlen(name) - 1] == '$') {
508 return False;
511 /* Lookup name */
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 */
518 if (res) {
520 /* Return sid */
522 if ((sid != NULL) && (sids != NULL)) {
523 sid_copy(sid, &sids[0]);
526 /* Return name type */
528 if ((type != NULL) && (types != NULL)) {
529 *type = types[0];
533 /* Free memory */
535 if (types != NULL) free(types);
536 if (sids != NULL) free(sids);
538 return res;
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;
548 char **names;
549 BOOL res;
551 /* Lookup name */
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 */
558 if (res) {
560 /* Return name */
562 if ((names != NULL) && (name != NULL)) {
563 fstrcpy(name, names[0]);
566 /* Return name type */
568 if ((type != NULL) && (types != NULL)) {
569 *type = types[0];
573 /* Free memory */
575 safe_free(types);
576 free_char_array(num_names, names);
578 return res;
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)
596 POLICY_HND user_pol;
597 BOOL result;
599 if (!wb_samr_open_user(&domain->sam_dom_handle,
600 SEC_RIGHTS_MAXIMUM_ALLOWED,
601 user_rid, &user_pol)) {
602 return False;
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) {
609 result = False;
610 goto done;
613 result = True;
615 done:
616 cli_samr_close(domain->sam_dom_handle.cli,
617 domain->sam_dom_handle.mem_ctx, &user_pol);
619 return True;
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,
628 group_rid, info);
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;
666 /* Not found */
668 return 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;
681 return tmp;
685 /* Not found */
686 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 */
697 temp = state;
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);
706 next = temp->next;
708 free(temp);
709 temp = next;
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"));
723 return False;
726 /* Parse entry */
728 if (sscanf(paramstr, "%u-%u", &id_low, &id_high) != 2) {
729 DEBUG(0, ("winbind %s parameter invalid\n",
730 is_user ? "uid" : "gid"));
731 return False;
734 /* Store id info */
736 if (is_user) {
737 server_state.uid_low = id_low;
738 server_state.uid_high = id_high;
739 } else {
740 server_state.gid_low = id_low;
741 server_state.gid_high = id_high;
744 return True;
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))) {
755 return 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"));
762 return False;
765 if (server_state.gid_low > server_state.gid_high) {
766 DEBUG(0, ("gid range invalid\n"));
767 return False;
770 return True;
773 /* Convert a enum winbindd_cmd to a string */
775 struct cmdstr_table {
776 enum winbindd_cmd cmd;
777 char *desc;
780 static struct cmdstr_table cmdstr_table[] = {
782 /* User functions */
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" },
804 /* List things */
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" },
826 /* End of list */
828 { WINBINDD_NUM_CMDS, NULL }
831 char *winbindd_cmd_to_string(enum winbindd_cmd cmd)
833 struct cmdstr_table *table = cmdstr_table;
834 char *result = NULL;
836 for(table = cmdstr_table; table->desc; table++) {
837 if (cmd == table->cmd) {
838 result = table->desc;
839 break;
843 if (result == NULL) {
844 result = "invalid command";
847 return result;
850 /* find the sequence number for a domain */
852 uint32 domain_sequence_number(char *domain_name)
854 struct winbindd_domain *domain;
855 SAM_UNK_CTR ctr;
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
878 application. */
880 uint32 winbindd_query_dispinfo(struct winbindd_domain *domain,
881 uint32 *start_ndx, uint16 info_level,
882 uint32 *num_entries, SAM_DISPINFO_CTR *ctr)
884 uint32 status;
886 status = wb_samr_query_dispinfo(&domain->sam_dom_handle, start_ndx,
887 info_level, num_entries, ctr);
889 return status;
892 /* Check if a domain is present in a comma-separated list of domains */
894 BOOL check_domain_env(char *domain_env, char *domain)
896 fstring name;
897 char *tmp = domain_env;
899 while(next_token(&tmp, name, ",", sizeof(fstring))) {
900 if (strequal(name, domain)) {
901 return True;
905 return False;
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)
913 char *p;
914 char *sep = lp_winbind_separator();
915 if (!sep) sep = "\\";
916 p = strchr(domuser,*sep);
917 if (!p) p = strchr(domuser,'\\');
918 if (!p) {
919 fstrcpy(domain,"");
920 fstrcpy(user, domuser);
921 return;
924 fstrcpy(user, p+1);
925 fstrcpy(domain, domuser);
926 domain[PTR_DIFF(p, domuser)] = 0;
927 strupper(domain);