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