dit_access.c revision 4a19049349b8aa3a6f741b8303a0a60e1fa770c9
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* 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"
extern bool delete_map(char *name);
/* Enable standard YP code features defined in ypdefs.h */
/*
* Decs
*/
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.
*/
{
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
{
int count;
int res;
/* Initialize tsd */
__nisdb_get_tsd()->domainContext = 0;
continue;
break;
}
}
/* Loop 'attempts' times of forever if -1 */
/* It worked, return value irrelevant */
return (0);
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)
}
/* Make sure returned pointer is NULL */
/* If we get here access failed work out what to return */
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
*
*/
{
int count;
int res;
/* Initialize tsd */
__nisdb_get_tsd()->domainContext = 0;
continue;
break;
}
}
/* Loop 'attempts' times of forever if -1 */
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_NOT_ALLOWED_ON_RDN:
case MAP_NO_DN:
return (SUCCESS);
default:
break;
}
}
return (FAILURE);
}
return (FAILURE);
/*
* Didn't work. If not the special case where no repeats are
* done sleep.
*/
if (0 != storeRetry->attempts)
}
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
{
char *myself = "get_ttl_value";
/* Get the mapping structure corresponding to `map.domain' */
if (0 == table_map) {
"Get TTL request could not access map %s in domain %s "
return (-1);
}
switch (type) {
case TTL_MAX:
case TTL_MIN:
default:
/* If unknown TTL type drop through to TTL_RAND */
case TTL_RAND:
if (0 >= interval)
/*
* Must get a random value. We assume srand48() got
* called at initialization.
*/
case TTL_RUNNING:
}
}
/*
* 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)
{
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)
{
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)
{
}
/*
* 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
*/
char **
{
char *myself = "get_passwd_list";
int i, size;
char *end_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.
*/
return (NULL);
/* Set up target */
if (adjunct)
else
/* Work out hash table length */
/* For all hash table entries */
for (i = 0; i < size; i++) {
/* Walk linked list for this hash table entry */
/* Check right map */
continue;
continue;
/* Check right domain (minus trailing dot) */
continue;
continue;
continue;
/* Check if we need to enlarge array */
sizeof (char *));
return (NULL);
}
}
/* What we really need is strndup() */
return (NULL);
}
/* Copy from start to end_ptr */
res_count ++;
}
}
/* Terminate array */
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)
{
}
/*
* 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 */
/* Free the array */
}
/*
* 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
*/
{
char local_host[MAX_MASTER_NAME];
int res;
/* Last modified time is now */
/* Add domain name */
/* For N2L mode local machine is always the master */
/* Get the mapping structure corresponding to `map.domain' */
if (0 == table_map)
return (FAILURE);
/* Add secure and interdomain flags if required */
if (table_map->securemap_flag) {
*secure_flag = TRUE;
} else {
*secure_flag = FALSE;
}
if (table_map->usedns_flag)
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
*/
/* Name of temporary entries DBM file */
char *temp_entries;
/* Name of temporary TTL DBM file */
char *temp_ttl;
/* Temporary DBM handles */
char *myself = "update_map_from_dit";
int entry_count = 1;
int next_print = PRINT_FREQ;
return (FAILURE);
}
/*
* netgroup.byxxx maps are a special case. They are regenerated from
* the netgroup map, not the DIT, so handle special case.
*/
return (update_netgroup_byxxx(map));
}
/* Get the mapping information for the map */
if (statP == MAP_NO_MAPPING_EXISTS)
"%s: No mapping information available for %s,%s",
return (FAILURE);
}
/* Allocate and set up names */
&temp_entries, &temp_ttl)) {
"%s: Unable to create map names for %s",
return (FAILURE);
}
/* Create temp entry and TTL file */
== NULL) {
return (FAILURE);
}
== NULL) {
return (FAILURE);
}
/* Initialize domainContext tsd */
__nisdb_get_tsd()->domainContext = 0;
for (i = 0; i < ypDomains.numDomains; i++) {
if (0 == ypDomains.domainLabels[i])
continue;
break;
}
}
if (temp_entries_db)
if (temp_ttl_db)
return (FAILURE);
}
/* Try each mapping for the map */
/* Check if the mapping is the correct one */
continue;
}
/* Check if rulesFromLDAP are provided */
if (t->numRulesFromLDAP == 0) {
"%s: No rulesFromLDAP available for %s (%s)",
continue;
}
/* Set flag to indicate update is enabled */
flag = 1;
/* Create ldap request for enumeration */
objectDN)) == 0) {
"%s: Failed to create "
"ldapSearch request for "
"%s (%s) for base %s",
break;
}
if (log_flag) {
printf("Waiting for LDAP search results.\n");
}
/* Query LDAP */
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;
}
"%s: ldapSearch error %d "
"(%s) for %s (%s) for base %s",
break;
}
if (log_flag) {
printf("Processing search results.\n");
}
/* Obtain list of DNs for logging */
break;
}
/* For each entry in the result do the following */
for (i = 0; i < nr; i++) {
/* Convert LDAP data to NIS equivalents */
if (statP == MAP_INDEXLIST_ERROR)
continue;
"%s: Conversion error %d (LDAP to "
"name=value pairs) "
"for (dn: %s) for "
"%s (%s) for base %s",
continue;
}
/* Obtain the datum for value */
if (datval == 0) {
"%s: Conversion error %d "
"(name=value pairs to NIS)"
" for (dn: %s) for "
"%s (%s) for base %s",
continue;
}
/* Obtain the datum for key */
if (datkey == 0) {
"%s: Unable to obtain NIS "
"key from LDAP data (dn:%s) "
"for %s (%s) for base %s",
continue;
}
/* Write to the temporary map */
for (j = 0; j < nv; j++, entry_count ++) {
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.
*/
"%s: dbm store error "
"(errno=%d) "
"for (key=%s, value=%s) "
"for %s (%s) for base %s",
/* clear the error */
}
if (log_flag && (entry_count >=
next_print)) {
printf("%d entries processed\n",
next_print *= 2;
}
}
}
} /* End of for over objectDN */
}
if (t != 0 || flag == 0) {
if (temp_entries_db)
if (temp_ttl_db)
return (statP);
}
/* Set up enough of map_ctrl to call update_entry_ttl */
/* Generate new TTL file */
if (!is_special_key(&key))
/*
* We don't want all the entries to time out at the
* same time so create random TTLs.
*/
TTL_RAND))
"%s: Could not update TTL for "
"(key=%s) for map %s,%s",
}
/* Update map TTL */
}
/* Set up 'special' nis entries */
/* Close temp DBM files */
/* Lock access to the map for copy */
/* Move temp maps to real ones */
/* Free file names */
/* Unlock 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";
int i, j, size;
char *end_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.
*/
return (NULL);
/* Work out hash table length */
/* For all hash table entries */
for (i = 0; i < size; i++) {
/* Walk linked list for this hash table entry */
/* Check it's not a split field entry */
continue;
/* Check right domain (minus trailing dot) */
continue;
continue;
continue;
/* Check if we need to enlarge array */
sizeof (char *));
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_count ++;
}
}
/* Terminate array */
/* Bubble sort entries into the same order as mapping file */
for (i = res_count - 2; 0 <= i; i--) {
for (j = 0; j <= i; j++) {
}
}
}
/* Finally copy the real strings in to each entry */
/* Get hash table entry back */
/* What we really need is strndup() */
return (NULL);
}
/* Copy from start to end_ptr */
}
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.
*/
char *dn;
char *myself = "make_nis_container";
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 (statP == MAP_NO_MAPPING_EXISTS)
"%s: No mapping information available for %s,%s",
return (FAILURE);
}
/* Two times. One for readDN and other for writeDN */
for (i = 0; i < 2; i++) {
if (i == 0)
else {
"%s: No baseDN in writespec. Write "
"disabled for %s,%s",
break;
}
break;
}
"%s: Unable to create ldap container (dn: %s) "
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.
*/
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
*/
/* Name of temporary entries DBM file */
char *temp_entries;
/* Name of temporary TTL DBM file */
char *temp_ttl;
/* Temporary DBM handles */
char *myself = "update_netgroup_byxxx";
char *cmdbuf;
int cmd_length;
int res;
/* Temporary revnetgroup files */
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.
*/
else
/* Alloc enough cmd buf for revnet cmd */
strlen(" | /usr/sbin/revnetgroup -h > ") +
"%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.
*/
if (NULL == netgroupmap) {
return (FAILURE);
}
if (has_map_expired(netgroupmap)) {
}
/* Dump netgroup file through revnetgroup to a temp file */
/* Unmake the netgroup file in same domain as map */
} else {
}
/* Create temp file file in same domain as map */
return (FAILURE);
}
/* Allocate and set up names */
&temp_entries, &temp_ttl)) {
"%s: Unable to create map names for %s",
return (FAILURE);
}
/* Make the temporary DBM file */
"%s: Could not allocate cmdbuf.", myself);
return (FAILURE);
}
return (FAILURE);
}
/* Already have enough command buffer to rm temporary file */
/* If the temp file did not exist no problem. Probably had no entries */
return (FAILURE);
}
== NULL) {
return (FAILURE);
}
== NULL) {
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
*/
/*
* 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.
*/
if (!is_special_key(&key))
/*
* For these maps want all timouts to be maximum
*/
TTL_MAX))
"%s: Could not update TTL for "
"(key=%s) for map %s,%s",
}
/* Update map TTL */
/* Close temp DBM files */
/* Lock access to the map for copy */
/* Move temp maps to real ones */
/* Free file names */
/* Unlock map */
return (SUCCESS);
}