addisc.c revision cd37da7426f0c49c14ad9a8a07638ca971477566
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include <resolv.h>
#include <netdb.h>
#include <ctype.h>
#include <errno.h>
#include <ldap.h>
#include "idmapd.h"
#include "addisc.h"
#define DNS_MAX_NAME NS_MAXDNAME
/* SRV RR names for various queries */
#define LDAP_SRV_HEAD "_ldap._tcp."
#define SITE_SRV_MIDDLE "%s._sites."
#define GC_SRV_TAIL "gc._msdcs"
#define DC_SRV_TAIL "dc._msdcs"
#define ALL_GC_SRV_TAIL "_gc._tcp"
#define PDC_SRV "_ldap._tcp.pdc._msdcs.%s"
/* A RR name for all GCs -- last resort this works */
#define GC_ALL_A_NAME_FSTR "gc._msdcs.%s."
#define me "idmapd"
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_item {
enum ad_item_type type;
union {
char *str;
} value;
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_subnet {
char subnet[24];
} ad_subnet_t;
struct ad_disc {
struct __res_state state;
int subnets_changed;
/* Site specfic versions */
};
#define min(x, y) (((x) > (y)) ? (y) : (x))
/*LINTLIBRARY*/
/*
* Function definitions
*/
static void
{
}
static int
{
return (FALSE);
return (FALSE);
return (TRUE);
}
static void
{
if (ttl == 0)
else
}
static void
{
if (ttl == 0)
else
}
static ad_item_t *
{
return (global);
if (req == AD_DISC_GLOBAL)
else if (req == AD_DISC_SITE_SPECIFIC)
else
return (NULL);
return (item);
}
/*
* The last subnet is NULL
*/
static ad_subnet_t *
{
int sock, n, i;
char *s;
return (NULL);
}
lifn.lifn_flags = 0;
"%s: Failed to find the number of network interfaces (%s)",
return (NULL);
}
return (NULL);
}
lifc.lifc_flags = 0;
return (NULL);
}
return (NULL);
}
return (NULL);
}
continue;
continue;
continue;
s = inet_ntoa(((struct sockaddr_in *)
"%s/%d", s, prefix_len);
}
return (results);
}
static int
{
int num_subnets1;
int num_subnets2;
int matched;
int i, j;
;
num_subnets1 = i;
;
num_subnets2 = i;
if (num_subnets1 != num_subnets2)
return (1);
for (i = 0; i < num_subnets1; i++) {
for (j = 0; j < num_subnets2; j++) {
break;
}
}
if (!matched)
return (1);
}
return (0);
}
/* Convert a DN's DC components into a DNS domainname */
static char *
{
char dns[DNS_MAX_NAME];
char *dns_name;
int i, j;
int num = 0;
j = 0;
i = 0;
/*
* Find all DC=<value> and form DNS name of the
* form <value1>.<value2>...
*/
i += 3;
dns[j++] = '.';
num++;
} else {
/* Skip attr=value as it is not DC= */
i++;
}
/* Skip over separator ',' or '+' */
}
dns[j] = '\0';
return (dns_name);
}
/* Format the DN of an AD LDAP subnet object for some subnet */
static char *
{
char *result;
int len;
"CN=%s,CN=Subnets,CN=Sites,%s",
"CN=%s,CN=Subnets,CN=Sites,%s",
return (result);
}
/* Make a list of subnet object DNs from a list of subnets */
static char **
{
char **results;
int i, j;
;
return (NULL);
== NULL) {
for (j = 0; j < i; j++)
return (NULL);
}
}
return (results);
}
int
{
int i, j;
int num_ds1;
int num_ds2;
int match;
;
num_ds1 = i;
;
num_ds2 = j;
return (1);
for (i = 0; i < num_ds1; i++) {
for (j = 0; j < num_ds1; j++) {
break;
}
}
if (!match)
return (1);
}
return (0);
}
static ad_disc_ds_t *
{
int i;
int size;
;
return (new);
}
/* Compare SRC RRs; used with qsort() */
static int
{
return (1);
return (-1);
return (1);
return (-1);
return (0);
}
/*
* Query or search the SRV RRs for a given name.
*
* If name == NULL then search (as in res_nsearch(3RESOLV), honoring any
*
* The output TTL will be the one of the SRV RR with the lowest TTL.
*/
{
union {
} msg;
/* LINTED E_FUNC_SET_NOT_USED */
char *query_type;
char namebuf[NS_MAXDNAME];
/* Set negative result TTL */
/* 1. query necessary resource records */
/* Search, querydomain or query */
query_type = "search";
query_type = "query";
}
if (len < 0) {
return (NULL);
}
" into %ib buffer",
return (NULL);
}
/* 2. parse the reply, skip header and question sections */
"%s: DNS query invalid message format", me);
return (NULL);
}
}
/* 3. walk through the answer section */
sizeof (namebuf));
if (len < 0) {
"%s: DNS query invalid message format", me);
return (NULL);
}
"%s: DNS query invalid message format", me);
return (NULL);
}
continue;
}
if (len < 0) {
me);
return (NULL);
}
"%s: Found %s %d IN SRV [%d][%d] %s:%d", me,
/* 3. move ptr to the end of current record */
}
if (ancount > 1)
(int (*)(const void *, const void *))srvcmp);
return (srv_res);
}
static int
/* ARGSUSED */
{
return (LDAP_PARAM_ERROR);
}
return (LDAP_SUCCESS);
}
/*
* A utility function to get the value of some attribute of one of one
* or more AD LDAP objects named by the dn_list; first found one wins.
*/
static char *
{
int i;
int zero = 0;
char *saslmech = "GSSAPI";
int scope = LDAP_SCOPE_BASE;
char *attrs[2];
domainControllers[i].port);
"AD DC %s:%d (%s)", me,
continue;
}
&ldversion);
&timeoutms);
NULL /* defaults */);
if (rc == LDAP_SUCCESS)
break;
(void) ldap_unbind(*ld);
}
}
"connections to any domain controllers; discovery of "
"some items will fail", me);
return (NULL);
}
if (rc == LDAP_SUCCESS) {
}
(void) ldap_msgfree(results);
return (val);
}
}
(void) ldap_msgfree(results);
}
}
(void) ldap_unbind(*ld);
return (NULL);
}
ad_disc_init(void)
{
"%s: Could not load DNS resolver configuration",
me);
return (NULL);
}
}
return (ctx);
}
void
{
return;
}
void
{
}
/*
* Find DNS domainName
*
*/
static void
{
return;
return;
/* Try to find our domain by searching for DCs for it */
/*
* 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;
/*
* We have the FQDN of the SRV RR name, so now we extract the
* domainname suffix from it.
*/
1 /* for the dot between RR name and domainname */);
return;
}
/* Eat any trailing dot */
}
char *
{
char *domain_name = NULL;
return (domain_name);
}
/*
* Find a Domain Controller
*
*/
static void
{
int validate_global = FALSE;
int validate_site = FALSE;
return;
if (req == AD_DISC_GLOBAL)
else {
if (req == AD_DISC_PREFER_SITE)
}
&ctx->domain_name);
/*
* The DNS SRV record for
* _ldap._tcp.dc._msdcs.<DnsDominName>
*/
}
AD_TYPE_AUTO, ttl);
}
&ctx->domain_name) ||
&ctx->domain_name);
char rr_name[DNS_MAX_NAME];
/*
* The DNS SRV record for
* _ldap._tcp.<SiteName>._sites.dc._msdcs.<DnsDominName>
*/
}
AD_TYPE_AUTO, ttl);
}
}
{
return (domain_controller);
}
static void
{
char **dn_subnets = NULL;
char *dn_root[2];
char *config_naming_context = NULL;
char *site_object = NULL;
char *forest_name;
int len;
int i;
int update_required = FALSE;
return;
/*
* We dont want to cause a recursive loop by validating
* the site specific domain controller.
*/
subnets = find_subnets();
subnets = find_subnets();
}
if (update_required) {
&ctx->domain_controller);
dn_root[0] = "";
dn_root, "configurationNamingContext");
if (config_naming_context == NULL)
goto out;
/*
* 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 ".")
*/
char *str = "CN=Configuration,";
forest_name, AD_TYPE_AUTO, 0);
}
}
if (dn_subnets == NULL)
goto out;
dn_subnets, "siteobject");
if (site_object != NULL) {
/*
* The site object should be of the form
* CN=<site>,CN=Sites,CN=Configuration,
* <DN Domain>
*/
for (len = 0;
;
}
}
}
}
out:
(void) ldap_unbind(ld);
if (dn_subnets != NULL) {
for (i = 0; dn_subnets[i] != NULL; i++)
free(dn_subnets[i]);
}
if (config_naming_context != NULL)
if (site_object != NULL)
}
}
char *
{
return (site_name);
}
static void
{
char *config_naming_context;
char *forest_name = NULL;
char *dn_list[2];
return;
/*
* We dont want to cause a recursive loop by validating
* the site specific domain controller.
*/
&ctx->domain_controller);
dn_list[0] = "";
"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,";
}
}
(void) ldap_unbind(ld);
}
}
}
char *
{
char *forest_name = NULL;
return (forest_name);
}
static void
{
int validate_global = FALSE;
int validate_site = FALSE;
return;
if (req == AD_DISC_GLOBAL)
else {
if (req == AD_DISC_PREFER_SITE)
}
/*
* The DNS SRV record for the non-site
* Global Catalogue is:
*
* _ldap._tcp.gc._msdcs.<DnsForestName>
*/
}
AD_TYPE_AUTO, ttl);
}
&ctx->forest_name);
char rr_name[DNS_MAX_NAME];
/*
* The DNS SRV record for the site
* Global Catalogue is:
*
* _ldap._tcp.<sitename>._sites.gc.
* _msdcs.<DnsForestName>
*/
sizeof (rr_name),
}
AD_TYPE_AUTO, ttl);
}
}
{
return (global_catalog);
}
int
{
char *domain_name = NULL;
if (domainName != NULL) {
if (domain_name == NULL)
return (-1);
AD_TYPE_FIXED, 0);
return (0);
}
int
const ad_disc_ds_t *domainController)
{
if (domainController != NULL) {
if (domain_controller == NULL)
return (-1);
AD_TYPE_FIXED, 0);
return (0);
}
int
{
return (-1);
return (0);
}
int
{
char *forest_name = NULL;
if (forestName != NULL) {
if (forest_name == NULL)
return (-1);
AD_TYPE_FIXED, 0);
return (0);
}
int
{
if (globalCatalog != NULL) {
if (global_catalog == NULL)
return (-1);
AD_TYPE_FIXED, 0);
return (0);
}
int
{
return (0);
}
/*
* ad_disc_get_TTL
*
* This routines the time to live for AD
* auto discovered items.
*
* Returns:
* -1 if there are no TTL items
* 0 if there are expired items
* else the number of seconds
*/
int
{
int ttl;
if (ttl == 0)
else
if (ttl == 0)
else
if (ttl == 0)
else
if (ttl == 0)
return (-1);
if (ttl < 0)
ttl = 0;
return (ttl);
}
int
{
return (TRUE);
}
return (ctx->subnets_changed);
}