diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/idmap/idmapd/adutils.c | 25 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/dbutils.c | 318 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/idmap_config.c | 465 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/idmap_config.h | 35 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/idmapd.h | 6 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/init.c | 142 | ||||
-rw-r--r-- | usr/src/cmd/idmap/idmapd/server.c | 22 | ||||
-rw-r--r-- | usr/src/lib/libadutils/common/adutils.c | 98 | ||||
-rw-r--r-- | usr/src/lib/libadutils/common/adutils_impl.h | 19 | ||||
-rw-r--r-- | usr/src/lib/libadutils/common/libadutils.h | 9 | ||||
-rw-r--r-- | usr/src/lib/libadutils/common/mapfile-vers | 3 | ||||
-rw-r--r-- | usr/src/lib/libidmap/common/addisc.c | 1506 | ||||
-rw-r--r-- | usr/src/lib/libidmap/common/addisc.h | 137 | ||||
-rw-r--r-- | usr/src/lib/libidmap/common/idmap_api.c | 27 | ||||
-rw-r--r-- | usr/src/lib/libidmap/common/mapfile-vers | 4 | ||||
-rw-r--r-- | usr/src/lib/libidmap/common/namemaps.c | 4 | ||||
-rw-r--r-- | usr/src/lib/nsswitch/ad/common/ad_common.c | 3 | ||||
-rw-r--r-- | usr/src/uts/common/sys/idmap.h | 4 |
18 files changed, 2143 insertions, 684 deletions
diff --git a/usr/src/cmd/idmap/idmapd/adutils.c b/usr/src/cmd/idmap/idmapd/adutils.c index f19706822e..a8fefb2f01 100644 --- a/usr/src/cmd/idmap/idmapd/adutils.c +++ b/usr/src/cmd/idmap/idmapd/adutils.c @@ -99,8 +99,8 @@ typedef struct idmap_q { /* Batch context structure; typedef is in header file */ struct idmap_query_state { adutils_query_state_t *qs; - int qcount; /* how many queries */ - uint32_t qlastsent; + int qsize; /* Queue size */ + uint32_t qcount; /* Number of queued requests */ const char *ad_unixuser_attr; const char *ad_unixgroup_attr; idmap_q_t queries[1]; /* array of query results */ @@ -192,8 +192,7 @@ idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries, *state = NULL; - if (ad == NULL) - return (IDMAP_ERR_INTERNAL); + assert(ad != NULL); new_state = calloc(1, sizeof (idmap_query_state_t) + (nqueries - 1) * sizeof (idmap_q_t)); @@ -207,7 +206,7 @@ idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries, return (map_adrc2idmaprc(rc)); } - new_state->qcount = nqueries; + new_state->qsize = nqueries; *state = new_state; return (IDMAP_SUCCESS); } @@ -626,9 +625,11 @@ idmap_batch_add1(idmap_query_state_t *state, const char *filter, NULL }; - qid = atomic_inc_32_nv(&state->qlastsent) - 1; + qid = atomic_inc_32_nv(&state->qcount) - 1; q = &(state->queries[qid]); + assert(qid < state->qsize); + /* * Remember the expected canonname, domainname and unix type * so we can check the results * against it @@ -745,8 +746,7 @@ idmap_name2sid_batch_add1(idmap_query_state_t *state, *strchr(ecanonname, '@') = '\0'; } else { /* 'name' not qualified and dname not given */ - dname = adutils_lookup_batch_getdefdomain( - state->qs); + dname = adutils_lookup_batch_getdefdomain(state->qs); assert(dname != NULL); if (*dname == '\0') { free(ecanonname); @@ -765,6 +765,12 @@ idmap_name2sid_batch_add1(idmap_query_state_t *state, } } + if (!adutils_lookup_check_domain(state->qs, dname)) { + free(ecanonname); + free(edomain); + return (IDMAP_ERR_DOMAIN_NOTFOUND); + } + s_name = sanitize_for_ldap_filter(name); if (s_name == NULL) { free(ecanonname); @@ -814,6 +820,9 @@ idmap_sid2name_batch_add1(idmap_query_state_t *state, * computer. */ + if (!adutils_lookup_check_sid_prefix(state->qs, sid)) + return (IDMAP_ERR_DOMAIN_NOTFOUND); + ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid)); if (ret != 0) return (IDMAP_ERR_SID); diff --git a/usr/src/cmd/idmap/idmapd/dbutils.c b/usr/src/cmd/idmap/idmapd/dbutils.c index 771eaf8250..4e3db282eb 100644 --- a/usr/src/cmd/idmap/idmapd/dbutils.c +++ b/usr/src/cmd/idmap/idmapd/dbutils.c @@ -1801,15 +1801,15 @@ out: return (retcode); } -/* - * Batch AD lookups - */ -idmap_retcode -ad_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch, - idmap_ids_res *result) + + +static int +ad_lookup_batch_int(lookup_state_t *state, idmap_mapping_batch *batch, + idmap_ids_res *result, int index, int *num_processed) { idmap_retcode retcode; - int i, add, type, is_wuser, is_user; + int i, num_queued, type, is_wuser, is_user; + int next_request; int retries = 0, eunixtype; char **unixname; idmap_mapping *req; @@ -1818,6 +1818,8 @@ ad_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch, idmap_how *how; char **dn, **attr, **value; + *num_processed = 0; + /* * Since req->id2.idtype is unused, we will use it here * to retrieve the value of sid_type. But it needs to be @@ -1829,42 +1831,33 @@ ad_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch, * be an option if req->id2.idtype cannot be re-used in * future. */ - - if (state->ad_nqueries == 0) - return (IDMAP_SUCCESS); - - for (i = 0; i < batch->idmap_mapping_batch_len; i++) { - req = &batch->idmap_mapping_batch_val[i]; - res = &result->ids.ids_val[i]; - - /* Skip if not marked for AD lookup or already in error. */ - if (!(req->direction & _IDMAP_F_LOOKUP_AD) || - res->retcode != IDMAP_SUCCESS) - continue; - - /* Init status */ - res->retcode = IDMAP_ERR_RETRIABLE_NET_ERR; - } - retry: - RDLOCK_CONFIG(); - retcode = idmap_lookup_batch_start(_idmapdstate.ad, state->ad_nqueries, - &qs); - UNLOCK_CONFIG(); + retcode = idmap_lookup_batch_start(_idmapdstate.ads[index], + state->ad_nqueries, &qs); if (retcode != IDMAP_SUCCESS) { if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < ADUTILS_DEF_NUM_RETRIES) goto retry; degrade_svc(1, "failed to create batch for AD lookup"); - goto out; + goto out; } + num_queued = 0; restore_svc(); - idmap_lookup_batch_set_unixattr(qs, state->ad_unixuser_attr, - state->ad_unixgroup_attr); + if (index == 0) { + /* + * Directory based name mapping is only performed within the + * joined forest (index == 0). We don't trust other "trusted" + * forests to provide DS-based name mapping information because + * AD's definition of "cross-forest trust" does not encompass + * this sort of behavior. + */ + idmap_lookup_batch_set_unixattr(qs, + state->ad_unixuser_attr, state->ad_unixgroup_attr); + } - for (i = 0, add = 0; i < batch->idmap_mapping_batch_len; i++) { + for (i = 0; i < batch->idmap_mapping_batch_len; i++) { req = &batch->idmap_mapping_batch_val[i]; res = &result->ids.ids_val[i]; how = &res->info.how; @@ -1872,8 +1865,9 @@ retry: retcode = IDMAP_SUCCESS; req->id2.idtype = IDMAP_NONE; - /* Skip if not marked for AD lookup */ - if (!(req->direction & _IDMAP_F_LOOKUP_AD)) + /* Skip if not marked for this AD lookup */ + if (!(req->direction & _IDMAP_F_LOOKUP_AD) || + (req->direction & _IDMAP_F_LOOKUP_OTHER_AD)) continue; if (res->retcode != IDMAP_ERR_RETRIABLE_NET_ERR) @@ -1899,7 +1893,7 @@ retry: unixname = &req->id2name; } } - add = 1; + if (unixname != NULL) { /* * Get how info for DS-based name @@ -1925,6 +1919,8 @@ retry: &req->id1domain : NULL, (int *)&req->id2.idtype, unixname, &res->retcode); + if (retcode == IDMAP_SUCCESS) + num_queued++; } else { /* Lookup AD by winname */ assert(req->id1name != NULL); @@ -1937,6 +1933,8 @@ retry: &req->id1.idmap_id_u.sid.rid, (int *)&req->id2.idtype, unixname, &res->retcode); + if (retcode == IDMAP_SUCCESS) + num_queued++; } } else if (IS_REQUEST_UID(*req) || IS_REQUEST_GID(*req)) { @@ -1945,7 +1943,7 @@ retry: if (res->id.idmap_id_u.sid.prefix != NULL && req->id2name != NULL) { - /* Already have SID and winname -- done */ + /* Already have SID and winname. done */ res->retcode = IDMAP_SUCCESS; continue; } @@ -1958,7 +1956,7 @@ retry: * we are not retrieving unixname from * AD. */ - add = 1; + retcode = idmap_sid2name_batch_add1( qs, res->id.idmap_id_u.sid.prefix, &res->id.idmap_id_u.sid.rid, @@ -1967,6 +1965,8 @@ retry: &req->id2name, &req->id2domain, (int *)&req->id2.idtype, NULL, &res->retcode); + if (retcode == IDMAP_SUCCESS) + num_queued++; } else if (req->id2name != NULL) { /* * winname but no SID -- lookup AD by @@ -1975,7 +1975,6 @@ retry: * we are not retrieving unixname from * AD. */ - add = 1; retcode = idmap_name2sid_batch_add1( qs, req->id2name, req->id2domain, _IDMAP_T_UNDEF, @@ -1984,10 +1983,12 @@ retry: &res->id.idmap_id_u.sid.rid, (int *)&req->id2.idtype, NULL, &res->retcode); + if (retcode == IDMAP_SUCCESS) + num_queued++; } else if (req->id1name != NULL) { /* - * No SID and no winname but we've unixname -- - * lookup AD by unixname to get SID. + * No SID and no winname but we've unixname. + * Lookup AD by unixname to get SID. */ is_user = (IS_REQUEST_UID(*req)) ? 1 : 0; if (res->id.idtype == IDMAP_USID) @@ -1996,7 +1997,7 @@ retry: is_wuser = 0; else is_wuser = is_user; - add = 1; + idmap_info_free(&res->info); res->info.src = IDMAP_MAP_SRC_NEW; how->map_type = IDMAP_MAP_TYPE_DS_AD; @@ -2009,17 +2010,25 @@ retry: &res->id.idmap_id_u.sid.rid, &req->id2name, &req->id2domain, (int *)&req->id2.idtype, &res->retcode); + if (retcode == IDMAP_SUCCESS) + num_queued++; } } - if (retcode != IDMAP_SUCCESS) { + + if (retcode == IDMAP_ERR_DOMAIN_NOTFOUND) { + req->direction |= _IDMAP_F_LOOKUP_OTHER_AD; + retcode = IDMAP_SUCCESS; + } else if (retcode != IDMAP_SUCCESS) { idmap_lookup_release_batch(&qs); + num_queued = 0; + next_request = i + 1; break; } - } + } /* End of for loop */ if (retcode == IDMAP_SUCCESS) { /* add keeps track if we added an entry to the batch */ - if (add) + if (num_queued > 0) retcode = idmap_lookup_batch_end(&qs); else idmap_lookup_release_batch(&qs); @@ -2031,6 +2040,16 @@ retry: else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) degrade_svc(1, "some AD lookups timed out repeatedly"); + if (retcode != IDMAP_SUCCESS) { + /* Mark any unproccessed requests for an other AD */ + for (i = next_request; i < batch->idmap_mapping_batch_len; + i++) { + req = &batch->idmap_mapping_batch_val[i]; + req->direction |= _IDMAP_F_LOOKUP_OTHER_AD; + + } + } + if (retcode != IDMAP_SUCCESS) idmapdlog(LOG_NOTICE, "Failed to batch AD lookup requests"); @@ -2041,8 +2060,8 @@ out: * 2. Reset req->id2.idtype to IDMAP_NONE * 3. If batch_start or batch_add failed then set the status * of each request marked for AD lookup to that error. - * 4. Evaluate the type of the AD object (i.e. user or group) and - * update the idtype in request. + * 4. Evaluate the type of the AD object (i.e. user or group) + * and update the idtype in request. */ for (i = 0; i < batch->idmap_mapping_batch_len; i++) { req = &batch->idmap_mapping_batch_val[i]; @@ -2050,24 +2069,26 @@ out: req->id2.idtype = IDMAP_NONE; res = &result->ids.ids_val[i]; how = &res->info.how; - if (!(req->direction & _IDMAP_F_LOOKUP_AD)) + if (!(req->direction & _IDMAP_F_LOOKUP_AD) || + (req->direction & _IDMAP_F_LOOKUP_OTHER_AD)) continue; + /* Count number processed */ + (*num_processed)++; + /* Reset AD lookup flag */ req->direction &= ~(_IDMAP_F_LOOKUP_AD); /* - * If batch_start or batch_add failed then set the status - * of each request marked for AD lookup to that error. + * If batch_start or batch_add failed then set the + * status of each request marked for AD lookup to + * that error. */ if (retcode != IDMAP_SUCCESS) { res->retcode = retcode; continue; } - if (!add) - continue; - if (res->retcode == IDMAP_ERR_NOTFOUND) { /* Nothing found - remove the preset info */ idmap_info_free(&res->info); @@ -2083,11 +2104,13 @@ out: res->id.idtype = IDMAP_UID; req->id1.idtype = IDMAP_USID; break; + case _IDMAP_T_GROUP: if (res->id.idtype == IDMAP_POSIXID) res->id.idtype = IDMAP_GID; req->id1.idtype = IDMAP_GSID; break; + default: res->retcode = IDMAP_ERR_SID; break; @@ -2107,11 +2130,12 @@ out: req->id2name == NULL && /* no winname */ req->id1name != NULL) /* unixname */ /* - * If AD lookup by unixname failed - * with non fatal error then clear - * the error (i.e set res->retcode - * to success). This allows the next - * pass to process other mapping + * If AD lookup by unixname + * failed with non fatal error + * then clear the error (ie set + * res->retcode to success). + * This allows the next pass to + * process other mapping * mechanisms for this request. */ res->retcode = IDMAP_SUCCESS; @@ -2123,10 +2147,12 @@ out: if (res->id.idtype == IDMAP_SID) res->id.idtype = IDMAP_USID; break; + case _IDMAP_T_GROUP: if (res->id.idtype == IDMAP_SID) res->id.idtype = IDMAP_GSID; break; + default: res->retcode = IDMAP_ERR_SID; break; @@ -2134,6 +2160,92 @@ out: } } + return (retcode); +} + + + +/* + * Batch AD lookups + */ +idmap_retcode +ad_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch, + idmap_ids_res *result) +{ + idmap_retcode retcode; + int i, j; + idmap_mapping *req; + idmap_id_res *res; + int num_queries; + int num_processed; + + if (state->ad_nqueries == 0) + return (IDMAP_SUCCESS); + + for (i = 0; i < batch->idmap_mapping_batch_len; i++) { + req = &batch->idmap_mapping_batch_val[i]; + res = &result->ids.ids_val[i]; + + /* Skip if not marked for AD lookup or already in error. */ + if (!(req->direction & _IDMAP_F_LOOKUP_AD) || + res->retcode != IDMAP_SUCCESS) + continue; + + /* Init status */ + res->retcode = IDMAP_ERR_RETRIABLE_NET_ERR; + } + + RDLOCK_CONFIG(); + num_queries = state->ad_nqueries; + if (_idmapdstate.num_ads > 0) { + for (i = 0; i < _idmapdstate.num_ads && num_queries > 0; i++) { + + retcode = ad_lookup_batch_int(state, batch, result, i, + &num_processed); + num_queries -= num_processed; + + if (num_queries > 0) { + for (j = 0; j < batch->idmap_mapping_batch_len; + j++) { + req = + &batch->idmap_mapping_batch_val[j]; + res = &result->ids.ids_val[j]; + if (!(req->direction & + _IDMAP_F_LOOKUP_AD)) + continue; + /* + * Reset the other AD lookup flag so + * that we can try the next AD + */ + req->direction &= + ~(_IDMAP_F_LOOKUP_OTHER_AD); + + if ((i + 1) >= _idmapdstate.num_ads) { + /* + * There are no more ADs to try + */ + req->direction &= + ~(_IDMAP_F_LOOKUP_AD); + res->retcode = + IDMAP_ERR_DOMAIN_NOTFOUND; + } + } + } + } + } else { + /* Case of no ADs */ + retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY; + for (i = 0; i < batch->idmap_mapping_batch_len; i++) { + req = &batch->idmap_mapping_batch_val[i]; + res = &result->ids.ids_val[i]; + if (!(req->direction & _IDMAP_F_LOOKUP_AD)) + continue; + req->direction &= ~(_IDMAP_F_LOOKUP_AD); + res->retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY; + } + } + UNLOCK_CONFIG(); + /* AD lookups done. Reset state->ad_nqueries and return */ state->ad_nqueries = 0; return (retcode); @@ -2238,7 +2350,7 @@ sid2pid_first_pass(lookup_state_t *state, idmap_mapping *req, goto out; if (ALLOW_WK_OR_LOCAL_SIDS_ONLY(req)) { - retcode = IDMAP_ERR_NONEGENERATED; + retcode = IDMAP_ERR_NONE_GENERATED; goto out; } } @@ -2249,7 +2361,7 @@ sid2pid_first_pass(lookup_state_t *state, idmap_mapping *req, goto out; if (DO_NOT_ALLOC_NEW_ID_MAPPING(req) || AVOID_NAMESERVICE(req)) { - retcode = IDMAP_ERR_NONEGENERATED; + retcode = IDMAP_ERR_NONE_GENERATED; goto out; } @@ -3588,42 +3700,70 @@ ad_lookup_by_winname(lookup_state_t *state, char **sidprefix, idmap_rid_t *rid, int *wintype, char **unixname) { - int retries = 0; + int retries; idmap_query_state_t *qs = NULL; idmap_retcode rc, retcode; + int i; + int found_ad = 0; -retry: RDLOCK_CONFIG(); - retcode = idmap_lookup_batch_start(_idmapdstate.ad, 1, &qs); - UNLOCK_CONFIG(); - if (retcode != IDMAP_SUCCESS) { - if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && - retries++ < ADUTILS_DEF_NUM_RETRIES) - goto retry; - degrade_svc(1, "failed to create request for AD lookup " - "by winname"); - return (retcode); - } - - restore_svc(); - - if (state != NULL) - idmap_lookup_batch_set_unixattr(qs, state->ad_unixuser_attr, - state->ad_unixgroup_attr); + if (_idmapdstate.num_ads > 0) { + for (i = 0; i < _idmapdstate.num_ads && !found_ad; i++) { + retries = 0; +retry: + retcode = idmap_lookup_batch_start(_idmapdstate.ads[i], + 1, &qs); + if (retcode != IDMAP_SUCCESS) { + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && + retries++ < ADUTILS_DEF_NUM_RETRIES) + goto retry; + degrade_svc(1, "failed to create request for " + "AD lookup by winname"); + return (retcode); + } - retcode = idmap_name2sid_batch_add1(qs, name, domain, eunixtype, - dn, attr, value, canonname, sidprefix, rid, wintype, unixname, &rc); + restore_svc(); - if (retcode != IDMAP_SUCCESS) - idmap_lookup_release_batch(&qs); - else - retcode = idmap_lookup_batch_end(&qs); + if (state != NULL && i == 0) { + /* + * Directory based name mapping is only + * performed within the joined forest (i == 0). + * We don't trust other "trusted" forests to + * provide DS-based name mapping information + * because AD's definition of "cross-forest + * trust" does not encompass this sort of + * behavior. + */ + idmap_lookup_batch_set_unixattr(qs, + state->ad_unixuser_attr, + state->ad_unixgroup_attr); + } - if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && - retries++ < ADUTILS_DEF_NUM_RETRIES) - goto retry; - else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) - degrade_svc(1, "some AD lookups timed out repeatedly"); + retcode = idmap_name2sid_batch_add1(qs, name, domain, + eunixtype, dn, attr, value, canonname, sidprefix, + rid, wintype, unixname, &rc); + if (retcode == IDMAP_ERR_DOMAIN_NOTFOUND) { + idmap_lookup_release_batch(&qs); + continue; + } + found_ad = 1; + if (retcode != IDMAP_SUCCESS) + idmap_lookup_release_batch(&qs); + else + retcode = idmap_lookup_batch_end(&qs); + + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && + retries++ < ADUTILS_DEF_NUM_RETRIES) + goto retry; + else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) + degrade_svc(1, + "some AD lookups timed out repeatedly"); + } + } else { + /* No AD case */ + retcode = IDMAP_ERR_NO_ACTIVEDIRECTORY; + } + UNLOCK_CONFIG(); if (retcode != IDMAP_SUCCESS) { idmapdlog(LOG_NOTICE, "AD lookup by winname failed"); @@ -3989,7 +4129,7 @@ pid2sid_first_pass(lookup_state_t *state, idmap_mapping *req, } if (DO_NOT_ALLOC_NEW_ID_MAPPING(req)) { - retcode = IDMAP_ERR_NONEGENERATED; + retcode = IDMAP_ERR_NONE_GENERATED; goto out; } diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.c b/usr/src/cmd/idmap/idmapd/idmap_config.c index 16d9c2b2ea..fb000d5f0d 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_config.c +++ b/usr/src/cmd/idmap/idmapd/idmap_config.c @@ -23,7 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" /* * Config routines common to idmap(1M) and idmapd(1M) @@ -41,6 +40,7 @@ #include <pthread.h> #include <port.h> #include <net/route.h> +#include <sys/u8_textprep.h> #include "addisc.h" #define MACHINE_SID_LEN (9 + 3 * 11) @@ -507,6 +507,12 @@ destruction: return (rc); } + + +/* + * This function updates a boolean value. + * If nothing has changed it returns 0 else 1 + */ static int update_bool(bool_t *value, bool_t *new, char *name) { @@ -518,6 +524,11 @@ update_bool(bool_t *value, bool_t *new, char *name) return (1); } + +/* + * This function updates a string value. + * If nothing has changed it returns 0 else 1 + */ static int update_string(char **value, char **new, char *name) { @@ -538,6 +549,11 @@ update_string(char **value, char **new, char *name) return (1); } + +/* + * This function updates a directory service structure. + * If nothing has changed it returns 0 else 1 + */ static int update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new, char *name) { @@ -554,7 +570,7 @@ update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new, char *name) return (0); } - if (*value) + if (*value != NULL) free(*value); *value = *new; @@ -573,6 +589,221 @@ update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new, char *name) return (1); } +/* + * This function updates a trusted domains structure. + * If nothing has changed it returns 0 else 1 + */ +static int +update_trusted_domains(ad_disc_trusteddomains_t **value, + ad_disc_trusteddomains_t **new, char *name) +{ + int i; + + if (*value == *new) + /* Nothing to do */ + return (0); + + if (*value != NULL && *new != NULL && + ad_disc_compare_trusteddomains(*value, *new) == 0) { + free(*new); + *new = NULL; + return (0); + } + + if (*value != NULL) + free(*value); + + *value = *new; + *new = NULL; + + if (*value == NULL) { + /* We're unsetting this DS property */ + idmapdlog(LOG_INFO, "change %s=<none>", name); + return (1); + } + + /* List all the new domains */ + for (i = 0; (*value)[i].domain[0] != '\0'; i++) + idmapdlog(LOG_INFO, "change %s=%s direction=%s", name, + (*value)[i].domain, + (*value)[i].direction == 3 ? "bi-directional" : "inbound"); + return (1); +} + + +/* + * This function updates a domains in a forest structure. + * If nothing has changed it returns 0 else 1 + */ +static int +update_domains_in_forest(ad_disc_domainsinforest_t **value, + ad_disc_domainsinforest_t **new, char *name) +{ + int i; + + if (*value == *new) + /* Nothing to do */ + return (0); + + if (*value != NULL && *new != NULL && + ad_disc_compare_domainsinforest(*value, *new) == 0) { + free(*new); + *new = NULL; + return (0); + } + + if (*value != NULL) + free(*value); + + *value = *new; + *new = NULL; + + if (*value == NULL) { + /* We're unsetting this DS property */ + idmapdlog(LOG_INFO, "change %s=<none>", name); + return (1); + } + + /* List all the new domains */ + for (i = 0; (*value)[i].domain[0] != '\0'; i++) + idmapdlog(LOG_INFO, "change %s=%s", name, + (*value)[i].domain); + return (1); +} + + +static void +free_trusted_forests(idmap_trustedforest_t **value, int *num_values) +{ + int i; + + for (i = 0; i < *num_values; i++) { + free((*value)[i].forest_name); + free((*value)[i].global_catalog); + free((*value)[i].domains_in_forest); + } + free(*value); + *value = NULL; + *num_values = 0; +} + + +static int +compare_trusteddomainsinforest(ad_disc_domainsinforest_t *df1, + ad_disc_domainsinforest_t *df2) +{ + int i, j; + int num_df1 = 0; + int num_df2 = 0; + int match; + int err; + + for (i = 0; df1[i].domain[0] != '\0'; i++) + if (df1[i].trusted) + num_df1++; + + for (j = 0; df2[j].domain[0] != '\0'; j++) + if (df2[j].trusted) + num_df2++; + + if (num_df1 != num_df2) + return (1); + + for (i = 0; df1[i].domain[0] != '\0'; i++) { + if (df1[i].trusted) { + match = FALSE; + for (j = 0; df2[j].domain[0] != '\0'; j++) { + if (df2[j].trusted && + u8_strcmp(df1[i].domain, df2[i].domain, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) + == 0 && err == 0 && + strcmp(df1[i].sid, df2[i].sid) == 0) { + match = TRUE; + break; + } + } + if (!match) + return (1); + } + } + return (0); +} + + + +/* + * This function updates trusted forest structure. + * If nothing has changed it returns 0 else 1 + */ +static int +update_trusted_forest(idmap_trustedforest_t **value, int *num_value, + idmap_trustedforest_t **new, int *num_new, char *name) +{ + int i, j; + int match; + + if (*value == *new) + /* Nothing to do */ + return (0); + + if (*value != NULL && *new != NULL) { + if (*num_value != *num_new) + goto not_equal; + for (i = 0; i < *num_value; i++) { + match = FALSE; + for (j = 0; j < *num_new; j++) { + if (strcmp((*value)[i].forest_name, + (*new)[j].forest_name) == 0 && + ad_disc_compare_ds( + (*value)[i].global_catalog, + (*new)[i].global_catalog) == 0 && + compare_trusteddomainsinforest( + (*value)[i].domains_in_forest, + (*new)[i].domains_in_forest) == 0) { + match = TRUE; + break; + } + } + if (!match) + goto not_equal; + } + free_trusted_forests(new, num_new); + return (0); + } +not_equal: + if (*value != NULL) + free_trusted_forests(value, num_value); + *value = *new; + *num_value = *num_new; + *new = NULL; + *num_new = 0; + + if (*value == NULL) { + /* We're unsetting this DS property */ + idmapdlog(LOG_INFO, "change %s=<none>", name); + return (1); + } + + /* List all the trusted forests */ + for (i = 0; i < *num_value; i++) { + for (j = 0; (*value)[i].domains_in_forest[j].domain[0] != '\0'; + j++) { + /* List trusted Domains in the forest. */ + if ((*value)[i].domains_in_forest[j].trusted) + idmapdlog(LOG_INFO, "change %s=%s domain=%s", + name, (*value)[i].forest_name, + (*value)[i].domains_in_forest[j].domain); + } + /* List the hosts */ + for (j = 0; (*value)[i].global_catalog[j].host[0] != '\0'; j++) + idmapdlog(LOG_INFO, "change %s=%s host=%s port=%d", + name, (*value)[i].forest_name, + (*value)[i].global_catalog[j].host, + (*value)[i].global_catalog[j].port); + } + return (1); +} + #define MAX_CHECK_TIME (20 * 60) @@ -863,9 +1094,11 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, &pgcfg->domain_name); if (rc != 0) errors++; - else + else { (void) ad_disc_set_DomainName(handles->ad_ctx, pgcfg->domain_name); + pgcfg->domain_name_auto_disc = FALSE; + } rc = get_val_astring(handles, "default_domain", &pgcfg->default_domain); @@ -932,16 +1165,20 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, &pgcfg->domain_controller); if (rc != 0) errors++; - else + else { (void) ad_disc_set_DomainController(handles->ad_ctx, pgcfg->domain_controller); + pgcfg->domain_controller_auto_disc = FALSE; + } rc = get_val_astring(handles, "forest_name", &pgcfg->forest_name); if (rc != 0) errors++; - else + else { (void) ad_disc_set_ForestName(handles->ad_ctx, pgcfg->forest_name); + pgcfg->forest_name_auto_disc = FALSE; + } rc = get_val_astring(handles, "site_name", &pgcfg->site_name); if (rc != 0) @@ -954,9 +1191,11 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, &pgcfg->global_catalog); if (rc != 0) errors++; - else + else { (void) ad_disc_set_GlobalCatalog(handles->ad_ctx, pgcfg->global_catalog); + pgcfg->global_catalog_auto_disc = FALSE; + } /* * Read directory-based name mappings related SMF properties @@ -1017,6 +1256,7 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, } + /* * This is the half of idmap_cfg_load() that auto-discovers values of * discoverable properties that weren't already set via SMF properties. @@ -1029,28 +1269,173 @@ void idmap_cfg_discover(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg) { ad_disc_t ad_ctx = handles->ad_ctx; + ad_disc_t trusted_ctx; + int i, j, k, l; + char *forestname; + int num_trusteddomains; + int new_forest; + int err; + char *trusteddomain; + idmap_ad_disc_ds_t *globalcatalog; + idmap_trustedforest_t *trustedforests; + ad_disc_domainsinforest_t *domainsinforest; ad_disc_refresh(ad_ctx); if (pgcfg->default_domain == NULL) - pgcfg->default_domain = ad_disc_get_DomainName(ad_ctx); + pgcfg->default_domain = ad_disc_get_DomainName(ad_ctx, + NULL); if (pgcfg->domain_name == NULL) - pgcfg->domain_name = ad_disc_get_DomainName(ad_ctx); + pgcfg->domain_name = ad_disc_get_DomainName(ad_ctx, + &pgcfg->domain_name_auto_disc); if (pgcfg->domain_controller == NULL) pgcfg->domain_controller = - ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE); + ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE, + &pgcfg->domain_controller_auto_disc); if (pgcfg->forest_name == NULL) - pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx); + pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx, + &pgcfg->forest_name_auto_disc); if (pgcfg->site_name == NULL) - pgcfg->site_name = ad_disc_get_SiteName(ad_ctx); + pgcfg->site_name = ad_disc_get_SiteName(ad_ctx, + &pgcfg->site_name_auto_disc); if (pgcfg->global_catalog == NULL) pgcfg->global_catalog = - ad_disc_get_GlobalCatalog(ad_ctx, AD_DISC_PREFER_SITE); + ad_disc_get_GlobalCatalog(ad_ctx, AD_DISC_PREFER_SITE, + &pgcfg->global_catalog_auto_disc); + + pgcfg->domains_in_forest = + ad_disc_get_DomainsInForest(ad_ctx, NULL); + + pgcfg->trusted_domains = + ad_disc_get_TrustedDomains(ad_ctx, NULL); + + if (pgcfg->forest_name != NULL && pgcfg->trusted_domains != NULL && + pgcfg->trusted_domains[0].domain[0] != '\0') { + /* + * We have trusted domains. We need to go through every + * one and find its forest. If it is a new forest we then need + * to find its Global Catalog and the domains in the forest + */ + for (i = 0; pgcfg->trusted_domains[i].domain[0] != '\0'; i++) + continue; + num_trusteddomains = i; + + trustedforests = calloc(num_trusteddomains, + sizeof (idmap_trustedforest_t)); + j = 0; + for (i = 0; pgcfg->trusted_domains[i].domain[0] != '\0'; i++) { + trusteddomain = pgcfg->trusted_domains[i].domain; + trusted_ctx = ad_disc_init(); + ad_disc_set_DomainName(trusted_ctx, + trusteddomain); + forestname = + ad_disc_get_ForestName(trusted_ctx, NULL); + if (forestname == NULL) { + idmapdlog(LOG_DEBUG, "unable to discover " + "Forest Name for the trusted domain %s", + trusteddomain); + ad_disc_fini(trusted_ctx); + continue; + } + + if (strcasecmp(forestname, pgcfg->forest_name) == 0) { + /* + * Ignore the domain as it is part of + * the primary forest + */ + free(forestname); + ad_disc_fini(trusted_ctx); + continue; + } + + /* Is this a new forest? */ + new_forest = TRUE; + for (k = 0; k < j; k++) { + if (strcasecmp(forestname, + trustedforests[k].forest_name) == 0) { + new_forest = FALSE; + domainsinforest = + trustedforests[k].domains_in_forest; + break; + } + } + if (!new_forest) { + /* Mark the domain as trusted */ + for (l = 0; + domainsinforest[l].domain[0] != '\0'; l++) { + if (u8_strcmp(trusteddomain, + domainsinforest[l].domain, 0, + U8_STRCMP_CI_LOWER, + U8_UNICODE_LATEST, &err) == 0 && + err == 0) { + domainsinforest[l].trusted = + TRUE; + break; + } + } + free(forestname); + ad_disc_fini(trusted_ctx); + continue; + } + + /* + * Get the Global Catalog and the domains in + * this new forest. + */ + globalcatalog = + ad_disc_get_GlobalCatalog(trusted_ctx, + AD_DISC_PREFER_SITE, NULL); + if (globalcatalog == NULL) { + idmapdlog(LOG_DEBUG, + "unable to discover Global " + "Catalog for the trusted domain %s", + trusteddomain); + free(forestname); + ad_disc_fini(trusted_ctx); + continue; + } + domainsinforest = + ad_disc_get_DomainsInForest(trusted_ctx, + NULL); + if (domainsinforest == NULL) { + idmapdlog(LOG_DEBUG, + "unable to discover Domains in the Forest " + "for the trusted domain %s", + trusteddomain); + free(globalcatalog); + free(forestname); + ad_disc_fini(trusted_ctx); + continue; + } + + trustedforests[j].forest_name = forestname; + trustedforests[j].global_catalog = globalcatalog; + trustedforests[j].domains_in_forest = domainsinforest; + j++; + /* Mark the domain as trusted */ + for (l = 0; domainsinforest[l].domain[0] != '\0'; + l++) { + if (u8_strcmp(trusteddomain, + domainsinforest[l].domain, 0, + U8_STRCMP_CI_LOWER, + U8_UNICODE_LATEST, &err) == 0 && + err == 0) { + domainsinforest[l].trusted = TRUE; + break; + } + } + ad_disc_fini(trusted_ctx); + } + if (j > 0) { + pgcfg->num_trusted_forests = j; + pgcfg->trusted_forests = trustedforests; + } + } if (pgcfg->domain_name == NULL) idmapdlog(LOG_DEBUG, "unable to discover Domain Name"); @@ -1062,8 +1447,14 @@ idmap_cfg_discover(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg) idmapdlog(LOG_DEBUG, "unable to discover Site Name"); if (pgcfg->global_catalog == NULL) idmapdlog(LOG_DEBUG, "unable to discover Global Catalog"); + if (pgcfg->domains_in_forest == NULL) + idmapdlog(LOG_DEBUG, + "unable to discover Domains in the Forest"); + if (pgcfg->trusted_domains == NULL) + idmapdlog(LOG_DEBUG, "unable to discover Trusted Domains"); } + /* * idmap_cfg_load() is called at startup, and periodically via the * update thread when the auto-discovery TTLs expire, as well as part of @@ -1093,6 +1484,7 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags) int rc = 0; int errors = 0; int changed = 0; + int ad_reload_required = 0; idmap_pg_config_t new_pgcfg, *live_pgcfg; live_pgcfg = &cfg->pgcfg; @@ -1141,30 +1533,55 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags) changed += update_string(&live_pgcfg->domain_name, &new_pgcfg.domain_name, "domain_name"); + live_pgcfg->domain_name_auto_disc = new_pgcfg.domain_name_auto_disc; changed += update_dirs(&live_pgcfg->domain_controller, &new_pgcfg.domain_controller, "domain_controller"); + live_pgcfg->domain_controller_auto_disc = + new_pgcfg.domain_controller_auto_disc; changed += update_string(&live_pgcfg->forest_name, &new_pgcfg.forest_name, "forest_name"); + live_pgcfg->forest_name_auto_disc = new_pgcfg.forest_name_auto_disc; changed += update_string(&live_pgcfg->site_name, &new_pgcfg.site_name, "site_name"); + live_pgcfg->site_name_auto_disc = new_pgcfg.site_name_auto_disc; if (update_dirs(&live_pgcfg->global_catalog, &new_pgcfg.global_catalog, "global_catalog")) { changed++; - /* - * Right now we only update the ad_t used for AD lookups - * when the GC list is updated. When we add mixed - * ds-based mapping we'll also need to update the ad_t - * used to talk to the domain, not just the one used to - * talk to the GC. - */ if (live_pgcfg->global_catalog != NULL && live_pgcfg->global_catalog[0].host[0] != '\0') - reload_ad(); + ad_reload_required = TRUE; } + live_pgcfg->global_catalog_auto_disc = + new_pgcfg.global_catalog_auto_disc; + + if (update_domains_in_forest(&live_pgcfg->domains_in_forest, + &new_pgcfg.domains_in_forest, "domains_in_forest")) { + changed++; + ad_reload_required = TRUE; + } + + if (update_trusted_domains(&live_pgcfg->trusted_domains, + &new_pgcfg.trusted_domains, "trusted_domains")) { + changed++; + if (live_pgcfg->trusted_domains != NULL && + live_pgcfg->trusted_domains[0].domain[0] != '\0') + ad_reload_required = TRUE; + } + + if (update_trusted_forest(&live_pgcfg->trusted_forests, + &live_pgcfg->num_trusted_forests, &new_pgcfg.trusted_forests, + &new_pgcfg.num_trusted_forests, "trusted_forest")) { + changed++; + if (live_pgcfg->trusted_forests != NULL) + ad_reload_required = TRUE; + } + + if (ad_reload_required) + reload_ad(); idmap_cfg_unload(&new_pgcfg); @@ -1292,6 +1709,14 @@ idmap_cfg_unload(idmap_pg_config_t *pgcfg) free(pgcfg->global_catalog); pgcfg->global_catalog = NULL; } + if (pgcfg->trusted_domains) { + free(pgcfg->trusted_domains); + pgcfg->trusted_domains = NULL; + } + if (pgcfg->trusted_forests) + free_trusted_forests(&pgcfg->trusted_forests, + &pgcfg->num_trusted_forests); + if (pgcfg->ad_unixuser_attr) { free(pgcfg->ad_unixuser_attr); pgcfg->ad_unixuser_attr = NULL; diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.h b/usr/src/cmd/idmap/idmapd/idmap_config.h index b088184105..3ca051facd 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_config.h +++ b/usr/src/cmd/idmap/idmapd/idmap_config.h @@ -26,7 +26,6 @@ #ifndef _IDMAP_CONFIG_H #define _IDMAP_CONFIG_H -#pragma ident "%Z%%M% %I% %E% SMI" #include "idmap.h" #include "addisc.h" @@ -53,6 +52,15 @@ typedef struct idmap_cfg_handles { /* * This structure stores AD and AD-related configuration */ +typedef struct idmap_trustedforest { + char *forest_name; + idmap_ad_disc_ds_t + *global_catalog; /* global catalog hosts */ + ad_disc_domainsinforest_t + *domains_in_forest; +} idmap_trustedforest_t; + + typedef struct idmap_pg_config { uint64_t list_size_limit; /* @@ -65,16 +73,29 @@ typedef struct idmap_pg_config { * track its procedence separately. The dflt_dom_set_in_smf * field does just that. */ - bool_t dflt_dom_set_in_smf; + char *machine_sid; /* machine sid */ char *default_domain; /* default domain name */ + bool_t dflt_dom_set_in_smf; char *domain_name; /* AD domain name */ - char *machine_sid; /* machine sid */ - idmap_ad_disc_ds_t *domain_controller; - /* domain controller hosts */ + int domain_name_auto_disc; + idmap_ad_disc_ds_t + *domain_controller; /* domain controller hosts */ + int domain_controller_auto_disc; char *forest_name; /* forest name */ + int forest_name_auto_disc; char *site_name; /* site name */ - idmap_ad_disc_ds_t *global_catalog; - /* global catalog hosts */ + int site_name_auto_disc; + idmap_ad_disc_ds_t + *global_catalog; /* global catalog hosts */ + int global_catalog_auto_disc; + ad_disc_domainsinforest_t + *domains_in_forest; + ad_disc_trusteddomains_t + *trusted_domains; /* Trusted Domains */ + int num_trusted_forests; + idmap_trustedforest_t + *trusted_forests; /* Array of trusted forests */ + /* * Following properties are associated with directory-based * name-mappings. diff --git a/usr/src/cmd/idmap/idmapd/idmapd.h b/usr/src/cmd/idmap/idmapd/idmapd.h index b4cab338c4..035212a15b 100644 --- a/usr/src/cmd/idmap/idmapd/idmapd.h +++ b/usr/src/cmd/idmap/idmapd/idmapd.h @@ -79,7 +79,8 @@ typedef struct idmapd_state { gid_t limit_gid; int new_eph_db; /* was the ephem ID db [re-]created? */ bool_t eph_map_unres_sids; - adutils_ad_t *ad; + int num_ads; + adutils_ad_t **ads; } idmapd_state_t; extern idmapd_state_t _idmapdstate; @@ -178,6 +179,9 @@ typedef struct msg_table { #define _IDMAP_F_EXP_EPH_UID 0x00000010 /* Same as above. Used for sid2gid request */ #define _IDMAP_F_EXP_EPH_GID 0x00000020 +/* This request is not valid for the current forest */ +#define _IDMAP_F_LOOKUP_OTHER_AD 0x00000040 + /* * Check if we are done. If so, subsequent passes can be skipped diff --git a/usr/src/cmd/idmap/idmapd/init.c b/usr/src/cmd/idmap/idmapd/init.c index f1eb9d9aaf..0a6d1ef046 100644 --- a/usr/src/cmd/idmap/idmapd/init.c +++ b/usr/src/cmd/idmap/idmapd/init.c @@ -103,11 +103,15 @@ load_config() void reload_ad() { - int i; - adutils_ad_t *old; - adutils_ad_t *new; - + int i, j; + adutils_ad_t **new_ads = NULL; + adutils_ad_t **old_ads; + int new_num_ads; + int old_num_ads; idmap_pg_config_t *pgcfg = &_idmapdstate.cfg->pgcfg; + idmap_trustedforest_t *trustfor = pgcfg->trusted_forests; + int num_trustfor = pgcfg->num_trusted_forests; + ad_disc_domainsinforest_t *domain_in_forest; if (pgcfg->global_catalog == NULL || pgcfg->global_catalog[0].host[0] == '\0') { @@ -122,36 +126,109 @@ reload_ad() return; } - old = _idmapdstate.ad; + old_ads = _idmapdstate.ads; + old_num_ads = _idmapdstate.num_ads; + + new_num_ads = 1 + num_trustfor; + new_ads = calloc(new_num_ads, sizeof (adutils_ad_t *)); + if (new_ads == NULL) { + degrade_svc(0, "could not allocate AD context array " + "(out of memory)"); + return; + } - if (adutils_ad_alloc(&new, pgcfg->default_domain, + if (adutils_ad_alloc(&new_ads[0], pgcfg->default_domain, ADUTILS_AD_GLOBAL_CATALOG) != ADUTILS_SUCCESS) { - degrade_svc(0, "could not initialize AD context"); + free(new_ads); + degrade_svc(0, "could not initialize AD context " + "(out of memory)"); return; } for (i = 0; pgcfg->global_catalog[i].host[0] != '\0'; i++) { - if (idmap_add_ds(new, + if (idmap_add_ds(new_ads[0], pgcfg->global_catalog[i].host, pgcfg->global_catalog[i].port) != 0) { - adutils_ad_free(&new); - degrade_svc(0, "could not initialize AD GC context"); + adutils_ad_free(&new_ads[0]); + free(new_ads); + degrade_svc(0, "could not set AD hosts " + "(out of memory)"); return; } } - _idmapdstate.ad = new; + if (pgcfg->domains_in_forest != NULL) { + for (i = 0; pgcfg->domains_in_forest[i].domain[0] != '\0'; + i++) { + if (adutils_add_domain(new_ads[0], + pgcfg->domains_in_forest[i].domain, + pgcfg->domains_in_forest[i].sid) != 0) { + adutils_ad_free(&new_ads[0]); + free(new_ads); + degrade_svc(0, "could not set AD domains " + "(out of memory)"); + return; + } + } + } + + for (i = 0; i < num_trustfor; i++) { + if (adutils_ad_alloc(&new_ads[i + 1], NULL, + ADUTILS_AD_GLOBAL_CATALOG) != ADUTILS_SUCCESS) { + degrade_svc(0, "could not initialize trusted AD " + "context (out of memory)"); + new_num_ads = i + 1; + goto out; + } + for (j = 0; trustfor[i].global_catalog[j].host[0] != '\0'; + j++) { + if (idmap_add_ds(new_ads[i + 1], + trustfor[i].global_catalog[j].host, + trustfor[i].global_catalog[j].port) != 0) { + adutils_ad_free(&new_ads[i + 1]); + degrade_svc(0, "could not set trusted " + "AD hosts (out of memory)"); + new_num_ads = i + 1; + goto out; + } + } + for (j = 0; trustfor[i].domains_in_forest[j].domain[0] != '\0'; + j++) { + domain_in_forest = &trustfor[i].domains_in_forest[j]; + /* Only add domains which are marked */ + if (domain_in_forest->trusted) { + if (adutils_add_domain(new_ads[i + 1], + domain_in_forest->domain, + domain_in_forest->sid) != 0) { + adutils_ad_free(&new_ads[i + 1]); + degrade_svc(0, "could not set trusted " + "AD domains (out of memory)"); + new_num_ads = i + 1; + goto out; + } + } + } + } + +out: + _idmapdstate.ads = new_ads; + _idmapdstate.num_ads = new_num_ads; + - if (old != NULL) - adutils_ad_free(&old); + if (old_ads != NULL) { + for (i = 0; i < old_num_ads; i++) + adutils_ad_free(&old_ads[i]); + free(old_ads); + } } void print_idmapdstate() { - int i; + int i, j; idmap_pg_config_t *pgcfg; + idmap_trustedforest_t *tf; RDLOCK_CONFIG(); @@ -188,6 +265,43 @@ print_idmapdstate() pgcfg->global_catalog[i].host, pgcfg->global_catalog[i].port); } + if (pgcfg->domains_in_forest == NULL || + pgcfg->domains_in_forest[0].domain[0] == '\0') { + idmapdlog(LOG_DEBUG, "No domains in forest %s known", + CHECK_NULL(pgcfg->forest_name)); + } else { + for (i = 0; pgcfg->domains_in_forest[i].domain[0] != '\0'; i++) + idmapdlog(LOG_DEBUG, "domains in forest %s = %s", + CHECK_NULL(pgcfg->forest_name), + pgcfg->domains_in_forest[i].domain); + } + if (pgcfg->trusted_domains == NULL || + pgcfg->trusted_domains[0].domain[0] == '\0') { + idmapdlog(LOG_DEBUG, "No trusted domains known"); + } else { + for (i = 0; pgcfg->trusted_domains[i].domain[0] != '\0'; i++) + idmapdlog(LOG_DEBUG, "trusted domain = %s", + pgcfg->trusted_domains[i].domain); + } + + for (i = 0; i < pgcfg->num_trusted_forests; i++) { + tf = &pgcfg->trusted_forests[i]; + for (j = 0; tf->global_catalog[j].host[0] != '\0'; j++) + idmapdlog(LOG_DEBUG, + "trusted forest %s global_catalog=%s port=%d", + tf->forest_name, + tf->global_catalog[j].host, + tf->global_catalog[j].port); + for (j = 0; tf->domains_in_forest[j].domain[0] != '\0'; j++) { + if (tf->domains_in_forest[j].trusted) { + idmapdlog(LOG_DEBUG, + "trusted forest %s domain=%s", + tf->forest_name, + tf->domains_in_forest[j].domain); + } + } + } + idmapdlog(LOG_DEBUG, "ds_name_mapping_enabled=%s", (pgcfg->ds_name_mapping_enabled == TRUE) ? "true" : "false"); idmapdlog(LOG_DEBUG, "ad_unixuser_attr=%s", diff --git a/usr/src/cmd/idmap/idmapd/server.c b/usr/src/cmd/idmap/idmapd/server.c index 46c7fa4232..22fb63f122 100644 --- a/usr/src/cmd/idmap/idmapd/server.c +++ b/usr/src/cmd/idmap/idmapd/server.c @@ -23,7 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" /* * Service routines @@ -996,7 +995,6 @@ idmap_get_prop_1_svc(idmap_prop_type request, idmap_prop_res *result, struct svc_req *rqstp) { idmap_pg_config_t *pgcfg; - ad_disc_t ad_ctx; /* Init */ (void) memset(result, 0, sizeof (*result)); @@ -1007,7 +1005,7 @@ idmap_get_prop_1_svc(idmap_prop_type request, /* Just shortcuts: */ pgcfg = &_idmapdstate.cfg->pgcfg; - ad_ctx = _idmapdstate.cfg->handles.ad_ctx; + switch (request) { case PROP_LIST_SIZE_LIMIT: @@ -1023,7 +1021,7 @@ idmap_get_prop_1_svc(idmap_prop_type request, STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val, pgcfg->domain_name); result->auto_discovered = - ad_ctx->domain_name.type == AD_TYPE_AUTO ? TRUE : FALSE; + pgcfg->domain_name_auto_disc; break; case PROP_MACHINE_SID: result->auto_discovered = FALSE; @@ -1036,32 +1034,24 @@ idmap_get_prop_1_svc(idmap_prop_type request, pgcfg->domain_controller, sizeof (idmap_ad_disc_ds_t)); } - result->auto_discovered = - ad_ctx->domain_controller.type == AD_TYPE_AUTO - ? TRUE : FALSE; + result->auto_discovered = pgcfg->domain_controller_auto_disc; break; case PROP_FOREST_NAME: STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val, pgcfg->forest_name); - result->auto_discovered = - ad_ctx->forest_name.type == AD_TYPE_AUTO - ? TRUE : FALSE; + result->auto_discovered = pgcfg->forest_name_auto_disc; break; case PROP_SITE_NAME: STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val, pgcfg->site_name); - result->auto_discovered = - ad_ctx->site_name.type == AD_TYPE_AUTO - ? TRUE : FALSE; + result->auto_discovered = pgcfg->site_name_auto_disc; break; case PROP_GLOBAL_CATALOG: if (pgcfg->global_catalog != NULL) { (void) memcpy(&result->value.idmap_prop_val_u.dsval, pgcfg->global_catalog, sizeof (idmap_ad_disc_ds_t)); } - result->auto_discovered = - ad_ctx->global_catalog.type == AD_TYPE_AUTO - ? TRUE : FALSE; + result->auto_discovered = pgcfg->global_catalog_auto_disc; break; case PROP_AD_UNIXUSER_ATTR: STRDUP_CHECK(result->value.idmap_prop_val_u.utf8val, diff --git a/usr/src/lib/libadutils/common/adutils.c b/usr/src/lib/libadutils/common/adutils.c index d838858671..d914f109d2 100644 --- a/usr/src/lib/libadutils/common/adutils.c +++ b/usr/src/lib/libadutils/common/adutils.c @@ -66,6 +66,7 @@ static binary_attrs_t binattrs[] = { {NULL, NULL} }; + void adutils_set_log(int pri, bool_t syslog, bool_t degraded) { @@ -74,6 +75,7 @@ adutils_set_log(int pri, bool_t syslog, bool_t degraded) idmap_log_degraded(degraded); } + /* * Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com" */ @@ -85,6 +87,7 @@ adutils_dns2dn(const char *dns) return (ldap_dns_to_dn((char *)dns, &nameparts)); } + /* * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other * attributes (CN, etc...). @@ -632,6 +635,8 @@ adutils_ad_free(adutils_ad_t **ad) (void) pthread_mutex_unlock(&(*ad)->lock); (void) pthread_mutex_destroy(&(*ad)->lock); + if ((*ad)->known_domains) + free((*ad)->known_domains); free((*ad)->dflt_w2k_dom); free(*ad); @@ -761,7 +766,8 @@ retry: * around the wrong number of times. */ for (;;) { - if (adh != NULL && adh->ld != NULL && !adh->dead) + if (adh != NULL && adh->owner == ad && adh->ld != NULL && + !adh->dead) break; if (adh == NULL || (adh = adh->next) == NULL) adh = host_head; @@ -919,6 +925,88 @@ delete_ds(adutils_ad_t *ad, const char *host, int port) } } +/* + * Add known domain name and domain SID to AD configuration. + */ + +adutils_rc +adutils_add_domain(adutils_ad_t *ad, const char *domain, const char *sid) +{ + struct known_domain *new; + int num = ad->num_known_domains; + + ad->num_known_domains++; + new = realloc(ad->known_domains, + sizeof (struct known_domain) * ad->num_known_domains); + if (new != NULL) { + ad->known_domains = new; + (void) strlcpy(ad->known_domains[num].name, domain, + sizeof (ad->known_domains[num].name)); + (void) strlcpy(ad->known_domains[num].sid, sid, + sizeof (ad->known_domains[num].sid)); + return (ADUTILS_SUCCESS); + } else { + if (ad->known_domains != NULL) { + free(ad->known_domains); + ad->known_domains = NULL; + } + ad->num_known_domains = 0; + return (ADUTILS_ERR_MEMORY); + } +} + + +/* + * Check that this AD supports this domain. + * If there are no known domains assume that the + * domain is supported by this AD. + * + * Returns 1 if this domain is supported by this AD + * else returns 0; + */ + +int +adutils_lookup_check_domain(adutils_query_state_t *qs, const char *domain) +{ + adutils_ad_t *ad = qs->qadh->owner; + int i, err; + + for (i = 0; i < ad->num_known_domains; i++) { + if (u8_strcmp(domain, ad->known_domains[i].name, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 && + err == 0) + return (1); + } + + return ((i == 0) ? 1 : 0); +} + + +/* + * Check that this AD supports the SID prefix. + * The SID prefix should match the domain SID. + * If there are no known domains assume that the + * SID prefix is supported by this AD. + * + * Returns 1 if this sid prefix is supported by this AD + * else returns 0; + */ + +int +adutils_lookup_check_sid_prefix(adutils_query_state_t *qs, const char *sid) +{ + adutils_ad_t *ad = qs->qadh->owner; + int i; + + + for (i = 0; i < ad->num_known_domains; i++) { + if (strcmp(sid, ad->known_domains[i].sid) == 0) + return (1); + } + + return ((i == 0) ? 1 : 0); +} + adutils_rc adutils_lookup_batch_start(adutils_ad_t *ad, int nqueries, @@ -964,9 +1052,9 @@ adutils_lookup_batch_start(adutils_ad_t *ad, int nqueries, new_state->ref_cnt = 1; new_state->qadh = adh; - new_state->qcount = nqueries; + new_state->qsize = nqueries; new_state->qadh_gen = adh->generation; - new_state->qlastsent = 0; + new_state->qcount = 0; new_state->ldap_res_search_cb = ldap_res_search_cb; new_state->ldap_res_search_argp = ldap_res_search_argp; (void) pthread_cond_init(&new_state->cv, NULL); @@ -1588,9 +1676,11 @@ adutils_lookup_batch_add(adutils_query_state_t *state, struct timeval tv; adutils_q_t *q; - qid = atomic_inc_32_nv(&state->qlastsent) - 1; + qid = atomic_inc_32_nv(&state->qcount) - 1; q = &(state->queries[qid]); + assert(qid < state->qsize); + /* * Remember the expected domain so we can check the results * against it diff --git a/usr/src/lib/libadutils/common/adutils_impl.h b/usr/src/lib/libadutils/common/adutils_impl.h index 847d5e384d..530e3a2ca3 100644 --- a/usr/src/lib/libadutils/common/adutils_impl.h +++ b/usr/src/lib/libadutils/common/adutils_impl.h @@ -43,6 +43,14 @@ extern "C" { #define ADUTILS_SEARCH_TIMEOUT 3 #define ADUTILS_LDAP_OPEN_TIMEOUT 1 +/* + * Maximum string SID size. 4 bytes for "S-1-", 15 for 2^48 (max authority), + * another '-', and ridcount (max 15) 10-digit RIDs plus '-' in between, plus + * a null. + */ +#define MAXSID 185 +#define MAXDOMAINNAME 256 + typedef struct adutils_sid { uchar_t version; uchar_t sub_authority_count; @@ -52,10 +60,17 @@ typedef struct adutils_sid { struct adutils_host; +struct known_domain { + char name[MAXDOMAINNAME]; + char sid[MAXSID]; +}; + /* A set of DSs for a given AD partition */ struct adutils_ad { char *dflt_w2k_dom; /* used to qualify bare names */ + int num_known_domains; + struct known_domain *known_domains; pthread_mutex_t lock; uint32_t ref; struct adutils_host *last_adh; @@ -124,10 +139,10 @@ typedef struct adutils_q { /* Batch context structure */ struct adutils_query_state { struct adutils_query_state *next; - int qcount; /* how many queries */ + int qsize; /* Size of queries */ int ref_cnt; /* reference count */ pthread_cond_t cv; /* Condition wait variable */ - uint32_t qlastsent; + uint32_t qcount; /* Number of items queued */ uint32_t qinflight; /* how many queries in flight */ uint16_t qdead; /* oops, lost LDAP connection */ adutils_host_t *qadh; /* LDAP connection */ diff --git a/usr/src/lib/libadutils/common/libadutils.h b/usr/src/lib/libadutils/common/libadutils.h index 9a6d82a0b2..8f88e2d27a 100644 --- a/usr/src/lib/libadutils/common/libadutils.h +++ b/usr/src/lib/libadutils/common/libadutils.h @@ -135,6 +135,9 @@ extern adutils_rc adutils_ad_alloc(adutils_ad_t **new_ad, extern void adutils_ad_free(adutils_ad_t **ad); extern adutils_rc adutils_add_ds(adutils_ad_t *ad, const char *host, int port); +extern adutils_rc adutils_add_domain(adutils_ad_t *ad, + const char *domain_name, + const char *domain_sid); extern void adutils_set_log(int pri, bool_t syslog, bool_t degraded); extern void adutils_freeresult(adutils_result_t **result); @@ -169,6 +172,12 @@ extern void adutils_lookup_batch_release( adutils_query_state_t **state); extern const char *adutils_lookup_batch_getdefdomain( adutils_query_state_t *state); +extern int adutils_lookup_check_domain( + adutils_query_state_t *state, + const char *domain); +extern int adutils_lookup_check_sid_prefix( + adutils_query_state_t *state, + const char *sid); #ifdef __cplusplus } diff --git a/usr/src/lib/libadutils/common/mapfile-vers b/usr/src/lib/libadutils/common/mapfile-vers index 07d0065906..da06606624 100644 --- a/usr/src/lib/libadutils/common/mapfile-vers +++ b/usr/src/lib/libadutils/common/mapfile-vers @@ -37,11 +37,14 @@ SUNWprivate { adutils_lookup_batch_end; adutils_lookup_batch_release; adutils_lookup_batch_getdefdomain; + adutils_lookup_check_domain; + adutils_lookup_check_sid_prefix; adutils_dn2dns; adutils_reap_idle_connections; adutils_ad_alloc; adutils_ad_free; adutils_add_ds; + adutils_add_domain; adutils_set_log; local: *; diff --git a/usr/src/lib/libidmap/common/addisc.c b/usr/src/lib/libidmap/common/addisc.c index 45a74f07fb..be49e53f41 100644 --- a/usr/src/lib/libidmap/common/addisc.c +++ b/usr/src/lib/libidmap/common/addisc.c @@ -24,7 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" /* * Active Directory Auto-Discovery. @@ -83,6 +82,7 @@ #include <string.h> #include <strings.h> #include <unistd.h> +#include <assert.h> #include <stdlib.h> #include <net/if.h> #include <net/if.h> @@ -99,12 +99,70 @@ #include <errno.h> #include <ldap.h> #include <sasl/sasl.h> +#include <sys/u8_textprep.h> #include "addisc.h" +enum ad_item_state { + AD_STATE_INVALID = 0, /* The value is not valid */ + AD_STATE_FIXED, /* The value was fixed by caller */ + AD_STATE_AUTO /* The value is auto discovered */ + }; + +enum ad_data_type { + AD_STRING = 123, + AD_DIRECTORY, + AD_DOMAINS_IN_FOREST, + AD_TRUSTED_DOMAINS + }; + + +typedef struct ad_subnet { + char subnet[24]; +} ad_subnet_t; + + +typedef struct ad_item { + enum ad_item_state state; + enum ad_data_type type; + void *value; + time_t ttl; + unsigned int version; /* Version is only changed */ + /* if the value changes */ +#define PARAM1 0 +#define PARAM2 1 + int param_version[2]; + /* These holds the version of */ + /* dependents so that a dependent */ + /* change can be detected */ +} ad_item_t; + +typedef struct ad_disc { + struct __res_state res_state; + int res_ninitted; + ad_subnet_t *subnets; + int subnets_changed; + time_t subnets_last_check; + ad_item_t domain_name; /* DNS hostname string */ + ad_item_t domain_controller; /* Directory hostname and */ + /* port array */ + ad_item_t site_name; /* String */ + ad_item_t forest_name; /* DNS forestname string */ + ad_item_t global_catalog; /* Directory hostname and */ + /* port array */ + ad_item_t domains_in_forest; /* DNS domainname and SID */ + /* array */ + ad_item_t trusted_domains; /* DNS domainname and trust */ + /* direction array */ + /* Site specfic versions */ + ad_item_t site_domain_controller; /* Directory hostname and */ + /* port array */ + ad_item_t site_global_catalog; /* Directory hostname and */ + /* port array */ +} ad_disc; + #define DNS_MAX_NAME NS_MAXDNAME -#define DN_MAX_NAME (DNS_MAX_NAME + 512) /* SRV RR names for various queries */ @@ -124,25 +182,22 @@ * idmapd is running before the network is up! */ #define DO_RES_NINIT(ctx) if (!(ctx)->res_ninitted) \ - (ctx)->res_ninitted = res_ninit(&ctx->state) != -1 + (ctx)->res_ninitted = (res_ninit(&ctx->res_state) != -1) #define is_fixed(item) \ - ((item)->type == AD_TYPE_FIXED) + ((item)->state == AD_STATE_FIXED) #define is_changed(item, num, param) \ ((item)->param_version[num] != (param)->version) -#define is_valid(item) \ - ((item)->type != AD_TYPE_INVALID && (item)->value.str != NULL) - /*LINTLIBRARY*/ /* * Function definitions */ -static void validate_SiteName(ad_disc_t ctx); +static ad_item_t * +validate_SiteName(ad_disc_t ctx); -static idmap_ad_disc_ds_t *dsdup(const idmap_ad_disc_ds_t *); static void @@ -152,33 +207,43 @@ update_version(ad_item_t *item, int num, ad_item_t *param) } + static int -is_expired(ad_item_t *item) +is_valid(ad_item_t *item) { - if (item->type == AD_TYPE_FIXED) - return (FALSE); - if (item->type == AD_TYPE_AUTO && - (item->ttl == 0 || item->ttl > time(NULL))) - return (FALSE); - return (TRUE); + if (item->value != NULL) { + if (item->state == AD_STATE_FIXED) + return (TRUE); + if (item->state == AD_STATE_AUTO && + (item->ttl == 0 || item->ttl > time(NULL))) + return (TRUE); + } + return (FALSE); } static void -update_string(ad_item_t *item, char *value, enum ad_item_type type, +update_item(ad_item_t *item, void *value, enum ad_item_state state, uint32_t ttl) { - if (item->value.str != NULL && value != NULL) { - if (strcmp(item->value.str, value) != 0) + if (item->value != NULL && value != NULL) { + if ((item->type == AD_STRING && + strcmp(item->value, value) != 0) || + (item->type == AD_DIRECTORY && + ad_disc_compare_ds(item->value, value) != 0)|| + (item->type == AD_DOMAINS_IN_FOREST && + ad_disc_compare_domainsinforest(item->value, value) != 0) || + (item->type == AD_TRUSTED_DOMAINS && + ad_disc_compare_trusteddomains(item->value, value) != 0)) item->version++; - } else if (item->value.str != value) + } else if (item->value != value) item->version++; - if (item->value.str != NULL) - free(item->value.str); + if (item->value != NULL) + free(item->value); - item->value.str = value; - item->type = type; + item->value = value; + item->state = state; if (ttl == 0) item->ttl = 0; @@ -187,53 +252,180 @@ update_string(ad_item_t *item, char *value, enum ad_item_type type, } -static void -update_ds(ad_item_t *item, idmap_ad_disc_ds_t *value, enum ad_item_type type, - uint32_t ttl) +/* Compare DS lists */ +int +ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2) { - if (item->value.ds != NULL && value != NULL) { - if (ad_disc_compare_ds(item->value.ds, value) != 0) - item->version++; - } else if (item->value.ds != value) - item->version++; + int i, j; + int num_ds1; + int num_ds2; + int match; + + for (i = 0; ds1[i].host[0] != '\0'; i++) + continue; + num_ds1 = i; + for (j = 0; ds2[j].host[0] != '\0'; j++) + continue; + num_ds2 = j; + if (num_ds1 != num_ds2) + return (1); + + for (i = 0; i < num_ds1; i++) { + match = FALSE; + for (j = 0; j < num_ds2; j++) { + if (strcmp(ds1[i].host, ds2[i].host) == 0 && + ds1[i].port == ds2[i].port) { + match = TRUE; + break; + } + } + if (!match) + return (1); + } + return (0); +} - if (item->value.ds != NULL) - free(item->value.ds); - item->value.ds = value; - item->type = type; +/* Copy a list of DSs */ +static idmap_ad_disc_ds_t * +ds_dup(const idmap_ad_disc_ds_t *srv) +{ + int i; + int size; + idmap_ad_disc_ds_t *new = NULL; - if (ttl == 0) - item->ttl = 0; - else - item->ttl = time(NULL) + ttl; + for (i = 0; srv[i].host[0] != '\0'; i++) + continue; + + size = (i + 1) * sizeof (idmap_ad_disc_ds_t); + new = malloc(size); + if (new != NULL) + memcpy(new, srv, size); + return (new); } -static ad_item_t * -get_item(ad_item_t *global, ad_item_t *site, enum ad_disc_req req) +int +ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1, + ad_disc_trusteddomains_t *td2) { - ad_item_t *item; - if (is_fixed(global)) - return (global); + int i, j; + int num_td1; + int num_td2; + int match; + int err; - if (req == AD_DISC_GLOBAL) - item = global; - else if (req == AD_DISC_SITE_SPECIFIC) - item = site; - else if (is_valid(site)) - item = site; - else - item = global; + for (i = 0; td1[i].domain[0] != '\0'; i++) + continue; + num_td1 = i; - if (!is_valid(item)) - return (NULL); + for (j = 0; td2[j].domain[0] != '\0'; j++) + continue; + num_td2 = j; + + if (num_td1 != num_td2) + return (1); - return (item); + for (i = 0; i < num_td1; i++) { + match = FALSE; + for (j = 0; j < num_td2; j++) { + if (u8_strcmp(td1[i].domain, td2[i].domain, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 && + err == 0) { + match = TRUE; + break; + } + } + if (!match) + return (1); + } + return (0); } +/* Copy a list of Trusted Domains */ +static ad_disc_trusteddomains_t * +td_dup(const ad_disc_trusteddomains_t *td) +{ + int i; + int size; + ad_disc_trusteddomains_t *new = NULL; + + for (i = 0; td[i].domain[0] != '\0'; i++) + continue; + + size = (i + 1) * sizeof (ad_disc_trusteddomains_t); + new = malloc(size); + if (new != NULL) + memcpy(new, td, size); + return (new); +} + + + +int +ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1, + ad_disc_domainsinforest_t *df2) +{ + int i, j; + int num_df1; + int num_df2; + int match; + int err; + + for (i = 0; df1[i].domain[0] != '\0'; i++) + continue; + num_df1 = i; + + for (j = 0; df2[j].domain[0] != '\0'; j++) + continue; + num_df2 = j; + + if (num_df1 != num_df2) + return (1); + + for (i = 0; i < num_df1; i++) { + match = FALSE; + for (j = 0; j < num_df2; j++) { + if (u8_strcmp(df1[i].domain, df2[i].domain, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 && + err == 0 && + strcmp(df1[i].sid, df2[i].sid) == 0) { + match = TRUE; + break; + } + } + if (!match) + return (1); + } + return (0); +} + + + +/* Copy a list of Trusted Domains */ +static ad_disc_domainsinforest_t * +df_dup(const ad_disc_domainsinforest_t *df) +{ + int i; + int size; + ad_disc_domainsinforest_t *new = NULL; + + for (i = 0; df[i].domain[0] != '\0'; i++) + continue; + + size = (i + 1) * sizeof (ad_disc_domainsinforest_t); + new = malloc(size); + if (new != NULL) + memcpy(new, df, size); + return (new); +} + + + + + /* * Returns an array of IPv4 address/prefix length * The last subnet is NULL @@ -334,11 +526,11 @@ cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2) int i, j; for (i = 0; subnets1[i].subnet[0] != '\0'; i++) - ; + continue; num_subnets1 = i; for (i = 0; subnets2[i].subnet[0] != '\0'; i++) - ; + continue; num_subnets2 = i; if (num_subnets1 != num_subnets2) @@ -374,27 +566,29 @@ DN_to_DNS(const char *dn_name) j = 0; i = 0; + if (dn_name == NULL) + return (NULL); /* * Find all DC=<value> and form DNS name of the * form <value1>.<value2>... */ - while (dn_name[i] != NULL) { + while (dn_name[i] != '\0') { if (strncasecmp(&dn_name[i], "DC=", 3) == 0) { i += 3; - if (dn_name[i] != NULL && num > 0) + if (dn_name[i] != '\0' && num > 0) dns[j++] = '.'; - while (dn_name[i] != NULL && + while (dn_name[i] != '\0' && dn_name[i] != ',' && dn_name[i] != '+') dns[j++] = dn_name[i++]; num++; } else { /* Skip attr=value as it is not DC= */ - while (dn_name[i] != NULL && + while (dn_name[i] != '\0' && dn_name[i] != ',' && dn_name[i] != '+') i++; } /* Skip over separator ',' or '+' */ - if (dn_name[i] != NULL) i++; + if (dn_name[i] != '\0') i++; } dns[j] = '\0'; dns_name = malloc(j + 1); @@ -432,7 +626,7 @@ subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn) int i, j; for (i = 0; subnets[i].subnet[0] != '\0'; i++) - ; + continue; results = calloc(i + 1, sizeof (char *)); if (results == NULL) @@ -451,57 +645,6 @@ subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn) return (results); } -/* Compare DS lists */ -int -ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2) -{ - int i, j; - int num_ds1; - int num_ds2; - int match; - - for (i = 0; ds1[i].host[0] != '\0'; i++) - ; - num_ds1 = i; - for (j = 0; ds2[j].host[0] != '\0'; j++) - ; - num_ds2 = j; - if (num_ds1 != num_ds2) - return (1); - - for (i = 0; i < num_ds1; i++) { - match = FALSE; - for (j = 0; j < num_ds1; j++) { - if (strcmp(ds1[i].host, ds2[i].host) == 0) { - match = TRUE; - break; - } - } - if (!match) - return (1); - } - return (0); -} - - -/* Copy a list of DSs */ -static idmap_ad_disc_ds_t * -dsdup(const idmap_ad_disc_ds_t *srv) -{ - int i; - int size; - idmap_ad_disc_ds_t *new = NULL; - - for (i = 0; srv[i].host[0] != '\0'; i++) - ; - - size = (i + 1) * sizeof (idmap_ad_disc_ds_t); - new = malloc(size); - if (new != NULL) - memcpy(new, srv, size); - return (new); -} - /* Compare SRC RRs; used with qsort() */ static int @@ -678,6 +821,59 @@ saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) return (LDAP_SUCCESS); } +/* + * A utility function to bind to a Directory server + */ + +static LDAP* +ldap_lookup_init(idmap_ad_disc_ds_t *ds) +{ + int i; + int rc, ldversion; + int zero = 0; + int timeoutms = 5 * 1000; + char *saslmech = "GSSAPI"; + uint32_t saslflags = LDAP_SASL_INTERACTIVE; + LDAP *ld = NULL; + + for (i = 0; ds[i].host[0] != '\0'; i++) { + ld = ldap_init(ds[i].host, ds[i].port); + if (ld == NULL) { + idmapdlog(LOG_DEBUG, "Couldn't connect to " + "AD DC %s:%d (%s)", + ds[i].host, ds[i].port, + strerror(errno)); + continue; + } + + ldversion = LDAP_VERSION3; + (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, + &ldversion); + + (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, + LDAP_OPT_OFF); + (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); + (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); + /* setup TCP/IP connect timeout */ + (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, + &timeoutms); + (void) ldap_set_option(ld, LDAP_OPT_RESTART, + LDAP_OPT_ON); + + rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */, + saslmech, NULL, NULL, saslflags, &saslcallback, + NULL /* defaults */); + if (rc == LDAP_SUCCESS) + break; + + idmapdlog(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)", + ds[i].host, ds[i].port, ldap_err2string(rc)); + (void) ldap_unbind(ld); + ld = NULL; + } + return (ld); +} + /* * A utility function to get the value of some attribute of one of one @@ -688,11 +884,7 @@ ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, char **dn_list, char *attr) { int i; - int rc, ldversion; - int zero = 0; - int timeoutms = 5 * 1000; - char *saslmech = "GSSAPI"; - uint32_t saslflags = LDAP_SASL_INTERACTIVE; + int rc; int scope = LDAP_SCOPE_BASE; char *attrs[2]; LDAPMessage *results = NULL; @@ -703,55 +895,11 @@ ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, attrs[0] = attr; attrs[1] = NULL; - rc = LDAP_INVALID_CREDENTIALS; + if (*ld == NULL) + *ld = ldap_lookup_init(domainControllers); - if (*ld == NULL) { - for (i = 0; domainControllers[i].host[0] != '\0'; i++) { - *ld = ldap_init(domainControllers[i].host, - domainControllers[i].port); - if (*ld == NULL) { - idmapdlog(LOG_INFO, "Couldn't connect to " - "AD DC %s:%d (%s)", - domainControllers[i].host, - domainControllers[i].port, - strerror(errno)); - continue; - } - - ldversion = LDAP_VERSION3; - (void) ldap_set_option(*ld, LDAP_OPT_PROTOCOL_VERSION, - &ldversion); - - (void) ldap_set_option(*ld, LDAP_OPT_REFERRALS, - LDAP_OPT_OFF); - (void) ldap_set_option(*ld, LDAP_OPT_TIMELIMIT, &zero); - (void) ldap_set_option(*ld, LDAP_OPT_SIZELIMIT, &zero); - /* setup TCP/IP connect timeout */ - (void) ldap_set_option(*ld, LDAP_X_OPT_CONNECT_TIMEOUT, - &timeoutms); - (void) ldap_set_option(*ld, LDAP_OPT_RESTART, - LDAP_OPT_ON); - - rc = ldap_sasl_interactive_bind_s(*ld, "" /* binddn */, - saslmech, NULL, NULL, saslflags, &saslcallback, - NULL /* defaults */); - - if (rc == LDAP_SUCCESS) - break; - idmapdlog(LOG_INFO, "LDAP SASL bind to %s:%d " - "failed (%s)", domainControllers[i].host, - domainControllers[i].port, ldap_err2string(rc)); - (void) ldap_unbind(*ld); - *ld = NULL; - } - } - - if (*ld == NULL) { - idmapdlog(LOG_NOTICE, "Couldn't open and SASL bind LDAP " - "connections to any domain controllers; discovery of " - "some items will fail"); + if (*ld == NULL) return (NULL); - } for (i = 0; dn_list[i] != NULL; i++) { rc = ldap_search_s(*ld, dn_list[i], scope, @@ -776,13 +924,271 @@ ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, results = NULL; } } - (void) ldap_unbind(*ld); - *ld = NULL; return (NULL); } +/* + * Lookup the trusted domains in the global catalog. + * + * Returns: + * array of trusted domains which is terminated by + * an empty trusted domain. + * NULL an error occured + */ +ad_disc_trusteddomains_t * +ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, + char *base_dn) +{ + int scope = LDAP_SCOPE_SUBTREE; + char *attrs[3]; + int rc; + LDAPMessage *results = NULL; + LDAPMessage *entry; + char *filter; + char **partner = NULL; + char **direction = NULL; + int num = 0; + ad_disc_trusteddomains_t *trusted_domains = NULL; + + + if (*ld == NULL) + *ld = ldap_lookup_init(globalCatalog); + + if (*ld == NULL) + return (NULL); + + attrs[0] = "trustPartner"; + attrs[1] = "trustDirection"; + attrs[2] = NULL; + + /* trustDirection values - inbound = 1 and bidirectional = 3 */ + filter = "(&(objectclass=trustedDomain)" + "(|(trustDirection=3)(trustDirection=1)))"; + + rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results); + if (rc == LDAP_SUCCESS) { + for (entry = ldap_first_entry(*ld, results); + entry != NULL; entry = ldap_next_entry(*ld, entry)) { + partner = ldap_get_values(*ld, entry, "trustPartner"); + direction = ldap_get_values( + *ld, entry, "trustDirection"); + + if (partner != NULL && direction != NULL) { + num++; + trusted_domains = realloc(trusted_domains, + (num + 1) * + sizeof (ad_disc_trusteddomains_t)); + if (trusted_domains == NULL) { + ldap_value_free(partner); + ldap_value_free(direction); + ldap_msgfree(results); + return (NULL); + } + /* Last element should be zero */ + memset(&trusted_domains[num], 0, + sizeof (ad_disc_trusteddomains_t)); + strcpy(trusted_domains[num - 1].domain, + partner[0]); + trusted_domains[num - 1].direction = + atoi(direction[0]); + } + if (partner != NULL) + ldap_value_free(partner); + if (direction != NULL) + ldap_value_free(direction); + } + } else if (rc == LDAP_NO_RESULTS_RETURNED) { + /* This is not an error - return empty trusted domain */ + trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t)); + } + if (results != NULL) + ldap_msgfree(results); + + return (trusted_domains); +} + +static int +decode_sid(BerValue *bval, char *sid_txt) +{ + int i, j; + uchar_t *v; + uint32_t a; + struct sid { + uchar_t version; + uchar_t sub_authority_count; + uint64_t authority; /* really, 48-bits */ + uint32_t sub_authorities[16]; + } sid; + char *ptr; + int len; + int rlen; + + + /* + * The binary format of a SID is as follows: + * + * byte #0: version, always 0x01 + * byte #1: RID count, always <= 0x0f + * bytes #2-#7: SID authority, big-endian 48-bit unsigned int + * + * followed by RID count RIDs, each a little-endian, unsigned + * 32-bit int. + */ + /* + * Sanity checks: must have at least one RID, version must be + * 0x01, and the length must be 8 + rid count * 4 + */ + if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 && + bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) { + v = (uchar_t *)bval->bv_val; + sid.version = v[0]; + sid.sub_authority_count = v[1]; + sid.authority = + /* big endian -- so start from the left */ + ((u_longlong_t)v[2] << 40) | + ((u_longlong_t)v[3] << 32) | + ((u_longlong_t)v[4] << 24) | + ((u_longlong_t)v[5] << 16) | + ((u_longlong_t)v[6] << 8) | + (u_longlong_t)v[7]; + for (i = 0; i < sid.sub_authority_count; i++) { + j = 8 + (i * 4); + /* little endian -- so start from the right */ + a = (v[j + 3] << 24) | (v[j + 2] << 16) | + (v[j + 1] << 8) | (v[j]); + sid.sub_authorities[i] = a; + } + + ptr = sid_txt; + len = AD_DISC_MAXSID; + rlen = snprintf(ptr, len, "S-1-%llu", sid.authority); + + ptr += rlen; + len -= rlen; + + for (i = 0; i < sid.sub_authority_count; i++) { + assert(len > 0); + rlen = snprintf( + ptr, len, "-%u", sid.sub_authorities[i]); + ptr += rlen; + len -= rlen; + } + assert(len > 0); + return (0); + } + return (-1); +} + +/* + * This functions finds all the domains in a forest. + * It first finds all the naming contexts by finding the + * root DSE attribute namingContext. For each naming context + * it performes an entry search looking for Domain object class + * returning the attribute objectSid. + */ +ad_disc_domainsinforest_t * +ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) +{ + int scope = LDAP_SCOPE_BASE; + char *attrs[2]; + char *root_attrs[2]; + int rc; + LDAPMessage *result = NULL; + LDAPMessage *entry; + char *filter; + char **nc = NULL; + struct berval **sid_ber; + int num = 0; + ad_disc_domainsinforest_t *domains = NULL; + int i; + char *name; + + + if (*ld == NULL) + *ld = ldap_lookup_init(globalCatalogs); + + if (*ld == NULL) { + idmapdlog(LOG_NOTICE, "Couldn't open and SASL bind LDAP " + "connections to any domain controllers; discovery of " + "some items will fail"); + return (NULL); + } + + root_attrs[0] = "namingContexts"; + root_attrs[1] = NULL; + + attrs[0] = "objectSid"; + attrs[1] = NULL; + + filter = "(objectclass=Domain)"; + + /* Find naming contexts */ + rc = ldap_search_s(*ld, LDAP_ROOT_DSE, scope, "(objectClass=*)", + root_attrs, 0, &result); + if (rc == LDAP_SUCCESS) { + entry = ldap_first_entry(*ld, result); + if (entry != NULL) { + nc = ldap_get_values(*ld, entry, "namingContexts"); + } + } + if (result != NULL) + ldap_msgfree(result); + if (nc == NULL) + return (NULL); + + /* Find domains */ + for (i = 0; nc[i] != NULL; i++) { + rc = ldap_search_s(*ld, nc[i], scope, filter, attrs, 0, + &result); + if (rc == LDAP_SUCCESS) { + entry = ldap_first_entry(*ld, result); + if (entry != NULL) { + sid_ber = ldap_get_values_len(*ld, entry, + "objectSid"); + if (sid_ber != NULL) { + num++; + domains = realloc(domains, + (num + 1) * + sizeof (ad_disc_domainsinforest_t)); + if (domains == NULL) { + ldap_value_free_len(sid_ber); + ldap_msgfree(result); + ldap_value_free(nc); + return (NULL); + } + memset(&domains[num], 0, + sizeof (ad_disc_domainsinforest_t)); + if (decode_sid(sid_ber[0], + domains[num - 1].sid) < 0) { + ldap_value_free_len(sid_ber); + ldap_msgfree(result); + ldap_value_free(nc); + return (NULL); + } + ldap_value_free_len(sid_ber); + + name = DN_to_DNS(nc[i]); + if (name == NULL) { + free(domains); + ldap_msgfree(result); + ldap_value_free(nc); + return (NULL); + } + strcpy(domains[num - 1].domain, name); + free(name); + } + } + } + if (result != NULL) + ldap_msgfree(result); + } + ldap_value_free(nc); + + return (domains); +} + ad_disc_t ad_disc_init(void) @@ -791,6 +1197,17 @@ ad_disc_init(void) ctx = calloc(1, sizeof (struct ad_disc)); if (ctx != NULL) DO_RES_NINIT(ctx); + + ctx->domain_name.type = AD_STRING; + ctx->domain_controller.type = AD_DIRECTORY; + ctx->site_name.type = AD_STRING; + ctx->forest_name.type = AD_STRING; + ctx->global_catalog.type = AD_DIRECTORY; + ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST; + ctx->trusted_domains.type = AD_TRUSTED_DOMAINS; + /* Site specific versions */ + ctx->site_domain_controller.type = AD_DIRECTORY; + ctx->site_global_catalog.type = AD_DIRECTORY; return (ctx); } @@ -802,31 +1219,38 @@ ad_disc_fini(ad_disc_t ctx) return; if (ctx->res_ninitted) - res_ndestroy(&ctx->state); + res_ndestroy(&ctx->res_state); if (ctx->subnets != NULL) free(ctx->subnets); - if (ctx->domain_name.value.str != NULL) - free(ctx->domain_name.value.str); + if (ctx->domain_name.value != NULL) + free(ctx->domain_name.value); + + if (ctx->domain_controller.value != NULL) + free(ctx->domain_controller.value); + + if (ctx->site_name.value != NULL) + free(ctx->site_name.value); - if (ctx->domain_controller.value.str != NULL) - free(ctx->domain_controller.value.str); + if (ctx->forest_name.value != NULL) + free(ctx->forest_name.value); - if (ctx->site_name.value.str != NULL) - free(ctx->site_name.value.str); + if (ctx->global_catalog.value != NULL) + free(ctx->global_catalog.value); - if (ctx->forest_name.value.str != NULL) - free(ctx->forest_name.value.str); + if (ctx->domains_in_forest.value != NULL) + free(ctx->domains_in_forest.value); - if (ctx->global_catalog.value.str != NULL) - free(ctx->global_catalog.value.str); + if (ctx->trusted_domains.value != NULL) + free(ctx->trusted_domains.value); - if (ctx->site_domain_controller.value.str != NULL) - free(ctx->site_domain_controller.value.str); + /* Site specific versions */ + if (ctx->site_domain_controller.value != NULL) + free(ctx->site_domain_controller.value); - if (ctx->site_global_catalog.value.str != NULL) - free(ctx->site_global_catalog.value.str); + if (ctx->site_global_catalog.value != NULL) + free(ctx->site_global_catalog.value); free(ctx); } @@ -835,59 +1259,63 @@ void ad_disc_refresh(ad_disc_t ctx) { if (ctx->res_ninitted) - res_ndestroy(&ctx->state); - (void) memset(&ctx->state, 0, sizeof (ctx->state)); - ctx->res_ninitted = res_ninit(&ctx->state) != -1; + res_ndestroy(&ctx->res_state); + (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state)); + ctx->res_ninitted = res_ninit(&ctx->res_state) != -1; - if (ctx->domain_name.type == AD_TYPE_AUTO) - ctx->domain_name.type = AD_TYPE_INVALID; + if (ctx->domain_name.state == AD_STATE_AUTO) + ctx->domain_name.state = AD_STATE_INVALID; - if (ctx->domain_controller.type == AD_TYPE_AUTO) - ctx->domain_controller.type = AD_TYPE_INVALID; + if (ctx->domain_controller.state == AD_STATE_AUTO) + ctx->domain_controller.state = AD_STATE_INVALID; - if (ctx->site_name.type == AD_TYPE_AUTO) - ctx->site_name.type = AD_TYPE_INVALID; + if (ctx->site_name.state == AD_STATE_AUTO) + ctx->site_name.state = AD_STATE_INVALID; - if (ctx->forest_name.type == AD_TYPE_AUTO) - ctx->forest_name.type = AD_TYPE_INVALID; + if (ctx->forest_name.state == AD_STATE_AUTO) + ctx->forest_name.state = AD_STATE_INVALID; - if (ctx->global_catalog.type == AD_TYPE_AUTO) - ctx->global_catalog.type = AD_TYPE_INVALID; + if (ctx->global_catalog.state == AD_STATE_AUTO) + ctx->global_catalog.state = AD_STATE_INVALID; - if (ctx->site_domain_controller.type == AD_TYPE_AUTO) - ctx->site_domain_controller.type = AD_TYPE_INVALID; + if (ctx->domains_in_forest.state == AD_STATE_AUTO) + ctx->domains_in_forest.state = AD_STATE_INVALID; - if (ctx->site_global_catalog.type == AD_TYPE_AUTO) - ctx->site_global_catalog.type = AD_TYPE_INVALID; + if (ctx->trusted_domains.state == AD_STATE_AUTO) + ctx->trusted_domains.state = AD_STATE_INVALID; + + if (ctx->site_domain_controller.state == AD_STATE_AUTO) + ctx->site_domain_controller.state = AD_STATE_INVALID; + + if (ctx->site_global_catalog.state == AD_STATE_AUTO) + ctx->site_global_catalog.state = AD_STATE_INVALID; } /* Discover joined Active Directory domainName */ -static void +static ad_item_t * validate_DomainName(ad_disc_t ctx) { idmap_ad_disc_ds_t *domain_controller = NULL; char *dname, *srvname; uint32_t ttl = 0; - if (is_fixed(&ctx->domain_name)) - return; + if (is_valid(&ctx->domain_name)) + return (&ctx->domain_name); - if (!is_expired(&ctx->domain_name)) - return; /* Try to find our domain by searching for DCs for it */ DO_RES_NINIT(ctx); - domain_controller = srv_query(&ctx->state, LDAP_SRV_HEAD - DC_SRV_TAIL, ctx->domain_name.value.str, &srvname, &ttl); + domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD + DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl); /* * If we can't find DCs by via res_nsearch() then there's no * point in trying anything else to discover the AD domain name. */ if (domain_controller == NULL) - return; + return (NULL); free(domain_controller); /* @@ -901,85 +1329,98 @@ validate_DomainName(ad_disc_t ctx) if (dname == NULL) { idmapdlog(LOG_ERR, "Out of memory"); - return; + return (NULL); } /* Eat any trailing dot */ if (*(dname + strlen(dname)) == '.') *(dname + strlen(dname)) = '\0'; - update_string(&ctx->domain_name, dname, AD_TYPE_AUTO, 0); + update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl); + + return (&ctx->domain_name); } char * -ad_disc_get_DomainName(ad_disc_t ctx) +ad_disc_get_DomainName(ad_disc_t ctx, int *auto_discovered) { char *domain_name = NULL; + ad_item_t *domain_name_item; - validate_DomainName(ctx); + domain_name_item = validate_DomainName(ctx); + + if (domain_name_item) { + domain_name = strdup(domain_name_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (domain_name_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = FALSE; - if (is_valid(&ctx->domain_name)) - domain_name = strdup(ctx->domain_name.value.str); return (domain_name); } /* Discover domain controllers */ -static void +static ad_item_t * validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) { uint32_t ttl = 0; idmap_ad_disc_ds_t *domain_controller = NULL; int validate_global = FALSE; int validate_site = FALSE; + ad_item_t *domain_name_item; + ad_item_t *site_name_item = NULL; + /* If the values is fixed there will not be a site specific version */ if (is_fixed(&ctx->domain_controller)) - return; + return (&ctx->domain_controller); + + domain_name_item = validate_DomainName(ctx); + if (domain_name_item == NULL) + return (NULL); - validate_DomainName(ctx); if (req == AD_DISC_GLOBAL) validate_global = TRUE; else { - validate_SiteName(ctx); - if (is_valid(&ctx->site_name)) + site_name_item = validate_SiteName(ctx); + if (site_name_item != NULL) validate_site = TRUE; - if (req == AD_DISC_PREFER_SITE) + else if (req == AD_DISC_PREFER_SITE) validate_global = TRUE; } - if (validate_global && (is_expired(&ctx->domain_controller) || - is_changed(&ctx->domain_controller, PARAM1, &ctx->domain_name))) { - - update_version(&ctx->domain_controller, PARAM1, - &ctx->domain_name); - - if (is_valid(&ctx->domain_name)) { + if (validate_global) { + if (!is_valid(&ctx->domain_controller) || + is_changed(&ctx->domain_controller, PARAM1, + domain_name_item)) { /* * Lookup DNS SRV RR named * _ldap._tcp.dc._msdcs.<DomainName> */ DO_RES_NINIT(ctx); - domain_controller = srv_query(&ctx->state, + domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD DC_SRV_TAIL, - ctx->domain_name.value.str, NULL, &ttl); - } - update_ds(&ctx->domain_controller, domain_controller, - AD_TYPE_AUTO, ttl); - } + domain_name_item->value, NULL, &ttl); - if (validate_site && (is_expired(&ctx->site_domain_controller) || - is_changed(&ctx->site_domain_controller, PARAM1, - &ctx->domain_name) || - is_changed(&ctx->site_domain_controller, PARAM2, - &ctx->site_name))) { + if (domain_controller == NULL) + return (NULL); - update_version(&ctx->site_domain_controller, PARAM1, - &ctx->domain_name); - update_version(&ctx->site_domain_controller, PARAM2, - &ctx->site_name); + update_item(&ctx->domain_controller, domain_controller, + AD_STATE_AUTO, ttl); + update_version(&ctx->domain_controller, PARAM1, + domain_name_item); + } + return (&ctx->domain_controller); + } - if (is_valid(&ctx->domain_name)) { + if (validate_site) { + if (!is_valid(&ctx->site_domain_controller) || + is_changed(&ctx->site_domain_controller, PARAM1, + domain_name_item) || + is_changed(&ctx->site_domain_controller, PARAM2, + site_name_item)) { char rr_name[DNS_MAX_NAME]; /* * Lookup DNS SRV RR named @@ -987,33 +1428,48 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) */ (void) snprintf(rr_name, sizeof (rr_name), LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, - ctx->site_name.value.str); + site_name_item->value); DO_RES_NINIT(ctx); - domain_controller = srv_query(&ctx->state, rr_name, - ctx->domain_name.value.str, NULL, &ttl); + domain_controller = srv_query(&ctx->res_state, rr_name, + domain_name_item->value, NULL, &ttl); + if (domain_controller == NULL) + return (NULL); + + update_item(&ctx->site_domain_controller, + domain_controller, AD_STATE_AUTO, ttl); + update_version(&ctx->site_domain_controller, PARAM1, + domain_name_item); + update_version(&ctx->site_domain_controller, PARAM2, + site_name_item); } - update_ds(&ctx->site_domain_controller, domain_controller, - AD_TYPE_AUTO, ttl); + return (&ctx->site_domain_controller); } + return (NULL); } idmap_ad_disc_ds_t * -ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req) +ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, + int *auto_discovered) { + ad_item_t *domain_controller_item; idmap_ad_disc_ds_t *domain_controller = NULL; - ad_item_t *item; - validate_DomainController(ctx, req); - item = get_item(&ctx->domain_controller, - &ctx->site_domain_controller, req); - if (item != NULL && is_valid(item)) - domain_controller = dsdup(item->value.ds); + domain_controller_item = validate_DomainController(ctx, req); + + if (domain_controller_item != NULL) { + domain_controller = ds_dup(domain_controller_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (domain_controller_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = FALSE; + return (domain_controller); } /* Discover site name (for multi-homed systems the first one found wins) */ -static void +static ad_item_t * validate_SiteName(ad_disc_t ctx) { LDAP *ld = NULL; @@ -1027,14 +1483,17 @@ validate_SiteName(ad_disc_t ctx) int len; int i; int update_required = FALSE; + ad_item_t *domain_controller_item; if (is_fixed(&ctx->site_name)) - return; + return (&ctx->site_name); /* Can't rely on site-specific DCs */ - validate_DomainController(ctx, AD_DISC_GLOBAL); + domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); + if (domain_controller_item == NULL) + return (NULL); - if (is_expired(&ctx->site_name) || + if (!is_valid(&ctx->site_name) || is_changed(&ctx->site_name, PARAM1, &ctx->domain_controller) || ctx->subnets == NULL || ctx->subnets_changed) { subnets = find_subnets(); @@ -1049,86 +1508,77 @@ validate_SiteName(ad_disc_t ctx) if (!update_required) { free(subnets); - return; + return (&ctx->site_name); } - update_version(&ctx->site_name, PARAM1, &ctx->domain_controller); + if (subnets == NULL) + return (NULL); - if (is_valid(&ctx->domain_name) && - is_valid(&ctx->domain_controller) && - subnets != NULL) { - dn_root[0] = ""; - dn_root[1] = NULL; + dn_root[0] = ""; + dn_root[1] = NULL; - config_naming_context = ldap_lookup_entry_attr( - &ld, ctx->domain_controller.value.ds, - dn_root, "configurationNamingContext"); - if (config_naming_context == NULL) - goto out; + config_naming_context = ldap_lookup_entry_attr( + &ld, ctx->domain_controller.value, + dn_root, "configurationNamingContext"); + if (config_naming_context == NULL) + goto out; + /* + * configurationNamingContext also provides the Forest + * Name. + */ + if (!is_fixed(&ctx->forest_name)) { /* - * configurationNamingContext also provides the Forest - * Name. + * The configurationNamingContext should be of + * form: + * CN=Configuration,<DNforestName> + * Remove the first part and convert to DNS form + * (replace ",DC=" with ".") */ - if (!is_fixed(&ctx->forest_name)) { - /* - * The configurationNamingContext should be of - * form: - * CN=Configuration,<DNforestName> - * Remove the first part and convert to DNS form - * (replace ",DC=" with ".") - */ - char *str = "CN=Configuration,"; - int len = strlen(str); - if (strncasecmp(config_naming_context, str, len) == 0) { - forest_name = DN_to_DNS( - config_naming_context + len); - update_string(&ctx->forest_name, - forest_name, AD_TYPE_AUTO, 0); - } + char *str = "CN=Configuration,"; + int len = strlen(str); + if (strncasecmp(config_naming_context, str, len) == 0) { + forest_name = DN_to_DNS(config_naming_context + len); + update_item(&ctx->forest_name, forest_name, + AD_STATE_AUTO, 0); } - dn_subnets = subnets_to_DNs(subnets, config_naming_context); - if (dn_subnets == NULL) - goto out; - - site_object = ldap_lookup_entry_attr( - &ld, ctx->domain_controller.value.ds, - dn_subnets, "siteobject"); - if (site_object != NULL) { - /* - * The site object should be of the form - * CN=<site>,CN=Sites,CN=Configuration, - * <DN Domain> - */ - if (strncasecmp(site_object, "CN=", 3) == 0) { - for (len = 0; - site_object[len + 3] != ','; len++) + } + + dn_subnets = subnets_to_DNs(subnets, config_naming_context); + if (dn_subnets == NULL) + goto out; + + site_object = ldap_lookup_entry_attr( + &ld, domain_controller_item->value, + dn_subnets, "siteobject"); + if (site_object != NULL) { + /* + * The site object should be of the form + * CN=<site>,CN=Sites,CN=Configuration, + * <DN Domain> + */ + if (strncasecmp(site_object, "CN=", 3) == 0) { + for (len = 0; site_object[len + 3] != ','; len++) ; - site_name = malloc(len + 1); - (void) strncpy(site_name, &site_object[3], len); - site_name[len] = '\0'; - } + site_name = malloc(len + 1); + (void) strncpy(site_name, &site_object[3], len); + site_name[len] = '\0'; + update_item(&ctx->site_name, site_name, + AD_STATE_AUTO, 0); } + } - if (ctx->subnets != NULL) { - free(ctx->subnets); - ctx->subnets = NULL; - } - ctx->subnets = subnets; - subnets = NULL; - ctx->subnets_changed = FALSE; + if (ctx->subnets != NULL) { + free(ctx->subnets); + ctx->subnets = NULL; } + ctx->subnets = subnets; + subnets = NULL; + ctx->subnets_changed = FALSE; + out: if (ld != NULL) (void) ldap_unbind(ld); - update_string(&ctx->site_name, site_name, AD_TYPE_AUTO, 0); - - if (site_name == NULL || *site_name == '\0') { - /* No site name -> no site-specific DSs */ - update_ds(&ctx->site_domain_controller, NULL, AD_TYPE_AUTO, 0); - update_ds(&ctx->site_global_catalog, NULL, AD_TYPE_AUTO, 0); - } - if (dn_subnets != NULL) { for (i = 0; dn_subnets[i] != NULL; i++) free(dn_subnets[i]); @@ -1140,144 +1590,174 @@ out: free(site_object); free(subnets); + if (site_name == NULL) + return (NULL); + return (&ctx->site_name); } char * -ad_disc_get_SiteName(ad_disc_t ctx) +ad_disc_get_SiteName(ad_disc_t ctx, int *auto_discovered) { + ad_item_t *site_name_item; char *site_name = NULL; - validate_SiteName(ctx); - if (is_valid(&ctx->site_name)) - site_name = strdup(ctx->site_name.value.str); + site_name_item = validate_SiteName(ctx); + if (site_name_item != NULL) { + site_name = strdup(site_name_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (site_name_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = FALSE; + return (site_name); } /* Discover forest name */ -static void +static ad_item_t * validate_ForestName(ad_disc_t ctx) { LDAP *ld = NULL; char *config_naming_context; char *forest_name = NULL; char *dn_list[2]; + ad_item_t *domain_controller_item; if (is_fixed(&ctx->forest_name)) - return; + return (&ctx->forest_name); /* * We may not have a site name yet, so we won't rely on * site-specific DCs. (But maybe we could replace * validate_ForestName() with validate_siteName()?) */ - validate_DomainController(ctx, AD_DISC_GLOBAL); - if (is_expired(&ctx->forest_name) || - is_changed(&ctx->forest_name, PARAM1, &ctx->domain_controller)) { + domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); + if (domain_controller_item == NULL) + return (NULL); - update_version(&ctx->forest_name, PARAM1, - &ctx->domain_controller); - - if (is_valid(&ctx->domain_controller)) { - dn_list[0] = ""; - dn_list[1] = NULL; - config_naming_context = ldap_lookup_entry_attr( - &ld, ctx->domain_controller.value.ds, - dn_list, - "configurationNamingContext"); - if (config_naming_context != NULL) { - /* - * The configurationNamingContext should be of - * form: - * CN=Configuration,<DNforestName> - * Remove the first part and convert to DNS form - * (replace ",DC=" with ".") - */ - char *str = "CN=Configuration,"; - int len = strlen(str); - if (strncasecmp(config_naming_context, - str, len) == 0) { - forest_name = DN_to_DNS( - config_naming_context + len); - } - free(config_naming_context); + if (!is_valid(&ctx->forest_name) || + is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) { + + dn_list[0] = ""; + dn_list[1] = NULL; + config_naming_context = ldap_lookup_entry_attr( + &ld, ctx->domain_controller.value, + dn_list, "configurationNamingContext"); + if (config_naming_context != NULL) { + /* + * The configurationNamingContext should be of + * form: + * CN=Configuration,<DNforestName> + * Remove the first part and convert to DNS form + * (replace ",DC=" with ".") + */ + char *str = "CN=Configuration,"; + int len = strlen(str); + if (strncasecmp(config_naming_context, str, len) == 0) { + forest_name = DN_to_DNS( + config_naming_context + len); } - if (ld != NULL) - (void) ldap_unbind(ld); + free(config_naming_context); } - update_string(&ctx->forest_name, forest_name, AD_TYPE_AUTO, 0); + if (ld != NULL) + (void) ldap_unbind(ld); + + if (forest_name == NULL) + return (NULL); + + update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); + update_version(&ctx->forest_name, PARAM1, + domain_controller_item); } + return (&ctx->forest_name); } char * -ad_disc_get_ForestName(ad_disc_t ctx) +ad_disc_get_ForestName(ad_disc_t ctx, int *auto_discovered) { + ad_item_t *forest_name_item; char *forest_name = NULL; - validate_ForestName(ctx); + forest_name_item = validate_ForestName(ctx); + + if (forest_name_item != NULL) { + forest_name = strdup(forest_name_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (forest_name_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = FALSE; - if (is_valid(&ctx->forest_name)) - forest_name = strdup(ctx->forest_name.value.str); return (forest_name); } /* Discover global catalog servers */ -static void +static ad_item_t * validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) { idmap_ad_disc_ds_t *global_catalog = NULL; uint32_t ttl = 0; int validate_global = FALSE; int validate_site = FALSE; + ad_item_t *forest_name_item; + ad_item_t *site_name_item; + /* If the values is fixed there will not be a site specific version */ if (is_fixed(&ctx->global_catalog)) - return; + return (&ctx->global_catalog); + + forest_name_item = validate_ForestName(ctx); + if (forest_name_item == NULL) + return (NULL); - validate_ForestName(ctx); if (req == AD_DISC_GLOBAL) validate_global = TRUE; else { - validate_SiteName(ctx); - if (is_valid(&ctx->site_name)) + site_name_item = validate_SiteName(ctx); + if (site_name_item != NULL) validate_site = TRUE; - if (req == AD_DISC_PREFER_SITE) + else if (req == AD_DISC_PREFER_SITE) validate_global = TRUE; } - if (validate_global && (is_expired(&ctx->global_catalog) || - is_changed(&ctx->global_catalog, PARAM1, &ctx->forest_name))) { - - update_version(&ctx->global_catalog, PARAM1, &ctx->forest_name); - - if (is_valid(&ctx->forest_name)) { + if (validate_global) { + if (!is_valid(&ctx->global_catalog) || + is_changed(&ctx->global_catalog, PARAM1, + forest_name_item)) { /* * Lookup DNS SRV RR named * _ldap._tcp.gc._msdcs.<ForestName> */ DO_RES_NINIT(ctx); global_catalog = - srv_query(&ctx->state, LDAP_SRV_HEAD GC_SRV_TAIL, - ctx->forest_name.value.str, NULL, &ttl); - } - update_ds(&ctx->global_catalog, global_catalog, - AD_TYPE_AUTO, ttl); - } + srv_query(&ctx->res_state, + LDAP_SRV_HEAD GC_SRV_TAIL, + ctx->forest_name.value, NULL, &ttl); - if (validate_site && (is_expired(&ctx->site_global_catalog) || - is_changed(&ctx->site_global_catalog, PARAM1, &ctx->forest_name) || - is_changed(&ctx->site_global_catalog, PARAM2, &ctx->site_name))) { + if (global_catalog == NULL) + return (NULL); - update_version(&ctx->site_global_catalog, PARAM1, - &ctx->forest_name); - update_version(&ctx->site_global_catalog, PARAM2, - &ctx->site_name); + update_item(&ctx->global_catalog, global_catalog, + AD_STATE_AUTO, ttl); + update_version(&ctx->global_catalog, PARAM1, + forest_name_item); + } + return (&ctx->global_catalog); + } - if (is_valid(&ctx->forest_name) && is_valid(&ctx->site_name)) { + if (validate_site) { + if (!is_valid(&ctx->site_global_catalog) || + is_changed(&ctx->site_global_catalog, PARAM1, + forest_name_item) || + is_changed(&ctx->site_global_catalog, PARAM2, + site_name_item)) { char rr_name[DNS_MAX_NAME]; + /* * Lookup DNS SRV RR named: * _ldap._tcp.<siteName>._sites.gc. @@ -1286,33 +1766,187 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) (void) snprintf(rr_name, sizeof (rr_name), LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, - ctx->site_name.value.str); + ctx->site_name.value); DO_RES_NINIT(ctx); - global_catalog = - srv_query(&ctx->state, rr_name, - ctx->forest_name.value.str, NULL, &ttl); + global_catalog = srv_query(&ctx->res_state, rr_name, + ctx->forest_name.value, NULL, &ttl); + + if (global_catalog == NULL) + return (NULL); + update_item(&ctx->site_global_catalog, global_catalog, + AD_STATE_AUTO, ttl); + update_version(&ctx->site_global_catalog, PARAM1, + forest_name_item); + update_version(&ctx->site_global_catalog, PARAM2, + site_name_item); } - update_ds(&ctx->site_global_catalog, global_catalog, - AD_TYPE_AUTO, ttl); + return (&ctx->site_global_catalog); } + return (NULL); } idmap_ad_disc_ds_t * -ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) +ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req, + int *auto_discovered) { idmap_ad_disc_ds_t *global_catalog = NULL; - ad_item_t *item; + ad_item_t *global_catalog_item; + + global_catalog_item = validate_GlobalCatalog(ctx, req); - validate_GlobalCatalog(ctx, req); + if (global_catalog_item != NULL) { + global_catalog = ds_dup(global_catalog_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (global_catalog_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = FALSE; - item = get_item(&ctx->global_catalog, &ctx->site_global_catalog, req); - if (item != NULL && is_valid(item)) - global_catalog = dsdup(item->value.ds); return (global_catalog); } +static ad_item_t * +validate_TrustedDomains(ad_disc_t ctx) +{ + LDAP *ld = NULL; + ad_item_t *global_catalog_item; + ad_item_t *forest_name_item; + ad_disc_trusteddomains_t *trusted_domains; + char *dn = NULL; + char *forest_name_dn; + int len; + int num_parts; + + if (is_fixed(&ctx->trusted_domains)) + return (&ctx->trusted_domains); + + global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); + if (global_catalog_item == NULL) + return (NULL); + + forest_name_item = validate_ForestName(ctx); + if (forest_name_item == NULL) + return (NULL); + + if (!is_valid(&ctx->trusted_domains) || + is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) || + is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) { + + forest_name_dn = ldap_dns_to_dn(forest_name_item->value, + &num_parts); + if (forest_name_dn == NULL) + return (NULL); + + len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1; + dn = malloc(len); + if (dn == NULL) { + free(forest_name_dn); + return (NULL); + } + (void) snprintf(dn, len, "CN=System,%s", forest_name_dn); + free(forest_name_dn); + + trusted_domains = ldap_lookup_trusted_domains( + &ld, global_catalog_item->value, dn); + + if (ld != NULL) + (void) ldap_unbind(ld); + free(dn); + + if (trusted_domains == NULL) + return (NULL); + + update_item(&ctx->trusted_domains, trusted_domains, + AD_STATE_AUTO, 0); + update_version(&ctx->trusted_domains, PARAM1, + global_catalog_item); + update_version(&ctx->trusted_domains, PARAM2, + forest_name_item); + } + + return (&ctx->trusted_domains); +} + + +ad_disc_trusteddomains_t * +ad_disc_get_TrustedDomains(ad_disc_t ctx, int *auto_discovered) +{ + ad_disc_trusteddomains_t *trusted_domains = NULL; + ad_item_t *trusted_domains_item; + + trusted_domains_item = validate_TrustedDomains(ctx); + + if (trusted_domains_item != NULL) { + trusted_domains = td_dup(trusted_domains_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (trusted_domains_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = FALSE; + + return (trusted_domains); +} + + +static ad_item_t * +validate_DomainsInForest(ad_disc_t ctx) +{ + ad_item_t *global_catalog_item; + LDAP *ld = NULL; + ad_disc_domainsinforest_t *domains_in_forest; + + if (is_fixed(&ctx->domains_in_forest)) + return (&ctx->domains_in_forest); + + global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); + if (global_catalog_item == NULL) + return (NULL); + + if (!is_valid(&ctx->domains_in_forest) || + is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) { + + domains_in_forest = ldap_lookup_domains_in_forest( + &ld, global_catalog_item->value); + + if (ld != NULL) + (void) ldap_unbind(ld); + + if (domains_in_forest == NULL) + return (NULL); + + update_item(&ctx->domains_in_forest, domains_in_forest, + AD_STATE_AUTO, 0); + update_version(&ctx->domains_in_forest, PARAM1, + global_catalog_item); + } + return (&ctx->domains_in_forest); +} + + +ad_disc_domainsinforest_t * +ad_disc_get_DomainsInForest(ad_disc_t ctx, int *auto_discovered) +{ + ad_disc_domainsinforest_t *domains_in_forest = NULL; + ad_item_t *domains_in_forest_item; + + domains_in_forest_item = validate_DomainsInForest(ctx); + + if (domains_in_forest_item != NULL) { + domains_in_forest = df_dup(domains_in_forest_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (domains_in_forest_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = FALSE; + + return (domains_in_forest); +} + + + + int ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) { @@ -1321,10 +1955,10 @@ ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) domain_name = strdup(domainName); if (domain_name == NULL) return (-1); - update_string(&ctx->domain_name, domain_name, - AD_TYPE_FIXED, 0); - } else if (ctx->domain_name.type == AD_TYPE_FIXED) - ctx->domain_name.type = AD_TYPE_INVALID; + update_item(&ctx->domain_name, domain_name, + AD_STATE_FIXED, 0); + } else if (ctx->domain_name.state == AD_STATE_FIXED) + ctx->domain_name.state = AD_STATE_INVALID; return (0); } @@ -1335,13 +1969,13 @@ ad_disc_set_DomainController(ad_disc_t ctx, { idmap_ad_disc_ds_t *domain_controller = NULL; if (domainController != NULL) { - domain_controller = dsdup(domainController); + domain_controller = ds_dup(domainController); if (domain_controller == NULL) return (-1); - update_ds(&ctx->domain_controller, domain_controller, - AD_TYPE_FIXED, 0); - } else if (ctx->domain_controller.type == AD_TYPE_FIXED) - ctx->domain_controller.type = AD_TYPE_INVALID; + update_item(&ctx->domain_controller, domain_controller, + AD_STATE_FIXED, 0); + } else if (ctx->domain_controller.state == AD_STATE_FIXED) + ctx->domain_controller.state = AD_STATE_INVALID; return (0); } @@ -1354,9 +1988,9 @@ ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) site_name = strdup(siteName); if (site_name == NULL) return (-1); - update_string(&ctx->site_name, site_name, AD_TYPE_FIXED, 0); - } else if (ctx->site_name.type == AD_TYPE_FIXED) - ctx->site_name.type = AD_TYPE_INVALID; + update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0); + } else if (ctx->site_name.state == AD_STATE_FIXED) + ctx->site_name.state = AD_STATE_INVALID; return (0); } @@ -1368,10 +2002,10 @@ ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) forest_name = strdup(forestName); if (forest_name == NULL) return (-1); - update_string(&ctx->forest_name, forest_name, - AD_TYPE_FIXED, 0); - } else if (ctx->forest_name.type == AD_TYPE_FIXED) - ctx->forest_name.type = AD_TYPE_INVALID; + update_item(&ctx->forest_name, forest_name, + AD_STATE_FIXED, 0); + } else if (ctx->forest_name.state == AD_STATE_FIXED) + ctx->forest_name.state = AD_STATE_INVALID; return (0); } @@ -1381,13 +2015,13 @@ ad_disc_set_GlobalCatalog(ad_disc_t ctx, { idmap_ad_disc_ds_t *global_catalog = NULL; if (globalCatalog != NULL) { - global_catalog = dsdup(globalCatalog); + global_catalog = ds_dup(globalCatalog); if (global_catalog == NULL) return (-1); - update_ds(&ctx->global_catalog, global_catalog, - AD_TYPE_FIXED, 0); - } else if (ctx->global_catalog.type == AD_TYPE_FIXED) - ctx->global_catalog.type = AD_TYPE_INVALID; + update_item(&ctx->global_catalog, global_catalog, + AD_STATE_FIXED, 0); + } else if (ctx->global_catalog.state == AD_STATE_FIXED) + ctx->global_catalog.state = AD_STATE_INVALID; return (0); } @@ -1395,20 +2029,20 @@ ad_disc_set_GlobalCatalog(ad_disc_t ctx, int ad_disc_unset(ad_disc_t ctx) { - if (ctx->domain_name.type == AD_TYPE_FIXED) - ctx->domain_name.type = AD_TYPE_INVALID; + if (ctx->domain_name.state == AD_STATE_FIXED) + ctx->domain_name.state = AD_STATE_INVALID; - if (ctx->domain_controller.type == AD_TYPE_FIXED) - ctx->domain_controller.type = AD_TYPE_INVALID; + if (ctx->domain_controller.state == AD_STATE_FIXED) + ctx->domain_controller.state = AD_STATE_INVALID; - if (ctx->site_name.type == AD_TYPE_FIXED) - ctx->site_name.type = AD_TYPE_INVALID; + if (ctx->site_name.state == AD_STATE_FIXED) + ctx->site_name.state = AD_STATE_INVALID; - if (ctx->forest_name.type == AD_TYPE_FIXED) - ctx->forest_name.type = AD_TYPE_INVALID; + if (ctx->forest_name.state == AD_STATE_FIXED) + ctx->forest_name.state = AD_STATE_INVALID; - if (ctx->global_catalog.type == AD_TYPE_FIXED) - ctx->global_catalog.type = AD_TYPE_INVALID; + if (ctx->global_catalog.state == AD_STATE_FIXED) + ctx->global_catalog.state = AD_STATE_INVALID; return (0); } diff --git a/usr/src/lib/libidmap/common/addisc.h b/usr/src/lib/libidmap/common/addisc.h index fd89f2c853..6622965e23 100644 --- a/usr/src/lib/libidmap/common/addisc.h +++ b/usr/src/lib/libidmap/common/addisc.h @@ -27,8 +27,6 @@ #ifndef _ADINFO_H #define _ADINFO_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include "idmap_priv.h" #include "idmap_prot.h" #include "idmap_impl.h" @@ -37,55 +35,32 @@ extern "C" { #endif +/* + * Maximum string SID size. 4 bytes for "S-1-", 15 for 2^48 (max authority), + * another '-', and ridcount (max 15) 10-digit RIDs plus '-' in between, plus + * a null. + */ -enum ad_item_type { - AD_TYPE_INVALID = 0, /* The value is not valid */ - AD_TYPE_FIXED, /* The value was fixed by caller */ - AD_TYPE_AUTO /* The value is auto discovered */ - }; - - -typedef struct ad_subnet { - char subnet[24]; -} ad_subnet_t; - - -typedef struct ad_item { - enum ad_item_type type; - union { - char *str; - idmap_ad_disc_ds_t *ds; - } value; - time_t ttl; - unsigned int version; /* Version is only changed if the */ - /* value changes */ -#define PARAM1 0 -#define PARAM2 1 - int param_version[2]; - /* These holds the version of */ - /* dependents so that a dependent */ - /* change can be detected */ -} ad_item_t; - -typedef struct ad_disc { - struct __res_state state; - int res_ninitted; - ad_subnet_t *subnets; - int subnets_changed; - time_t subnets_last_check; - ad_item_t domain_name; - ad_item_t domain_controller; - ad_item_t site_name; - ad_item_t forest_name; - ad_item_t global_catalog; - /* Site specfic versions */ - ad_item_t site_domain_controller; - ad_item_t site_global_catalog; -} ad_disc; +#define AD_DISC_MAXSID 185 typedef struct ad_disc *ad_disc_t; +typedef struct ad_disc_domains_in_forest { + char domain[AD_DISC_MAXHOSTNAME]; + char sid[AD_DISC_MAXSID]; + int trusted; /* This is not used by auto */ + /* discovery. It is provided so that */ + /* domains in a forest can be marked */ + /* as trusted. */ +} ad_disc_domainsinforest_t; + + +typedef struct ad_disc_trusted_domains { + char domain[AD_DISC_MAXHOSTNAME]; + int direction; +} ad_disc_trusteddomains_t; + enum ad_disc_req { AD_DISC_PREFER_SITE = 0, /* Prefer Site specific version */ @@ -97,40 +72,78 @@ ad_disc_t ad_disc_init(void); void ad_disc_fini(ad_disc_t); -void ad_disc_refresh(ad_disc_t); +/* + * The following routines auto discover the specific item + */ +char * +ad_disc_get_DomainName(ad_disc_t ctx, int *auto_discovered); -char *ad_disc_get_DomainName(ad_disc_t ctx); +idmap_ad_disc_ds_t * +ad_disc_get_DomainController(ad_disc_t ctx, + enum ad_disc_req req, int *auto_discovered); -idmap_ad_disc_ds_t *ad_disc_get_DomainController(ad_disc_t ctx, - enum ad_disc_req req); +char * +ad_disc_get_SiteName(ad_disc_t ctx, int *auto_discovered); -char *ad_disc_get_SiteName(ad_disc_t ctx); +char * +ad_disc_get_ForestName(ad_disc_t ctx, int *auto_discovered); -char *ad_disc_get_ForestName(ad_disc_t ctx); +idmap_ad_disc_ds_t * +ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req, + int *auto_discovered); -idmap_ad_disc_ds_t *ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req); +ad_disc_trusteddomains_t * +ad_disc_get_TrustedDomains(ad_disc_t ctx, int *auto_discovered); -int ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2); +ad_disc_domainsinforest_t * +ad_disc_get_DomainsInForest(ad_disc_t ctx, int *auto_discovered); -int ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName); -int ad_disc_set_DomainController(ad_disc_t ctx, - const idmap_ad_disc_ds_t *domainController); +/* + * The following routines over ride auto discovery with the + * specified values + */ +int +ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName); -int ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName); +int +ad_disc_set_DomainController(ad_disc_t ctx, + const idmap_ad_disc_ds_t *domainController); -int ad_disc_set_ForestName(ad_disc_t ctx, const char *ForestName); +int +ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName); -int ad_disc_set_GlobalCatalog(ad_disc_t ctx, - const idmap_ad_disc_ds_t *GlobalCatalog); +int +ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName); + +int +ad_disc_set_GlobalCatalog(ad_disc_t ctx, + const idmap_ad_disc_ds_t *globalCatalog); -int ad_disc_unset(ad_disc_t ctx); +/* + * This routine forces all auto discovery item to be recomputed + * on request + */ +void ad_disc_refresh(ad_disc_t); +/* This routine unsets all overridden values */ +int ad_disc_unset(ad_disc_t ctx); + +/* This routine test for subnet changes */ int ad_disc_SubnetChanged(ad_disc_t); +/* This routine returns the Time To Live for auto discovered items */ int ad_disc_get_TTL(ad_disc_t); +int ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2); + +int ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1, + ad_disc_trusteddomains_t *td2); + +int ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *td1, + ad_disc_domainsinforest_t *td2); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libidmap/common/idmap_api.c b/usr/src/lib/libidmap/common/idmap_api.c index 69ed433d98..0fe0d8f418 100644 --- a/usr/src/lib/libidmap/common/idmap_api.c +++ b/usr/src/lib/libidmap/common/idmap_api.c @@ -224,7 +224,7 @@ idmap_fini(idmap_handle_t *handle) } -idmap_stat +static idmap_stat idmap_get_prop(idmap_handle_t *handle, idmap_prop_type pr, idmap_prop_res *res) { CLIENT *clnt; @@ -243,24 +243,9 @@ idmap_get_prop(idmap_handle_t *handle, idmap_prop_type pr, idmap_prop_res *res) } return (res->retcode); /* This might not be IDMAP_SUCCESS! */ - -#if 0 - (void) memset(&res, 0, sizeof (res)); - pr = PROP_DOMAIN_CONTROLLER; - - clntstat = clnt_call(clnt, IDMAP_GET_PROP, - (xdrproc_t)xdr_idmap_prop_type, (caddr_t)&pr, - (xdrproc_t)xdr_idmap_prop_res, (caddr_t)&res, TIMEOUT); - - if (clntstat != RPC_SUCCESS) { - fprintf(stderr, "clntstat != RPC_SUCCESS\n"); - rc = _idmap_rpc2stat(clnt); - goto cleanup; - } -#endif - } + idmap_stat idmap_get_prop_ds(idmap_handle_t *handle, idmap_prop_type pr, idmap_ad_disc_ds_t *dc) @@ -2066,7 +2051,7 @@ static stat_table_t stattable[] = { {IDMAP_ERR_BAD_UTF8, gettext("Invalid or illegal UTF-8 sequence found in " "a given Windows entity name or domain name"), EINVAL}, - {IDMAP_ERR_NONEGENERATED, + {IDMAP_ERR_NONE_GENERATED, gettext("Mapping not found and none created (see -c option)"), EINVAL}, {IDMAP_ERR_PROP_UNKNOWN, @@ -2080,6 +2065,9 @@ static stat_table_t stattable[] = { gettext("Native LDAP operation failed"), EINVAL}, {IDMAP_ERR_NS_LDAP_BAD_WINNAME, gettext("Improper winname form found in Native LDAP"), EINVAL}, + {IDMAP_ERR_NO_ACTIVEDIRECTORY, + gettext("No AD servers"), + EINVAL}, {-1, NULL, 0} }; #undef gettext @@ -2167,12 +2155,13 @@ idmap_string2stat(const char *str) return_cmp(W2U_NAMERULE_CONFLICT); return_cmp(U2W_NAMERULE_CONFLICT); return_cmp(BAD_UTF8); - return_cmp(NONEGENERATED); + return_cmp(NONE_GENERATED); return_cmp(PROP_UNKNOWN); return_cmp(NS_LDAP_CFG); return_cmp(NS_LDAP_PARTIAL); return_cmp(NS_LDAP_OP_FAILED); return_cmp(NS_LDAP_BAD_WINNAME); + return_cmp(NO_ACTIVEDIRECTORY); #undef return_cmp return (IDMAP_ERR_OTHER); diff --git a/usr/src/lib/libidmap/common/mapfile-vers b/usr/src/lib/libidmap/common/mapfile-vers index 7360322a8a..905cc8b64f 100644 --- a/usr/src/lib/libidmap/common/mapfile-vers +++ b/usr/src/lib/libidmap/common/mapfile-vers @@ -98,6 +98,8 @@ SUNWprivate { ad_disc_get_DomainName; ad_disc_set_DomainName; ad_disc_compare_ds; + ad_disc_compare_trusteddomains; + ad_disc_compare_domainsinforest; ad_disc_SubnetChanged; ad_disc_get_GlobalCatalog; ad_disc_set_GlobalCatalog; @@ -109,6 +111,8 @@ SUNWprivate { ad_disc_set_SiteName; ad_disc_refresh; ad_disc_get_SiteName; + ad_disc_get_TrustedDomains; + ad_disc_get_DomainsInForest; local: *; }; diff --git a/usr/src/lib/libidmap/common/namemaps.c b/usr/src/lib/libidmap/common/namemaps.c index a72a7e360b..ce43ece4ce 100644 --- a/usr/src/lib/libidmap/common/namemaps.c +++ b/usr/src/lib/libidmap/common/namemaps.c @@ -23,14 +23,12 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" #include <errno.h> #include <ldap.h> #include <sasl/sasl.h> #include <libintl.h> #include <strings.h> -#include <strings.h> #include "idmap_impl.h" #include "ns_sldap.h" @@ -249,7 +247,7 @@ idmap_init_ad(idmap_nm_handle_t *p) goto cleanup; } - dc = ad_disc_get_DomainController(ad_ctx, AD_DISC_GLOBAL); + dc = ad_disc_get_DomainController(ad_ctx, AD_DISC_GLOBAL, NULL); if (dc == NULL) { rc = IDMAP_ERR_ARG; idmapdlog(LOG_ERR, diff --git a/usr/src/lib/nsswitch/ad/common/ad_common.c b/usr/src/lib/nsswitch/ad/common/ad_common.c index 9265defe13..749e269165 100644 --- a/usr/src/lib/nsswitch/ad/common/ad_common.c +++ b/usr/src/lib/nsswitch/ad/common/ad_common.c @@ -62,7 +62,8 @@ nssad_cfg_discover_props(const char *domain, ad_disc_t ad_ctx, return (-1); if (props->domain_controller == NULL) props->domain_controller = - ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE); + ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE, + NULL); return (0); } diff --git a/usr/src/uts/common/sys/idmap.h b/usr/src/uts/common/sys/idmap.h index 3a405e4097..d0f270e697 100644 --- a/usr/src/uts/common/sys/idmap.h +++ b/usr/src/uts/common/sys/idmap.h @@ -26,7 +26,6 @@ #ifndef _SYS_IDMAP_H #define _SYS_IDMAP_H -#pragma ident "%Z%%M% %I% %E% SMI" /* Idmap status codes */ #define IDMAP_SUCCESS 0 @@ -64,12 +63,13 @@ #define IDMAP_ERR_W2U_NAMERULE_CONFLICT -9970 #define IDMAP_ERR_U2W_NAMERULE_CONFLICT -9969 #define IDMAP_ERR_BAD_UTF8 -9968 -#define IDMAP_ERR_NONEGENERATED -9967 +#define IDMAP_ERR_NONE_GENERATED -9967 #define IDMAP_ERR_PROP_UNKNOWN -9966 #define IDMAP_ERR_NS_LDAP_OP_FAILED -9965 #define IDMAP_ERR_NS_LDAP_PARTIAL -9964 #define IDMAP_ERR_NS_LDAP_CFG -9963 #define IDMAP_ERR_NS_LDAP_BAD_WINNAME -9962 +#define IDMAP_ERR_NO_ACTIVEDIRECTORY -9961 /* Reserved GIDs for some well-known SIDs */ #define IDMAP_WK_LOCAL_SYSTEM_GID 2147483648U /* 0x80000000 */ |