winbindd: only use NetBIOS name when searching domain list in add_trusted_domain_from...
[Samba.git] / source3 / winbindd / winbindd_util.c
blob37725cf67fe55e8248240441ea0d2076609ecf5c
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000-2001
7 Copyright (C) 2001 by Martin Pool <mbp@samba.org>
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 3 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, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "winbindd.h"
25 #include "lib/util_unixsids.h"
26 #include "secrets.h"
27 #include "../libcli/security/security.h"
28 #include "../libcli/auth/pam_errors.h"
29 #include "passdb/machine_sid.h"
30 #include "passdb.h"
31 #include "source4/lib/messaging/messaging.h"
32 #include "librpc/gen_ndr/ndr_lsa.h"
33 #include "auth/credentials/credentials.h"
34 #include "libsmb/samlogon_cache.h"
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_WINBIND
39 static struct winbindd_domain *
40 add_trusted_domain_from_tdc(const struct winbindd_tdc_domain *tdc);
42 /**
43 * @file winbindd_util.c
45 * Winbind daemon for NT domain authentication nss module.
46 **/
49 /* The list of trusted domains. Note that the list can be deleted and
50 recreated using the init_domain_list() function so pointers to
51 individual winbindd_domain structures cannot be made. Keep a copy of
52 the domain name instead. */
54 static struct winbindd_domain *_domain_list = NULL;
56 struct winbindd_domain *domain_list(void)
58 /* Initialise list */
60 if ((!_domain_list) && (!init_domain_list())) {
61 smb_panic("Init_domain_list failed");
64 return _domain_list;
67 /* Free all entries in the trusted domain list */
69 static void free_domain_list(void)
71 struct winbindd_domain *domain = _domain_list;
73 while(domain) {
74 struct winbindd_domain *next = domain->next;
76 DLIST_REMOVE(_domain_list, domain);
77 TALLOC_FREE(domain);
78 domain = next;
82 /**
83 * Iterator for winbindd's domain list.
84 * To be used (e.g.) in tevent based loops.
86 struct winbindd_domain *wb_next_domain(struct winbindd_domain *domain)
88 if (domain == NULL) {
89 domain = domain_list();
90 } else {
91 domain = domain->next;
94 if ((domain != NULL) &&
95 (lp_server_role() != ROLE_ACTIVE_DIRECTORY_DC) &&
96 sid_check_is_our_sam(&domain->sid))
98 domain = domain->next;
101 return domain;
104 static bool is_internal_domain(const struct dom_sid *sid)
106 if (sid == NULL)
107 return False;
109 return (sid_check_is_our_sam(sid) || sid_check_is_builtin(sid));
112 static bool is_in_internal_domain(const struct dom_sid *sid)
114 if (sid == NULL)
115 return False;
117 return (sid_check_is_in_our_sam(sid) || sid_check_is_in_builtin(sid));
121 /* Add a trusted domain to our list of domains.
122 If the domain already exists in the list,
123 return it and don't re-initialize. */
125 static struct winbindd_domain *
126 add_trusted_domain(const char *domain_name, const char *alt_name,
127 const struct dom_sid *sid)
129 struct winbindd_tdc_domain tdc;
131 ZERO_STRUCT(tdc);
133 tdc.domain_name = domain_name;
134 tdc.dns_name = alt_name;
135 if (sid) {
136 sid_copy(&tdc.sid, sid);
139 return add_trusted_domain_from_tdc(&tdc);
142 /* Add a trusted domain out of a trusted domain cache
143 entry
145 static struct winbindd_domain *
146 add_trusted_domain_from_tdc(const struct winbindd_tdc_domain *tdc)
148 struct winbindd_domain *domain;
149 const char *alternative_name = NULL;
150 const char **ignored_domains, **dom;
151 int role = lp_server_role();
152 const char *domain_name = tdc->domain_name;
153 const struct dom_sid *sid = &tdc->sid;
155 if (is_null_sid(sid)) {
156 DBG_ERR("Got null SID for domain [%s]\n", domain_name);
157 return NULL;
160 ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
161 for (dom=ignored_domains; dom && *dom; dom++) {
162 if (gen_fnmatch(*dom, domain_name) == 0) {
163 DEBUG(2,("Ignoring domain '%s'\n", domain_name));
164 return NULL;
168 /* use alt_name if available to allow DNS lookups */
170 if (tdc->dns_name && *tdc->dns_name) {
171 alternative_name = tdc->dns_name;
174 /* We can't call domain_list() as this function is called from
175 init_domain_list() and we'll get stuck in a loop. */
176 for (domain = _domain_list; domain; domain = domain->next) {
177 if (strequal(domain_name, domain->name)) {
178 break;
182 if (domain != NULL) {
183 struct winbindd_domain *check_domain = NULL;
185 for (check_domain = _domain_list;
186 check_domain != NULL;
187 check_domain = check_domain->next)
189 if (check_domain == domain) {
190 continue;
193 if (dom_sid_equal(&check_domain->sid, sid)) {
194 break;
198 if (check_domain != NULL) {
199 DBG_ERR("SID [%s] already used by domain [%s], "
200 "expected [%s]\n",
201 sid_string_dbg(sid), check_domain->name,
202 domain->name);
203 return NULL;
207 if ((domain != NULL) && (alternative_name != NULL)) {
208 struct winbindd_domain *check_domain = NULL;
210 for (check_domain = _domain_list;
211 check_domain != NULL;
212 check_domain = check_domain->next)
214 if (check_domain == domain) {
215 continue;
218 if (strequal(check_domain->alt_name, alternative_name)) {
219 break;
223 if (check_domain != NULL) {
224 DBG_ERR("DNS name [%s] used by domain [%s], "
225 "expected [%s]\n",
226 alternative_name, check_domain->name,
227 domain->name);
228 return NULL;
232 if (domain != NULL) {
233 return domain;
236 /* Create new domain entry */
237 domain = talloc_zero(NULL, struct winbindd_domain);
238 if (domain == NULL) {
239 return NULL;
242 domain->children = talloc_zero_array(domain,
243 struct winbindd_child,
244 lp_winbind_max_domain_connections());
245 if (domain->children == NULL) {
246 TALLOC_FREE(domain);
247 return NULL;
250 domain->name = talloc_strdup(domain, domain_name);
251 if (domain->name == NULL) {
252 TALLOC_FREE(domain);
253 return NULL;
256 if (alternative_name) {
257 domain->alt_name = talloc_strdup(domain, alternative_name);
258 if (domain->alt_name == NULL) {
259 TALLOC_FREE(domain);
260 return NULL;
264 domain->backend = NULL;
265 domain->internal = is_internal_domain(sid);
266 domain->sequence_number = DOM_SEQUENCE_NONE;
267 domain->last_seq_check = 0;
268 domain->initialized = false;
269 domain->online = is_internal_domain(sid);
270 domain->check_online_timeout = 0;
271 domain->dc_probe_pid = (pid_t)-1;
272 domain->domain_flags = tdc->trust_flags;
273 domain->domain_type = tdc->trust_type;
274 domain->domain_trust_attribs = tdc->trust_attribs;
275 sid_copy(&domain->sid, sid);
277 /* Is this our primary domain ? */
278 if (role == ROLE_DOMAIN_MEMBER) {
279 domain->primary = strequal(domain_name, lp_workgroup());
280 } else {
281 domain->primary = strequal(domain_name, get_global_sam_name());
284 if (domain->primary) {
285 if (role == ROLE_ACTIVE_DIRECTORY_DC) {
286 domain->active_directory = true;
288 if (lp_security() == SEC_ADS) {
289 domain->active_directory = true;
291 } else if (!domain->internal) {
292 if (domain->domain_type == LSA_TRUST_TYPE_UPLEVEL) {
293 domain->active_directory = true;
297 /* Link to domain list */
298 DLIST_ADD_END(_domain_list, domain);
300 wcache_tdc_add_domain( domain );
302 setup_domain_child(domain);
304 DEBUG(2,
305 ("Added domain %s %s %s\n", domain->name, domain->alt_name,
306 !is_null_sid(&domain->sid) ? sid_string_dbg(&domain->sid) : ""));
308 return domain;
311 bool domain_is_forest_root(const struct winbindd_domain *domain)
313 const uint32_t fr_flags =
314 (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
316 return ((domain->domain_flags & fr_flags) == fr_flags);
319 /********************************************************************
320 rescan our domains looking for new trusted domains
321 ********************************************************************/
323 struct trustdom_state {
324 struct winbindd_domain *domain;
325 struct winbindd_request request;
328 static void trustdom_list_done(struct tevent_req *req);
329 static void rescan_forest_root_trusts( void );
330 static void rescan_forest_trusts( void );
332 static void add_trusted_domains( struct winbindd_domain *domain )
334 struct trustdom_state *state;
335 struct tevent_req *req;
337 state = talloc_zero(NULL, struct trustdom_state);
338 if (state == NULL) {
339 DEBUG(0, ("talloc failed\n"));
340 return;
342 state->domain = domain;
344 state->request.length = sizeof(state->request);
345 state->request.cmd = WINBINDD_LIST_TRUSTDOM;
347 req = wb_domain_request_send(state, server_event_context(),
348 domain, &state->request);
349 if (req == NULL) {
350 DEBUG(1, ("wb_domain_request_send failed\n"));
351 TALLOC_FREE(state);
352 return;
354 tevent_req_set_callback(req, trustdom_list_done, state);
357 static void trustdom_list_done(struct tevent_req *req)
359 struct trustdom_state *state = tevent_req_callback_data(
360 req, struct trustdom_state);
361 struct winbindd_response *response;
362 int res, err;
363 char *p;
364 struct winbindd_tdc_domain trust_params = {0};
365 ptrdiff_t extra_len;
366 bool within_forest = false;
369 * Only when we enumerate our primary domain
370 * or our forest root domain, we should keep
371 * the NETR_TRUST_FLAG_IN_FOREST flag, in
372 * all other cases we need to clear it as the domain
373 * is not part of our forest.
375 if (state->domain->primary) {
376 within_forest = true;
377 } else if (domain_is_forest_root(state->domain)) {
378 within_forest = true;
381 res = wb_domain_request_recv(req, state, &response, &err);
382 if ((res == -1) || (response->result != WINBINDD_OK)) {
383 DBG_WARNING("Could not receive trusts for domain %s\n",
384 state->domain->name);
385 TALLOC_FREE(state);
386 return;
389 if (response->length < sizeof(struct winbindd_response)) {
390 DBG_ERR("ill-formed trustdom response - short length\n");
391 TALLOC_FREE(state);
392 return;
395 extra_len = response->length - sizeof(struct winbindd_response);
397 p = (char *)response->extra_data.data;
399 while ((p - (char *)response->extra_data.data) < extra_len) {
400 char *q, *sidstr, *alt_name;
402 DBG_DEBUG("parsing response line '%s'\n", p);
404 ZERO_STRUCT(trust_params);
405 trust_params.domain_name = p;
407 alt_name = strchr(p, '\\');
408 if (alt_name == NULL) {
409 DBG_ERR("Got invalid trustdom response\n");
410 break;
413 *alt_name = '\0';
414 alt_name += 1;
416 sidstr = strchr(alt_name, '\\');
417 if (sidstr == NULL) {
418 DBG_ERR("Got invalid trustdom response\n");
419 break;
422 *sidstr = '\0';
423 sidstr += 1;
425 /* use the real alt_name if we have one, else pass in NULL */
426 if (!strequal(alt_name, "(null)")) {
427 trust_params.dns_name = alt_name;
430 q = strtok(sidstr, "\\");
431 if (q == NULL) {
432 DBG_ERR("Got invalid trustdom response\n");
433 break;
436 if (!string_to_sid(&trust_params.sid, sidstr)) {
437 DEBUG(0, ("Got invalid trustdom response\n"));
438 break;
441 q = strtok(NULL, "\\");
442 if (q == NULL) {
443 DBG_ERR("Got invalid trustdom response\n");
444 break;
447 trust_params.trust_flags = (uint32_t)strtoul(q, NULL, 10);
449 q = strtok(NULL, "\\");
450 if (q == NULL) {
451 DBG_ERR("Got invalid trustdom response\n");
452 break;
455 trust_params.trust_type = (uint32_t)strtoul(q, NULL, 10);
457 q = strtok(NULL, "\n");
458 if (q == NULL) {
459 DBG_ERR("Got invalid trustdom response\n");
460 break;
463 trust_params.trust_attribs = (uint32_t)strtoul(q, NULL, 10);
465 if (!within_forest) {
466 trust_params.trust_flags &= ~NETR_TRUST_FLAG_IN_FOREST;
469 if (!state->domain->primary) {
470 trust_params.trust_flags &= ~NETR_TRUST_FLAG_PRIMARY;
474 * We always call add_trusted_domain() cause on an existing
475 * domain structure, it will update the SID if necessary.
476 * This is important because we need the SID for sibling
477 * domains.
479 (void)add_trusted_domain_from_tdc(&trust_params);
481 p = q + strlen(q) + 1;
485 Cases to consider when scanning trusts:
486 (a) we are calling from a child domain (primary && !forest_root)
487 (b) we are calling from the root of the forest (primary && forest_root)
488 (c) we are calling from a trusted forest domain (!primary
489 && !forest_root)
492 if (state->domain->primary) {
493 /* If this is our primary domain and we are not in the
494 forest root, we have to scan the root trusts first */
496 if (!domain_is_forest_root(state->domain))
497 rescan_forest_root_trusts();
498 else
499 rescan_forest_trusts();
501 } else if (domain_is_forest_root(state->domain)) {
502 /* Once we have done root forest trust search, we can
503 go on to search the trusted forests */
505 rescan_forest_trusts();
508 TALLOC_FREE(state);
510 return;
513 /********************************************************************
514 Scan the trusts of our forest root
515 ********************************************************************/
517 static void rescan_forest_root_trusts( void )
519 struct winbindd_tdc_domain *dom_list = NULL;
520 size_t num_trusts = 0;
521 int i;
523 /* The only transitive trusts supported by Windows 2003 AD are
524 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
525 first two are handled in forest and listed by
526 DsEnumerateDomainTrusts(). Forest trusts are not so we
527 have to do that ourselves. */
529 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
530 return;
532 for ( i=0; i<num_trusts; i++ ) {
533 struct winbindd_domain *d = NULL;
535 /* Find the forest root. Don't necessarily trust
536 the domain_list() as our primary domain may not
537 have been initialized. */
539 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
540 continue;
543 /* Here's the forest root */
545 d = find_domain_from_name_noinit( dom_list[i].domain_name );
547 if ( !d ) {
548 d = add_trusted_domain_from_tdc(&dom_list[i]);
551 if (d == NULL) {
552 continue;
555 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
556 "for domain tree root %s (%s)\n",
557 d->name, d->alt_name ));
559 d->domain_flags = dom_list[i].trust_flags;
560 d->domain_type = dom_list[i].trust_type;
561 d->domain_trust_attribs = dom_list[i].trust_attribs;
563 add_trusted_domains( d );
565 break;
568 TALLOC_FREE( dom_list );
570 return;
573 /********************************************************************
574 scan the transitive forest trusts (not our own)
575 ********************************************************************/
578 static void rescan_forest_trusts( void )
580 struct winbindd_domain *d = NULL;
581 struct winbindd_tdc_domain *dom_list = NULL;
582 size_t num_trusts = 0;
583 int i;
585 /* The only transitive trusts supported by Windows 2003 AD are
586 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
587 first two are handled in forest and listed by
588 DsEnumerateDomainTrusts(). Forest trusts are not so we
589 have to do that ourselves. */
591 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
592 return;
594 for ( i=0; i<num_trusts; i++ ) {
595 uint32_t flags = dom_list[i].trust_flags;
596 uint32_t type = dom_list[i].trust_type;
597 uint32_t attribs = dom_list[i].trust_attribs;
599 d = find_domain_from_name_noinit( dom_list[i].domain_name );
601 /* ignore our primary and internal domains */
603 if ( d && (d->internal || d->primary ) )
604 continue;
606 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
607 (type == LSA_TRUST_TYPE_UPLEVEL) &&
608 (attribs == LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
610 /* add the trusted domain if we don't know
611 about it */
613 if ( !d ) {
614 d = add_trusted_domain_from_tdc(&dom_list[i]);
617 if (d == NULL) {
618 continue;
621 DEBUG(10,("Following trust path for domain %s (%s)\n",
622 d->name, d->alt_name ));
623 add_trusted_domains( d );
627 TALLOC_FREE( dom_list );
629 return;
632 /*********************************************************************
633 The process of updating the trusted domain list is a three step
634 async process:
635 (a) ask our domain
636 (b) ask the root domain in our forest
637 (c) ask the a DC in any Win2003 trusted forests
638 *********************************************************************/
640 void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
641 struct timeval now, void *private_data)
643 TALLOC_FREE(te);
645 /* I use to clear the cache here and start over but that
646 caused problems in child processes that needed the
647 trust dom list early on. Removing it means we
648 could have some trusted domains listed that have been
649 removed from our primary domain's DC until a full
650 restart. This should be ok since I think this is what
651 Windows does as well. */
653 /* this will only add new domains we didn't already know about
654 in the domain_list()*/
656 add_trusted_domains( find_our_domain() );
658 te = tevent_add_timer(
659 ev, NULL, timeval_current_ofs(WINBINDD_RESCAN_FREQ, 0),
660 rescan_trusted_domains, NULL);
662 * If te == NULL, there's not much we can do here. Don't fail, the
663 * only thing we miss is new trusted domains.
666 return;
669 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
670 struct winbindd_cli_state *state)
672 /* Ensure null termination */
673 state->request->domain_name
674 [sizeof(state->request->domain_name)-1]='\0';
675 state->request->data.init_conn.dcname
676 [sizeof(state->request->data.init_conn.dcname)-1]='\0';
678 if (strlen(state->request->data.init_conn.dcname) > 0) {
679 fstrcpy(domain->dcname, state->request->data.init_conn.dcname);
682 init_dc_connection(domain, false);
684 if (!domain->initialized) {
685 /* If we return error here we can't do any cached authentication,
686 but we may be in disconnected mode and can't initialize correctly.
687 Do what the previous code did and just return without initialization,
688 once we go online we'll re-initialize.
690 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
691 "online = %d\n", domain->name, (int)domain->online ));
694 fstrcpy(state->response->data.domain_info.name, domain->name);
695 fstrcpy(state->response->data.domain_info.alt_name, domain->alt_name);
696 sid_to_fstring(state->response->data.domain_info.sid, &domain->sid);
698 state->response->data.domain_info.native_mode
699 = domain->native_mode;
700 state->response->data.domain_info.active_directory
701 = domain->active_directory;
702 state->response->data.domain_info.primary
703 = domain->primary;
705 return WINBINDD_OK;
708 static void wb_imsg_new_trusted_domain(struct imessaging_context *msg,
709 void *private_data,
710 uint32_t msg_type,
711 struct server_id server_id,
712 DATA_BLOB *data)
714 TALLOC_CTX *frame = talloc_stackframe();
715 struct lsa_TrustDomainInfoInfoEx info;
716 enum ndr_err_code ndr_err;
717 struct winbindd_domain *d = NULL;
719 DEBUG(5, ("wb_imsg_new_trusted_domain\n"));
721 if (data == NULL) {
722 TALLOC_FREE(frame);
723 return;
726 ndr_err = ndr_pull_struct_blob_all(data, frame, &info,
727 (ndr_pull_flags_fn_t)ndr_pull_lsa_TrustDomainInfoInfoEx);
728 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
729 TALLOC_FREE(frame);
730 return;
733 d = find_domain_from_name_noinit(info.netbios_name.string);
734 if (d != NULL) {
735 TALLOC_FREE(frame);
736 return;
739 d = add_trusted_domain(info.netbios_name.string,
740 info.domain_name.string,
741 info.sid);
742 if (d == NULL) {
743 TALLOC_FREE(frame);
744 return;
747 if (d->internal) {
748 TALLOC_FREE(frame);
749 return;
752 if (d->primary) {
753 TALLOC_FREE(frame);
754 return;
757 if (info.trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
758 d->domain_flags |= NETR_TRUST_FLAG_INBOUND;
760 if (info.trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) {
761 d->domain_flags |= NETR_TRUST_FLAG_OUTBOUND;
763 if (info.trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
764 d->domain_flags |= NETR_TRUST_FLAG_IN_FOREST;
766 d->domain_type = info.trust_type;
767 d->domain_trust_attribs = info.trust_attributes;
769 TALLOC_FREE(frame);
773 * We did not get the secret when we queried secrets.tdb, so read it
774 * from secrets.tdb and re-sync the databases
776 static bool migrate_secrets_tdb_to_ldb(struct winbindd_domain *domain)
778 bool ok;
779 struct cli_credentials *creds;
780 NTSTATUS can_migrate = pdb_get_trust_credentials(domain->name,
781 NULL, domain, &creds);
782 if (!NT_STATUS_IS_OK(can_migrate)) {
783 DEBUG(0, ("Failed to fetch our own, local AD domain join "
784 "password for winbindd's internal use, both from "
785 "secrets.tdb and secrets.ldb: %s\n",
786 nt_errstr(can_migrate)));
787 return false;
791 * NOTE: It is very unlikely we end up here if there is an
792 * oldpass, because a new password is created at
793 * classicupgrade, so this is not a concern.
795 ok = secrets_store_machine_pw_sync(cli_credentials_get_password(creds),
796 NULL /* oldpass */,
797 cli_credentials_get_domain(creds),
798 cli_credentials_get_realm(creds),
799 cli_credentials_get_salt_principal(creds),
800 0, /* Supported enc types, unused */
801 &domain->sid,
802 cli_credentials_get_password_last_changed_time(creds),
803 cli_credentials_get_secure_channel_type(creds),
804 false /* do_delete: Do not delete */);
805 TALLOC_FREE(creds);
806 if (ok == false) {
807 DEBUG(0, ("Failed to write our our own, "
808 "local AD domain join password for "
809 "winbindd's internal use into secrets.tdb\n"));
810 return false;
812 return true;
815 /* Look up global info for the winbind daemon */
816 bool init_domain_list(void)
818 int role = lp_server_role();
819 struct pdb_domain_info *pdb_domain_info = NULL;
820 NTSTATUS status;
822 /* Free existing list */
823 free_domain_list();
825 /* BUILTIN domain */
827 (void)add_trusted_domain("BUILTIN", NULL, &global_sid_Builtin);
829 /* Local SAM */
832 * In case the passdb backend is passdb_dsdb the domain SID comes from
833 * dsdb, not from secrets.tdb. As we use the domain SID in various
834 * places, we must ensure the domain SID is migrated from dsdb to
835 * secrets.tdb before get_global_sam_sid() is called the first time.
837 * The migration is done as part of the passdb_dsdb initialisation,
838 * calling pdb_get_domain_info() triggers it.
840 pdb_domain_info = pdb_get_domain_info(talloc_tos());
842 if ( role == ROLE_ACTIVE_DIRECTORY_DC ) {
843 struct winbindd_domain *domain;
844 enum netr_SchannelType sec_chan_type;
845 const char *account_name;
846 struct samr_Password current_nt_hash;
847 bool ok;
849 if (pdb_domain_info == NULL) {
850 DEBUG(0, ("Failed to fetch our own, local AD "
851 "domain info from sam.ldb\n"));
852 return false;
854 domain = add_trusted_domain(pdb_domain_info->name,
855 pdb_domain_info->dns_domain,
856 &pdb_domain_info->sid);
857 TALLOC_FREE(pdb_domain_info);
858 if (domain == NULL) {
859 DEBUG(0, ("Failed to add our own, local AD "
860 "domain to winbindd's internal list\n"));
861 return false;
865 * We need to call this to find out if we are an RODC
867 ok = get_trust_pw_hash(domain->name,
868 current_nt_hash.hash,
869 &account_name,
870 &sec_chan_type);
871 if (!ok) {
873 * If get_trust_pw_hash() fails, then try and
874 * fetch the password from the more recent of
875 * secrets.{ldb,tdb} using the
876 * pdb_get_trust_credentials()
878 ok = migrate_secrets_tdb_to_ldb(domain);
880 if (!ok) {
881 DEBUG(0, ("Failed to migrate our own, "
882 "local AD domain join password for "
883 "winbindd's internal use into "
884 "secrets.tdb\n"));
885 return false;
887 ok = get_trust_pw_hash(domain->name,
888 current_nt_hash.hash,
889 &account_name,
890 &sec_chan_type);
891 if (!ok) {
892 DEBUG(0, ("Failed to find our our own, just "
893 "written local AD domain join "
894 "password for winbindd's internal "
895 "use in secrets.tdb\n"));
896 return false;
899 if (sec_chan_type == SEC_CHAN_RODC) {
900 domain->rodc = true;
903 } else {
904 (void)add_trusted_domain(get_global_sam_name(), NULL,
905 get_global_sam_sid());
907 /* Add ourselves as the first entry. */
909 if ( role == ROLE_DOMAIN_MEMBER ) {
910 struct winbindd_domain *domain;
911 struct dom_sid our_sid;
913 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
914 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
915 return False;
918 domain = add_trusted_domain(lp_workgroup(), lp_realm(),
919 &our_sid);
920 if (domain) {
921 /* Even in the parent winbindd we'll need to
922 talk to the DC, so try and see if we can
923 contact it. Theoretically this isn't neccessary
924 as the init_dc_connection() in init_child_recv()
925 will do this, but we can start detecting the DC
926 early here. */
927 set_domain_online_request(domain);
931 status = imessaging_register(winbind_imessaging_context(), NULL,
932 MSG_WINBIND_NEW_TRUSTED_DOMAIN,
933 wb_imsg_new_trusted_domain);
934 if (!NT_STATUS_IS_OK(status)) {
935 DEBUG(0, ("imessaging_register(MSG_WINBIND_NEW_TRUSTED_DOMAIN) - %s\n",
936 nt_errstr(status)));
937 return false;
940 return True;
944 * Given a domain name, return the struct winbindd domain info for it
946 * @note Do *not* pass lp_workgroup() to this function. domain_list
947 * may modify it's value, and free that pointer. Instead, our local
948 * domain may be found by calling find_our_domain().
949 * directly.
952 * @return The domain structure for the named domain, if it is working.
955 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
957 struct winbindd_domain *domain;
959 /* Search through list */
961 for (domain = domain_list(); domain != NULL; domain = domain->next) {
962 if (strequal(domain_name, domain->name)) {
963 return domain;
965 if (domain->alt_name == NULL) {
966 continue;
968 if (strequal(domain_name, domain->alt_name)) {
969 return domain;
973 /* Not found */
975 return NULL;
978 struct winbindd_domain *find_domain_from_name(const char *domain_name)
980 struct winbindd_domain *domain;
982 domain = find_domain_from_name_noinit(domain_name);
984 if (domain == NULL)
985 return NULL;
987 if (!domain->initialized)
988 init_dc_connection(domain, false);
990 return domain;
993 /* Given a domain sid, return the struct winbindd domain info for it */
995 struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid)
997 struct winbindd_domain *domain;
999 /* Search through list */
1001 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1002 if (dom_sid_compare_domain(sid, &domain->sid) == 0)
1003 return domain;
1006 /* Not found */
1008 return NULL;
1011 /* Given a domain sid, return the struct winbindd domain info for it */
1013 struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid)
1015 struct winbindd_domain *domain;
1017 domain = find_domain_from_sid_noinit(sid);
1019 if (domain == NULL)
1020 return NULL;
1022 if (!domain->initialized)
1023 init_dc_connection(domain, false);
1025 return domain;
1028 struct winbindd_domain *find_our_domain(void)
1030 struct winbindd_domain *domain;
1032 /* Search through list */
1034 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1035 if (domain->primary)
1036 return domain;
1039 smb_panic("Could not find our domain");
1040 return NULL;
1043 /* Find the appropriate domain to lookup a name or SID */
1045 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
1047 DBG_DEBUG("SID [%s]\n", sid_string_dbg(sid));
1050 * SIDs in the S-1-22-{1,2} domain and well-known SIDs should be handled
1051 * by our passdb.
1054 if ( sid_check_is_in_unix_groups(sid) ||
1055 sid_check_is_unix_groups(sid) ||
1056 sid_check_is_in_unix_users(sid) ||
1057 sid_check_is_unix_users(sid) ||
1058 sid_check_is_wellknown_domain(sid, NULL) ||
1059 sid_check_is_in_wellknown_domain(sid) )
1061 return find_domain_from_sid(get_global_sam_sid());
1064 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
1065 * one to contact the external DC's. On member servers the internal
1066 * domains are different: These are part of the local SAM. */
1068 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
1069 DEBUG(10, ("calling find_domain_from_sid\n"));
1070 return find_domain_from_sid(sid);
1073 /* On a member server a query for SID or name can always go to our
1074 * primary DC. */
1076 DEBUG(10, ("calling find_our_domain\n"));
1077 return find_our_domain();
1080 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
1082 if ( strequal(domain_name, unix_users_domain_name() ) ||
1083 strequal(domain_name, unix_groups_domain_name() ) )
1086 * The "Unix User" and "Unix Group" domain our handled by
1087 * passdb
1089 return find_domain_from_name_noinit( get_global_sam_name() );
1092 if (IS_DC || strequal(domain_name, "BUILTIN") ||
1093 strequal(domain_name, get_global_sam_name()))
1094 return find_domain_from_name_noinit(domain_name);
1097 return find_our_domain();
1100 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1102 static bool assume_domain(const char *domain)
1104 /* never assume the domain on a standalone server */
1106 if ( lp_server_role() == ROLE_STANDALONE )
1107 return False;
1109 /* domain member servers may possibly assume for the domain name */
1111 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1112 if ( !strequal(lp_workgroup(), domain) )
1113 return False;
1115 if ( lp_winbind_use_default_domain() )
1116 return True;
1119 /* only left with a domain controller */
1121 if ( strequal(get_global_sam_name(), domain) ) {
1122 return True;
1125 return False;
1128 /* Parse a string of the form DOMAIN\user into a domain and a user */
1130 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
1132 char *p = strchr(domuser,*lp_winbind_separator());
1134 if ( !p ) {
1135 fstrcpy(user, domuser);
1136 p = strchr(domuser, '@');
1138 if ( assume_domain(lp_workgroup()) && p == NULL) {
1139 fstrcpy(domain, lp_workgroup());
1140 } else if (p != NULL) {
1141 fstrcpy(domain, p + 1);
1142 user[PTR_DIFF(p, domuser)] = 0;
1143 } else {
1144 return False;
1146 } else {
1147 fstrcpy(user, p+1);
1148 fstrcpy(domain, domuser);
1149 domain[PTR_DIFF(p, domuser)] = 0;
1152 return strupper_m(domain);
1155 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1156 char **domain, char **user)
1158 fstring fstr_domain, fstr_user;
1159 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1160 return False;
1162 *domain = talloc_strdup(mem_ctx, fstr_domain);
1163 *user = talloc_strdup(mem_ctx, fstr_user);
1164 return ((*domain != NULL) && (*user != NULL));
1167 /* Ensure an incoming username from NSS is fully qualified. Replace the
1168 incoming fstring with DOMAIN <separator> user. Returns the same
1169 values as parse_domain_user() but also replaces the incoming username.
1170 Used to ensure all names are fully qualified within winbindd.
1171 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1172 The protocol definitions of auth_crap, chng_pswd_auth_crap
1173 really should be changed to use this instead of doing things
1174 by hand. JRA. */
1176 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
1178 if (!parse_domain_user(username_inout, domain, user)) {
1179 return False;
1181 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1182 domain, *lp_winbind_separator(),
1183 user);
1184 return True;
1188 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1189 'winbind separator' options.
1190 This means:
1191 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1192 lp_workgroup()
1194 If we are a PDC or BDC, and this is for our domain, do likewise.
1196 On an AD DC we always fill DOMAIN\\USERNAME.
1198 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1200 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
1202 fstring tmp_user;
1204 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
1205 can_assume = false;
1208 fstrcpy(tmp_user, user);
1209 (void)strlower_m(tmp_user);
1211 if (can_assume && assume_domain(domain)) {
1212 strlcpy(name, tmp_user, sizeof(fstring));
1213 } else {
1214 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1215 domain, *lp_winbind_separator(),
1216 tmp_user);
1221 * talloc version of fill_domain_username()
1222 * return NULL on talloc failure.
1224 char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
1225 const char *domain,
1226 const char *user,
1227 bool can_assume)
1229 char *tmp_user, *name;
1231 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
1232 can_assume = false;
1235 tmp_user = talloc_strdup(mem_ctx, user);
1236 if (!strlower_m(tmp_user)) {
1237 TALLOC_FREE(tmp_user);
1238 return NULL;
1241 if (can_assume && assume_domain(domain)) {
1242 name = tmp_user;
1243 } else {
1244 name = talloc_asprintf(mem_ctx, "%s%c%s",
1245 domain,
1246 *lp_winbind_separator(),
1247 tmp_user);
1248 TALLOC_FREE(tmp_user);
1251 return name;
1255 * Client list accessor functions
1258 static struct winbindd_cli_state *_client_list;
1259 static int _num_clients;
1261 /* Return list of all connected clients */
1263 struct winbindd_cli_state *winbindd_client_list(void)
1265 return _client_list;
1268 /* Return list-tail of all connected clients */
1270 struct winbindd_cli_state *winbindd_client_list_tail(void)
1272 return DLIST_TAIL(_client_list);
1275 /* Return previous (read:newer) client in list */
1277 struct winbindd_cli_state *
1278 winbindd_client_list_prev(struct winbindd_cli_state *cli)
1280 return DLIST_PREV(cli);
1283 /* Add a connection to the list */
1285 void winbindd_add_client(struct winbindd_cli_state *cli)
1287 cli->last_access = time(NULL);
1288 DLIST_ADD(_client_list, cli);
1289 _num_clients++;
1292 /* Remove a client from the list */
1294 void winbindd_remove_client(struct winbindd_cli_state *cli)
1296 DLIST_REMOVE(_client_list, cli);
1297 _num_clients--;
1300 /* Move a client to head or list */
1302 void winbindd_promote_client(struct winbindd_cli_state *cli)
1304 cli->last_access = time(NULL);
1305 DLIST_PROMOTE(_client_list, cli);
1308 /* Return number of open clients */
1310 int winbindd_num_clients(void)
1312 return _num_clients;
1315 NTSTATUS lookup_usergroups_cached(TALLOC_CTX *mem_ctx,
1316 const struct dom_sid *user_sid,
1317 uint32_t *p_num_groups, struct dom_sid **user_sids)
1319 struct netr_SamInfo3 *info3 = NULL;
1320 NTSTATUS status = NT_STATUS_NO_MEMORY;
1321 uint32_t num_groups = 0;
1323 DEBUG(3,(": lookup_usergroups_cached\n"));
1325 *user_sids = NULL;
1326 *p_num_groups = 0;
1328 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1330 if (info3 == NULL) {
1331 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1335 * Before bug #7843 the "Domain Local" groups were added with a
1336 * lookupuseraliases call, but this isn't done anymore for our domain
1337 * so we need to resolve resource groups here.
1339 * When to use Resource Groups:
1340 * http://technet.microsoft.com/en-us/library/cc753670%28v=WS.10%29.aspx
1342 status = sid_array_from_info3(mem_ctx, info3,
1343 user_sids,
1344 &num_groups,
1345 false);
1347 if (!NT_STATUS_IS_OK(status)) {
1348 TALLOC_FREE(info3);
1349 return status;
1352 TALLOC_FREE(info3);
1353 *p_num_groups = num_groups;
1354 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1356 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1358 return status;
1361 /*********************************************************************
1362 We use this to remove spaces from user and group names
1363 ********************************************************************/
1365 NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
1366 const char *domain_name,
1367 const char *name,
1368 char **normalized)
1370 struct winbindd_domain *domain = NULL;
1371 NTSTATUS nt_status;
1373 if (!name || !normalized) {
1374 return NT_STATUS_INVALID_PARAMETER;
1377 if (!lp_winbind_normalize_names()) {
1378 return NT_STATUS_PROCEDURE_NOT_FOUND;
1381 domain = find_domain_from_name_noinit(domain_name);
1382 if (domain == NULL) {
1383 DBG_ERR("Failed to find domain '%s'\n", domain_name);
1384 return NT_STATUS_NO_SUCH_DOMAIN;
1387 /* Alias support and whitespace replacement are mutually
1388 exclusive */
1390 nt_status = resolve_username_to_alias(mem_ctx, domain,
1391 name, normalized );
1392 if (NT_STATUS_IS_OK(nt_status)) {
1393 /* special return code to let the caller know we
1394 mapped to an alias */
1395 return NT_STATUS_FILE_RENAMED;
1398 /* check for an unreachable domain */
1400 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1401 DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
1402 domain->name));
1403 set_domain_offline(domain);
1404 return nt_status;
1407 /* deal with whitespace */
1409 *normalized = talloc_strdup(mem_ctx, name);
1410 if (!(*normalized)) {
1411 return NT_STATUS_NO_MEMORY;
1414 all_string_sub( *normalized, " ", "_", 0 );
1416 return NT_STATUS_OK;
1419 /*********************************************************************
1420 We use this to do the inverse of normalize_name_map()
1421 ********************************************************************/
1423 NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
1424 char *name,
1425 char **normalized)
1427 NTSTATUS nt_status;
1428 struct winbindd_domain *domain = find_our_domain();
1430 if (!name || !normalized) {
1431 return NT_STATUS_INVALID_PARAMETER;
1434 if (!lp_winbind_normalize_names()) {
1435 return NT_STATUS_PROCEDURE_NOT_FOUND;
1438 /* Alias support and whitespace replacement are mutally
1439 exclusive */
1441 /* When mapping from an alias to a username, we don't know the
1442 domain. But we only need a domain structure to cache
1443 a successful lookup , so just our own domain structure for
1444 the seqnum. */
1446 nt_status = resolve_alias_to_username(mem_ctx, domain,
1447 name, normalized);
1448 if (NT_STATUS_IS_OK(nt_status)) {
1449 /* Special return code to let the caller know we mapped
1450 from an alias */
1451 return NT_STATUS_FILE_RENAMED;
1454 /* check for an unreachable domain */
1456 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1457 DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
1458 domain->name));
1459 set_domain_offline(domain);
1460 return nt_status;
1463 /* deal with whitespace */
1465 *normalized = talloc_strdup(mem_ctx, name);
1466 if (!(*normalized)) {
1467 return NT_STATUS_NO_MEMORY;
1470 all_string_sub(*normalized, "_", " ", 0);
1472 return NT_STATUS_OK;
1475 /*********************************************************************
1476 ********************************************************************/
1478 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1480 struct winbindd_tdc_domain *tdc = NULL;
1481 TALLOC_CTX *frame = talloc_stackframe();
1482 bool ret = false;
1484 /* We can contact the domain if it is our primary domain */
1486 if (domain->primary) {
1487 ret = true;
1488 goto done;
1491 /* Trust the TDC cache and not the winbindd_domain flags */
1493 if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1494 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1495 domain->name));
1496 ret = false;
1497 goto done;
1500 /* Can always contact a domain that is in out forest */
1502 if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1503 ret = true;
1504 goto done;
1508 * On a _member_ server, we cannot contact the domain if it
1509 * is running AD and we have no inbound trust.
1512 if (!IS_DC &&
1513 domain->active_directory &&
1514 ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
1516 DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
1517 "and we have no inbound trust.\n", domain->name));
1518 goto done;
1521 /* Assume everything else is ok (probably not true but what
1522 can you do?) */
1524 ret = true;
1526 done:
1527 talloc_destroy(frame);
1529 return ret;
1532 /*********************************************************************
1533 ********************************************************************/
1535 bool winbindd_internal_child(struct winbindd_child *child)
1537 if ((child == idmap_child()) || (child == locator_child())) {
1538 return True;
1541 return False;
1544 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1546 /*********************************************************************
1547 ********************************************************************/
1549 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1551 char *var = NULL;
1552 char addr[INET6_ADDRSTRLEN];
1553 const char *kdc = NULL;
1554 int lvl = 11;
1556 if (!domain || !domain->alt_name || !*domain->alt_name) {
1557 return;
1560 if (domain->initialized && !domain->active_directory) {
1561 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1562 domain->alt_name));
1563 return;
1566 print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1567 kdc = addr;
1568 if (!*kdc) {
1569 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1570 domain->alt_name));
1571 kdc = domain->dcname;
1574 if (!kdc || !*kdc) {
1575 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1576 domain->alt_name));
1577 return;
1580 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1581 domain->alt_name) == -1) {
1582 return;
1585 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1586 var, kdc));
1588 setenv(var, kdc, 1);
1589 free(var);
1592 /*********************************************************************
1593 ********************************************************************/
1595 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1597 struct winbindd_domain *our_dom = find_our_domain();
1599 winbindd_set_locator_kdc_env(domain);
1601 if (domain != our_dom) {
1602 winbindd_set_locator_kdc_env(our_dom);
1606 /*********************************************************************
1607 ********************************************************************/
1609 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1611 char *var = NULL;
1613 if (!domain || !domain->alt_name || !*domain->alt_name) {
1614 return;
1617 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1618 domain->alt_name) == -1) {
1619 return;
1622 unsetenv(var);
1623 free(var);
1625 #else
1627 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1629 return;
1632 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1634 return;
1637 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */
1639 void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
1641 resp->data.auth.nt_status = NT_STATUS_V(result);
1642 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
1644 /* we might have given a more useful error above */
1645 if (*resp->data.auth.error_string == '\0')
1646 fstrcpy(resp->data.auth.error_string,
1647 get_friendly_nt_error_msg(result));
1648 resp->data.auth.pam_error = nt_status_to_pam(result);
1651 bool is_domain_offline(const struct winbindd_domain *domain)
1653 if (get_global_winbindd_state_offline()) {
1654 return true;
1656 return !domain->online;
1659 bool is_domain_online(const struct winbindd_domain *domain)
1661 return !is_domain_offline(domain);
1665 * Parse an char array into a list of sids.
1667 * The input sidstr should consist of 0-terminated strings
1668 * representing sids, separated by newline characters '\n'.
1669 * The list is terminated by an empty string, i.e.
1670 * character '\0' directly following a character '\n'
1671 * (or '\0' right at the start of sidstr).
1673 bool parse_sidlist(TALLOC_CTX *mem_ctx, const char *sidstr,
1674 struct dom_sid **sids, uint32_t *num_sids)
1676 const char *p;
1678 p = sidstr;
1679 if (p == NULL)
1680 return False;
1682 while (p[0] != '\0') {
1683 struct dom_sid sid;
1684 const char *q = NULL;
1686 if (!dom_sid_parse_endp(p, &sid, &q)) {
1687 DEBUG(1, ("Could not parse sid %s\n", p));
1688 return false;
1690 if (q[0] != '\n') {
1691 DEBUG(1, ("Got invalid sidstr: %s\n", p));
1692 return false;
1694 if (!NT_STATUS_IS_OK(add_sid_to_array(mem_ctx, &sid, sids,
1695 num_sids)))
1697 return False;
1699 p = q+1;
1701 return True;
1704 bool parse_xidlist(TALLOC_CTX *mem_ctx, const char *xidstr,
1705 struct unixid **pxids, uint32_t *pnum_xids)
1707 const char *p;
1708 struct unixid *xids = NULL;
1709 uint32_t num_xids = 0;
1711 p = xidstr;
1712 if (p == NULL) {
1713 return false;
1716 while (p[0] != '\0') {
1717 struct unixid *tmp;
1718 struct unixid xid;
1719 unsigned long long id;
1720 char *endp;
1722 switch (p[0]) {
1723 case 'U':
1724 xid = (struct unixid) { .type = ID_TYPE_UID };
1725 break;
1726 case 'G':
1727 xid = (struct unixid) { .type = ID_TYPE_GID };
1728 break;
1729 default:
1730 return false;
1733 p += 1;
1735 id = strtoull(p, &endp, 10);
1736 if ((id == ULLONG_MAX) && (errno == ERANGE)) {
1737 goto fail;
1739 if (*endp != '\n') {
1740 goto fail;
1742 p = endp+1;
1744 xid.id = id;
1745 if ((unsigned long long)xid.id != id) {
1746 goto fail;
1749 tmp = talloc_realloc(mem_ctx, xids, struct unixid, num_xids+1);
1750 if (tmp == NULL) {
1751 return 0;
1753 xids = tmp;
1755 xids[num_xids] = xid;
1756 num_xids += 1;
1759 *pxids = xids;
1760 *pnum_xids = num_xids;
1761 return true;
1763 fail:
1764 TALLOC_FREE(xids);
1765 return false;