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 
303 		case TTL_RAND:
304 			interval = table_map->initTtlHi - table_map->initTtlLo;
305 			if (0 >= interval)
306 				return (table_map->initTtlLo);
307 
308 			/*
309 			 * Must get a random value. We assume srand48() got
310 			 * called at initialization.
311 			 */
312 			return (lrand48() % interval);
313 
314 		case TTL_RUNNING:
315 			return (table_map->ttl);
316 
317 
318 	}
319 }
320 
321 /*
322  * FUNCTION :	get_mapping_domain_list()
323  *
324  * DESCRIPTION:	Gets a list of domain names specified, by nisLDAPdomainContext
325  *		attributes, in the mapping file. This is used only for initial
326  *		DIT setup. Once the DIT has been set up get_domain_list() is
327  *		used instead.
328  *
329  * GIVEN :	Pointer returned array.
330  *
331  * RETURNS :	Number of element in returned array.
332  *		Array of elements this is in static memory
333  *		and must not be freed by the caller.
334  */
335 int
336 get_mapping_domain_list(char ***ptr)
337 {
338 	*ptr = ypDomains.domainLabels;
339 	return (ypDomains.numDomains);
340 }
341 
342 /*
343  * FUNCTION :	get_mapping_yppasswdd_domain_list()
344  *
345  * DESCRIPTION:	Gets a list of domain names specified, by the
346  *		nisLDAPyppasswddDomains attribute, in the mapping file. This
347  *		is the list of domains for which passwords should be changed.
348  *
349  * GIVEN :	Pointer returned array
350  *
351  * RETURNS :	Number of element in returned array.
352  *		0 if no nisLDAPyppasswddDomains attribute is present.
353  *		Array of elements this is in static memory
354  *		and must not be freed by the caller.
355  */
356 int
357 get_mapping_yppasswdd_domain_list(char ***ptr)
358 {
359 	*ptr = ypDomains.yppasswddDomainLabels;
360 	return (ypDomains.numYppasswdd);
361 }
362 
363 /*
364  * FUNCTION :	free_map_list()
365  *
366  * DESCRIPTION:	Frees a map list.
367  *
368  * GIVEN :	Pointer to the map list.
369  *
370  * RETURNS :	Nothing
371  */
372 void
373 free_map_list(char **map_list)
374 {
375 	free_null_terminated_list(map_list);
376 }
377 
378 /*
379  * FUNCTION :	get_passwd_list()
380  *
381  * DESCRIPTION:	Gets a list of either passwd or passwd.adjunct map files
382  *		defined in the mapping file. These are the files which have
383  *		'magic' nisLDAPdatabaseIdMapping entries aliasing them to
384  *		passwd or passwd.adjunct. This function is required so that
385  *		yppasswdd can work out which maps to synchronize with any
386  *		password changes.
387  *
388  *		This information is not currently stored by the parser but
389  *		we can recover it from the hash table. This makes hard work but
390  *		passwords should not be changed very frequently
391  *
392  * GIVEN :	Flag indicating if a list is required for passwd or
393  *		passwd.adjunct
394  *		Domain to return the list for.
395  *
396  * RETURNS :	Null terminated list of map names in malloced memory. To be
397  *		freed by caller. (Possibly empty if no passwd maps found)
398  *		NULL on error
399  */
400 char **
401 get_passwd_list(bool_t adjunct, char *domain)
402 {
403 	char *myself = "get_passwd_list";
404 	__nis_hash_item_mt *it;
405 	int	i, size;
406 	char 	*end_ptr;
407 	char	*target;	/* What we are looking for */
408 	int	target_len;
409 	int	domain_len;
410 	char	**res;		/* Result array */
411 	char	**res_old;	/* Old value of res during realloc */
412 	int	array_size;	/* Current malloced size */
413 	int	res_count = 0;	/* Current result count */
414 
415 	/*
416 	 * Always need an array even if just for terminator. Normally one
417 	 * chunk will be enough.
418 	 */
419 	res = am(myself, ARRAY_CHUNK * sizeof (char *));
420 	if (NULL == res)
421 		return (NULL);
422 	array_size = ARRAY_CHUNK;
423 
424 	/* Set up target */
425 	if (adjunct)
426 		target = PASSWD_ADJUNCT_PREFIX;
427 	else
428 		target = PASSWD_PREFIX;
429 	target_len = strlen(target);
430 	domain_len = strlen(domain);
431 
432 	/* Work out hash table length */
433 	size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]);
434 	/* For all hash table entries */
435 	for (i = 0; i < size; i++) {
436 		/* Walk linked list for this hash table entry */
437 		for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) {
438 			/* Check right map */
439 			if ((target_len + domain_len + 1) > strlen(it->name))
440 				continue;
441 			if (0 != strncmp(it->name, target, target_len))
442 				continue;
443 
444 			/* Check right domain (minus trailing dot) */
445 			if (strlen(domain) >= strlen(it->name))
446 				continue;
447 			end_ptr = it->name + strlen(it->name) -
448 			    strlen(domain) - 1;
449 			if (',' != *(end_ptr - 1))
450 				continue;
451 			if (0 != strncmp(end_ptr, domain, strlen(domain)))
452 				continue;
453 
454 			/* Check if we need to enlarge array */
455 			if ((res_count + 1) >= array_size) {
456 				array_size += ARRAY_CHUNK;
457 				res_old = res;
458 				res = realloc(res, array_size *
459 				    sizeof (char *));
460 				if (NULL == res) {
461 					res_old[res_count] = NULL;
462 					free_passwd_list(res_old);
463 					return (NULL);
464 				}
465 			}
466 
467 			/* What we really need is strndup() */
468 			res[res_count] = am(myself, end_ptr - it->name + 1);
469 			if (NULL == res[res_count]) {
470 				free_passwd_list(res);
471 				return (NULL);
472 			}
473 
474 			/* Copy from start to end_ptr */
475 			(void) memcpy(res[res_count], it->name,
476 			    end_ptr-it->name - 1);
477 			res_count ++;
478 		}
479 	}
480 
481 	/* Terminate array */
482 	res[res_count] = NULL;
483 	return (res);
484 }
485 
486 /*
487  * FUNCTION :	free_passwd_list()
488  *
489  * DESCRIPTION:	Frees a password list obtained with get_passwd_list()
490  *
491  * INPUTS :	Address of list to free.
492  *
493  * OUTPUTS :	Nothing
494  */
495 void
496 free_passwd_list(char **list)
497 {
498 	free_null_terminated_list(list);
499 }
500 
501 /*
502  * FUNCTION :	free_null_terminated_list()
503  *
504  * DESCRIPTION:	Frees a generic null terminated list.
505  *
506  * INPUTS :	Address of list to free.
507  *
508  * OUTPUTS :	Nothing
509  */
510 void
511 free_null_terminated_list(char **list)
512 {
513 	int index;
514 
515 	/* Free all the strings */
516 	for (index = 0; NULL != list[index]; index ++)
517 		sfree(list[index]);
518 
519 	/* Free the array */
520 	sfree(list);
521 }
522 
523 
524 /*
525  * FUNCTION :	add_special_entries()
526  *
527  * DESCRIPTION:	Adds the special (YP_*) entries to a map.
528  *
529  *		Part of dit_access because requires access to the mapping
530  *		file in order to work out if secure and interdomain entries
531  *		should be created.
532  *
533  * GIVEN :	Pointer to an open, temporary, DBM file
534  *		Pointer to map information (do not use DBM fields).
535  *		Pointer to a location in which to return security flag
536  *
537  * RETURNS :	SUCCESS = All entries created
538  *		FAILURE = Some entries not created
539  */
540 suc_code
541 add_special_entries(DBM *db, map_ctrl *map, bool_t *secure_flag)
542 {
543 	char local_host[MAX_MASTER_NAME];
544 	__nis_table_mapping_t *table_map;
545 	int res;
546 
547 	/* Last modified time is now */
548 	update_timestamp(db);
549 
550 	/* Add domain name */
551 	addpair(db, yp_domain_name, map->domain);
552 
553 	/* For N2L mode local machine is always the master */
554 	sysinfo(SI_HOSTNAME, local_host, sizeof (local_host));
555 	addpair(db, yp_master_name, local_host);
556 
557 	/*  Get the mapping structure corresponding to `map.domain' */
558 	table_map = mappingFromMap(map->map_name, map->domain, &res);
559 	if (0 == table_map)
560 		return (FAILURE);
561 
562 	/* Add secure and interdomain flags if required */
563 	if (table_map->securemap_flag) {
564 		addpair(db, yp_secure, "");
565 		*secure_flag = TRUE;
566 	} else {
567 		*secure_flag = FALSE;
568 	}
569 	if (table_map->usedns_flag)
570 		addpair(db, yp_interdomain, "");
571 
572 	return (SUCCESS);
573 }
574 
575 /*
576  * FUNCTION:	update_map_from_dit()
577  *
578  * DESCRIPTION:	Core code called to update an entire map.
579  *		Information is recovered from LDAP and used to build a duplicate
580  *		copy of the live maps. When this is complete the maps are
581  *		locked and then overwritten by the new copy.
582  *
583  * INPUTS:	map_ctrl containing lots of information about the map and a
584  *		pointer to it's lock which will be required.
585  *		Flag indicating if progress logging is required.
586  *
587  * OUTPUTS:	SUCCESS = Map updated
588  *		FAILURE = Map not updated
589  */
590 suc_code
591 update_map_from_dit(map_ctrl *map, bool_t log_flag) {
592 	__nis_table_mapping_t	*t;
593 	__nis_rule_value_t	*rv;
594 	__nis_ldap_search_t	*ls;
595 	__nis_object_dn_t	*objectDN = NULL;
596 	datum			*datval, *datkey;
597 	int			nr = 0, i, j, nv, numDNs;
598 	int			statP = SUCCESS, flag;
599 	char			*objname, **dn;
600 	/* Name of temporary entries DBM file */
601 	char			*temp_entries;
602 	/* Name of temporary TTL DBM file */
603 	char			*temp_ttl;
604 	/* Temporary DBM handles */
605 	DBM			*temp_entries_db;
606 	DBM			*temp_ttl_db;
607 	map_ctrl		temp_map;
608 	datum			key;
609 	char			*myself = "update_map_from_dit";
610 	bool_t			secure_flag;
611 	int			entry_count = 1;
612 	int			next_print = PRINT_FREQ;
613 	int			search_flag = SUCCESS;
614 
615 	int			m;
616 
617 	/* list of maps whose keys will be transliterated to lowercase */
618 	char			*xlate_to_lcase_maps[] = {
619 		"hosts.byname",
620 		"ipnodes.byname",
621 		NULL
622 	};
623 	bool_t			xlate_to_lcase = FALSE;
624 
625 	if (!map || !map->map_name || !map->domain) {
626 		return (FAILURE);
627 	}
628 
629 	__nisdb_get_tsd()->escapeFlag = '\0';
630 
631 	/*
632 	 * netgroup.byxxx maps are a special case. They are regenerated from
633 	 * the netgroup map, not the DIT, so handle special case.
634 	 */
635 	if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) ||
636 		0 == (strcmp(map->map_name,  NETGROUP_BYUSER))) {
637 		return (update_netgroup_byxxx(map));
638 	}
639 
640 	/* Get the mapping information for the map */
641 	if ((t = mappingFromMap(map->map_name, map->domain, &statP)) == 0) {
642 		if (statP == MAP_NO_MAPPING_EXISTS)
643 			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
644 			"%s: No mapping information available for %s,%s",
645 				myself, map->map_name, map->domain);
646 		return (FAILURE);
647 	}
648 
649 	/* Allocate and set up names */
650 	if (SUCCESS != alloc_temp_names(map->map_path,
651 				&temp_entries, &temp_ttl)) {
652 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
653 			"%s: Unable to create map names for %s",
654 			myself, map->map_path);
655 		return (FAILURE);
656 	}
657 
658 	/* Create temp entry and TTL file */
659 	if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644))
660 						== NULL) {
661 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
662 						myself, temp_entries);
663 		sfree(temp_entries);
664 		sfree(temp_ttl);
665 		return (FAILURE);
666 	}
667 
668 	if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644))
669 						== NULL) {
670 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
671 						myself, temp_ttl);
672 		dbm_close(temp_entries_db);
673 		delete_map(temp_entries);
674 		sfree(temp_entries);
675 		sfree(temp_ttl);
676 		return (FAILURE);
677 	}
678 
679 	/* Initialize domainContext tsd */
680 	__nisdb_get_tsd()->domainContext = 0;
681 	for (i = 0; i < ypDomains.numDomains; i++) {
682 		if (0 == ypDomains.domainLabels[i])
683 			continue;
684 		if (0 == strcasecmp(map->domain, ypDomains.domainLabels[i])) {
685 			__nisdb_get_tsd()->domainContext = ypDomains.domains[i];
686 			break;
687 		}
688 	}
689 
690 	if (!(objname = getFullMapName(map->map_name, map->domain))) {
691 		if (temp_entries_db)
692 			dbm_close(temp_entries_db);
693 		if (temp_ttl_db)
694 			dbm_close(temp_ttl_db);
695 		delete_map(temp_entries);
696 		sfree(temp_entries);
697 		delete_map(temp_ttl);
698 		sfree(temp_ttl);
699 		return (FAILURE);
700 	}
701 
702 	/*
703 	 * set xlate_to_lcase to TRUE if map_name is found in
704 	 * xlate_to_lcase_maps[]
705 	 */
706 	m = 0;
707 	while (xlate_to_lcase_maps[m] != NULL) {
708 		if (strncmp(map->map_name, xlate_to_lcase_maps[m],
709 			strlen(xlate_to_lcase_maps[m])) == 0) {
710 			xlate_to_lcase = TRUE;
711 			break;
712 		}
713 		++m;
714 	}
715 
716 	/* Try each mapping for the map */
717 	for (flag = 0; t != 0 && search_flag != FAILURE; t = t->next) {
718 
719 		/* Check if the mapping is the correct one */
720 		if (strcmp(objname, t->objName) != 0) {
721 			continue;
722 		}
723 
724 		/* Check if rulesFromLDAP are provided */
725 		if (t->numRulesFromLDAP == 0) {
726 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
727 				"%s: No rulesFromLDAP available for %s (%s)",
728 				myself, t->dbId, map->map_name);
729 			continue;
730 		}
731 
732 		/* Set flag to indicate update is enabled */
733 		flag = 1;
734 		/* Create ldap request for enumeration */
735 		for (objectDN = t->objectDN;
736 				objectDN && objectDN->read.base;
737 				objectDN = objectDN->next) {
738 			if ((ls = createLdapRequest(t, 0, 0, 1, NULL,
739 						objectDN)) == 0) {
740 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
741 					"%s: Failed to create "
742 					"ldapSearch request for "
743 					"%s (%s) for base %s",
744 					myself, t->dbId,
745 					map->map_name,
746 					objectDN->read.base);
747 				statP = FAILURE;
748 				search_flag = FAILURE;
749 				break;
750 			}
751 
752 			if (log_flag) {
753 				printf("Waiting for LDAP search results.\n");
754 			}
755 
756 			/* Query LDAP */
757 			nr = (ls->isDN)?0:-1;
758 			rv = ldapSearch(ls, &nr, 0, &statP);
759 			freeLdapSearch(ls);
760 			if (rv == 0) {
761 				if (statP == LDAP_NO_SUCH_OBJECT) {
762 				/*
763 				 * No Entry exists in the ldap server. Not
764 				 * a problem. Maybe there are just no entries
765 				 * in this map.
766 				 */
767 					continue;
768 				}
769 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
770 					"%s: ldapSearch error %d "
771 					"(%s) for %s (%s) for base %s",
772 					myself, statP, ldap_err2string(statP),
773 					t->dbId, map->map_name,
774 					objectDN->read.base);
775 				statP = FAILURE;
776 				search_flag = FAILURE;
777 				break;
778 			}
779 
780 			if (log_flag) {
781 				printf("Processing search results.\n");
782 			}
783 
784 			/* Obtain list of DNs for logging */
785 			if ((dn = findDNs(myself, rv, nr, 0, &numDNs)) == 0) {
786 				statP = FAILURE;
787 				search_flag = FAILURE;
788 				break;
789 			}
790 
791 			/* For each entry in the result  do the following */
792 			for (i = 0; i < nr; i++) {
793 			/* Convert LDAP data to NIS equivalents */
794 				statP = buildNISRuleValue(t, &rv[i],
795 						map->domain);
796 				if (statP == MAP_INDEXLIST_ERROR)
797 					continue;
798 				if (statP != SUCCESS) {
799 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
800 					    "%s: Conversion error %d (LDAP to "
801 					    "name=value pairs) "
802 					    "for (dn: %s) for "
803 					    "%s (%s) for base %s",
804 					    myself, statP, NIL(dn[i]),
805 					    t->dbId, map->map_name,
806 					    objectDN->read.base);
807 					continue;
808 				}
809 
810 				/* Obtain the datum for value */
811 				datval = ruleValueToDatum(t, &rv[i], &statP);
812 				if (datval == 0) {
813 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
814 						"%s: Conversion error %d "
815 						"(name=value pairs to NIS)"
816 						" for (dn: %s) for "
817 						"%s (%s) for base %s",
818 						myself, statP, NIL(dn[i]),
819 						t->dbId, map->map_name,
820 						objectDN->read.base);
821 					continue;
822 				}
823 
824 				/* Obtain the datum for key */
825 				datkey = getKeyFromRuleValue(t, &rv[i],
826 				    &nv, &statP, xlate_to_lcase);
827 				if (datkey == 0) {
828 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
829 						"%s: Unable to obtain NIS "
830 						"key from LDAP data (dn:%s) "
831 						"for %s (%s) for base %s",
832 						myself, NIL(dn[i]), t->dbId,
833 						map->map_name,
834 						objectDN->read.base);
835 					sfree(datval->dptr);
836 					sfree(datval);
837 					continue;
838 				}
839 
840 				/* Write to the temporary map */
841 				for (j = 0; j < nv; j++, entry_count ++) {
842 					if (datkey[j].dsize == 0)
843 						continue;
844 					errno = 0;
845 					/* DBM_INSERT to match */
846 					/* singleReadFromDIT */
847 					if (dbm_store(temp_entries_db,
848 						datkey[j],
849 						*datval,
850 						DBM_INSERT) < 0) {
851 						/*
852 						 * For some cases errno may
853 						 * still be 0 but dbm_error
854 						 * isn't informative at all.
855 						 */
856 						logmsg(MSG_NOTIMECHECK,
857 						    LOG_WARNING,
858 						    "%s: dbm store error "
859 						    "(errno=%d) "
860 						    "for (key=%s, value=%s) "
861 						    "for %s (%s) for base %s",
862 						    myself,
863 						    errno,
864 						    datkey[j].dptr,
865 						    datval->dptr, t->dbId,
866 						    map->map_name,
867 						    objectDN->read.base);
868 						/* clear the error */
869 						dbm_clearerr(temp_entries_db);
870 					}
871 					sfree(datkey[j].dptr);
872 
873 					if (log_flag && (entry_count >=
874 							next_print)) {
875 						printf("%d entries processed\n",
876 							entry_count);
877 						next_print *= 2;
878 					}
879 
880 				}
881 				sfree(datkey);
882 				sfree(datval->dptr);
883 				sfree(datval);
884 			}
885 
886 			freeRuleValue(rv, nr);
887 			freeDNs(dn, numDNs);
888 		} /* End of for over objectDN */
889 	}
890 	sfree(objname);
891 
892 	if (t != 0 || flag == 0 || search_flag == FAILURE) {
893 		if (temp_entries_db)
894 			dbm_close(temp_entries_db);
895 		if (temp_ttl_db)
896 			dbm_close(temp_ttl_db);
897 		delete_map(temp_entries);
898 		sfree(temp_entries);
899 		delete_map(temp_ttl);
900 		sfree(temp_ttl);
901 		return (statP);
902 	}
903 	/* Set up enough of map_ctrl to call update_entry_ttl */
904 	temp_map.map_name = map->map_name;
905 	temp_map.domain = map->domain;
906 	temp_map.ttl = temp_ttl_db;
907 
908 	/* Generate new TTL file */
909 	key = dbm_firstkey(temp_entries_db);
910 	while (key.dptr != 0) {
911 		if (!is_special_key(&key))
912 			/*
913 			 * We don't want all the entries to time out at the
914 			 * same time so create random TTLs.
915 			 */
916 			if (FAILURE == update_entry_ttl(&temp_map, &key,
917 								TTL_RAND))
918 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
919 					"%s: Could not update TTL for "
920 					"(key=%s) for map %s,%s",
921 					myself, NIL(key.dptr), map->map_name,
922 					map->domain);
923 		key = dbm_nextkey(temp_entries_db);
924 	}
925 
926 	/* Update map TTL */
927 	if (SUCCESS != update_map_ttl(&temp_map)) {
928 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not update map TTL "
929 			"for %s,%s", myself, map->map_name, map->domain);
930 	}
931 
932 	/* Set up 'special' nis entries */
933 	add_special_entries(temp_entries_db, map, &secure_flag);
934 
935 	/* Close temp DBM files */
936 	dbm_close(temp_entries_db);
937 	dbm_close(temp_ttl_db);
938 
939 	/* Lock access to the map for copy */
940 	lock_map_ctrl(map);
941 
942 	/* Move temp maps to real ones */
943 	rename_map(temp_entries, map->map_path, secure_flag);
944 	rename_map(temp_ttl, map->ttl_path, secure_flag);
945 
946 	/* Free file names */
947 	sfree(temp_entries);
948 	sfree(temp_ttl);
949 
950 	/* Unlock map */
951 	unlock_map_ctrl(map);
952 
953 	return (SUCCESS);
954 }
955 
956 /*
957  * FUNCTION :	get_mapping_map_list()
958  *
959  * DESCRIPTION:	Gets a list of nis maps for a given domain specified in the
960  *		mapping file. This information is not saved so have to go
961  *		through the entire hash table. At least this is only done at
962  *		initialization time.
963  *
964  * GIVEN :	Domain name
965  *
966  * RETURNS :	List of map names in malloced memory. MUST BE FREED BY CALLER.
967  */
968 char **
969 get_mapping_map_list(char *domain)
970 {
971 	char *myself = "get_mapping_map_list";
972 	__nis_hash_item_mt *it;
973 	int	i, j, size;
974 	char 	*end_ptr;
975 	char	**res;		/* Result array */
976 	char	**res_old;	/* Old value of res during realloc */
977 	int	array_size;	/* Current malloced size */
978 	int	res_count = 0;	/* Current result count */
979 
980 	/*
981 	 * Always need an array even if just for terminator. Normally one
982 	 * chunk will be enough.
983 	 */
984 	res = am(myself, ARRAY_CHUNK * sizeof (char *));
985 	if (NULL == res)
986 		return (NULL);
987 	array_size = ARRAY_CHUNK;
988 
989 	/* Work out hash table length */
990 	size = sizeof (ldapMappingList.keys) / sizeof (ldapMappingList.keys[0]);
991 	/* For all hash table entries */
992 	for (i = 0; i < size; i++) {
993 		/* Walk linked list for this hash table entry */
994 		for (it = ldapMappingList.keys[i]; NULL != it; it = it->next) {
995 
996 			/* Check it's not a split field entry */
997 			if (0 != ((__nis_table_mapping_t *)it)->numSplits)
998 				continue;
999 
1000 			/* Check right domain (minus trailing dot) */
1001 			if (strlen(domain) >= strlen(it->name))
1002 				continue;
1003 			end_ptr = it->name + strlen(it->name) -
1004 			    strlen(domain) - 1;
1005 			if (',' != *(end_ptr - 1))
1006 				continue;
1007 			if (0 != strncmp(end_ptr, domain, strlen(domain)))
1008 				continue;
1009 
1010 			/* Check if we need to enlarge array */
1011 			if ((res_count + 1) >= array_size) {
1012 				array_size += ARRAY_CHUNK;
1013 				res_old = res;
1014 				res = realloc(res, array_size *
1015 				    sizeof (char *));
1016 				if (NULL == res) {
1017 					res_old[res_count] = NULL;
1018 					free_passwd_list(res_old);
1019 					return (NULL);
1020 				}
1021 			}
1022 
1023 			/*
1024 			 * We will need the sequence number when we come to
1025 			 * sort the entries so for now store a pointer to
1026 			 * the __nis_hash_item_mt.
1027 			 */
1028 			res[res_count] = (char *)it;
1029 			res_count ++;
1030 		}
1031 	}
1032 
1033 	/* Terminate array */
1034 	res[res_count] = NULL;
1035 
1036 	/* Bubble sort entries into the same order as mapping file */
1037 	for (i = res_count - 2; 0 <= i; i--) {
1038 		for (j = 0; j <= i; j++) {
1039 			if (((__nis_table_mapping_t *)res[j + 1])->seq_num <
1040 			    ((__nis_table_mapping_t *)res[j])->seq_num) {
1041 				end_ptr = res[j];
1042 				res[j] = res[j+1];
1043 				res[j + 1] = end_ptr;
1044 			}
1045 		}
1046 	}
1047 
1048 	/* Finally copy the real strings in to each entry */
1049 	for (i = 0; NULL != res[i]; i ++) {
1050 
1051 		/* Get hash table entry back */
1052 		it = (__nis_hash_item_mt *)res[i];
1053 
1054 		end_ptr = it->name + strlen(it->name) - strlen(domain) - 1;
1055 
1056 		/* What we really need is strndup() */
1057 		res[i] = am(myself, end_ptr - it->name + 1);
1058 		if (NULL == res[i]) {
1059 			free_map_list(res);
1060 			return (NULL);
1061 		}
1062 
1063 		/* Copy from start to end_ptr */
1064 		(void) memcpy(res[i], it->name, end_ptr-it->name - 1);
1065 	}
1066 
1067 	return (res);
1068 }
1069 
1070 /*
1071  * FUNCTION :	make_nis_container()
1072  *
1073  * DESCRIPTION: Sets up container for map_name in the DIT.
1074  *
1075  * GIVEN :	Map name
1076  *		The domain name.
1077  *		Flag indicating if container should be created.
1078  *
1079  * RETURNS :	SUCCESS	= It worked
1080  *		FAILURE	= There was a problem.
1081  */
1082 suc_code
1083 make_nis_container(char *map_name, char *domain, bool_t init_containers) {
1084 	int			i, rc, statP = SUCCESS;
1085 	__nis_table_mapping_t	*t;
1086 	char			*dn;
1087 	char			*myself = "make_nis_container";
1088 
1089 	if (!map_name || !domain)
1090 		return (FAILURE);
1091 
1092 	if (FALSE == init_containers) {
1093 		/*
1094 		 * If we are not creating containers it is debatable what we
1095 		 * should do . Maybe we should check for a pre-
1096 		 * existing container and return failure if it does not exist.
1097 		 *
1098 		 * For now we assume the user will not have called us in this
1099 		 * mode unless they know what they are doing. So return
1100 		 * success. If they have got it wrong then latter writes will
1101 		 * fail.
1102 		 */
1103 		return (SUCCESS);
1104 	}
1105 
1106 	/* Get the mapping information for the map */
1107 	if ((t = mappingFromMap(map_name, domain, &statP)) == 0) {
1108 		if (statP == MAP_NO_MAPPING_EXISTS)
1109 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1110 			"%s: No mapping information available for %s,%s",
1111 				myself, NIL(map_name), NIL(domain));
1112 		return (FAILURE);
1113 	}
1114 
1115 	/* Two times. One for readDN and other for writeDN */
1116 	for (i = 0; i < 2; i++) {
1117 		if (i == 0)
1118 			dn = t->objectDN->read.base;
1119 		else {
1120 			if (t->objectDN->write.base == 0) {
1121 				logmsg(MSG_NOTIMECHECK, LOG_INFO,
1122 					"%s: No baseDN in writespec. Write "
1123 					"disabled for %s,%s",
1124 					myself, map_name, domain);
1125 				break;
1126 			}
1127 			if (!strcasecmp(dn, t->objectDN->write.base))
1128 				break;
1129 			dn = t->objectDN->write.base;
1130 		}
1131 
1132 		if ((rc = makeNISObject(0, dn)) == FAILURE) {
1133 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1134 				"%s: Unable to create ldap container (dn: %s) "
1135 				"for %s,%s", myself, dn, map_name, domain);
1136 			return (FAILURE);
1137 		}
1138 	}
1139 	return (SUCCESS);
1140 }
1141 
1142 /*
1143  * FUNCTION :	make_nis_domain()
1144  *
1145  * DESCRIPTION:	Sets up a nisDomainObject in the DIT
1146  *
1147  * GIVEN:	Name of the domain
1148  *		Flag indicating if domain should be create or possibly just
1149  *		checked for.
1150  */
1151 suc_code
1152 make_nis_domain(char *domain, bool_t init_containers) {
1153 
1154 	if (FALSE == init_containers) {
1155 		/*
1156 		 * If we are not creating containers it is debatable what we
1157 		 * should do with domains. Maybe we should check for a pre-
1158 		 * existing domain and return failure if it does not exist.
1159 		 *
1160 		 * For now we assume the user will not have called us in this
1161 		 * mode unless they know what they are doing. So return
1162 		 * success. If they have got it wrong then latter writes will
1163 		 * fail.
1164 		 */
1165 		return (SUCCESS);
1166 	}
1167 
1168 	/* Create the domain */
1169 	return (makeNISObject(domain, 0));
1170 }
1171 
1172 /*
1173  * FUNCTION:	update_netgroup_byxxx()
1174  *
1175  * DESCRIPTION:	Updates the netgroup.byxxx series of maps based on the current
1176  *		netgroup file. We invoke revnetgroup so that if any changes
1177  *		are made to this utility the same changes are made here.
1178  *
1179  * INPUTS:	map_ctrl containing lots of information about the map and a
1180  *		pointer to it's lock which will be required.
1181  *
1182  * OUTPUTS:	SUCCESS = Map updated
1183  *		FAILURE = Map not updated
1184  */
1185 suc_code
1186 update_netgroup_byxxx(map_ctrl *map) {
1187 	/* Name of temporary entries DBM file */
1188 	char			*temp_entries;
1189 	/* Name of temporary TTL DBM file */
1190 	char			*temp_ttl;
1191 	/* Temporary DBM handles */
1192 	DBM			*temp_entries_db;
1193 	DBM			*temp_ttl_db;
1194 	map_ctrl		temp_map;
1195 	char			*myself = "update_netgroup_byxxx";
1196 	char			*cmdbuf;
1197 	int			cmd_length;
1198 	datum			key;
1199 	map_ctrl		*netgroupmap;
1200 	int			res;
1201 	/* Temporary revnetgroup files */
1202 	const char 		*byhost = NETGROUP_BYHOST "_REV" TEMP_POSTFIX;
1203 	const char 		*byuser = NETGROUP_BYUSER "_REV" TEMP_POSTFIX;
1204 	const char		*temp_file_name;
1205 
1206 
1207 	/*
1208 	 * We need to use two different temporary files: one for netgroup.byhost
1209 	 * and other for netgroup.byuser, since these two maps can be updated
1210 	 * simultaneously. These temporary files will hold the output of
1211 	 * revnetgroup [-h|-u] command. They are then used to generate the
1212 	 * corresponding dbm files and thereafter deleted.
1213 	 */
1214 	if (0 == strcmp(map->map_name, NETGROUP_BYHOST))
1215 		temp_file_name = byhost;
1216 	else
1217 		temp_file_name = byuser;
1218 
1219 	/* Alloc enough cmd buf for revnet cmd */
1220 	cmd_length = strlen("/usr/sbin/makedbm -u ") +
1221 			(strlen(map->map_path) - strlen(map->map_name)) +
1222 			strlen(NETGROUP_MAP) +
1223 			strlen(" | /usr/sbin/revnetgroup -h > ") +
1224 			(strlen(map->map_path) - strlen(map->map_name)) +
1225 			strlen(temp_file_name) + 1;
1226 	cmdbuf = am(myself, cmd_length);
1227 
1228 	if (NULL == cmdbuf) {
1229 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1230 				"%s: Could not alloc cmdbuf.", myself);
1231 		return (FAILURE);
1232 	}
1233 
1234 	/*
1235 	 * If necessary update (and wait for) netgroups map. This is a lot of
1236 	 * work but if the netgroup map itself is not being accessed it may
1237 	 * contain information that is not up to date with the DIT.
1238 	 *
1239 	 * We use the cmdbuf to store the qualified netgroup map name there will
1240 	 * be enough space for this but we are not yet creating the cmd.
1241 	 */
1242 	strlcpy(cmdbuf, map->map_path, strlen(map->map_path) -
1243 						strlen(map->map_name) + 1);
1244 	strcat(cmdbuf, NETGROUP_MAP);
1245 	netgroupmap = (map_ctrl *)shim_dbm_open(cmdbuf,
1246 						O_RDWR | O_CREAT, 0644);
1247 	if (NULL == netgroupmap) {
1248 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1249 				"%s: Could not update %s.", myself, cmdbuf);
1250 		sfree(cmdbuf);
1251 		return (FAILURE);
1252 	}
1253 
1254 	if (has_map_expired(netgroupmap)) {
1255 		lock_map_ctrl(netgroupmap);
1256 		update_map_if_required(netgroupmap, TRUE);
1257 		unlock_map_ctrl(netgroupmap);
1258 	}
1259 	shim_dbm_close((DBM *)netgroupmap);
1260 
1261 	/* Dump netgroup file through revnetgroup to a temp file */
1262 	strcpy(cmdbuf, "/usr/sbin/makedbm -u ");
1263 
1264 	/* Unmake the netgroup file in same domain as map */
1265 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1266 						strlen(map->map_name));
1267 	strcat(cmdbuf, NETGROUP_MAP);
1268 
1269 	if (0 == strcmp(map->map_name, NETGROUP_BYHOST)) {
1270 		strcat(cmdbuf, " | /usr/sbin/revnetgroup -h > ");
1271 	} else {
1272 		strcat(cmdbuf, " | /usr/sbin/revnetgroup -u > ");
1273 	}
1274 
1275 	/* Create temp file file in same domain as map */
1276 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1277 						strlen(map->map_name));
1278 	strcat(cmdbuf, temp_file_name);
1279 
1280 	if (0 > system(cmdbuf)) {
1281 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" "
1282 			"(errno=%d)", myself, cmdbuf, errno);
1283 		sfree(cmdbuf);
1284 		return (FAILURE);
1285 	}
1286 	sfree(cmdbuf);
1287 
1288 	/* Allocate and set up names */
1289 	if (SUCCESS != alloc_temp_names(map->map_path,
1290 				&temp_entries, &temp_ttl)) {
1291 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1292 			"%s: Unable to create map names for %s",
1293 			myself, map->map_path);
1294 		return (FAILURE);
1295 	}
1296 
1297 	/* Make the temporary DBM file */
1298 	cmdbuf = am(myself, strlen("/usr/sbin/makedbm") +
1299 			(strlen(map->map_path) - strlen(map->map_name)) +
1300 			strlen(temp_file_name) +
1301 			strlen(temp_entries) + 3);
1302 	if (NULL == cmdbuf) {
1303 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1304 				"%s: Could not allocate cmdbuf.", myself);
1305 		sfree(temp_entries);
1306 		sfree(temp_ttl);
1307 		return (FAILURE);
1308 	}
1309 
1310 	strcpy(cmdbuf, "/usr/sbin/makedbm ");
1311 	strncat(cmdbuf, map->map_path, strlen(map->map_path) -
1312 						strlen(map->map_name));
1313 	strcat(cmdbuf, temp_file_name);
1314 	strcat(cmdbuf, " ");
1315 	strcat(cmdbuf, temp_entries);
1316 
1317 	if (0 > system(cmdbuf)) {
1318 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not run \"%s\" "
1319 			"(errno=%d)", myself, cmdbuf, errno);
1320 		sfree(cmdbuf);
1321 		sfree(temp_entries);
1322 		sfree(temp_ttl);
1323 		return (FAILURE);
1324 	}
1325 
1326 	/* Already have enough command buffer to rm temporary file */
1327 	strlcpy(cmdbuf, map->map_path, strlen(map->map_path) -
1328 						strlen(map->map_name) + 1);
1329 	strcat(cmdbuf, temp_file_name);
1330 	res = unlink(cmdbuf);
1331 	/* If the temp file did not exist no problem. Probably had no entries */
1332 	if ((0 != res) && (ENOENT != errno)) {
1333 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not delete \"%s\" "
1334 			"(errno=%d)", myself, cmdbuf, errno);
1335 		sfree(temp_entries);
1336 		sfree(temp_ttl);
1337 		sfree(cmdbuf);
1338 		return (FAILURE);
1339 	}
1340 	sfree(cmdbuf);
1341 
1342 	if ((temp_entries_db = dbm_open(temp_entries, O_RDWR | O_CREAT, 0644))
1343 						== NULL) {
1344 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
1345 						myself, temp_entries);
1346 		sfree(temp_entries);
1347 		sfree(temp_ttl);
1348 		return (FAILURE);
1349 	}
1350 
1351 	if ((temp_ttl_db = dbm_open(temp_ttl, O_RDWR | O_CREAT, 0644))
1352 						== NULL) {
1353 		logmsg(MSG_NOTIMECHECK, LOG_ERR, "%s: Could not open %s",
1354 						myself, temp_ttl);
1355 		dbm_close(temp_entries_db);
1356 		sfree(temp_entries);
1357 		sfree(temp_ttl);
1358 		return (FAILURE);
1359 	}
1360 
1361 	/*
1362 	 * Set up enough of map_ctrl to call update_entry_ttl. Since there is
1363 	 * no mapping, and thus not TTL, defined for these maps use the TTL
1364 	 * values for netgroup map
1365 	 */
1366 	temp_map.map_name = NETGROUP_MAP;
1367 	temp_map.domain = map->domain;
1368 	temp_map.ttl = temp_ttl_db;
1369 
1370 	/*
1371 	 * Generate new TTL file.  Since these maps work only on the whole map
1372 	 * expiry these will not actually be used but there presence makes it
1373 	 * easier to handle these maps in the same way as other maps.
1374 	 */
1375 	key = dbm_firstkey(temp_entries_db);
1376 	while (key.dptr != 0) {
1377 		if (!is_special_key(&key))
1378 			/*
1379 			 * For these maps want all timouts to be maximum
1380 			 */
1381 			if (FAILURE == update_entry_ttl(&temp_map, &key,
1382 								TTL_MAX))
1383 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1384 					"%s: Could not update TTL for "
1385 					"(key=%s) for map %s,%s",
1386 					myself, NIL(key.dptr), map->map_name,
1387 					map->domain);
1388 		key = dbm_nextkey(temp_entries_db);
1389 	}
1390 
1391 	/* Update map TTL */
1392 	update_map_ttl(&temp_map);
1393 
1394 	/* Close temp DBM files */
1395 	dbm_close(temp_entries_db);
1396 	dbm_close(temp_ttl_db);
1397 
1398 	/* Lock access to the map for copy */
1399 	lock_map_ctrl(map);
1400 
1401 	/* Move temp maps to real ones */
1402 	rename_map(temp_entries, map->map_path, FALSE);
1403 	rename_map(temp_ttl, map->ttl_path, FALSE);
1404 
1405 	/* Free file names */
1406 	sfree(temp_entries);
1407 	sfree(temp_ttl);
1408 
1409 	/* Unlock map */
1410 	unlock_map_ctrl(map);
1411 
1412 	return (SUCCESS);
1413 }
1414