xref: /illumos-gate/usr/src/lib/libnisdb/yptol/dit_access.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * DESCRIPTION: Contains top level functions to read/write to the DIT. These
27  *		are the API between the shim and the mapping system.
28  *		Things calling these should have no knowledge of LDAP. Things
29  *		called by them should have no knowledge of NIS.
30  *
31  *		Error handling here may appear to be limited but, because the
32  *		NIS protocol cannot carry meaningful information about why a
33  *		N2L operation failed, functions that don't work log
34  *		an error and then just return FAILURE.
35  *
36  */
37 
38 /*
39  * Includes. WE WANT TO USE REAL DBM FUNCTIONS SO DO NOT INCLUDE SHIM_HOOKS.H.
40  */
41 #include <unistd.h>
42 #include <syslog.h>
43 #include <ndbm.h>
44 #include <sys/systeminfo.h>
45 #include <string.h>
46 #include <lber.h>
47 #include <ldap.h>
48 #include <errno.h>
49 #include "ypsym.h"
50 #include "ypdefs.h"
51 #include "shim.h"
52 #include "../ldap_structs.h"
53 #include "../ldap_parse.h"
54 #include "../nisdb_ldap.h"
55 #include "../ldap_util.h"
56 #include "../ldap_op.h"
57 #include "../ldap_attr.h"
58 #include "../nis_parse_ldap_conf.h"
59 #include "../nisdb_mt.h"
60 #include "yptol.h"
61 #include "dit_access_utils.h"
62 #include "stdio.h"
63 
64 extern bool delete_map(char *name);
65 extern bool rename_map(char *from, char *to, bool_t secure_map);
66 
67 /* Enable standard YP code features defined in ypdefs.h */
68 USE_YP_MASTER_NAME
69 USE_YP_DOMAIN_NAME
70 USE_YP_SECURE
71 USE_YP_INTERDOMAIN
72 
73 /*
74  * Decs
75  */
76 suc_code add_special_entries(DBM *, map_ctrl *, bool_t *);
77 void free_null_terminated_list(char **list);
78 
79 
80 /*
81  * FUNCTION:    is_yptol_mode();
82  *
83  * DESCRIPTION:	Determines if we should run in N2L or traditional mode based
84  *		on the presence of the N2L mapping file. If there are problems
85  *		with the file, e.g. unreadable, this will be picked up latter.
86  *
87  * INPUTS:     	Nothing
88  *
89  * OUTPUTS:   	TRUE = Run in N2L mode
90  *		FALSE = Run in traditional mode.
91  */
92 bool_t
93 is_yptol_mode()
94 {
95 	struct stat filestat;
96 
97 	if (stat(YP_DEFAULTCONFFILE, &filestat) != -1)
98 		return (TRUE);
99 
100 	return (FALSE);
101 }
102 
103 /*
104  * FUNCTION:    read_from_dit();
105  *
106  * DESCRIPTION:	Read (i.e. get and map) a single NIS entry from the LDAP DIT.
107  *		Also handles retry attempts, on failure, and interpretation of
108  *		internal error codes.
109  *
110  * INPUTS:     	Map name (unqualified)
111  *		Domain name
112  *		Entry key
113  *		Pointer to return location
114  *
115  * OUTPUTS:   	If successful DBM datum containing result.
116  *		On error DBM datum pointing to NULL and, if the cached value
117  *		is not to be used, an error code.
118  */
119 int
120 read_from_dit(char *map, char *domain, datum *key, datum *value)
121 {
122 	int count;
123 	int res;
124 	__nisdb_retry_t	*retrieveRetry;
125 
126 	/* Initialize tsd */
127 	__nisdb_get_tsd()->domainContext = 0;
128 	__nisdb_get_tsd()->escapeFlag = '\0';
129 
130 	for (count = 0; count < ypDomains.numDomains; count++) {
131 		if (0 == ypDomains.domainLabels[count])
132 			continue;
133 		if (0 == strcasecmp(domain, ypDomains.domainLabels[count])) {
134 			__nisdb_get_tsd()->domainContext =
135 			    ypDomains.domains[count];
136 			break;
137 		}
138 	}
139 
140 	retrieveRetry = &ldapDBTableMapping.retrieveErrorRetry;
141 
142 	/* Loop 'attempts' times of forever if -1 */
143 	for (count = retrieveRetry->attempts; (0 <= count) ||
144 	    (-1 == retrieveRetry->attempts); count --) {
145 		if (TRUE == singleReadFromDIT(map, domain, key, value, &res))
146 			/* It worked, return value irrelevant */
147 			return (0);
148 
149 		if (LDAP_TIMEOUT == res) { /* Exceeded search timeout */
150 			value->dptr = NULL;
151 			return (0);
152 		}
153 
154 		if (is_fatal_error(res))
155 			break;
156 
157 		/*
158 		 * Didn't work. If not the special case where no repeats are
159 		 * done sleep.
160 		 */
161 		if (0 != retrieveRetry->attempts)
162 			(void) poll(NULL, 0, retrieveRetry->timeout*1000);
163 	}
164 
165 	/* Make sure returned pointer is NULL */
166 	value->dptr = NULL;
167 
168 	/* If we get here access failed work out what to return */
169 	if (ldapDBTableMapping.retrieveError == use_cached)
170 		return (0);
171 
172 	return (res);
173 }
174 
175 /*
176  * FUNCTION:    write_to_dit();
177  *
178  * DESCRIPTION:	Maps and writes a NIS entry to the LDAP DIT.
179  *		Also handles retry attempts, on failure, and interpretation of
180  *		internal error codes.
181  *
182  * INPUTS:     	Pointer to (unqualified) map name
183  *		Pointer to domain name
184  *		The entries key
185  *		What to write
186  *		Replace flag indicating
187  *			TRUE = Replace (overwrite) any existing entries
188  *			FALSE = Return error if there are existing entries
189  *		Flag indicating if we should tolerate mapping errors.
190  *
191  * OUTPUTS:   	SUCCESS = Write was successful
192  *		FAILURE = Write failed
193  *
194  */
195 suc_code
196 write_to_dit(char *map, char *domain, datum key, datum value,
197 					bool_t replace, bool_t ignore_map_errs)
198 {
199 	int count;
200 	int res;
201 	__nisdb_retry_t	*storeRetry = &ldapDBTableMapping.storeErrorRetry;
202 
203 	/* Initialize tsd */
204 	__nisdb_get_tsd()->domainContext = 0;
205 	__nisdb_get_tsd()->escapeFlag = '\0';
206 
207 	for (count = 0; count < ypDomains.numDomains; count++) {
208 		if (0 == ypDomains.domainLabels[count])
209 			continue;
210 		if (0 == strcasecmp(domain, ypDomains.domainLabels[count])) {
211 			__nisdb_get_tsd()->domainContext =
212 			    ypDomains.domains[count];
213 			break;
214 		}
215 	}
216 
217 	storeRetry = &ldapDBTableMapping.storeErrorRetry;
218 
219 	/* Loop 'attempts' times of forever if -1 */
220 	for (count = storeRetry->attempts; (0 <= count) ||
221 	    (-1 == storeRetry->attempts); count --) {
222 		res = singleWriteToDIT(map, domain, &key, &value, replace);
223 		if (LDAP_SUCCESS == res)
224 			return (SUCCESS);
225 
226 		if (is_fatal_error(res)) {
227 			/*
228 			 * The mapping failed and will fail again if it is
229 			 * retried. However there are some cases where an
230 			 * actual mapping fault (rather than a LDAP problem)
231 			 * may be ignored.
232 			 */
233 			if (ignore_map_errs) {
234 				switch (res) {
235 					case LDAP_INVALID_DN_SYNTAX:
236 					case LDAP_OBJECT_CLASS_VIOLATION:
237 					case LDAP_NOT_ALLOWED_ON_RDN:
238 					case MAP_NAMEFIELD_MATCH_ERROR:
239 					case MAP_NO_DN:
240 						return (SUCCESS);
241 					default:
242 						break;
243 				}
244 			}
245 			return (FAILURE);
246 		}
247 
248 		if (ldapDBTableMapping.storeError != sto_retry)
249 			return (FAILURE);
250 
251 		/*
252 		 * Didn't work. If not the special case where no repeats are
253 		 * done sleep.
254 		 */
255 		if (0 != storeRetry->attempts)
256 			(void) poll(NULL, 0, storeRetry->timeout*1000);
257 
258 	}
259 	return (FAILURE);
260 }
261 
262 /*
263  * FUNCTION :	get_ttl_value()
264  *
265  * DESCRIPTION:	Get the TTL value, derived from mapping file or DIT, for a
266  *		entry.
267  *
268  * GIVEN :	Pointer to map
269  *		A flag indication if TTL should be max, min or random
270  *
271  * RETURNS :	TTL value in seconds.
272  *		-1 on failure
273  */
274 int
275 get_ttl_value(map_ctrl *map, TTL_TYPE type)
276 {
277 	__nis_table_mapping_t *table_map;
278 	int interval, res;
279 	char *myself = "get_ttl_value";
280 
281 	/*  Get the mapping structure corresponding to `map.domain' */
282 	table_map = mappingFromMap(map->map_name, map->domain, &res);
283 
284 	if (0 == table_map) {
285 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
286 		    "Get TTL request could not access map %s in domain %s "
287 		    "(error %d)", map->map_name, map->domain, res);
288 		return (-1);
289 	}
290 
291 	switch (type) {
292 		case TTL_MAX:
293 			return (table_map->initTtlHi);
294 
295 		case TTL_MIN:
296 			return (table_map->initTtlLo);
297 
298 		default:
299 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
300 			"%s passed illegal TTL type (%d)", myself, type);
301 			/* If unknown TTL type drop through to TTL_RAND */
302 			/* FALLTHROUGH */
303 
304 		case TTL_RAND:
305 			interval = table_map->initTtlHi - table_map->initTtlLo;
306 			if (0 >= interval)
307 				return (table_map->initTtlLo);
308 
309 			/*
310 			 * Must get a random value. We assume srand48() got
311 			 * called at initialization.
312 			 */
313 			return (lrand48() % interval);
314 
315 		case TTL_RUNNING:
316 			return (table_map->ttl);
317 
318 
319 	}
320 }
321 
322 /*
323  * FUNCTION :	get_mapping_domain_list()
324  *
325  * DESCRIPTION:	Gets a list of domain names specified, by nisLDAPdomainContext
326  *		attributes, in the mapping file. This is used only for initial
327  *		DIT setup. Once the DIT has been set up get_domain_list() is
328  *		used instead.
329  *
330  * GIVEN :	Pointer returned array.
331  *
332  * RETURNS :	Number of element in returned array.
333  *		Array of elements this is in static memory
334  *		and must not be freed by the caller.
335  */
336 int
337 get_mapping_domain_list(char ***ptr)
338 {
339 	*ptr = ypDomains.domainLabels;
340 	return (ypDomains.numDomains);
341 }
342 
343 /*
344  * FUNCTION :	get_mapping_yppasswdd_domain_list()
345  *
346  * DESCRIPTION:	Gets a list of domain names specified, by the
347  *		nisLDAPyppasswddDomains attribute, in the mapping file. This
348  *		is the list of domains for which passwords should be changed.
349  *
350  * GIVEN :	Pointer returned array
351  *
352  * RETURNS :	Number of element in returned array.
353  *		0 if no nisLDAPyppasswddDomains attribute is present.
354  *		Array of elements this is in static memory
355  *		and must not be freed by the caller.
356  */
357 int
358 get_mapping_yppasswdd_domain_list(char ***ptr)
359 {
360 	*ptr = ypDomains.yppasswddDomainLabels;
361 	return (ypDomains.numYppasswdd);
362 }
363 
364 /*
365  * FUNCTION :	free_map_list()
366  *
367  * DESCRIPTION:	Frees a map list.
368  *
369  * GIVEN :	Pointer to the map list.
370  *
371  * RETURNS :	Nothing
372  */
373 void
374 free_map_list(char **map_list)
375 {
376 	free_null_terminated_list(map_list);
377 }
378 
379 /*
380  * FUNCTION :	get_passwd_list()
381  *
382  * DESCRIPTION:	Gets a list of either passwd or passwd.adjunct map files
383  *		defined in the mapping file. These are the files which have
384  *		'magic' nisLDAPdatabaseIdMapping entries aliasing them to
385  *		passwd or passwd.adjunct. This function is required so that
386  *		yppasswdd can work out which maps to synchronize with any
387  *		password changes.
388  *
389  *		This information is not currently stored by the parser but
390  *		we can recover it from the hash table. This makes hard work but
391  *		passwords should not be changed very frequently
392  *
393  * GIVEN :	Flag indicating if a list is required for passwd or
394  *		passwd.adjunct
395  *		Domain to return the list for.
396  *
397  * RETURNS :	Null terminated list of map names in malloced memory. To be
398  *		freed by caller. (Possibly empty if no passwd maps found)
399  *		NULL on error
400  */
401 char **
402 get_passwd_list(bool_t adjunct, char *domain)
403 {
404 	char *myself = "get_passwd_list";
405 	__nis_hash_item_mt *it;
406 	int	i, size;
407 	char 	*end_ptr;
408 	char	*target;	/* What we are looking for */
409 	int	target_len;
410 	int	domain_len;
411 	char	**res;		/* Result array */
412 	char	**res_old;	/* Old value of res during realloc */
413 	int	array_size;	/* Current malloced size */
414 	int	res_count = 0;	/* Current result count */
415 
416 	/*
417 	 * Always need an array even if just for terminator. Normally one
418 	 * chunk will be enough.
419 	 */
420 	res = am(myself, ARRAY_CHUNK * sizeof (char *));
421 	if (NULL == res)
422 		return (NULL);
423 	array_size = ARRAY_CHUNK;
424 
425 	/* Set up target */
426 	if (adjunct)
427 		target = PASSWD_ADJUNCT_PREFIX;
428 	else
429 		target = PASSWD_PREFIX;
430 	target_len = strlen(target);
431 	domain_len = strlen(domain);
432 
433 	/* Work out hash table length */
434 	size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]);
435 	/* For all hash table entries */
436 	for (i = 0; i < size; i++) {
437 		/* Walk linked list for this hash table entry */
438 		for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) {
439 			/* Check right map */
440 			if ((target_len + domain_len + 1) > strlen(it->name))
441 				continue;
442 			if (0 != strncmp(it->name, target, target_len))
443 				continue;
444 
445 			/* Check right domain (minus trailing dot) */
446 			if (strlen(domain) >= strlen(it->name))
447 				continue;
448 			end_ptr = it->name + strlen(it->name) -
449 			    strlen(domain) - 1;
450 			if (',' != *(end_ptr - 1))
451 				continue;
452 			if (0 != strncmp(end_ptr, domain, strlen(domain)))
453 				continue;
454 
455 			/* Check if we need to enlarge array */
456 			if ((res_count + 1) >= array_size) {
457 				array_size += ARRAY_CHUNK;
458 				res_old = res;
459 				res = realloc(res, array_size *
460 				    sizeof (char *));
461 				if (NULL == res) {
462 					res_old[res_count] = NULL;
463 					free_passwd_list(res_old);
464 					return (NULL);
465 				}
466 			}
467 
468 			/* What we really need is strndup() */
469 			res[res_count] = am(myself, end_ptr - it->name + 1);
470 			if (NULL == res[res_count]) {
471 				free_passwd_list(res);
472 				return (NULL);
473 			}
474 
475 			/* Copy from start to end_ptr */
476 			(void) memcpy(res[res_count], it->name,
477 			    end_ptr-it->name - 1);
478 			res_count ++;
479 		}
480 	}
481 
482 	/* Terminate array */
483 	res[res_count] = NULL;
484 	return (res);
485 }
486 
487 /*
488  * FUNCTION :	free_passwd_list()
489  *
490  * DESCRIPTION:	Frees a password list obtained with get_passwd_list()
491  *
492  * INPUTS :	Address of list to free.
493  *
494  * OUTPUTS :	Nothing
495  */
496 void
497 free_passwd_list(char **list)
498 {
499 	free_null_terminated_list(list);
500 }
501 
502 /*
503  * FUNCTION :	free_null_terminated_list()
504  *
505  * DESCRIPTION:	Frees a generic null terminated list.
506  *
507  * INPUTS :	Address of list to free.
508  *
509  * OUTPUTS :	Nothing
510  */
511 void
512 free_null_terminated_list(char **list)
513 {
514 	int index;
515 
516 	/* Free all the strings */
517 	for (index = 0; NULL != list[index]; index ++)
518 		sfree(list[index]);
519 
520 	/* Free the array */
521 	sfree(list);
522 }
523 
524 
525 /*
526  * FUNCTION :	add_special_entries()
527  *
528  * DESCRIPTION:	Adds the special (YP_*) entries to a map.
529  *
530  *		Part of dit_access because requires access to the mapping
531  *		file in order to work out if secure and interdomain entries
532  *		should be created.
533  *
534  * GIVEN :	Pointer to an open, temporary, DBM file
535  *		Pointer to map information (do not use DBM fields).
536  *		Pointer to a location in which to return security flag
537  *
538  * RETURNS :	SUCCESS = All entries created
539  *		FAILURE = Some entries not created
540  */
541 suc_code
542 add_special_entries(DBM *db, map_ctrl *map, bool_t *secure_flag)
543 {
544 	char local_host[MAX_MASTER_NAME];
545 	__nis_table_mapping_t *table_map;
546 	int res;
547 
548 	/* Last modified time is now */
549 	update_timestamp(db);
550 
551 	/* Add domain name */
552 	addpair(db, yp_domain_name, map->domain);
553 
554 	/* For N2L mode local machine is always the master */
555 	sysinfo(SI_HOSTNAME, local_host, sizeof (local_host));
556 	addpair(db, yp_master_name, local_host);
557 
558 	/*  Get the mapping structure corresponding to `map.domain' */
559 	table_map = mappingFromMap(map->map_name, map->domain, &res);
560 	if (0 == table_map)
561 		return (FAILURE);
562 
563 	/* Add secure and interdomain flags if required */
564 	if (table_map->securemap_flag) {
565 		addpair(db, yp_secure, "");
566 		*secure_flag = TRUE;
567 	} else {
568 		*secure_flag = FALSE;
569 	}
570 	if (table_map->usedns_flag)
571 		addpair(db, yp_interdomain, "");
572 
573 	return (SUCCESS);
574 }
575 
576 /*
577  * FUNCTION:	update_map_from_dit()
578  *
579  * DESCRIPTION:	Core code called to update an entire map.
580  *		Information is recovered from LDAP and used to build a duplicate
581  *		copy of the live maps. When this is complete the maps are
582  *		locked and then overwritten by the new copy.
583  *
584  * INPUTS:	map_ctrl containing lots of information about the map and a
585  *		pointer to it's lock which will be required.
586  *		Flag indicating if progress logging is required.
587  *
588  * OUTPUTS:	SUCCESS = Map updated
589  *		FAILURE = Map not updated
590  */
591 suc_code
592 update_map_from_dit(map_ctrl *map, bool_t log_flag) {
593 	__nis_table_mapping_t	*t;
594 	__nis_rule_value_t	*rv;
595 	__nis_ldap_search_t	*ls;
596 	__nis_object_dn_t	*objectDN = NULL;
597 	datum			*datval, *datkey;
598 	int			nr = 0, i, j, nv, numDNs;
599 	int			statP = SUCCESS, flag;
600 	char			*objname, **dn;
601 	/* Name of temporary entries DBM file */
602 	char			*temp_entries;
603 	/* Name of temporary TTL DBM file */
604 	char			*temp_ttl;
605 	/* Temporary DBM handles */
606 	DBM			*temp_entries_db;
607 	DBM			*temp_ttl_db;
608 	map_ctrl		temp_map;
609 	datum			key;
610 	char			*myself = "update_map_from_dit";
611 	bool_t			secure_flag;
612 	int			entry_count = 1;
613 	int			next_print = PRINT_FREQ;
614 	int			search_flag = SUCCESS;
615 
616 	int			m;
617 
618 	/* list of maps whose keys will be transliterated to lowercase */
619 	char			*xlate_to_lcase_maps[] = {
620 		"hosts.byname",
621 		"ipnodes.byname",
622 		NULL
623 	};
624 	bool_t			xlate_to_lcase = FALSE;
625 
626 	if (!map || !map->map_name || !map->domain) {
627 		return (FAILURE);
628 	}
629 
630 	__nisdb_get_tsd()->escapeFlag = '\0';
631 
632 	/*
633 	 * netgroup.byxxx maps are a special case. They are regenerated from
634 	 * the netgroup map, not the DIT, so handle special case.
635 	 */
636 	if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) ||
637 		0 == (strcmp(map->map_name,  NETGROUP_BYUSER))) {
638 		return (update_netgroup_byxxx(map));
639 	}
640 
641 	/* Get the mapping information for the map */
642 	if ((t = mappingFromMap(map->map_name, map->domain, &statP)) == 0) {
643 		if (statP == MAP_NO_MAPPING_EXISTS)
644 			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
645 			"%s: No mapping information available for %s,%s",
646 				myself, map->map_name, map->domain);
647 		return (FAILURE);
648 	}
649 
650 	/* Allocate and set up names */
651 	if (SUCCESS != alloc_temp_names(map->map_path,
652 				&temp_entries, &temp_ttl)) {
653 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
654 			"%s: Unable to create map names for %s",
655 			myself, map->map_path);
656 		return (FAILURE);
657 	}
658 
659 	/* Create temp entry and TTL file */
660 	if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644))
661 						== NULL) {
662 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
663 						myself, temp_entries);
664 		sfree(temp_entries);
665 		sfree(temp_ttl);
666 		return (FAILURE);
667 	}
668 
669 	if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644))
670 						== NULL) {
671 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
672 						myself, temp_ttl);
673 		dbm_close(temp_entries_db);
674 		delete_map(temp_entries);
675 		sfree(temp_entries);
676 		sfree(temp_ttl);
677 		return (FAILURE);
678 	}
679 
680 	/* Initialize domainContext tsd */
681 	__nisdb_get_tsd()->domainContext = 0;
682 	for (i = 0; i < ypDomains.numDomains; i++) {
683 		if (0 == ypDomains.domainLabels[i])
684 			continue;
685 		if (0 == strcasecmp(map->domain, ypDomains.domainLabels[i])) {
686 			__nisdb_get_tsd()->domainContext = ypDomains.domains[i];
687 			break;
688 		}
689 	}
690 
691 	if (!(objname = getFullMapName(map->map_name, map->domain))) {
692 		if (temp_entries_db)
693 			dbm_close(temp_entries_db);
694 		if (temp_ttl_db)
695 			dbm_close(temp_ttl_db);
696 		delete_map(temp_entries);
697 		sfree(temp_entries);
698 		delete_map(temp_ttl);
699 		sfree(temp_ttl);
700 		return (FAILURE);
701 	}
702 
703 	/*
704 	 * set xlate_to_lcase to TRUE if map_name is found in
705 	 * xlate_to_lcase_maps[]
706 	 */
707 	m = 0;
708 	while (xlate_to_lcase_maps[m] != NULL) {
709 		if (strncmp(map->map_name, xlate_to_lcase_maps[m],
710 			strlen(xlate_to_lcase_maps[m])) == 0) {
711 			xlate_to_lcase = TRUE;
712 			break;
713 		}
714 		++m;
715 	}
716 
717 	/* Try each mapping for the map */
718 	for (flag = 0; t != 0 && search_flag != FAILURE; t = t->next) {
719 
720 		/* Check if the mapping is the correct one */
721 		if (strcmp(objname, t->objName) != 0) {
722 			continue;
723 		}
724 
725 		/* Check if rulesFromLDAP are provided */
726 		if (t->numRulesFromLDAP == 0) {
727 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
728 				"%s: No rulesFromLDAP available for %s (%s)",
729 				myself, t->dbId, map->map_name);
730 			continue;
731 		}
732 
733 		/* Set flag to indicate update is enabled */
734 		flag = 1;
735 		/* Create ldap request for enumeration */
736 		for (objectDN = t->objectDN;
737 				objectDN && objectDN->read.base;
738 				objectDN = objectDN->next) {
739 			if ((ls = createLdapRequest(t, 0, 0, 1, NULL,
740 						objectDN)) == 0) {
741 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
742 					"%s: Failed to create "
743 					"ldapSearch request for "
744 					"%s (%s) for base %s",
745 					myself, t->dbId,
746 					map->map_name,
747 					objectDN->read.base);
748 				statP = FAILURE;
749 				search_flag = FAILURE;
750 				break;
751 			}
752 
753 			if (log_flag) {
754 				printf("Waiting for LDAP search results.\n");
755 			}
756 
757 			/* Query LDAP */
758 			nr = (ls->isDN)?0:-1;
759 			rv = ldapSearch(ls, &nr, 0, &statP);
760 			freeLdapSearch(ls);
761 			if (rv == 0) {
762 				if (statP == LDAP_NO_SUCH_OBJECT) {
763 				/*
764 				 * No Entry exists in the ldap server. Not
765 				 * a problem. Maybe there are just no entries
766 				 * in this map.
767 				 */
768 					continue;
769 				}
770 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
771 					"%s: ldapSearch error %d "
772 					"(%s) for %s (%s) for base %s",
773 					myself, statP, ldap_err2string(statP),
774 					t->dbId, map->map_name,
775 					objectDN->read.base);
776 				statP = FAILURE;
777 				search_flag = FAILURE;
778 				break;
779 			}
780 
781 			if (log_flag) {
782 				printf("Processing search results.\n");
783 			}
784 
785 			/* Obtain list of DNs for logging */
786 			if ((dn = findDNs(myself, rv, nr, 0, &numDNs)) == 0) {
787 				statP = FAILURE;
788 				search_flag = FAILURE;
789 				break;
790 			}
791 
792 			/* For each entry in the result  do the following */
793 			for (i = 0; i < nr; i++) {
794 			/* Convert LDAP data to NIS equivalents */
795 				statP = buildNISRuleValue(t, &rv[i],
796 						map->domain);
797 				if (statP == MAP_INDEXLIST_ERROR)
798 					continue;
799 				if (statP != SUCCESS) {
800 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
801 					    "%s: Conversion error %d (LDAP to "
802 					    "name=value pairs) "
803 					    "for (dn: %s) for "
804 					    "%s (%s) for base %s",
805 					    myself, statP, NIL(dn[i]),
806 					    t->dbId, map->map_name,
807 					    objectDN->read.base);
808 					continue;
809 				}
810 
811 				/* Obtain the datum for value */
812 				datval = ruleValueToDatum(t, &rv[i], &statP);
813 				if (datval == 0) {
814 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
815 						"%s: Conversion error %d "
816 						"(name=value pairs to NIS)"
817 						" for (dn: %s) for "
818 						"%s (%s) for base %s",
819 						myself, statP, NIL(dn[i]),
820 						t->dbId, map->map_name,
821 						objectDN->read.base);
822 					continue;
823 				}
824 
825 				/* Obtain the datum for key */
826 				datkey = getKeyFromRuleValue(t, &rv[i],
827 				    &nv, &statP, xlate_to_lcase);
828 				if (datkey == 0) {
829 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
830 						"%s: Unable to obtain NIS "
831 						"key from LDAP data (dn:%s) "
832 						"for %s (%s) for base %s",
833 						myself, NIL(dn[i]), t->dbId,
834 						map->map_name,
835 						objectDN->read.base);
836 					sfree(datval->dptr);
837 					sfree(datval);
838 					continue;
839 				}
840 
841 				/* Write to the temporary map */
842 				for (j = 0; j < nv; j++, entry_count ++) {
843 					if (datkey[j].dsize == 0)
844 						continue;
845 					errno = 0;
846 					/* DBM_INSERT to match */
847 					/* singleReadFromDIT */
848 					if (dbm_store(temp_entries_db,
849 						datkey[j],
850 						*datval,
851 						DBM_INSERT) < 0) {
852 						/*
853 						 * For some cases errno may
854 						 * still be 0 but dbm_error
855 						 * isn't informative at all.
856 						 */
857 						logmsg(MSG_NOTIMECHECK,
858 						    LOG_WARNING,
859 						    "%s: dbm store error "
860 						    "(errno=%d) "
861 						    "for (key=%s, value=%s) "
862 						    "for %s (%s) for base %s",
863 						    myself,
864 						    errno,
865 						    datkey[j].dptr,
866 						    datval->dptr, t->dbId,
867 						    map->map_name,
868 						    objectDN->read.base);
869 						/* clear the error */
870 						dbm_clearerr(temp_entries_db);
871 					}
872 					sfree(datkey[j].dptr);
873 
874 					if (log_flag && (entry_count >=
875 							next_print)) {
876 						printf("%d entries processed\n",
877 							entry_count);
878 						next_print *= 2;
879 					}
880 
881 				}
882 				sfree(datkey);
883 				sfree(datval->dptr);
884 				sfree(datval);
885 			}
886 
887 			freeRuleValue(rv, nr);
888 			freeDNs(dn, numDNs);
889 		} /* End of for over objectDN */
890 	}
891 	sfree(objname);
892 
893 	if (t != 0 || flag == 0 || search_flag == FAILURE) {
894 		if (temp_entries_db)
895 			dbm_close(temp_entries_db);
896 		if (temp_ttl_db)
897 			dbm_close(temp_ttl_db);
898 		delete_map(temp_entries);
899 		sfree(temp_entries);
900 		delete_map(temp_ttl);
901 		sfree(temp_ttl);
902 		return (statP);
903 	}
904 	/* Set up enough of map_ctrl to call update_entry_ttl */
905 	temp_map.map_name = map->map_name;
906 	temp_map.domain = map->domain;
907 	temp_map.ttl = temp_ttl_db;
908 
909 	/* Generate new TTL file */
910 	key = dbm_firstkey(temp_entries_db);
911 	while (key.dptr != 0) {
912 		if (!is_special_key(&key))
913 			/*
914 			 * We don't want all the entries to time out at the
915 			 * same time so create random TTLs.
916 			 */
917 			if (FAILURE == update_entry_ttl(&temp_map, &key,
918 								TTL_RAND))
919 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
920 					"%s: Could not update TTL for "
921 					"(key=%s) for map %s,%s",
922 					myself, NIL(key.dptr), map->map_name,
923 					map->domain);
924 		key = dbm_nextkey(temp_entries_db);
925 	}
926 
927 	/* Update map TTL */
928 	if (SUCCESS != update_map_ttl(&temp_map)) {
929 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not update map TTL "
930 			"for %s,%s", myself, map->map_name, map->domain);
931 	}
932 
933 	/* Set up 'special' nis entries */
934 	add_special_entries(temp_entries_db, map, &secure_flag);
935 
936 	/* Close temp DBM files */
937 	dbm_close(temp_entries_db);
938 	dbm_close(temp_ttl_db);
939 
940 	/* Lock access to the map for copy */
941 	lock_map_ctrl(map);
942 
943 	/* Move temp maps to real ones */
944 	rename_map(temp_entries, map->map_path, secure_flag);
945 	rename_map(temp_ttl, map->ttl_path, secure_flag);
946 
947 	/* Free file names */
948 	sfree(temp_entries);
949 	sfree(temp_ttl);
950 
951 	/* Unlock map */
952 	unlock_map_ctrl(map);
953 
954 	return (SUCCESS);
955 }
956 
957 /*
958  * FUNCTION :	get_mapping_map_list()
959  *
960  * DESCRIPTION:	Gets a list of nis maps for a given domain specified in the
961  *		mapping file. This information is not saved so have to go
962  *		through the entire hash table. At least this is only done at
963  *		initialization time.
964  *
965  * GIVEN :	Domain name
966  *
967  * RETURNS :	List of map names in malloced memory. MUST BE FREED BY CALLER.
968  */
969 char **
970 get_mapping_map_list(char *domain)
971 {
972 	char *myself = "get_mapping_map_list";
973 	__nis_hash_item_mt *it;
974 	int	i, j, size;
975 	char 	*end_ptr;
976 	char	**res;		/* Result array */
977 	char	**res_old;	/* Old value of res during realloc */
978 	int	array_size;	/* Current malloced size */
979 	int	res_count = 0;	/* Current result count */
980 
981 	/*
982 	 * Always need an array even if just for terminator. Normally one
983 	 * chunk will be enough.
984 	 */
985 	res = am(myself, ARRAY_CHUNK * sizeof (char *));
986 	if (NULL == res)
987 		return (NULL);
988 	array_size = ARRAY_CHUNK;
989 
990 	/* Work out hash table length */
991 	size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]);
992 	/* For all hash table entries */
993 	for (i = 0; i < size; i++) {
994 		/* Walk linked list for this hash table entry */
995 		for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) {
996 
997 			/* Check it's not a split field entry */
998 			if (0 != ((__nis_table_mapping_t *)it)->numSplits)
999 				continue;
1000 
1001 			/* Check right domain (minus trailing dot) */
1002 			if (strlen(domain) >= strlen(it->name))
1003 				continue;
1004 			end_ptr = it->name + strlen(it->name) -
1005 			    strlen(domain) - 1;
1006 			if (',' != *(end_ptr - 1))
1007 				continue;
1008 			if (0 != strncmp(end_ptr, domain, strlen(domain)))
1009 				continue;
1010 
1011 			/* Check if we need to enlarge array */
1012 			if ((res_count + 1) >= array_size) {
1013 				array_size += ARRAY_CHUNK;
1014 				res_old = res;
1015 				res = realloc(res, array_size *
1016 				    sizeof (char *));
1017 				if (NULL == res) {
1018 					res_old[res_count] = NULL;
1019 					free_passwd_list(res_old);
1020 					return (NULL);
1021 				}
1022 			}
1023 
1024 			/*
1025 			 * We will need the sequence number when we come to
1026 			 * sort the entries so for now store a pointer to
1027 			 * the __nis_hash_item_mt.
1028 			 */
1029 			res[res_count] = (char *)it;
1030 			res_count ++;
1031 		}
1032 	}
1033 
1034 	/* Terminate array */
1035 	res[res_count] = NULL;
1036 
1037 	/* Bubble sort entries into the same order as mapping file */
1038 	for (i = res_count - 2; 0 <= i; i--) {
1039 		for (j = 0; j <= i; j++) {
1040 			if (((__nis_table_mapping_t *)res[j + 1])->seq_num <
1041 			    ((__nis_table_mapping_t *)res[j])->seq_num) {
1042 				end_ptr = res[j];
1043 				res[j] = res[j+1];
1044 				res[j + 1] = end_ptr;
1045 			}
1046 		}
1047 	}
1048 
1049 	/* Finally copy the real strings in to each entry */
1050 	for (i = 0; NULL != res[i]; i ++) {
1051 
1052 		/* Get hash table entry back */
1053 		it = (__nis_hash_item_mt *)res[i];
1054 
1055 		end_ptr = it->name + strlen(it->name) - strlen(domain) - 1;
1056 
1057 		/* What we really need is strndup() */
1058 		res[i] = am(myself, end_ptr - it->name + 1);
1059 		if (NULL == res[i]) {
1060 			free_map_list(res);
1061 			return (NULL);
1062 		}
1063 
1064 		/* Copy from start to end_ptr */
1065 		(void) memcpy(res[i], it->name, end_ptr-it->name - 1);
1066 	}
1067 
1068 	return (res);
1069 }
1070 
1071 /*
1072  * FUNCTION :	make_nis_container()
1073  *
1074  * DESCRIPTION: Sets up container for map_name in the DIT.
1075  *
1076  * GIVEN :	Map name
1077  *		The domain name.
1078  *		Flag indicating if container should be created.
1079  *
1080  * RETURNS :	SUCCESS	= It worked
1081  *		FAILURE	= There was a problem.
1082  */
1083 suc_code
1084 make_nis_container(char *map_name, char *domain, bool_t init_containers) {
1085 	int			i, rc, statP = SUCCESS;
1086 	__nis_table_mapping_t	*t;
1087 	char			*dn;
1088 	char			*myself = "make_nis_container";
1089 
1090 	if (!map_name || !domain)
1091 		return (FAILURE);
1092 
1093 	if (FALSE == init_containers) {
1094 		/*
1095 		 * If we are not creating containers it is debatable what we
1096 		 * should do . Maybe we should check for a pre-
1097 		 * existing container and return failure if it does not exist.
1098 		 *
1099 		 * For now we assume the user will not have called us in this
1100 		 * mode unless they know what they are doing. So return
1101 		 * success. If they have got it wrong then latter writes will
1102 		 * fail.
1103 		 */
1104 		return (SUCCESS);
1105 	}
1106 
1107 	/* Get the mapping information for the map */
1108 	if ((t = mappingFromMap(map_name, domain, &statP)) == 0) {
1109 		if (statP == MAP_NO_MAPPING_EXISTS)
1110 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1111 			"%s: No mapping information available for %s,%s",
1112 				myself, NIL(map_name), NIL(domain));
1113 		return (FAILURE);
1114 	}
1115 
1116 	/* Two times. One for readDN and other for writeDN */
1117 	for (i = 0; i < 2; i++) {
1118 		if (i == 0)
1119 			dn = t->objectDN->read.base;
1120 		else {
1121 			if (t->objectDN->write.base == 0) {
1122 				logmsg(MSG_NOTIMECHECK, LOG_INFO,
1123 					"%s: No baseDN in writespec. Write "
1124 					"disabled for %s,%s",
1125 					myself, map_name, domain);
1126 				break;
1127 			}
1128 			if (!strcasecmp(dn, t->objectDN->write.base))
1129 				break;
1130 			dn = t->objectDN->write.base;
1131 		}
1132 
1133 		if ((rc = makeNISObject(0, dn)) == FAILURE) {
1134 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1135 				"%s: Unable to create ldap container (dn: %s) "
1136 				"for %s,%s", myself, dn, map_name, domain);
1137 			return (FAILURE);
1138 		}
1139 	}
1140 	return (SUCCESS);
1141 }
1142 
1143 /*
1144  * FUNCTION :	make_nis_domain()
1145  *
1146  * DESCRIPTION:	Sets up a nisDomainObject in the DIT
1147  *
1148  * GIVEN:	Name of the domain
1149  *		Flag indicating if domain should be create or possibly just
1150  *		checked for.
1151  */
1152 suc_code
1153 make_nis_domain(char *domain, bool_t init_containers) {
1154 
1155 	if (FALSE == init_containers) {
1156 		/*
1157 		 * If we are not creating containers it is debatable what we
1158 		 * should do with domains. Maybe we should check for a pre-
1159 		 * existing domain and return failure if it does not exist.
1160 		 *
1161 		 * For now we assume the user will not have called us in this
1162 		 * mode unless they know what they are doing. So return
1163 		 * success. If they have got it wrong then latter writes will
1164 		 * fail.
1165 		 */
1166 		return (SUCCESS);
1167 	}
1168 
1169 	/* Create the domain */
1170 	return (makeNISObject(domain, 0));
1171 }
1172 
1173 /*
1174  * FUNCTION:	update_netgroup_byxxx()
1175  *
1176  * DESCRIPTION:	Updates the netgroup.byxxx series of maps based on the current
1177  *		netgroup file. We invoke revnetgroup so that if any changes
1178  *		are made to this utility the same changes are made here.
1179  *
1180  * INPUTS:	map_ctrl containing lots of information about the map and a
1181  *		pointer to it's lock which will be required.
1182  *
1183  * OUTPUTS:	SUCCESS = Map updated
1184  *		FAILURE = Map not updated
1185  */
1186 suc_code
1187 update_netgroup_byxxx(map_ctrl *map) {
1188 	/* Name of temporary entries DBM file */
1189 	char			*temp_entries;
1190 	/* Name of temporary TTL DBM file */
1191 	char			*temp_ttl;
1192 	/* Temporary DBM handles */
1193 	DBM			*temp_entries_db;
1194 	DBM			*temp_ttl_db;
1195 	map_ctrl		temp_map;
1196 	char			*myself = "update_netgroup_byxxx";
1197 	char			*cmdbuf;
1198 	int			cmd_length;
1199 	datum			key;
1200 	map_ctrl		*netgroupmap;
1201 	int			res;
1202 	/* Temporary revnetgroup files */
1203 	const char 		*byhost = NETGROUP_BYHOST "_REV" TEMP_POSTFIX;
1204 	const char 		*byuser = NETGROUP_BYUSER "_REV" TEMP_POSTFIX;
1205 	const char		*temp_file_name;
1206 
1207 
1208 	/*
1209 	 * We need to use two different temporary files: one for netgroup.byhost
1210 	 * and other for netgroup.byuser, since these two maps can be updated
1211 	 * simultaneously. These temporary files will hold the output of
1212 	 * revnetgroup [-h|-u] command. They are then used to generate the
1213 	 * corresponding dbm files and thereafter deleted.
1214 	 */
1215 	if (0 == strcmp(map->map_name, NETGROUP_BYHOST))
1216 		temp_file_name = byhost;
1217 	else
1218 		temp_file_name = byuser;
1219 
1220 	/* Alloc enough cmd buf for revnet cmd */
1221 	cmd_length = strlen("/usr/sbin/makedbm -u ") +
1222 			(strlen(map->map_path) - strlen(map->map_name)) +
1223 			strlen(NETGROUP_MAP) +
1224 			strlen(" | /usr/sbin/revnetgroup -h > ") +
1225 			(strlen(map->map_path) - strlen(map->map_name)) +
1226 			strlen(temp_file_name) + 1;
1227 	cmdbuf = am(myself, cmd_length);
1228 
1229 	if (NULL == cmdbuf) {
1230 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1231 				"%s: Could not alloc cmdbuf.", myself);
1232 		return (FAILURE);
1233 	}
1234 
1235 	/*
1236 	 * If necessary update (and wait for) netgroups map. This is a lot of
1237 	 * work but if the netgroup map itself is not being accessed it may
1238 	 * contain information that is not up to date with the DIT.
1239 	 *
1240 	 * We use the cmdbuf to store the qualified netgroup map name there will
1241 	 * be enough space for this but we are not yet creating the cmd.
1242 	 */
1243 	strlcpy(cmdbuf, map->map_path, strlen(map->map_path) -
1244 						strlen(map->map_name) + 1);
1245 	strcat(cmdbuf, NETGROUP_MAP);
1246 	netgroupmap = (map_ctrl *)shim_dbm_open(cmdbuf,
1247 						O_RDWR | O_CREAT, 0644);
1248 	if (NULL == netgroupmap) {
1249 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1250 				"%s: Could not update %s.", myself, cmdbuf);
1251 		sfree(cmdbuf);
1252 		return (FAILURE);
1253 	}
1254 
1255 	if (has_map_expired(netgroupmap)) {
1256 		lock_map_ctrl(netgroupmap);
1257 		update_map_if_required(netgroupmap, TRUE);
1258 		unlock_map_ctrl(netgroupmap);
1259 	}
1260 	shim_dbm_close((DBM *)netgroupmap);
1261 
1262 	/* Dump netgroup file through revnetgroup to a temp file */
1263 	strcpy(cmdbuf, "/usr/sbin/makedbm -u ");
1264 
1265 	/* Unmake the netgroup file in same domain as map */
1266 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1267 						strlen(map->map_name));
1268 	strcat(cmdbuf, NETGROUP_MAP);
1269 
1270 	if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) {
1271 		strcat(cmdbuf, " | /usr/sbin/revnetgroup -h > ");
1272 	} else {
1273 		strcat(cmdbuf, " | /usr/sbin/revnetgroup -u > ");
1274 	}
1275 
1276 	/* Create temp file file in same domain as map */
1277 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1278 						strlen(map->map_name));
1279 	strcat(cmdbuf, temp_file_name);
1280 
1281 	if (0 > system(cmdbuf)) {
1282 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" "
1283 			"(errno=%d)", myself, cmdbuf, errno);
1284 		sfree(cmdbuf);
1285 		return (FAILURE);
1286 	}
1287 	sfree(cmdbuf);
1288 
1289 	/* Allocate and set up names */
1290 	if (SUCCESS != alloc_temp_names(map->map_path,
1291 				&temp_entries, &temp_ttl)) {
1292 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1293 			"%s: Unable to create map names for %s",
1294 			myself, map->map_path);
1295 		return (FAILURE);
1296 	}
1297 
1298 	/* Make the temporary DBM file */
1299 	cmdbuf = am(myself, strlen("/usr/sbin/makedbm") +
1300 			(strlen(map->map_path) - strlen(map->map_name)) +
1301 			strlen(temp_file_name) +
1302 			strlen(temp_entries) + 3);
1303 	if (NULL == cmdbuf) {
1304 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1305 				"%s: Could not allocate cmdbuf.", myself);
1306 		sfree(temp_entries);
1307 		sfree(temp_ttl);
1308 		return (FAILURE);
1309 	}
1310 
1311 	strcpy(cmdbuf, "/usr/sbin/makedbm ");
1312 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1313 						strlen(map->map_name));
1314 	strcat(cmdbuf, temp_file_name);
1315 	strcat(cmdbuf, " ");
1316 	strcat(cmdbuf, temp_entries);
1317 
1318 	if (0 > system(cmdbuf)) {
1319 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" "
1320 			"(errno=%d)", myself, cmdbuf, errno);
1321 		sfree(cmdbuf);
1322 		sfree(temp_entries);
1323 		sfree(temp_ttl);
1324 		return (FAILURE);
1325 	}
1326 
1327 	/* Already have enough command buffer to rm temporary file */
1328 	strlcpy(cmdbuf, map->map_path, strlen(map->map_path) -
1329 						strlen(map->map_name) + 1);
1330 	strcat(cmdbuf, temp_file_name);
1331 	res = unlink(cmdbuf);
1332 	/* If the temp file did not exist no problem. Probably had no entries */
1333 	if ((0 != res) && (ENOENT != errno)) {
1334 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not delete \"%s\" "
1335 			"(errno=%d)", myself, cmdbuf, errno);
1336 		sfree(temp_entries);
1337 		sfree(temp_ttl);
1338 		sfree(cmdbuf);
1339 		return (FAILURE);
1340 	}
1341 	sfree(cmdbuf);
1342 
1343 	if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644))
1344 						== NULL) {
1345 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
1346 						myself, temp_entries);
1347 		sfree(temp_entries);
1348 		sfree(temp_ttl);
1349 		return (FAILURE);
1350 	}
1351 
1352 	if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644))
1353 						== NULL) {
1354 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
1355 						myself, temp_ttl);
1356 		dbm_close(temp_entries_db);
1357 		sfree(temp_entries);
1358 		sfree(temp_ttl);
1359 		return (FAILURE);
1360 	}
1361 
1362 	/*
1363 	 * Set up enough of map_ctrl to call update_entry_ttl. Since there is
1364 	 * no mapping, and thus not TTL, defined for these maps use the TTL
1365 	 * values for netgroup map
1366 	 */
1367 	temp_map.map_name = NETGROUP_MAP;
1368 	temp_map.domain = map->domain;
1369 	temp_map.ttl = temp_ttl_db;
1370 
1371 	/*
1372 	 * Generate new TTL file.  Since these maps work only on the whole map
1373 	 * expiry these will not actually be used but there presence makes it
1374 	 * easier to handle these maps in the same way as other maps.
1375 	 */
1376 	key = dbm_firstkey(temp_entries_db);
1377 	while (key.dptr != 0) {
1378 		if (!is_special_key(&key))
1379 			/*
1380 			 * For these maps want all timouts to be maximum
1381 			 */
1382 			if (FAILURE == update_entry_ttl(&temp_map, &key,
1383 								TTL_MAX))
1384 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1385 					"%s: Could not update TTL for "
1386 					"(key=%s) for map %s,%s",
1387 					myself, NIL(key.dptr), map->map_name,
1388 					map->domain);
1389 		key = dbm_nextkey(temp_entries_db);
1390 	}
1391 
1392 	/* Update map TTL */
1393 	update_map_ttl(&temp_map);
1394 
1395 	/* Close temp DBM files */
1396 	dbm_close(temp_entries_db);
1397 	dbm_close(temp_ttl_db);
1398 
1399 	/* Lock access to the map for copy */
1400 	lock_map_ctrl(map);
1401 
1402 	/* Move temp maps to real ones */
1403 	rename_map(temp_entries, map->map_path, FALSE);
1404 	rename_map(temp_ttl, map->ttl_path, FALSE);
1405 
1406 	/* Free file names */
1407 	sfree(temp_entries);
1408 	sfree(temp_ttl);
1409 
1410 	/* Unlock map */
1411 	unlock_map_ctrl(map);
1412 
1413 	return (SUCCESS);
1414 }
1415