man pages: properly ident lists
[Samba.git] / source3 / winbindd / winbindd_util.c
blob6eed02e9fe889c5e62431eae8afaef1ccf374043
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 sid = NULL;
159 ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
160 for (dom=ignored_domains; dom && *dom; dom++) {
161 if (gen_fnmatch(*dom, domain_name) == 0) {
162 DEBUG(2,("Ignoring domain '%s'\n", domain_name));
163 return NULL;
167 /* use alt_name if available to allow DNS lookups */
169 if (tdc->dns_name && *tdc->dns_name) {
170 alternative_name = tdc->dns_name;
173 /* We can't call domain_list() as this function is called from
174 init_domain_list() and we'll get stuck in a loop. */
175 for (domain = _domain_list; domain; domain = domain->next) {
176 if (strequal(domain_name, domain->name) ||
177 strequal(domain_name, domain->alt_name))
179 break;
182 if (alternative_name) {
183 if (strequal(alternative_name, domain->name) ||
184 strequal(alternative_name, domain->alt_name))
186 break;
190 if (sid != NULL) {
191 if (dom_sid_equal(sid, &domain->sid)) {
192 break;
197 if (domain != NULL) {
199 * We found a match on domain->name or
200 * domain->alt_name. Possibly update the SID
201 * if the stored SID was the NULL SID
202 * and return the matching entry.
204 if ((sid != NULL)
205 && dom_sid_equal(&domain->sid, &global_sid_NULL)) {
206 sid_copy( &domain->sid, sid );
208 return domain;
211 /* Create new domain entry */
212 domain = talloc_zero(NULL, struct winbindd_domain);
213 if (domain == NULL) {
214 return NULL;
217 domain->children = talloc_zero_array(domain,
218 struct winbindd_child,
219 lp_winbind_max_domain_connections());
220 if (domain->children == NULL) {
221 TALLOC_FREE(domain);
222 return NULL;
225 domain->name = talloc_strdup(domain, domain_name);
226 if (domain->name == NULL) {
227 TALLOC_FREE(domain);
228 return NULL;
231 if (alternative_name) {
232 domain->alt_name = talloc_strdup(domain, alternative_name);
233 if (domain->alt_name == NULL) {
234 TALLOC_FREE(domain);
235 return NULL;
239 domain->backend = NULL;
240 domain->internal = is_internal_domain(sid);
241 domain->sequence_number = DOM_SEQUENCE_NONE;
242 domain->last_seq_check = 0;
243 domain->initialized = false;
244 domain->online = is_internal_domain(sid);
245 domain->check_online_timeout = 0;
246 domain->dc_probe_pid = (pid_t)-1;
247 if (sid != NULL) {
248 sid_copy(&domain->sid, sid);
250 domain->domain_flags = tdc->trust_flags;
251 domain->domain_type = tdc->trust_type;
252 domain->domain_trust_attribs = tdc->trust_attribs;
254 /* Is this our primary domain ? */
255 if (role == ROLE_DOMAIN_MEMBER) {
256 domain->primary = strequal(domain_name, lp_workgroup());
257 } else {
258 domain->primary = strequal(domain_name, get_global_sam_name());
261 if (domain->primary) {
262 if (role == ROLE_ACTIVE_DIRECTORY_DC) {
263 domain->active_directory = true;
265 if (lp_security() == SEC_ADS) {
266 domain->active_directory = true;
268 } else if (!domain->internal) {
269 if (domain->domain_type == LSA_TRUST_TYPE_UPLEVEL) {
270 domain->active_directory = true;
274 /* Link to domain list */
275 DLIST_ADD_END(_domain_list, domain);
277 wcache_tdc_add_domain( domain );
279 setup_domain_child(domain);
281 DEBUG(2,
282 ("Added domain %s %s %s\n", domain->name, domain->alt_name,
283 !is_null_sid(&domain->sid) ? sid_string_dbg(&domain->sid) : ""));
285 return domain;
288 bool domain_is_forest_root(const struct winbindd_domain *domain)
290 const uint32_t fr_flags =
291 (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
293 return ((domain->domain_flags & fr_flags) == fr_flags);
296 /********************************************************************
297 rescan our domains looking for new trusted domains
298 ********************************************************************/
300 struct trustdom_state {
301 struct winbindd_domain *domain;
302 struct winbindd_request request;
305 static void trustdom_list_done(struct tevent_req *req);
306 static void rescan_forest_root_trusts( void );
307 static void rescan_forest_trusts( void );
309 static void add_trusted_domains( struct winbindd_domain *domain )
311 struct trustdom_state *state;
312 struct tevent_req *req;
314 state = talloc_zero(NULL, struct trustdom_state);
315 if (state == NULL) {
316 DEBUG(0, ("talloc failed\n"));
317 return;
319 state->domain = domain;
321 state->request.length = sizeof(state->request);
322 state->request.cmd = WINBINDD_LIST_TRUSTDOM;
324 req = wb_domain_request_send(state, winbind_event_context(),
325 domain, &state->request);
326 if (req == NULL) {
327 DEBUG(1, ("wb_domain_request_send failed\n"));
328 TALLOC_FREE(state);
329 return;
331 tevent_req_set_callback(req, trustdom_list_done, state);
334 static void trustdom_list_done(struct tevent_req *req)
336 struct trustdom_state *state = tevent_req_callback_data(
337 req, struct trustdom_state);
338 struct winbindd_response *response;
339 int res, err;
340 char *p;
341 struct winbindd_tdc_domain trust_params = {0};
342 ptrdiff_t extra_len;
343 bool within_forest = false;
346 * Only when we enumerate our primary domain
347 * or our forest root domain, we should keep
348 * the NETR_TRUST_FLAG_IN_FOREST flag, in
349 * all other cases we need to clear it as the domain
350 * is not part of our forest.
352 if (state->domain->primary) {
353 within_forest = true;
354 } else if (domain_is_forest_root(state->domain)) {
355 within_forest = true;
358 res = wb_domain_request_recv(req, state, &response, &err);
359 if ((res == -1) || (response->result != WINBINDD_OK)) {
360 DBG_WARNING("Could not receive trusts for domain %s\n",
361 state->domain->name);
362 TALLOC_FREE(state);
363 return;
366 if (response->length < sizeof(struct winbindd_response)) {
367 DBG_ERR("ill-formed trustdom response - short length\n");
368 TALLOC_FREE(state);
369 return;
372 extra_len = response->length - sizeof(struct winbindd_response);
374 p = (char *)response->extra_data.data;
376 while ((p - (char *)response->extra_data.data) < extra_len) {
377 char *q, *sidstr, *alt_name;
379 DBG_DEBUG("parsing response line '%s'\n", p);
381 ZERO_STRUCT(trust_params);
382 trust_params.domain_name = p;
384 alt_name = strchr(p, '\\');
385 if (alt_name == NULL) {
386 DBG_ERR("Got invalid trustdom response\n");
387 break;
390 *alt_name = '\0';
391 alt_name += 1;
393 sidstr = strchr(alt_name, '\\');
394 if (sidstr == NULL) {
395 DBG_ERR("Got invalid trustdom response\n");
396 break;
399 *sidstr = '\0';
400 sidstr += 1;
402 /* use the real alt_name if we have one, else pass in NULL */
403 if (!strequal(alt_name, "(null)")) {
404 trust_params.dns_name = alt_name;
407 q = strtok(sidstr, "\\");
408 if (q == NULL) {
409 DBG_ERR("Got invalid trustdom response\n");
410 break;
413 if (!string_to_sid(&trust_params.sid, sidstr)) {
414 DEBUG(0, ("Got invalid trustdom response\n"));
415 break;
418 q = strtok(NULL, "\\");
419 if (q == NULL) {
420 DBG_ERR("Got invalid trustdom response\n");
421 break;
424 trust_params.trust_flags = (uint32_t)strtoul(q, NULL, 10);
426 q = strtok(NULL, "\\");
427 if (q == NULL) {
428 DBG_ERR("Got invalid trustdom response\n");
429 break;
432 trust_params.trust_type = (uint32_t)strtoul(q, NULL, 10);
434 q = strtok(NULL, "\n");
435 if (q == NULL) {
436 DBG_ERR("Got invalid trustdom response\n");
437 break;
440 trust_params.trust_attribs = (uint32_t)strtoul(q, NULL, 10);
442 if (!within_forest) {
443 trust_params.trust_flags &= ~NETR_TRUST_FLAG_IN_FOREST;
446 if (!state->domain->primary) {
447 trust_params.trust_flags &= ~NETR_TRUST_FLAG_PRIMARY;
451 * We always call add_trusted_domain() cause on an existing
452 * domain structure, it will update the SID if necessary.
453 * This is important because we need the SID for sibling
454 * domains.
456 (void)add_trusted_domain_from_tdc(&trust_params);
458 p = q + strlen(q) + 1;
462 Cases to consider when scanning trusts:
463 (a) we are calling from a child domain (primary && !forest_root)
464 (b) we are calling from the root of the forest (primary && forest_root)
465 (c) we are calling from a trusted forest domain (!primary
466 && !forest_root)
469 if (state->domain->primary) {
470 /* If this is our primary domain and we are not in the
471 forest root, we have to scan the root trusts first */
473 if (!domain_is_forest_root(state->domain))
474 rescan_forest_root_trusts();
475 else
476 rescan_forest_trusts();
478 } else if (domain_is_forest_root(state->domain)) {
479 /* Once we have done root forest trust search, we can
480 go on to search the trusted forests */
482 rescan_forest_trusts();
485 TALLOC_FREE(state);
487 return;
490 /********************************************************************
491 Scan the trusts of our forest root
492 ********************************************************************/
494 static void rescan_forest_root_trusts( void )
496 struct winbindd_tdc_domain *dom_list = NULL;
497 size_t num_trusts = 0;
498 int i;
500 /* The only transitive trusts supported by Windows 2003 AD are
501 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
502 first two are handled in forest and listed by
503 DsEnumerateDomainTrusts(). Forest trusts are not so we
504 have to do that ourselves. */
506 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
507 return;
509 for ( i=0; i<num_trusts; i++ ) {
510 struct winbindd_domain *d = NULL;
512 /* Find the forest root. Don't necessarily trust
513 the domain_list() as our primary domain may not
514 have been initialized. */
516 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
517 continue;
520 /* Here's the forest root */
522 d = find_domain_from_name_noinit( dom_list[i].domain_name );
524 if ( !d ) {
525 d = add_trusted_domain_from_tdc(&dom_list[i]);
528 if (d == NULL) {
529 continue;
532 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
533 "for domain tree root %s (%s)\n",
534 d->name, d->alt_name ));
536 d->domain_flags = dom_list[i].trust_flags;
537 d->domain_type = dom_list[i].trust_type;
538 d->domain_trust_attribs = dom_list[i].trust_attribs;
540 add_trusted_domains( d );
542 break;
545 TALLOC_FREE( dom_list );
547 return;
550 /********************************************************************
551 scan the transitive forest trusts (not our own)
552 ********************************************************************/
555 static void rescan_forest_trusts( void )
557 struct winbindd_domain *d = NULL;
558 struct winbindd_tdc_domain *dom_list = NULL;
559 size_t num_trusts = 0;
560 int i;
562 /* The only transitive trusts supported by Windows 2003 AD are
563 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
564 first two are handled in forest and listed by
565 DsEnumerateDomainTrusts(). Forest trusts are not so we
566 have to do that ourselves. */
568 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
569 return;
571 for ( i=0; i<num_trusts; i++ ) {
572 uint32_t flags = dom_list[i].trust_flags;
573 uint32_t type = dom_list[i].trust_type;
574 uint32_t attribs = dom_list[i].trust_attribs;
576 d = find_domain_from_name_noinit( dom_list[i].domain_name );
578 /* ignore our primary and internal domains */
580 if ( d && (d->internal || d->primary ) )
581 continue;
583 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
584 (type == LSA_TRUST_TYPE_UPLEVEL) &&
585 (attribs == LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
587 /* add the trusted domain if we don't know
588 about it */
590 if ( !d ) {
591 d = add_trusted_domain_from_tdc(&dom_list[i]);
594 if (d == NULL) {
595 continue;
598 DEBUG(10,("Following trust path for domain %s (%s)\n",
599 d->name, d->alt_name ));
600 add_trusted_domains( d );
604 TALLOC_FREE( dom_list );
606 return;
609 /*********************************************************************
610 The process of updating the trusted domain list is a three step
611 async process:
612 (a) ask our domain
613 (b) ask the root domain in our forest
614 (c) ask the a DC in any Win2003 trusted forests
615 *********************************************************************/
617 void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
618 struct timeval now, void *private_data)
620 TALLOC_FREE(te);
622 /* I use to clear the cache here and start over but that
623 caused problems in child processes that needed the
624 trust dom list early on. Removing it means we
625 could have some trusted domains listed that have been
626 removed from our primary domain's DC until a full
627 restart. This should be ok since I think this is what
628 Windows does as well. */
630 /* this will only add new domains we didn't already know about
631 in the domain_list()*/
633 add_trusted_domains( find_our_domain() );
635 te = tevent_add_timer(
636 ev, NULL, timeval_current_ofs(WINBINDD_RESCAN_FREQ, 0),
637 rescan_trusted_domains, NULL);
639 * If te == NULL, there's not much we can do here. Don't fail, the
640 * only thing we miss is new trusted domains.
643 return;
646 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
647 struct winbindd_cli_state *state)
649 /* Ensure null termination */
650 state->request->domain_name
651 [sizeof(state->request->domain_name)-1]='\0';
652 state->request->data.init_conn.dcname
653 [sizeof(state->request->data.init_conn.dcname)-1]='\0';
655 if (strlen(state->request->data.init_conn.dcname) > 0) {
656 fstrcpy(domain->dcname, state->request->data.init_conn.dcname);
659 init_dc_connection(domain, false);
661 if (!domain->initialized) {
662 /* If we return error here we can't do any cached authentication,
663 but we may be in disconnected mode and can't initialize correctly.
664 Do what the previous code did and just return without initialization,
665 once we go online we'll re-initialize.
667 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
668 "online = %d\n", domain->name, (int)domain->online ));
671 fstrcpy(state->response->data.domain_info.name, domain->name);
672 fstrcpy(state->response->data.domain_info.alt_name, domain->alt_name);
673 sid_to_fstring(state->response->data.domain_info.sid, &domain->sid);
675 state->response->data.domain_info.native_mode
676 = domain->native_mode;
677 state->response->data.domain_info.active_directory
678 = domain->active_directory;
679 state->response->data.domain_info.primary
680 = domain->primary;
682 return WINBINDD_OK;
685 static void wb_imsg_new_trusted_domain(struct imessaging_context *msg,
686 void *private_data,
687 uint32_t msg_type,
688 struct server_id server_id,
689 DATA_BLOB *data)
691 TALLOC_CTX *frame = talloc_stackframe();
692 struct lsa_TrustDomainInfoInfoEx info;
693 enum ndr_err_code ndr_err;
694 struct winbindd_domain *d = NULL;
696 DEBUG(5, ("wb_imsg_new_trusted_domain\n"));
698 if (data == NULL) {
699 TALLOC_FREE(frame);
700 return;
703 ndr_err = ndr_pull_struct_blob_all(data, frame, &info,
704 (ndr_pull_flags_fn_t)ndr_pull_lsa_TrustDomainInfoInfoEx);
705 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
706 TALLOC_FREE(frame);
707 return;
710 d = find_domain_from_name_noinit(info.netbios_name.string);
711 if (d != NULL) {
712 TALLOC_FREE(frame);
713 return;
716 d = add_trusted_domain(info.netbios_name.string,
717 info.domain_name.string,
718 info.sid);
719 if (d == NULL) {
720 TALLOC_FREE(frame);
721 return;
724 if (d->internal) {
725 TALLOC_FREE(frame);
726 return;
729 if (d->primary) {
730 TALLOC_FREE(frame);
731 return;
734 if (info.trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
735 d->domain_flags |= NETR_TRUST_FLAG_INBOUND;
737 if (info.trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) {
738 d->domain_flags |= NETR_TRUST_FLAG_OUTBOUND;
740 if (info.trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
741 d->domain_flags |= NETR_TRUST_FLAG_IN_FOREST;
743 d->domain_type = info.trust_type;
744 d->domain_trust_attribs = info.trust_attributes;
746 TALLOC_FREE(frame);
750 * We did not get the secret when we queried secrets.tdb, so read it
751 * from secrets.tdb and re-sync the databases
753 static bool migrate_secrets_tdb_to_ldb(struct winbindd_domain *domain)
755 bool ok;
756 struct cli_credentials *creds;
757 NTSTATUS can_migrate = pdb_get_trust_credentials(domain->name,
758 NULL, domain, &creds);
759 if (!NT_STATUS_IS_OK(can_migrate)) {
760 DEBUG(0, ("Failed to fetch our own, local AD domain join "
761 "password for winbindd's internal use, both from "
762 "secrets.tdb and secrets.ldb: %s\n",
763 nt_errstr(can_migrate)));
764 return false;
768 * NOTE: It is very unlikely we end up here if there is an
769 * oldpass, because a new password is created at
770 * classicupgrade, so this is not a concern.
772 ok = secrets_store_machine_pw_sync(cli_credentials_get_password(creds),
773 NULL /* oldpass */,
774 cli_credentials_get_domain(creds),
775 cli_credentials_get_realm(creds),
776 cli_credentials_get_salt_principal(creds),
777 0, /* Supported enc types, unused */
778 &domain->sid,
779 cli_credentials_get_password_last_changed_time(creds),
780 cli_credentials_get_secure_channel_type(creds),
781 false /* do_delete: Do not delete */);
782 TALLOC_FREE(creds);
783 if (ok == false) {
784 DEBUG(0, ("Failed to write our our own, "
785 "local AD domain join password for "
786 "winbindd's internal use into secrets.tdb\n"));
787 return false;
789 return true;
792 /* Look up global info for the winbind daemon */
793 bool init_domain_list(void)
795 int role = lp_server_role();
796 struct pdb_domain_info *pdb_domain_info = NULL;
797 NTSTATUS status;
799 /* Free existing list */
800 free_domain_list();
802 /* BUILTIN domain */
804 (void)add_trusted_domain("BUILTIN", NULL, &global_sid_Builtin);
806 /* Local SAM */
809 * In case the passdb backend is passdb_dsdb the domain SID comes from
810 * dsdb, not from secrets.tdb. As we use the domain SID in various
811 * places, we must ensure the domain SID is migrated from dsdb to
812 * secrets.tdb before get_global_sam_sid() is called the first time.
814 * The migration is done as part of the passdb_dsdb initialisation,
815 * calling pdb_get_domain_info() triggers it.
817 pdb_domain_info = pdb_get_domain_info(talloc_tos());
819 if ( role == ROLE_ACTIVE_DIRECTORY_DC ) {
820 struct winbindd_domain *domain;
821 enum netr_SchannelType sec_chan_type;
822 const char *account_name;
823 struct samr_Password current_nt_hash;
824 bool ok;
826 if (pdb_domain_info == NULL) {
827 DEBUG(0, ("Failed to fetch our own, local AD "
828 "domain info from sam.ldb\n"));
829 return false;
831 domain = add_trusted_domain(pdb_domain_info->name,
832 pdb_domain_info->dns_domain,
833 &pdb_domain_info->sid);
834 TALLOC_FREE(pdb_domain_info);
835 if (domain == NULL) {
836 DEBUG(0, ("Failed to add our own, local AD "
837 "domain to winbindd's internal list\n"));
838 return false;
842 * We need to call this to find out if we are an RODC
844 ok = get_trust_pw_hash(domain->name,
845 current_nt_hash.hash,
846 &account_name,
847 &sec_chan_type);
848 if (!ok) {
850 * If get_trust_pw_hash() fails, then try and
851 * fetch the password from the more recent of
852 * secrets.{ldb,tdb} using the
853 * pdb_get_trust_credentials()
855 ok = migrate_secrets_tdb_to_ldb(domain);
857 if (!ok) {
858 DEBUG(0, ("Failed to migrate our own, "
859 "local AD domain join password for "
860 "winbindd's internal use into "
861 "secrets.tdb\n"));
862 return false;
864 ok = get_trust_pw_hash(domain->name,
865 current_nt_hash.hash,
866 &account_name,
867 &sec_chan_type);
868 if (!ok) {
869 DEBUG(0, ("Failed to find our our own, just "
870 "written local AD domain join "
871 "password for winbindd's internal "
872 "use in secrets.tdb\n"));
873 return false;
876 if (sec_chan_type == SEC_CHAN_RODC) {
877 domain->rodc = true;
880 } else {
881 (void)add_trusted_domain(get_global_sam_name(), NULL,
882 get_global_sam_sid());
884 /* Add ourselves as the first entry. */
886 if ( role == ROLE_DOMAIN_MEMBER ) {
887 struct winbindd_domain *domain;
888 struct dom_sid our_sid;
890 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
891 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
892 return False;
895 domain = add_trusted_domain(lp_workgroup(), lp_realm(),
896 &our_sid);
897 if (domain) {
898 /* Even in the parent winbindd we'll need to
899 talk to the DC, so try and see if we can
900 contact it. Theoretically this isn't neccessary
901 as the init_dc_connection() in init_child_recv()
902 will do this, but we can start detecting the DC
903 early here. */
904 set_domain_online_request(domain);
908 status = imessaging_register(winbind_imessaging_context(), NULL,
909 MSG_WINBIND_NEW_TRUSTED_DOMAIN,
910 wb_imsg_new_trusted_domain);
911 if (!NT_STATUS_IS_OK(status)) {
912 DEBUG(0, ("imessaging_register(MSG_WINBIND_NEW_TRUSTED_DOMAIN) - %s\n",
913 nt_errstr(status)));
914 return false;
917 return True;
921 * Given a domain name, return the struct winbindd domain info for it
923 * @note Do *not* pass lp_workgroup() to this function. domain_list
924 * may modify it's value, and free that pointer. Instead, our local
925 * domain may be found by calling find_our_domain().
926 * directly.
929 * @return The domain structure for the named domain, if it is working.
932 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
934 struct winbindd_domain *domain;
936 /* Search through list */
938 for (domain = domain_list(); domain != NULL; domain = domain->next) {
939 if (strequal(domain_name, domain->name) ||
940 (domain->alt_name != NULL &&
941 strequal(domain_name, domain->alt_name))) {
942 return domain;
946 /* Not found */
948 return NULL;
951 struct winbindd_domain *find_domain_from_name(const char *domain_name)
953 struct winbindd_domain *domain;
955 domain = find_domain_from_name_noinit(domain_name);
957 if (domain == NULL)
958 return NULL;
960 if (!domain->initialized)
961 init_dc_connection(domain, false);
963 return domain;
966 /* Given a domain sid, return the struct winbindd domain info for it */
968 struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid)
970 struct winbindd_domain *domain;
972 /* Search through list */
974 for (domain = domain_list(); domain != NULL; domain = domain->next) {
975 if (dom_sid_compare_domain(sid, &domain->sid) == 0)
976 return domain;
979 /* Not found */
981 return NULL;
984 /* Given a domain sid, return the struct winbindd domain info for it */
986 struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid)
988 struct winbindd_domain *domain;
990 domain = find_domain_from_sid_noinit(sid);
992 if (domain == NULL)
993 return NULL;
995 if (!domain->initialized)
996 init_dc_connection(domain, false);
998 return domain;
1001 struct winbindd_domain *find_our_domain(void)
1003 struct winbindd_domain *domain;
1005 /* Search through list */
1007 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1008 if (domain->primary)
1009 return domain;
1012 smb_panic("Could not find our domain");
1013 return NULL;
1016 /* Find the appropriate domain to lookup a name or SID */
1018 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
1020 DBG_DEBUG("SID [%s]\n", sid_string_dbg(sid));
1023 * SIDs in the S-1-22-{1,2} domain and well-known SIDs should be handled
1024 * by our passdb.
1027 if ( sid_check_is_in_unix_groups(sid) ||
1028 sid_check_is_unix_groups(sid) ||
1029 sid_check_is_in_unix_users(sid) ||
1030 sid_check_is_unix_users(sid) ||
1031 sid_check_is_wellknown_domain(sid, NULL) ||
1032 sid_check_is_in_wellknown_domain(sid) )
1034 return find_domain_from_sid(get_global_sam_sid());
1037 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
1038 * one to contact the external DC's. On member servers the internal
1039 * domains are different: These are part of the local SAM. */
1041 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
1042 DEBUG(10, ("calling find_domain_from_sid\n"));
1043 return find_domain_from_sid(sid);
1046 /* On a member server a query for SID or name can always go to our
1047 * primary DC. */
1049 DEBUG(10, ("calling find_our_domain\n"));
1050 return find_our_domain();
1053 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
1055 if ( strequal(domain_name, unix_users_domain_name() ) ||
1056 strequal(domain_name, unix_groups_domain_name() ) )
1059 * The "Unix User" and "Unix Group" domain our handled by
1060 * passdb
1062 return find_domain_from_name_noinit( get_global_sam_name() );
1065 if (IS_DC || strequal(domain_name, "BUILTIN") ||
1066 strequal(domain_name, get_global_sam_name()))
1067 return find_domain_from_name_noinit(domain_name);
1070 return find_our_domain();
1073 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1075 static bool assume_domain(const char *domain)
1077 /* never assume the domain on a standalone server */
1079 if ( lp_server_role() == ROLE_STANDALONE )
1080 return False;
1082 /* domain member servers may possibly assume for the domain name */
1084 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1085 if ( !strequal(lp_workgroup(), domain) )
1086 return False;
1088 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1089 return True;
1092 /* only left with a domain controller */
1094 if ( strequal(get_global_sam_name(), domain) ) {
1095 return True;
1098 return False;
1101 /* Parse a string of the form DOMAIN\user into a domain and a user */
1103 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
1105 char *p = strchr(domuser,*lp_winbind_separator());
1107 if ( !p ) {
1108 fstrcpy(user, domuser);
1109 p = strchr(domuser, '@');
1111 if ( assume_domain(lp_workgroup()) && p == NULL) {
1112 fstrcpy(domain, lp_workgroup());
1113 } else if (p != NULL) {
1114 fstrcpy(domain, p + 1);
1115 user[PTR_DIFF(p, domuser)] = 0;
1116 } else {
1117 return False;
1119 } else {
1120 fstrcpy(user, p+1);
1121 fstrcpy(domain, domuser);
1122 domain[PTR_DIFF(p, domuser)] = 0;
1125 return strupper_m(domain);
1128 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1129 char **domain, char **user)
1131 fstring fstr_domain, fstr_user;
1132 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1133 return False;
1135 *domain = talloc_strdup(mem_ctx, fstr_domain);
1136 *user = talloc_strdup(mem_ctx, fstr_user);
1137 return ((*domain != NULL) && (*user != NULL));
1140 /* Ensure an incoming username from NSS is fully qualified. Replace the
1141 incoming fstring with DOMAIN <separator> user. Returns the same
1142 values as parse_domain_user() but also replaces the incoming username.
1143 Used to ensure all names are fully qualified within winbindd.
1144 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1145 The protocol definitions of auth_crap, chng_pswd_auth_crap
1146 really should be changed to use this instead of doing things
1147 by hand. JRA. */
1149 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
1151 if (!parse_domain_user(username_inout, domain, user)) {
1152 return False;
1154 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1155 domain, *lp_winbind_separator(),
1156 user);
1157 return True;
1161 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1162 'winbind separator' options.
1163 This means:
1164 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1165 lp_workgroup()
1167 If we are a PDC or BDC, and this is for our domain, do likewise.
1169 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
1170 username is then unqualified in unix
1172 On an AD DC we always fill DOMAIN\\USERNAME.
1174 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1176 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
1178 fstring tmp_user;
1180 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
1181 can_assume = false;
1184 fstrcpy(tmp_user, user);
1185 (void)strlower_m(tmp_user);
1187 if (can_assume && assume_domain(domain)) {
1188 strlcpy(name, tmp_user, sizeof(fstring));
1189 } else {
1190 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1191 domain, *lp_winbind_separator(),
1192 tmp_user);
1197 * talloc version of fill_domain_username()
1198 * return NULL on talloc failure.
1200 char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
1201 const char *domain,
1202 const char *user,
1203 bool can_assume)
1205 char *tmp_user, *name;
1207 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
1208 can_assume = false;
1211 tmp_user = talloc_strdup(mem_ctx, user);
1212 if (!strlower_m(tmp_user)) {
1213 TALLOC_FREE(tmp_user);
1214 return NULL;
1217 if (can_assume && assume_domain(domain)) {
1218 name = tmp_user;
1219 } else {
1220 name = talloc_asprintf(mem_ctx, "%s%c%s",
1221 domain,
1222 *lp_winbind_separator(),
1223 tmp_user);
1224 TALLOC_FREE(tmp_user);
1227 return name;
1231 * Client list accessor functions
1234 static struct winbindd_cli_state *_client_list;
1235 static int _num_clients;
1237 /* Return list of all connected clients */
1239 struct winbindd_cli_state *winbindd_client_list(void)
1241 return _client_list;
1244 /* Return list-tail of all connected clients */
1246 struct winbindd_cli_state *winbindd_client_list_tail(void)
1248 return DLIST_TAIL(_client_list);
1251 /* Return previous (read:newer) client in list */
1253 struct winbindd_cli_state *
1254 winbindd_client_list_prev(struct winbindd_cli_state *cli)
1256 return DLIST_PREV(cli);
1259 /* Add a connection to the list */
1261 void winbindd_add_client(struct winbindd_cli_state *cli)
1263 cli->last_access = time(NULL);
1264 DLIST_ADD(_client_list, cli);
1265 _num_clients++;
1268 /* Remove a client from the list */
1270 void winbindd_remove_client(struct winbindd_cli_state *cli)
1272 DLIST_REMOVE(_client_list, cli);
1273 _num_clients--;
1276 /* Move a client to head or list */
1278 void winbindd_promote_client(struct winbindd_cli_state *cli)
1280 cli->last_access = time(NULL);
1281 DLIST_PROMOTE(_client_list, cli);
1284 /* Return number of open clients */
1286 int winbindd_num_clients(void)
1288 return _num_clients;
1291 NTSTATUS lookup_usergroups_cached(TALLOC_CTX *mem_ctx,
1292 const struct dom_sid *user_sid,
1293 uint32_t *p_num_groups, struct dom_sid **user_sids)
1295 struct netr_SamInfo3 *info3 = NULL;
1296 NTSTATUS status = NT_STATUS_NO_MEMORY;
1297 uint32_t num_groups = 0;
1299 DEBUG(3,(": lookup_usergroups_cached\n"));
1301 *user_sids = NULL;
1302 *p_num_groups = 0;
1304 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1306 if (info3 == NULL) {
1307 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1311 * Before bug #7843 the "Domain Local" groups were added with a
1312 * lookupuseraliases call, but this isn't done anymore for our domain
1313 * so we need to resolve resource groups here.
1315 * When to use Resource Groups:
1316 * http://technet.microsoft.com/en-us/library/cc753670%28v=WS.10%29.aspx
1318 status = sid_array_from_info3(mem_ctx, info3,
1319 user_sids,
1320 &num_groups,
1321 false);
1323 if (!NT_STATUS_IS_OK(status)) {
1324 TALLOC_FREE(info3);
1325 return status;
1328 TALLOC_FREE(info3);
1329 *p_num_groups = num_groups;
1330 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1332 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1334 return status;
1337 /*********************************************************************
1338 We use this to remove spaces from user and group names
1339 ********************************************************************/
1341 NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
1342 struct winbindd_domain *domain,
1343 const char *name,
1344 char **normalized)
1346 NTSTATUS nt_status;
1348 if (!name || !normalized) {
1349 return NT_STATUS_INVALID_PARAMETER;
1352 if (!lp_winbind_normalize_names()) {
1353 return NT_STATUS_PROCEDURE_NOT_FOUND;
1356 /* Alias support and whitespace replacement are mutually
1357 exclusive */
1359 nt_status = resolve_username_to_alias(mem_ctx, domain,
1360 name, normalized );
1361 if (NT_STATUS_IS_OK(nt_status)) {
1362 /* special return code to let the caller know we
1363 mapped to an alias */
1364 return NT_STATUS_FILE_RENAMED;
1367 /* check for an unreachable domain */
1369 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1370 DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
1371 domain->name));
1372 set_domain_offline(domain);
1373 return nt_status;
1376 /* deal with whitespace */
1378 *normalized = talloc_strdup(mem_ctx, name);
1379 if (!(*normalized)) {
1380 return NT_STATUS_NO_MEMORY;
1383 all_string_sub( *normalized, " ", "_", 0 );
1385 return NT_STATUS_OK;
1388 /*********************************************************************
1389 We use this to do the inverse of normalize_name_map()
1390 ********************************************************************/
1392 NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
1393 char *name,
1394 char **normalized)
1396 NTSTATUS nt_status;
1397 struct winbindd_domain *domain = find_our_domain();
1399 if (!name || !normalized) {
1400 return NT_STATUS_INVALID_PARAMETER;
1403 if (!lp_winbind_normalize_names()) {
1404 return NT_STATUS_PROCEDURE_NOT_FOUND;
1407 /* Alias support and whitespace replacement are mutally
1408 exclusive */
1410 /* When mapping from an alias to a username, we don't know the
1411 domain. But we only need a domain structure to cache
1412 a successful lookup , so just our own domain structure for
1413 the seqnum. */
1415 nt_status = resolve_alias_to_username(mem_ctx, domain,
1416 name, normalized);
1417 if (NT_STATUS_IS_OK(nt_status)) {
1418 /* Special return code to let the caller know we mapped
1419 from an alias */
1420 return NT_STATUS_FILE_RENAMED;
1423 /* check for an unreachable domain */
1425 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1426 DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
1427 domain->name));
1428 set_domain_offline(domain);
1429 return nt_status;
1432 /* deal with whitespace */
1434 *normalized = talloc_strdup(mem_ctx, name);
1435 if (!(*normalized)) {
1436 return NT_STATUS_NO_MEMORY;
1439 all_string_sub(*normalized, "_", " ", 0);
1441 return NT_STATUS_OK;
1444 /*********************************************************************
1445 ********************************************************************/
1447 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1449 struct winbindd_tdc_domain *tdc = NULL;
1450 TALLOC_CTX *frame = talloc_stackframe();
1451 bool ret = false;
1453 /* We can contact the domain if it is our primary domain */
1455 if (domain->primary) {
1456 ret = true;
1457 goto done;
1460 /* Trust the TDC cache and not the winbindd_domain flags */
1462 if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1463 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1464 domain->name));
1465 ret = false;
1466 goto done;
1469 /* Can always contact a domain that is in out forest */
1471 if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1472 ret = true;
1473 goto done;
1477 * On a _member_ server, we cannot contact the domain if it
1478 * is running AD and we have no inbound trust.
1481 if (!IS_DC &&
1482 domain->active_directory &&
1483 ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
1485 DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
1486 "and we have no inbound trust.\n", domain->name));
1487 goto done;
1490 /* Assume everything else is ok (probably not true but what
1491 can you do?) */
1493 ret = true;
1495 done:
1496 talloc_destroy(frame);
1498 return ret;
1501 /*********************************************************************
1502 ********************************************************************/
1504 bool winbindd_internal_child(struct winbindd_child *child)
1506 if ((child == idmap_child()) || (child == locator_child())) {
1507 return True;
1510 return False;
1513 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1515 /*********************************************************************
1516 ********************************************************************/
1518 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1520 char *var = NULL;
1521 char addr[INET6_ADDRSTRLEN];
1522 const char *kdc = NULL;
1523 int lvl = 11;
1525 if (!domain || !domain->alt_name || !*domain->alt_name) {
1526 return;
1529 if (domain->initialized && !domain->active_directory) {
1530 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1531 domain->alt_name));
1532 return;
1535 print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1536 kdc = addr;
1537 if (!*kdc) {
1538 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1539 domain->alt_name));
1540 kdc = domain->dcname;
1543 if (!kdc || !*kdc) {
1544 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1545 domain->alt_name));
1546 return;
1549 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1550 domain->alt_name) == -1) {
1551 return;
1554 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1555 var, kdc));
1557 setenv(var, kdc, 1);
1558 free(var);
1561 /*********************************************************************
1562 ********************************************************************/
1564 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1566 struct winbindd_domain *our_dom = find_our_domain();
1568 winbindd_set_locator_kdc_env(domain);
1570 if (domain != our_dom) {
1571 winbindd_set_locator_kdc_env(our_dom);
1575 /*********************************************************************
1576 ********************************************************************/
1578 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1580 char *var = NULL;
1582 if (!domain || !domain->alt_name || !*domain->alt_name) {
1583 return;
1586 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1587 domain->alt_name) == -1) {
1588 return;
1591 unsetenv(var);
1592 free(var);
1594 #else
1596 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1598 return;
1601 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1603 return;
1606 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */
1608 void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
1610 resp->data.auth.nt_status = NT_STATUS_V(result);
1611 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
1613 /* we might have given a more useful error above */
1614 if (*resp->data.auth.error_string == '\0')
1615 fstrcpy(resp->data.auth.error_string,
1616 get_friendly_nt_error_msg(result));
1617 resp->data.auth.pam_error = nt_status_to_pam(result);
1620 bool is_domain_offline(const struct winbindd_domain *domain)
1622 if (get_global_winbindd_state_offline()) {
1623 return true;
1625 return !domain->online;
1628 bool is_domain_online(const struct winbindd_domain *domain)
1630 return !is_domain_offline(domain);
1634 * Parse an char array into a list of sids.
1636 * The input sidstr should consist of 0-terminated strings
1637 * representing sids, separated by newline characters '\n'.
1638 * The list is terminated by an empty string, i.e.
1639 * character '\0' directly following a character '\n'
1640 * (or '\0' right at the start of sidstr).
1642 bool parse_sidlist(TALLOC_CTX *mem_ctx, const char *sidstr,
1643 struct dom_sid **sids, uint32_t *num_sids)
1645 const char *p;
1647 p = sidstr;
1648 if (p == NULL)
1649 return False;
1651 while (p[0] != '\0') {
1652 struct dom_sid sid;
1653 const char *q = NULL;
1655 if (!dom_sid_parse_endp(p, &sid, &q)) {
1656 DEBUG(1, ("Could not parse sid %s\n", p));
1657 return false;
1659 if (q[0] != '\n') {
1660 DEBUG(1, ("Got invalid sidstr: %s\n", p));
1661 return false;
1663 if (!NT_STATUS_IS_OK(add_sid_to_array(mem_ctx, &sid, sids,
1664 num_sids)))
1666 return False;
1668 p = q+1;
1670 return True;
1673 bool parse_xidlist(TALLOC_CTX *mem_ctx, const char *xidstr,
1674 struct unixid **pxids, uint32_t *pnum_xids)
1676 const char *p;
1677 struct unixid *xids = NULL;
1678 uint32_t num_xids = 0;
1680 p = xidstr;
1681 if (p == NULL) {
1682 return false;
1685 while (p[0] != '\0') {
1686 struct unixid *tmp;
1687 struct unixid xid;
1688 unsigned long long id;
1689 char *endp;
1691 switch (p[0]) {
1692 case 'U':
1693 xid = (struct unixid) { .type = ID_TYPE_UID };
1694 break;
1695 case 'G':
1696 xid = (struct unixid) { .type = ID_TYPE_GID };
1697 break;
1698 default:
1699 return false;
1702 p += 1;
1704 id = strtoull(p, &endp, 10);
1705 if ((id == ULLONG_MAX) && (errno == ERANGE)) {
1706 goto fail;
1708 if (*endp != '\n') {
1709 goto fail;
1711 p = endp+1;
1713 xid.id = id;
1714 if ((unsigned long long)xid.id != id) {
1715 goto fail;
1718 tmp = talloc_realloc(mem_ctx, xids, struct unixid, num_xids+1);
1719 if (tmp == NULL) {
1720 return 0;
1722 xids = tmp;
1724 xids[num_xids] = xid;
1725 num_xids += 1;
1728 *pxids = xids;
1729 *pnum_xids = num_xids;
1730 return true;
1732 fail:
1733 TALLOC_FREE(xids);
1734 return false;