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