dit_access.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* DESCRIPTION: Contains top level functions to read/write to the DIT. These
* are the API between the shim and the mapping system.
* Things calling these should have no knowledge of LDAP. Things
* called by them should have no knowledge of NIS.
*
* Error handling here may appear to be limited but, because the
* NIS protocol cannot carry meaningful information about why a
* N2L operation failed, functions that don't work log
* an error and then just return FAILURE.
*
*/
/*
* Includes. WE WANT TO USE REAL DBM FUNCTIONS SO DO NOT INCLUDE SHIM_HOOKS.H.
*/
#include <unistd.h>
#include <syslog.h>
#include <ndbm.h>
#include <sys/systeminfo.h>
#include <string.h>
#include <lber.h>
#include <ldap.h>
#include <errno.h>
#include "ypsym.h"
#include "ypdefs.h"
#include "shim.h"
#include "../ldap_structs.h"
#include "../ldap_parse.h"
#include "../nisdb_ldap.h"
#include "../ldap_util.h"
#include "../ldap_op.h"
#include "../ldap_attr.h"
#include "../nis_parse_ldap_conf.h"
#include "../nisdb_mt.h"
#include "yptol.h"
#include "dit_access_utils.h"
#include "stdio.h"
/* Enable standard YP code features defined in ypdefs.h */
USE_YP_PREFIX
USE_YP_MASTER_NAME
USE_YP_LAST_MODIFIED
USE_YP_INPUT_FILE
USE_YP_OUTPUT_NAME
USE_YP_DOMAIN_NAME
USE_YP_SECURE
USE_YP_INTERDOMAIN
/*
* Decs
*/
suc_code add_special_entries(DBM *, map_ctrl *, bool_t *);
void free_null_terminated_list(char **list);
/*
* FUNCTION: is_yptol_mode();
*
* DESCRIPTION: Determines if we should run in N2L or traditional mode based
* on the presence of the N2L mapping file. If there are problems
* with the file, e.g. unreadable, this will be picked up latter.
*
* INPUTS: Nothing
*
* OUTPUTS: TRUE = Run in N2L mode
* FALSE = Run in traditional mode.
*/
bool_t
is_yptol_mode()
{
struct stat filestat;
if (stat(YP_DEFAULTCONFFILE, &filestat) != -1)
return (TRUE);
return (FALSE);
}
/*
* FUNCTION: read_from_dit();
*
* DESCRIPTION: Read (i.e. get and map) a single NIS entry from the LDAP DIT.
* Also handles retry attempts, on failure, and interpretation of
* internal error codes.
*
* INPUTS: Map name (unqualified)
* Domain name
* Entry key
* Pointer to return location
*
* OUTPUTS: If successful DBM datum containing result.
* On error DBM datum pointing to NULL and, if the cached value
* is not to be used, an error code.
*/
int
read_from_dit(char *map, char *domain, datum *key, datum *value)
{
int count;
int res;
__nisdb_retry_t *retrieveRetry;
/* Initialize tsd */
__nisdb_get_tsd()->domainContext = 0;
__nisdb_get_tsd()->escapeFlag = '\0';
for (count = 0; count < ypDomains.numDomains; count++) {
if (0 == ypDomains.domainLabels[count])
continue;
if (0 == strcasecmp(domain, ypDomains.domainLabels[count])) {
__nisdb_get_tsd()->domainContext =
ypDomains.domains[count];
break;
}
}
retrieveRetry = &ldapDBTableMapping.retrieveErrorRetry;
/* Loop 'attempts' times of forever if -1 */
for (count = retrieveRetry->attempts; (0 <= count) ||
(-1 == retrieveRetry->attempts); count --) {
if (TRUE == singleReadFromDIT(map, domain, key, value, &res))
/* It worked, return value irrelevant */
return (0);
if (LDAP_TIMEOUT == res) { /* Exceeded search timeout */
value->dptr = NULL;
return (0);
}
if (is_fatal_error(res))
break;
/*
* Didn't work. If not the special case where no repeats are
* done sleep.
*/
if (0 != retrieveRetry->attempts)
(void) poll(NULL, 0, retrieveRetry->timeout*1000);
}
/* Make sure returned pointer is NULL */
value->dptr = NULL;
/* If we get here access failed work out what to return */
if (ldapDBTableMapping.retrieveError == use_cached)
return (0);
return (res);
}
/*
* FUNCTION: write_to_dit();
*
* DESCRIPTION: Maps and writes a NIS entry to the LDAP DIT.
* Also handles retry attempts, on failure, and interpretation of
* internal error codes.
*
* INPUTS: Pointer to (unqualified) map name
* Pointer to domain name
* The entries key
* What to write
* Replace flag indicating
* TRUE = Replace (overwrite) any existing entries
* FALSE = Return error if there are existing entries
* Flag indicating if we should tolerate mapping errors.
*
* OUTPUTS: SUCCESS = Write was successful
* FAILURE = Write failed
*
*/
suc_code
write_to_dit(char *map, char *domain, datum key, datum value,
bool_t replace, bool_t ignore_map_errs)
{
int count;
int res;
__nisdb_retry_t *storeRetry = &ldapDBTableMapping.storeErrorRetry;
/* Initialize tsd */
__nisdb_get_tsd()->domainContext = 0;
__nisdb_get_tsd()->escapeFlag = '\0';
for (count = 0; count < ypDomains.numDomains; count++) {
if (0 == ypDomains.domainLabels[count])
continue;
if (0 == strcasecmp(domain, ypDomains.domainLabels[count])) {
__nisdb_get_tsd()->domainContext =
ypDomains.domains[count];
break;
}
}
storeRetry = &ldapDBTableMapping.storeErrorRetry;
/* Loop 'attempts' times of forever if -1 */
for (count = storeRetry->attempts; (0 <= count) ||
(-1 == storeRetry->attempts); count --) {
res = singleWriteToDIT(map, domain, &key, &value, replace);
if (LDAP_SUCCESS == res)
return (SUCCESS);
if (is_fatal_error(res)) {
/*
* The mapping failed and will fail again if it is
* retried. However there are some cases where an
* actual mapping fault (rather than a LDAP problem)
* may be ignored.
*/
if (ignore_map_errs) {
switch (res) {
case LDAP_INVALID_DN_SYNTAX:
case LDAP_OBJECT_CLASS_VIOLATION:
case LDAP_NOT_ALLOWED_ON_RDN:
case MAP_NAMEFIELD_MATCH_ERROR:
case MAP_NO_DN:
return (SUCCESS);
default:
break;
}
}
return (FAILURE);
}
if (ldapDBTableMapping.storeError != sto_retry)
return (FAILURE);
/*
* Didn't work. If not the special case where no repeats are
* done sleep.
*/
if (0 != storeRetry->attempts)
(void) poll(NULL, 0, storeRetry->timeout*1000);
}
return (FAILURE);
}
/*
* FUNCTION : get_ttl_value()
*
* DESCRIPTION: Get the TTL value, derived from mapping file or DIT, for a
* entry.
*
* GIVEN : Pointer to map
* A flag indication if TTL should be max, min or random
*
* RETURNS : TTL value in seconds.
* -1 on failure
*/
int
get_ttl_value(map_ctrl *map, TTL_TYPE type)
{
__nis_table_mapping_t *table_map;
struct timeval ret;
int interval, res;
char *myself = "get_ttl_value";
/* Get the mapping structure corresponding to `map.domain' */
table_map = mappingFromMap(map->map_name, map->domain, &res);
if (0 == table_map) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"Get TTL request could not access map %s in domain %s "
"(error %d)", map->map_name, map->domain, res);
return (-1);
}
switch (type) {
case TTL_MAX:
return (table_map->initTtlHi);
case TTL_MIN:
return (table_map->initTtlLo);
default:
logmsg(MSG_NOTIMECHECK, LOG_INFO,
"%s passed illegal TTL type (%d)", myself, type);
/* If unknown TTL type drop through to TTL_RAND */
case TTL_RAND:
interval = table_map->initTtlHi - table_map->initTtlLo;
if (0 >= interval)
return (table_map->initTtlLo);
/*
* Must get a random value. We assume srand48() got
* called at initialization.
*/
return (lrand48() % interval);
case TTL_RUNNING:
return (table_map->ttl);
}
}
/*
* FUNCTION : get_mapping_domain_list()
*
* DESCRIPTION: Gets a list of domain names specified, by nisLDAPdomainContext
* attributes, in the mapping file. This is used only for initial
* DIT setup. Once the DIT has been set up get_domain_list() is
* used instead.
*
* GIVEN : Pointer returned array.
*
* RETURNS : Number of element in returned array.
* Array of elements this is in static memory
* and must not be freed by the caller.
*/
int
get_mapping_domain_list(char ***ptr)
{
*ptr = ypDomains.domainLabels;
return (ypDomains.numDomains);
}
/*
* FUNCTION : get_mapping_yppasswdd_domain_list()
*
* DESCRIPTION: Gets a list of domain names specified, by the
* nisLDAPyppasswddDomains attribute, in the mapping file. This
* is the list of domains for which passwords should be changed.
*
* GIVEN : Pointer returned array
*
* RETURNS : Number of element in returned array.
* 0 if no nisLDAPyppasswddDomains attribute is present.
* Array of elements this is in static memory
* and must not be freed by the caller.
*/
int
get_mapping_yppasswdd_domain_list(char ***ptr)
{
*ptr = ypDomains.yppasswddDomainLabels;
return (ypDomains.numYppasswdd);
}
/*
* FUNCTION : free_map_list()
*
* DESCRIPTION: Frees a map list.
*
* GIVEN : Pointer to the map list.
*
* RETURNS : Nothing
*/
void
free_map_list(char **map_list)
{
free_null_terminated_list(map_list);
}
/*
* FUNCTION : get_passwd_list()
*
* DESCRIPTION: Gets a list of either passwd or passwd.adjunct map files
* defined in the mapping file. These are the files which have
* 'magic' nisLDAPdatabaseIdMapping entries aliasing them to
* passwd or passwd.adjunct. This function is required so that
* yppasswdd can work out which maps to synchronize with any
* password changes.
*
* This information is not currently stored by the parser but
* we can recover it from the hash table. This makes hard work but
* passwords should not be changed very frequently
*
* GIVEN : Flag indicating if a list is required for passwd or
* passwd.adjunct
* Domain to return the list for.
*
* RETURNS : Null terminated list of map names in malloced memory. To be
* freed by caller. (Possibly empty if no passwd maps found)
* NULL on error
*/
#define ARRAY_CHUNK 10
char **
get_passwd_list(bool_t adjunct, char *domain)
{
char *myself = "get_passwd_list";
__nis_hash_item_mt *it;
int i, size;
char *end_ptr, *copy_ptr;
char *target; /* What we are looking for */
int target_len;
int domain_len;
char **res; /* Result array */
char **res_old; /* Old value of res during realloc */
int array_size; /* Current malloced size */
int res_count = 0; /* Current result count */
/*
* Always need an array even if just for terminator. Normally one
* chunk will be enough.
*/
res = am(myself, ARRAY_CHUNK * sizeof (char *));
if (NULL == res)
return (NULL);
array_size = ARRAY_CHUNK;
/* Set up target */
if (adjunct)
target = PASSWD_ADJUNCT_PREFIX;
else
target = PASSWD_PREFIX;
target_len = strlen(target);
domain_len = strlen(domain);
/* Work out hash table length */
size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]);
/* For all hash table entries */
for (i = 0; i < size; i++) {
/* Walk linked list for this hash table entry */
for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) {
/* Check right map */
if ((target_len + domain_len + 1) > strlen(it->name))
continue;
if (0 != strncmp(it->name, target, target_len))
continue;
/* Check right domain (minus trailing dot) */
if (strlen(domain) >= strlen(it->name))
continue;
end_ptr = it->name + strlen(it->name) -
strlen(domain) - 1;
if (',' != *(end_ptr - 1))
continue;
if (0 != strncmp(end_ptr, domain, strlen(domain)))
continue;
/* Check if we need to enlarge array */
if ((res_count + 1) >= array_size) {
array_size += ARRAY_CHUNK;
res_old = res;
res = realloc(res, array_size *
sizeof (char *));
if (NULL == res) {
res_old[res_count] = NULL;
free_passwd_list(res_old);
return (NULL);
}
}
/* What we really need is strndup() */
res[res_count] = am(myself, end_ptr - it->name + 1);
if (NULL == res[res_count]) {
free_passwd_list(res);
return (NULL);
}
/* Copy from start to end_ptr */
memcpy(res[res_count], it->name, end_ptr-it->name - 1);
res_count ++;
}
}
/* Terminate array */
res[res_count] = NULL;
return (res);
}
/*
* FUNCTION : free_passwd_list()
*
* DESCRIPTION: Frees a password list obtained with get_passwd_list()
*
* INPUTS : Address of list to free.
*
* OUTPUTS : Nothing
*/
void
free_passwd_list(char **list)
{
free_null_terminated_list(list);
}
/*
* FUNCTION : free_null_terminated_list()
*
* DESCRIPTION: Frees a generic null terminated list.
*
* INPUTS : Address of list to free.
*
* OUTPUTS : Nothing
*/
void
free_null_terminated_list(char **list)
{
int index;
/* Free all the strings */
for (index = 0; NULL != list[index]; index ++)
sfree(list[index]);
/* Free the array */
sfree(list);
}
/*
* FUNCTION : add_special_entries()
*
* DESCRIPTION: Adds the special (YP_*) entries to a map.
*
* Part of dit_access because requires access to the mapping
* file in order to work out if secure and interdomain entries
* should be created.
*
* GIVEN : Pointer to an open, temporary, DBM file
* Pointer to map information (do not use DBM fields).
* Pointer to a location in which to return security flag
*
* RETURNS : SUCCESS = All entries created
* FAILURE = Some entries not created
*/
suc_code
add_special_entries(DBM *db, map_ctrl *map, bool_t *secure_flag)
{
char local_host[MAX_MASTER_NAME];
char time_string[MAX_ASCII_ORDER_NUMBER_LENGTH];
struct timeval now;
__nis_table_mapping_t *table_map;
int res;
/* Last modified time is now */
update_timestamp(db);
/* Add domain name */
addpair(db, yp_domain_name, map->domain);
/* For N2L input and output file are meaningless */
/* addpair(db, yp_input_file, infilename); */
/* addpair(db, yp_output_file, outfilename); */
/* For N2L mode local machine is always the master */
sysinfo(SI_HOSTNAME, local_host, sizeof (local_host));
addpair(db, yp_master_name, local_host);
/* Get the mapping structure corresponding to `map.domain' */
table_map = mappingFromMap(map->map_name, map->domain, &res);
if (0 == table_map)
return (FAILURE);
/* Add secure and interdomain flags if required */
if (table_map->securemap_flag) {
addpair(db, yp_secure, "");
*secure_flag = TRUE;
} else {
*secure_flag = FALSE;
}
if (table_map->usedns_flag)
addpair(db, yp_interdomain, "");
return (SUCCESS);
}
/*
* FUNCTION: update_map_from_dit()
*
* DESCRIPTION: Core code called to update an entire map.
* Information is recovered from LDAP and used to build a duplicate
* copy of the live maps. When this is complete the maps are
* locked and then overwritten by the new copy.
*
* INPUTS: map_ctrl containing lots of information about the map and a
* pointer to it's lock which will be required.
* Flag indicating if progress logging is required.
*
* OUTPUTS: SUCCESS = Map updated
* FAILURE = Map not updated
*/
suc_code
update_map_from_dit(map_ctrl *map, bool_t log_flag) {
__nis_table_mapping_t *t;
__nis_rule_value_t *rv, *frv;
__nis_ldap_search_t *ls;
__nis_object_dn_t *objectDN = NULL;
datum *datval, *datkey;
int nr = 0, i, j, nv, numDNs;
int statP = SUCCESS, flag;
char *objname, **dn;
/* Name of temporary entries DBM file */
char *temp_entries;
/* Name of temporary TTL DBM file */
char *temp_ttl;
/* Temporary DBM handles */
DBM *temp_entries_db;
DBM *temp_ttl_db;
map_ctrl temp_map;
datum key;
char *myself = "update_map_from_dit";
bool_t secure_flag;
int entry_count = 1;
int next_print = PRINT_FREQ;
if (!map || !map->map_name || !map->domain) {
return (FAILURE);
}
__nisdb_get_tsd()->escapeFlag = '\0';
/*
* netgroup.byxxx maps are a special case. They are regenerated from
* the netgroup map, not the DIT, so handle special case.
*/
if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) ||
0 == (strcmp(map->map_name, NETGROUP_BYUSER))) {
return (update_netgroup_byxxx(map));
}
/* Get the mapping information for the map */
if ((t = mappingFromMap(map->map_name, map->domain, &statP)) == 0) {
if (statP == MAP_NO_MAPPING_EXISTS)
logmsg(MSG_NOTIMECHECK, LOG_WARNING,
"%s: No mapping information available for %s,%s",
myself, map->map_name, map->domain);
return (FAILURE);
}
/* Allocate and set up names */
if (SUCCESS != alloc_temp_names(map->map_path,
&temp_entries, &temp_ttl)) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Unable to create map names for %s",
myself, map->map_path);
return (FAILURE);
}
/* Create temp entry and TTL file */
if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644))
== NULL) {
logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
myself, temp_entries);
sfree(temp_entries);
sfree(temp_ttl);
return (FAILURE);
}
if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644))
== NULL) {
logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
myself, temp_ttl);
dbm_close(temp_entries_db);
delete_map(temp_entries);
sfree(temp_entries);
sfree(temp_ttl);
return (FAILURE);
}
/* Initialize domainContext tsd */
__nisdb_get_tsd()->domainContext = 0;
for (i = 0; i < ypDomains.numDomains; i++) {
if (0 == ypDomains.domainLabels[i])
continue;
if (0 == strcasecmp(map->domain, ypDomains.domainLabels[i])) {
__nisdb_get_tsd()->domainContext = ypDomains.domains[i];
break;
}
}
if (!(objname = getFullMapName(map->map_name, map->domain))) {
if (temp_entries_db)
dbm_close(temp_entries_db);
if (temp_ttl_db)
dbm_close(temp_ttl_db);
delete_map(temp_entries);
sfree(temp_entries);
delete_map(temp_ttl);
sfree(temp_ttl);
return (FAILURE);
}
/* Try each mapping for the map */
for (flag = 0; t != 0; t = t->next) {
/* Check if the mapping is the correct one */
if (strcmp(objname, t->objName) != 0) {
continue;
}
/* Check if rulesFromLDAP are provided */
if (t->numRulesFromLDAP == 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: No rulesFromLDAP available for %s (%s)",
myself, t->dbId, map->map_name);
continue;
}
/* Set flag to indicate update is enabled */
flag = 1;
/* Create ldap request for enumeration */
for (objectDN = t->objectDN;
objectDN && objectDN->read.base;
objectDN = objectDN->next) {
if ((ls = createLdapRequest(t, 0, 0, 1, NULL,
objectDN)) == 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Failed to create "
"ldapSearch request for "
"%s (%s) for base %s",
myself, t->dbId,
map->map_name,
objectDN->read.base);
statP = FAILURE;
break;
}
if (log_flag) {
printf("Waiting for LDAP search results.\n");
}
/* Query LDAP */
nr = (ls->isDN)?0:-1;
rv = ldapSearch(ls, &nr, 0, &statP);
freeLdapSearch(ls);
if (rv == 0) {
if (statP == LDAP_NO_SUCH_OBJECT) {
/*
* No Entry exists in the ldap server. Not
* a problem. Maybe there are just no entries
* in this map.
*/
continue;
}
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: ldapSearch error %d "
"(%s) for %s (%s) for base %s",
myself, statP, ldap_err2string(statP),
t->dbId, map->map_name,
objectDN->read.base);
statP = FAILURE;
break;
}
if (log_flag) {
printf("Processing search results.\n");
}
/* Obtain list of DNs for logging */
if ((dn = findDNs(myself, rv, nr, 0, &numDNs)) == 0) {
statP = FAILURE;
break;
}
/* For each entry in the result do the following */
for (i = 0; i < nr; i++) {
/* Convert LDAP data to NIS equivalents */
statP = buildNISRuleValue(t, &rv[i],
map->domain);
if (statP == MAP_INDEXLIST_ERROR)
continue;
if (statP != SUCCESS) {
logmsg(MSG_NOTIMECHECK, LOG_WARNING,
"%s: Conversion error %d (LDAP to "
"name=value pairs) "
"for (dn: %s) for "
"%s (%s) for base %s",
myself, statP, NIL(dn[i]),
t->dbId, map->map_name,
objectDN->read.base);
continue;
}
/* Obtain the datum for value */
datval = ruleValueToDatum(t, &rv[i], &statP);
if (datval == 0) {
logmsg(MSG_NOTIMECHECK, LOG_WARNING,
"%s: Conversion error %d "
"(name=value pairs to NIS)"
" for (dn: %s) for "
"%s (%s) for base %s",
myself, statP, NIL(dn[i]),
t->dbId, map->map_name,
objectDN->read.base);
continue;
}
/* Obtain the datum for key */
datkey = getKeyFromRuleValue(t, &rv[i],
&nv, &statP);
if (datkey == 0) {
logmsg(MSG_NOTIMECHECK, LOG_WARNING,
"%s: Unable to obtain NIS "
"key from LDAP data (dn:%s) "
"for %s (%s) for base %s",
myself, NIL(dn[i]), t->dbId,
map->map_name,
objectDN->read.base);
sfree(datval->dptr);
sfree(datval);
continue;
}
/* Write to the temporary map */
for (j = 0; j < nv; j++, entry_count ++) {
if (datkey[j].dsize == 0)
continue;
errno = 0;
/* DBM_INSERT to match */
/* singleReadFromDIT */
if (dbm_store(temp_entries_db,
datkey[j],
*datval,
DBM_INSERT) < 0) {
/*
* For some cases errno may
* still be 0 but dbm_error
* isn't informative at all.
*/
logmsg(MSG_NOTIMECHECK,
LOG_WARNING,
"%s: dbm store error "
"(errno=%d) "
"for (key=%s, value=%s) "
"for %s (%s) for base %s",
myself,
errno,
datkey[j].dptr,
datval->dptr, t->dbId,
map->map_name,
objectDN->read.base);
/* clear the error */
dbm_clearerr(temp_entries_db);
}
sfree(datkey[j].dptr);
if (log_flag && (entry_count >=
next_print)) {
printf("%d entries processed\n",
entry_count);
next_print *= 2;
}
}
sfree(datkey);
sfree(datval->dptr);
sfree(datval);
}
freeRuleValue(rv, nr);
freeDNs(dn, numDNs);
} /* End of for over objectDN */
}
sfree(objname);
if (t != 0 || flag == 0) {
if (temp_entries_db)
dbm_close(temp_entries_db);
if (temp_ttl_db)
dbm_close(temp_ttl_db);
delete_map(temp_entries);
sfree(temp_entries);
delete_map(temp_ttl);
sfree(temp_ttl);
return (statP);
}
/* Set up enough of map_ctrl to call update_entry_ttl */
temp_map.map_name = map->map_name;
temp_map.domain = map->domain;
temp_map.ttl = temp_ttl_db;
/* Generate new TTL file */
key = dbm_firstkey(temp_entries_db);
while (key.dptr != 0) {
if (!is_special_key(&key))
/*
* We don't want all the entries to time out at the
* same time so create random TTLs.
*/
if (FAILURE == update_entry_ttl(&temp_map, &key,
TTL_RAND))
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Could not update TTL for "
"(key=%s) for map %s,%s",
myself, NIL(key.dptr), map->map_name,
map->domain);
key = dbm_nextkey(temp_entries_db);
}
/* Update map TTL */
if (SUCCESS != update_map_ttl(&temp_map)) {
logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not update map TTL "
"for %s,%s", myself, map->map_name, map->domain);
}
/* Set up 'special' nis entries */
add_special_entries(temp_entries_db, map, &secure_flag);
/* Close temp DBM files */
dbm_close(temp_entries_db);
dbm_close(temp_ttl_db);
/* Lock access to the map for copy */
lock_map_ctrl(map);
/* Move temp maps to real ones */
rename_map(temp_entries, map->map_path, secure_flag);
rename_map(temp_ttl, map->ttl_path, secure_flag);
/* Free file names */
sfree(temp_entries);
sfree(temp_ttl);
/* Unlock map */
unlock_map_ctrl(map);
return (SUCCESS);
}
/*
* FUNCTION : get_mapping_map_list()
*
* DESCRIPTION: Gets a list of nis maps for a given domain specified in the
* mapping file. This information is not saved so have to go
* through the entire hash table. At least this is only done at
* initialization time.
*
* GIVEN : Domain name
*
* RETURNS : List of map names in malloced memory. MUST BE FREED BY CALLER.
*/
char **
get_mapping_map_list(char *domain)
{
char *myself = "get_mapping_map_list";
__nis_hash_item_mt *it;
int i, j, size;
char *end_ptr, *copy_ptr;
char **res; /* Result array */
char **res_old; /* Old value of res during realloc */
int array_size; /* Current malloced size */
int res_count = 0; /* Current result count */
/*
* Always need an array even if just for terminator. Normally one
* chunk will be enough.
*/
res = am(myself, ARRAY_CHUNK * sizeof (char *));
if (NULL == res)
return (NULL);
array_size = ARRAY_CHUNK;
/* Work out hash table length */
size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]);
/* For all hash table entries */
for (i = 0; i < size; i++) {
/* Walk linked list for this hash table entry */
for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) {
/* Check it's not a split field entry */
if (0 != ((__nis_table_mapping_t *)it)->numSplits)
continue;
/* Check right domain (minus trailing dot) */
if (strlen(domain) >= strlen(it->name))
continue;
end_ptr = it->name + strlen(it->name) -
strlen(domain) - 1;
if (',' != *(end_ptr - 1))
continue;
if (0 != strncmp(end_ptr, domain, strlen(domain)))
continue;
/* Check if we need to enlarge array */
if ((res_count + 1) >= array_size) {
array_size += ARRAY_CHUNK;
res_old = res;
res = realloc(res, array_size *
sizeof (char *));
if (NULL == res) {
res_old[res_count] = NULL;
free_passwd_list(res_old);
return (NULL);
}
}
/*
* We will need the sequence number when we come to
* sort the entries so for now store a pointer to
* the __nis_hash_item_mt.
*/
res[res_count] = (char *)it;
res_count ++;
}
}
/* Terminate array */
res[res_count] = NULL;
/* Bubble sort entries into the same order as mapping file */
for (i = res_count - 2; 0 <= i; i--) {
for (j = 0; j <= i; j++) {
if (((__nis_table_mapping_t *)res[j + 1])->seq_num <
((__nis_table_mapping_t *)res[j])->seq_num) {
end_ptr = res[j];
res[j] = res[j+1];
res[j + 1] = end_ptr;
}
}
}
/* Finally copy the real strings in to each entry */
for (i = 0; NULL != res[i]; i ++) {
/* Get hash table entry back */
it = (__nis_hash_item_mt *)res[i];
end_ptr = it->name + strlen(it->name) - strlen(domain) - 1;
/* What we really need is strndup() */
res[i] = am(myself, end_ptr - it->name + 1);
if (NULL == res[i]) {
free_map_list(res);
return (NULL);
}
/* Copy from start to end_ptr */
memcpy(res[i], it->name, end_ptr-it->name - 1);
}
return (res);
}
/*
* FUNCTION : make_nis_container()
*
* DESCRIPTION: Sets up container for map_name in the DIT.
*
* GIVEN : Map name
* The domain name.
* Flag indicating if container should be created.
*
* RETURNS : SUCCESS = It worked
* FAILURE = There was a problem.
*/
suc_code
make_nis_container(char *map_name, char *domain, bool_t init_containers) {
int i, rc, statP = SUCCESS;
__nis_table_mapping_t *t;
char *dn;
char *myself = "make_nis_container";
if (!map_name || !domain)
return (FAILURE);
if (FALSE == init_containers) {
/*
* If we are not creating containers it is debatable what we
* should do . Maybe we should check for a pre-
* existing container and return failure if it does not exist.
*
* For now we assume the user will not have called us in this
* mode unless they know what they are doing. So return
* success. If they have got it wrong then latter writes will
* fail.
*/
return (SUCCESS);
}
/* Get the mapping information for the map */
if ((t = mappingFromMap(map_name, domain, &statP)) == 0) {
if (statP == MAP_NO_MAPPING_EXISTS)
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: No mapping information available for %s,%s",
myself, NIL(map_name), NIL(domain));
return (FAILURE);
}
/* Two times. One for readDN and other for writeDN */
for (i = 0; i < 2; i++) {
if (i == 0)
dn = t->objectDN->read.base;
else {
if (t->objectDN->write.base == 0) {
logmsg(MSG_NOTIMECHECK, LOG_INFO,
"%s: No baseDN in writespec. Write "
"disabled for %s,%s",
myself, map_name, domain);
break;
}
if (!strcasecmp(dn, t->objectDN->write.base))
break;
dn = t->objectDN->write.base;
}
if ((rc = makeNISObject(0, dn)) == FAILURE) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Unable to create ldap container (dn: %s) "
"for %s,%s", myself, dn, map_name, domain);
return (FAILURE);
}
}
return (SUCCESS);
}
/*
* FUNCTION : make_nis_domain()
*
* DESCRIPTION: Sets up a nisDomainObject in the DIT
*
* GIVEN: Name of the domain
* Flag indicating if domain should be create or possibly just
* checked for.
*/
suc_code
make_nis_domain(char *domain, bool_t init_containers) {
if (FALSE == init_containers) {
/*
* If we are not creating containers it is debatable what we
* should do with domains. Maybe we should check for a pre-
* existing domain and return failure if it does not exist.
*
* For now we assume the user will not have called us in this
* mode unless they know what they are doing. So return
* success. If they have got it wrong then latter writes will
* fail.
*/
return (SUCCESS);
}
/* Create the domain */
return (makeNISObject(domain, 0));
}
/*
* FUNCTION: update_netgroup_byxxx()
*
* DESCRIPTION: Updates the netgroup.byxxx series of maps based on the current
* netgroup file. We invoke revnetgroup so that if any changes
* are made to this utility the same changes are made here.
*
* INPUTS: map_ctrl containing lots of information about the map and a
* pointer to it's lock which will be required.
*
* OUTPUTS: SUCCESS = Map updated
* FAILURE = Map not updated
*/
suc_code
update_netgroup_byxxx(map_ctrl *map) {
/* Name of temporary entries DBM file */
char *temp_entries;
/* Name of temporary TTL DBM file */
char *temp_ttl;
/* Temporary DBM handles */
DBM *temp_entries_db;
DBM *temp_ttl_db;
map_ctrl temp_map;
char *myself = "update_netgroup_byxxx";
char *cmdbuf;
int cmd_length;
datum key;
map_ctrl *netgroupmap;
int res;
/* Temporary revnetgroup files */
const char *byhost = NETGROUP_BYHOST "_REV" TEMP_POSTFIX;
const char *byuser = NETGROUP_BYUSER "_REV" TEMP_POSTFIX;
const char *temp_file_name;
/*
* We need to use two different temporary files: one for netgroup.byhost
* and other for netgroup.byuser, since these two maps can be updated
* simultaneously. These temporary files will hold the output of
* revnetgroup [-h|-u] command. They are then used to generate the
* corresponding dbm files and thereafter deleted.
*/
if (0 == strcmp(map->map_name, NETGROUP_BYHOST))
temp_file_name = byhost;
else
temp_file_name = byuser;
/* Alloc enough cmd buf for revnet cmd */
cmd_length = strlen("/usr/sbin/makedbm -u ") +
(strlen(map->map_path) - strlen(map->map_name)) +
strlen(NETGROUP_MAP) +
strlen(" | /usr/sbin/revnetgroup -h > ") +
(strlen(map->map_path) - strlen(map->map_name)) +
strlen(temp_file_name) + 1;
cmdbuf = am(myself, cmd_length);
if (NULL == cmdbuf) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Could not alloc cmdbuf.", myself);
return (FAILURE);
}
/*
* If necessary update (and wait for) netgroups map. This is a lot of
* work but if the netgroup map itself is not being accessed it may
* contain information that is not up to date with the DIT.
*
* We use the cmdbuf to store the qualified netgroup map name there will
* be enough space for this but we are not yet creating the cmd.
*/
strlcpy(cmdbuf, map->map_path, strlen(map->map_path) -
strlen(map->map_name) + 1);
strcat(cmdbuf, NETGROUP_MAP);
netgroupmap = (map_ctrl *)shim_dbm_open(cmdbuf,
O_RDWR | O_CREAT, 0644);
if (NULL == netgroupmap) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Could not update %s.", myself, cmdbuf);
sfree(cmdbuf);
return (FAILURE);
}
if (has_map_expired(netgroupmap)) {
lock_map_ctrl(netgroupmap);
update_map_if_required(netgroupmap, TRUE);
unlock_map_ctrl(netgroupmap);
}
shim_dbm_close((DBM *)netgroupmap);
/* Dump netgroup file through revnetgroup to a temp file */
strcpy(cmdbuf, "/usr/sbin/makedbm -u ");
/* Unmake the netgroup file in same domain as map */
strncat(cmdbuf, map->map_path, strlen(map->map_path) -
strlen(map->map_name));
strcat(cmdbuf, NETGROUP_MAP);
if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) {
strcat(cmdbuf, " | /usr/sbin/revnetgroup -h > ");
} else {
strcat(cmdbuf, " | /usr/sbin/revnetgroup -u > ");
}
/* Create temp file file in same domain as map */
strncat(cmdbuf, map->map_path, strlen(map->map_path) -
strlen(map->map_name));
strcat(cmdbuf, temp_file_name);
if (0 > system(cmdbuf)) {
logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" "
"(errno=%d)", myself, cmdbuf, errno);
sfree(cmdbuf);
return (FAILURE);
}
sfree(cmdbuf);
/* Allocate and set up names */
if (SUCCESS != alloc_temp_names(map->map_path,
&temp_entries, &temp_ttl)) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Unable to create map names for %s",
myself, map->map_path);
return (FAILURE);
}
/* Make the temporary DBM file */
cmdbuf = am(myself, strlen("/usr/sbin/makedbm") +
(strlen(map->map_path) - strlen(map->map_name)) +
strlen(temp_file_name) +
strlen(temp_entries) + 3);
if (NULL == cmdbuf) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Could not allocate cmdbuf.", myself);
sfree(temp_entries);
sfree(temp_ttl);
return (FAILURE);
}
strcpy(cmdbuf, "/usr/sbin/makedbm ");
strncat(cmdbuf, map->map_path, strlen(map->map_path) -
strlen(map->map_name));
strcat(cmdbuf, temp_file_name);
strcat(cmdbuf, " ");
strcat(cmdbuf, temp_entries);
if (0 > system(cmdbuf)) {
logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" "
"(errno=%d)", myself, cmdbuf, errno);
sfree(cmdbuf);
sfree(temp_entries);
sfree(temp_ttl);
return (FAILURE);
}
/* Already have enough command buffer to rm temporary file */
strlcpy(cmdbuf, map->map_path, strlen(map->map_path) -
strlen(map->map_name) + 1);
strcat(cmdbuf, temp_file_name);
res = unlink(cmdbuf);
/* If the temp file did not exist no problem. Probably had no entries */
if ((0 != res) && (ENOENT != errno)) {
logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not delete \"%s\" "
"(errno=%d)", myself, cmdbuf, errno);
sfree(temp_entries);
sfree(temp_ttl);
sfree(cmdbuf);
return (FAILURE);
}
sfree(cmdbuf);
if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644))
== NULL) {
logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
myself, temp_entries);
sfree(temp_entries);
sfree(temp_ttl);
return (FAILURE);
}
if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644))
== NULL) {
logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
myself, temp_ttl);
dbm_close(temp_entries_db);
sfree(temp_entries);
sfree(temp_ttl);
return (FAILURE);
}
/*
* Set up enough of map_ctrl to call update_entry_ttl. Since there is
* no mapping, and thus not TTL, defined for these maps use the TTL
* values for netgroup map
*/
temp_map.map_name = NETGROUP_MAP;
temp_map.domain = map->domain;
temp_map.ttl = temp_ttl_db;
/*
* Generate new TTL file. Since these maps work only on the whole map
* expiry these will not actually be used but there presence makes it
* easier to handle these maps in the same way as other maps.
*/
key = dbm_firstkey(temp_entries_db);
while (key.dptr != 0) {
if (!is_special_key(&key))
/*
* For these maps want all timouts to be maximum
*/
if (FAILURE == update_entry_ttl(&temp_map, &key,
TTL_MAX))
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s: Could not update TTL for "
"(key=%s) for map %s,%s",
myself, NIL(key.dptr), map->map_name,
map->domain);
key = dbm_nextkey(temp_entries_db);
}
/* Update map TTL */
update_map_ttl(&temp_map);
/* Close temp DBM files */
dbm_close(temp_entries_db);
dbm_close(temp_ttl_db);
/* Lock access to the map for copy */
lock_map_ctrl(map);
/* Move temp maps to real ones */
rename_map(temp_entries, map->map_path, FALSE);
rename_map(temp_ttl, map->ttl_path, FALSE);
/* Free file names */
sfree(temp_entries);
sfree(temp_ttl);
/* Unlock map */
unlock_map_ctrl(map);
return (SUCCESS);
}