idmap_config.c revision 349d5d8f2e43f7f425bc3d025dda555187160ab7
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Config routines common to idmap(1M) and idmapd(1M)
*/
#include <stdlib.h>
#include <strings.h>
#include <libintl.h>
#include <ctype.h>
#include <errno.h>
#include "idmapd.h"
#include <stdio.h>
#include <stdarg.h>
#include <pthread.h>
#include <port.h>
#include "addisc.h"
#define CONFIG_PG "config"
#define GENERAL_PG "general"
#define RECONFIGURE 1
#define POKE_AUTO_DISCOVERY 2
/*LINTLIBRARY*/
static pthread_t update_thread_handle = 0;
static int idmapd_ev_port = -1;
static int rt_sock = -1;
static int
generate_machine_sid(char **machine_sid)
{
char *p;
/*
* Generate and split 128-bit UUID into four 32-bit RIDs
* The machine_sid will be of the form S-1-5-N1-N2-N3-N4
* We depart from Windows here, which instead of 128
* bits worth of random numbers uses 96 bits.
*/
if (*machine_sid == NULL) {
return (-1);
}
uuid_clear(uu);
for (i = 0; i < UUID_LEN/4; i++) {
j = i * 4;
p += rlen;
}
return (0);
}
static bool_t
{
return (exists);
}
/* Check if in the case of failure the original value of *val is preserved */
static int
{
int rc = 0;
/* this is OK: the property is just undefined */
goto destruction;
/* It is still OK when a property doesn't have any value */
goto destruction;
switch (type) {
case SCF_TYPE_BOOLEAN:
break;
case SCF_TYPE_COUNT:
break;
case SCF_TYPE_INTEGER:
break;
default:
type);
rc = -1;
break;
}
return (rc);
}
static char *
{
int rc = -1;
char buf_size = 127;
int length;
for (;;) {
if (length < 0) {
rc = -1;
goto destruction;
}
buf_size *= 2;
if (!buf) {
rc = -1;
goto destruction;
}
} else {
rc = 0;
break;
}
}
if (rc < 0) {
if (buf)
}
return (buf);
}
static int
ad_disc_ds_t **val)
{
int len, i;
int count = 0;
int rc = -1;
/* this is OK: the property is just undefined */
rc = 0;
goto destruction;
}
"scf_iter_property_values(%s) failed: %s",
goto destruction;
}
/* Workaround scf bugs -- can't reset an iteration */
if (count == 0) {
count++;
if (count == 0) {
/* no values */
rc = 0;
goto destruction;
}
goto restart;
}
goto destruction;
}
i = 0;
goto destruction;
}
*portstr++ = '\0';
(char **)NULL, 10);
}
/* Ignore this server if the hostname is too long */
i++;
}
rc = 0;
if (rc < 0) {
if (servers)
}
return (rc);
}
static int
{
int rc = 0;
/* this is OK: the property is just undefined */
goto destruction;
"scf_property_get_value(%s) failed: %s",
rc = -1;
goto destruction;
}
rc = -1;
if (rc < 0) {
if (*val)
}
return (rc);
}
static int
{
int rc = -1;
int ret = -2;
int i;
goto destruction;
}
"scf_transaction_start(%s) failed: %s",
goto destruction;
}
SCF_TYPE_ASTRING) < 0) {
"scf_transaction_property_new() failed: %s",
scf_strerror(scf_error()));
goto destruction;
}
"scf_value_set_astring() failed: %s",
scf_strerror(scf_error()));
goto destruction;
}
"scf_entry_add_value() failed: %s",
scf_strerror(scf_error()));
goto destruction;
}
break;
/*
* Property group set in scf_transaction_start()
* is not the most recent. Update pg, reset tx and
* retry tx.
*/
"scf_transaction_commit(%s) failed - Retry: %s",
"scf_pg_update() failed: %s",
scf_strerror(scf_error()));
goto destruction;
}
}
}
if (ret == 1)
rc = 0;
else if (ret != -2)
"scf_transaction_commit(%s) failed: %s",
return (rc);
}
static int
{
return (0);
return (0);
}
return (1);
}
static int
{
int i;
/* Nothing to do */
return (0);
return (0);
}
if (*value)
/* We're unsetting this DS property */
return (1);
}
/* List all the new DSs */
return (1);
}
/*
* Returns 1 if the PF_ROUTE socket event indicates that we should rescan the
* interfaces.
*
* Shamelessly based on smb_nics_changed() and other PF_ROUTE uses in ON.
*/
static
int
{
int nbytes;
int is_interesting = FALSE;
for (;;) {
break;
continue;
continue;
case RTM_NEWADDR:
case RTM_DELADDR:
case RTM_IFINFO:
break;
default:
break;
}
}
return (is_interesting);
}
/*
* Returns 1 if SIGHUP has been received (see hup_handler() elsewhere) or if an
* interface address was added or removed; otherwise it returns 0.
*
* Note that port_get() does not update its timeout argument when EINTR, unlike
* nanosleep(). We probably don't care very much here, but if we did care then
* we could always use a timer event and associate it with the same event port,
* then we could get accurate waiting regardless of EINTRs.
*/
static
int
{
switch (errno) {
case EINTR:
goto retry;
case ETIME:
/* Timeout */
return (FALSE);
default:
/* EBADF, EBADFD, EFAULT, EINVAL (end of time?)? */
exit(1);
/* NOTREACHED */
break;
}
}
/* PF_ROUTE socket read event, re-associate fd, handle event */
"routing socket with the event port: %s",
exit(1);
}
/*
* The network configuration may still be in flux. No matter,
* the resolver will re-transmit and timout if need be.
*/
return (pfroute_event_is_interesting(rt_sock));
}
int rc;
/*
* Blow away the ccache, we might have re-joined the
* domain or joined a new one
*/
/* HUP is the refresh method, so re-read SMF config */
if (rc < -1) {
"SMF properties");
exit(1);
} else if (rc == -1) {
"re-loading configuration may cause AD lookups "
"to fail");
}
return (FALSE);
}
return (FALSE);
}
void *
idmap_cfg_update_thread(void *arg)
{
poke_is_interesting = 1;
/*
* If ttl < 0 then we can wait for an event without timing out.
* If idmapd needs to notice that the system has been joined to
* a Windows domain then idmapd needs to be refreshed.
*/
if (ttl > MAX_CHECK_TIME)
/*
* If there are no interesting events, and this is not the first
* time through the loop, and we haven't waited the most that
* we're willing to wait, so do nothing but wait some more.
*/
continue;
(void) ad_disc_SubnetChanged(ad_ctx);
"SMF properties");
exit(1);
}
poke_is_interesting = 1;
else
poke_is_interesting = 0;
}
/*NOTREACHED*/
return (NULL);
}
int
idmap_cfg_start_updates(void)
{
if ((idmapd_ev_port = port_create()) < 0) {
return (-1);
}
(void) close(idmapd_ev_port);
return (-1);
}
(void) close(idmapd_ev_port);
return (-1);
}
(void) close(idmapd_ev_port);
return (-1);
}
idmap_cfg_update_thread, NULL)) != 0) {
(void) close(idmapd_ev_port);
return (-1);
}
return (0);
}
/*
* This is the half of idmap_cfg_load() that loads property values from
* SMF (using the config/ property group of the idmap FMRI).
*
* Return values: 0 -> success, -1 -> failure, -2 -> hard failures
* reading from SMF.
*/
static
int
int *errors)
{
int rc;
scf_strerror(scf_error()));
return (-2);
}
scf_strerror(scf_error()));
return (-2);
}
} else {
}
}
if (rc != 0) {
pgcfg->list_size_limit = 0;
errors++;
}
&pgcfg->domain_name);
if (rc != 0)
errors++;
else
pgcfg->domain_name);
&pgcfg->default_domain);
if (rc != 0) {
/*
* SCF failures fetching config/default_domain we treat
* as fatal as they may leave ID mapping rules that
* match unqualified winnames flapping in the wind.
*/
return (-2);
}
if (rc != 0)
errors++;
/*
* We treat default_domain as having been specified in SMF IFF
* either (the config/default_domain property was set) or (the
* old, obsolete, never documented config/mapping_domain
* property was set and the new config/domain_name property was
* not set).
*/
"Ignoring obsolete, undocumented "
"config/mapping_domain property");
}
"The config/mapping_domain property is "
"obsolete; support for it will be removed, "
"please use config/default_domain instead");
}
}
if (rc != 0)
errors++;
/* If machine_sid not configured, generate one */
return (-2);
pgcfg->machine_sid);
if (rc != 0)
errors++;
}
if (rc != 0)
errors++;
else
if (rc != 0)
errors++;
else
pgcfg->forest_name);
if (rc != 0)
errors++;
else
&pgcfg->global_catalog);
if (rc != 0)
errors++;
else
/*
* Read directory-based name mappings related SMF properties
*/
bool_val = 0;
if (rc != 0)
return (-2);
if (!bool_val)
return (rc);
if (rc != 0)
return (-2);
if (rc != 0)
return (-2);
if (rc != 0)
return (-2);
"Native LDAP based name mapping not supported at this "
"time. Please unset config/nldap_winname_attr and restart "
"idmapd.");
return (-3);
}
"If config/ds_name_mapping_enabled property is set to "
"true then atleast one of the following name mapping "
"attributes must be specified. (config/ad_unixuser_attr OR "
"config/ad_unixgroup_attr)");
return (-3);
}
return (rc);
}
/*
* This is the half of idmap_cfg_load() that auto-discovers values of
* discoverable properties that weren't already set via SMF properties.
*
* idmap_cfg_discover() is called *after* idmap_cfg_load_smf(), so it
* needs to be careful not to overwrite any properties set in SMF.
*/
static
void
{
}
/*
* idmap_cfg_load() is called at startup, and periodically via the
* update thread when the auto-discovery TTLs expire, as well as part of
* the refresh method, to update the current configuration. It always
* reads from SMF, but you still have to refresh the service after
* changing the config pg in order for the changes to take effect.
*
* There are two flags:
*
* - CFG_DISCOVER
* - CFG_LOG
*
* If CFG_DISCOVER is set then idmap_cfg_load() calls
* idmap_cfg_discover() to discover, via DNS and LDAP lookups, property
* values that weren't set in SMF.
*
* If CFG_LOG is set then idmap_cfg_load() will log (to LOG_NOTICE)
* whether the configuration changed. This should be used only from the
* refresh method.
*
* Return values: 0 -> success, -1 -> failure, -2 -> hard failures
* reading from SMF.
*/
int
{
int rc = 0;
int errors = 0;
int changed = 0;
goto err;
if (flags & CFG_DISCOVER)
}
/* Non-discoverable props updated here */
/* Props that can be discovered and set in SMF updated here */
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.
*/
reload_ad();
}
/*
* If the config changes as a result of a refresh of the
* service, then logging about it can provide useful
* feedback to the sysadmin.
*/
}
err:
if (rc < -1)
return (rc);
return ((errors == 0) ? 0 : -1);
}
/*
* Initialize 'cfg'.
*/
{
/* First the smf repository handles: */
if (!cfg) {
return (NULL);
}
scf_strerror(scf_error()));
goto error;
}
scf_strerror(scf_error()));
goto error;
}
scf_strerror(scf_error()));
goto error;
}
NULL, /* scope */
NULL, /* prop */
SCF_DECODE_FMRI_EXACT) < 0) {
scf_strerror(scf_error()));
goto error;
}
scf_strerror(scf_error()));
goto error;
}
/* Initialize AD Auto Discovery context */
goto error;
return (cfg);
(void) idmap_cfg_fini(cfg);
return (NULL);
}
void
{
if (pgcfg->default_domain) {
}
if (pgcfg->domain_name) {
}
if (pgcfg->machine_sid) {
}
if (pgcfg->domain_controller) {
}
if (pgcfg->forest_name) {
}
}
if (pgcfg->global_catalog) {
}
if (pgcfg->ad_unixuser_attr) {
}
if (pgcfg->ad_unixgroup_attr) {
}
if (pgcfg->nldap_winname_attr) {
}
}
int
{
return (0);
}
void
idmap_cfg_poke_updates(void)
{
if (idmapd_ev_port != -1)
}
/*ARGSUSED*/
void
idmap_cfg_hup_handler(int sig)
{
if (idmapd_ev_port >= 0)
}