8354 sync regcomp(3C) with upstream (fix make catalog)
[unleashed/tickless.git] / usr / src / lib / libnisdb / yptol / dit_access.c
blob0dbcb27174b3fb1a18e732ca7bb6d02fc62653ea
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
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.
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.
39 * Includes. WE WANT TO USE REAL DBM FUNCTIONS SO DO NOT INCLUDE SHIM_HOOKS.H.
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"
64 extern bool delete_map(char *name);
65 extern bool rename_map(char *from, char *to, bool_t secure_map);
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
74 * Decs
76 suc_code add_special_entries(DBM *, map_ctrl *, bool_t *);
77 void free_null_terminated_list(char **list);
81 * FUNCTION: is_yptol_mode();
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.
87 * INPUTS: Nothing
89 * OUTPUTS: TRUE = Run in N2L mode
90 * FALSE = Run in traditional mode.
92 bool_t
93 is_yptol_mode()
95 struct stat filestat;
97 if (stat(YP_DEFAULTCONFFILE, &filestat) != -1)
98 return (TRUE);
100 return (FALSE);
104 * FUNCTION: read_from_dit();
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.
110 * INPUTS: Map name (unqualified)
111 * Domain name
112 * Entry key
113 * Pointer to return location
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.
120 read_from_dit(char *map, char *domain, datum *key, datum *value)
122 int count;
123 int res;
124 __nisdb_retry_t *retrieveRetry;
126 /* Initialize tsd */
127 __nisdb_get_tsd()->domainContext = 0;
128 __nisdb_get_tsd()->escapeFlag = '\0';
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;
140 retrieveRetry = &ldapDBTableMapping.retrieveErrorRetry;
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);
149 if (LDAP_TIMEOUT == res) { /* Exceeded search timeout */
150 value->dptr = NULL;
151 return (0);
154 if (is_fatal_error(res))
155 break;
158 * Didn't work. If not the special case where no repeats are
159 * done sleep.
161 if (0 != retrieveRetry->attempts)
162 (void) poll(NULL, 0, retrieveRetry->timeout*1000);
165 /* Make sure returned pointer is NULL */
166 value->dptr = NULL;
168 /* If we get here access failed work out what to return */
169 if (ldapDBTableMapping.retrieveError == use_cached)
170 return (0);
172 return (res);
176 * FUNCTION: write_to_dit();
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.
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.
191 * OUTPUTS: SUCCESS = Write was successful
192 * FAILURE = Write failed
195 suc_code
196 write_to_dit(char *map, char *domain, datum key, datum value,
197 bool_t replace, bool_t ignore_map_errs)
199 int count;
200 int res;
201 __nisdb_retry_t *storeRetry = &ldapDBTableMapping.storeErrorRetry;
203 /* Initialize tsd */
204 __nisdb_get_tsd()->domainContext = 0;
205 __nisdb_get_tsd()->escapeFlag = '\0';
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;
217 storeRetry = &ldapDBTableMapping.storeErrorRetry;
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);
226 if (is_fatal_error(res)) {
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.
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;
245 return (FAILURE);
248 if (ldapDBTableMapping.storeError != sto_retry)
249 return (FAILURE);
252 * Didn't work. If not the special case where no repeats are
253 * done sleep.
255 if (0 != storeRetry->attempts)
256 (void) poll(NULL, 0, storeRetry->timeout*1000);
259 return (FAILURE);
263 * FUNCTION : get_ttl_value()
265 * DESCRIPTION: Get the TTL value, derived from mapping file or DIT, for a
266 * entry.
268 * GIVEN : Pointer to map
269 * A flag indication if TTL should be max, min or random
271 * RETURNS : TTL value in seconds.
272 * -1 on failure
275 get_ttl_value(map_ctrl *map, TTL_TYPE type)
277 __nis_table_mapping_t *table_map;
278 int interval, res;
279 char *myself = "get_ttl_value";
281 /* Get the mapping structure corresponding to `map.domain' */
282 table_map = mappingFromMap(map->map_name, map->domain, &res);
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);
291 switch (type) {
292 case TTL_MAX:
293 return (table_map->initTtlHi);
295 case TTL_MIN:
296 return (table_map->initTtlLo);
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 */
303 case TTL_RAND:
304 interval = table_map->initTtlHi - table_map->initTtlLo;
305 if (0 >= interval)
306 return (table_map->initTtlLo);
309 * Must get a random value. We assume srand48() got
310 * called at initialization.
312 return (lrand48() % interval);
314 case TTL_RUNNING:
315 return (table_map->ttl);
322 * FUNCTION : get_mapping_domain_list()
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.
329 * GIVEN : Pointer returned array.
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.
336 get_mapping_domain_list(char ***ptr)
338 *ptr = ypDomains.domainLabels;
339 return (ypDomains.numDomains);
343 * FUNCTION : get_mapping_yppasswdd_domain_list()
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.
349 * GIVEN : Pointer returned array
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.
357 get_mapping_yppasswdd_domain_list(char ***ptr)
359 *ptr = ypDomains.yppasswddDomainLabels;
360 return (ypDomains.numYppasswdd);
364 * FUNCTION : free_map_list()
366 * DESCRIPTION: Frees a map list.
368 * GIVEN : Pointer to the map list.
370 * RETURNS : Nothing
372 void
373 free_map_list(char **map_list)
375 free_null_terminated_list(map_list);
379 * FUNCTION : get_passwd_list()
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.
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
392 * GIVEN : Flag indicating if a list is required for passwd or
393 * passwd.adjunct
394 * Domain to return the list for.
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
400 char **
401 get_passwd_list(bool_t adjunct, char *domain)
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 */
416 * Always need an array even if just for terminator. Normally one
417 * chunk will be enough.
419 res = am(myself, ARRAY_CHUNK * sizeof (char *));
420 if (NULL == res)
421 return (NULL);
422 array_size = ARRAY_CHUNK;
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);
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;
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;
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);
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);
474 /* Copy from start to end_ptr */
475 (void) memcpy(res[res_count], it->name,
476 end_ptr-it->name - 1);
477 res_count ++;
481 /* Terminate array */
482 res[res_count] = NULL;
483 return (res);
487 * FUNCTION : free_passwd_list()
489 * DESCRIPTION: Frees a password list obtained with get_passwd_list()
491 * INPUTS : Address of list to free.
493 * OUTPUTS : Nothing
495 void
496 free_passwd_list(char **list)
498 free_null_terminated_list(list);
502 * FUNCTION : free_null_terminated_list()
504 * DESCRIPTION: Frees a generic null terminated list.
506 * INPUTS : Address of list to free.
508 * OUTPUTS : Nothing
510 void
511 free_null_terminated_list(char **list)
513 int index;
515 /* Free all the strings */
516 for (index = 0; NULL != list[index]; index ++)
517 sfree(list[index]);
519 /* Free the array */
520 sfree(list);
525 * FUNCTION : add_special_entries()
527 * DESCRIPTION: Adds the special (YP_*) entries to a map.
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.
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
537 * RETURNS : SUCCESS = All entries created
538 * FAILURE = Some entries not created
540 suc_code
541 add_special_entries(DBM *db, map_ctrl *map, bool_t *secure_flag)
543 char local_host[MAX_MASTER_NAME];
544 __nis_table_mapping_t *table_map;
545 int res;
547 /* Last modified time is now */
548 update_timestamp(db);
550 /* Add domain name */
551 addpair(db, yp_domain_name, map->domain);
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);
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);
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;
569 if (table_map->usedns_flag)
570 addpair(db, yp_interdomain, "");
572 return (SUCCESS);
576 * FUNCTION: update_map_from_dit()
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.
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.
587 * OUTPUTS: SUCCESS = Map updated
588 * FAILURE = Map not updated
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;
615 int m;
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
623 bool_t xlate_to_lcase = FALSE;
625 if (!map || !map->map_name || !map->domain) {
626 return (FAILURE);
629 __nisdb_get_tsd()->escapeFlag = '\0';
632 * netgroup.byxxx maps are a special case. They are regenerated from
633 * the netgroup map, not the DIT, so handle special case.
635 if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) ||
636 0 == (strcmp(map->map_name, NETGROUP_BYUSER))) {
637 return (update_netgroup_byxxx(map));
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);
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);
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);
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);
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;
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);
703 * set xlate_to_lcase to TRUE if map_name is found in
704 * xlate_to_lcase_maps[]
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;
713 ++m;
716 /* Try each mapping for the map */
717 for (flag = 0; t != 0 && search_flag != FAILURE; t = t->next) {
719 /* Check if the mapping is the correct one */
720 if (strcmp(objname, t->objName) != 0) {
721 continue;
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;
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;
752 if (log_flag) {
753 printf("Waiting for LDAP search results.\n");
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) {
763 * No Entry exists in the ldap server. Not
764 * a problem. Maybe there are just no entries
765 * in this map.
767 continue;
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;
780 if (log_flag) {
781 printf("Processing search results.\n");
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;
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;
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;
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;
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) {
852 * For some cases errno may
853 * still be 0 but dbm_error
854 * isn't informative at all.
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);
871 sfree(datkey[j].dptr);
873 if (log_flag && (entry_count >=
874 next_print)) {
875 printf("%d entries processed\n",
876 entry_count);
877 next_print *= 2;
881 sfree(datkey);
882 sfree(datval->dptr);
883 sfree(datval);
886 freeRuleValue(rv, nr);
887 freeDNs(dn, numDNs);
888 } /* End of for over objectDN */
890 sfree(objname);
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);
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;
908 /* Generate new TTL file */
909 key = dbm_firstkey(temp_entries_db);
910 while (key.dptr != 0) {
911 if (!is_special_key(&key))
913 * We don't want all the entries to time out at the
914 * same time so create random TTLs.
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);
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);
932 /* Set up 'special' nis entries */
933 add_special_entries(temp_entries_db, map, &secure_flag);
935 /* Close temp DBM files */
936 dbm_close(temp_entries_db);
937 dbm_close(temp_ttl_db);
939 /* Lock access to the map for copy */
940 lock_map_ctrl(map);
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);
946 /* Free file names */
947 sfree(temp_entries);
948 sfree(temp_ttl);
950 /* Unlock map */
951 unlock_map_ctrl(map);
953 return (SUCCESS);
957 * FUNCTION : get_mapping_map_list()
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.
964 * GIVEN : Domain name
966 * RETURNS : List of map names in malloced memory. MUST BE FREED BY CALLER.
968 char **
969 get_mapping_map_list(char *domain)
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 */
981 * Always need an array even if just for terminator. Normally one
982 * chunk will be enough.
984 res = am(myself, ARRAY_CHUNK * sizeof (char *));
985 if (NULL == res)
986 return (NULL);
987 array_size = ARRAY_CHUNK;
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) {
996 /* Check it's not a split field entry */
997 if (0 != ((__nis_table_mapping_t *)it)->numSplits)
998 continue;
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;
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);
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.
1028 res[res_count] = (char *)it;
1029 res_count ++;
1033 /* Terminate array */
1034 res[res_count] = NULL;
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;
1048 /* Finally copy the real strings in to each entry */
1049 for (i = 0; NULL != res[i]; i ++) {
1051 /* Get hash table entry back */
1052 it = (__nis_hash_item_mt *)res[i];
1054 end_ptr = it->name + strlen(it->name) - strlen(domain) - 1;
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);
1063 /* Copy from start to end_ptr */
1064 (void) memcpy(res[i], it->name, end_ptr-it->name - 1);
1067 return (res);
1071 * FUNCTION : make_nis_container()
1073 * DESCRIPTION: Sets up container for map_name in the DIT.
1075 * GIVEN : Map name
1076 * The domain name.
1077 * Flag indicating if container should be created.
1079 * RETURNS : SUCCESS = It worked
1080 * FAILURE = There was a problem.
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";
1089 if (!map_name || !domain)
1090 return (FAILURE);
1092 if (FALSE == init_containers) {
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.
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.
1103 return (SUCCESS);
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);
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;
1127 if (!strcasecmp(dn, t->objectDN->write.base))
1128 break;
1129 dn = t->objectDN->write.base;
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);
1139 return (SUCCESS);
1143 * FUNCTION : make_nis_domain()
1145 * DESCRIPTION: Sets up a nisDomainObject in the DIT
1147 * GIVEN: Name of the domain
1148 * Flag indicating if domain should be create or possibly just
1149 * checked for.
1151 suc_code
1152 make_nis_domain(char *domain, bool_t init_containers) {
1154 if (FALSE == init_containers) {
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.
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.
1165 return (SUCCESS);
1168 /* Create the domain */
1169 return (makeNISObject(domain, 0));
1173 * FUNCTION: update_netgroup_byxxx()
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.
1179 * INPUTS: map_ctrl containing lots of information about the map and a
1180 * pointer to it's lock which will be required.
1182 * OUTPUTS: SUCCESS = Map updated
1183 * FAILURE = Map not updated
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;
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.
1214 if (0 == strcmp(map->map_name, NETGROUP_BYHOST))
1215 temp_file_name = byhost;
1216 else
1217 temp_file_name = byuser;
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);
1228 if (NULL == cmdbuf) {
1229 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1230 "%s: Could not alloc cmdbuf.", myself);
1231 return (FAILURE);
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.
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.
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);
1254 if (has_map_expired(netgroupmap)) {
1255 lock_map_ctrl(netgroupmap);
1256 update_map_if_required(netgroupmap, TRUE);
1257 unlock_map_ctrl(netgroupmap);
1259 shim_dbm_close((DBM *)netgroupmap);
1261 /* Dump netgroup file through revnetgroup to a temp file */
1262 strcpy(cmdbuf, "/usr/sbin/makedbm -u ");
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);
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 > ");
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);
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);
1286 sfree(cmdbuf);
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);
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);
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);
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);
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);
1340 sfree(cmdbuf);
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);
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);
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
1366 temp_map.map_name = NETGROUP_MAP;
1367 temp_map.domain = map->domain;
1368 temp_map.ttl = temp_ttl_db;
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.
1375 key = dbm_firstkey(temp_entries_db);
1376 while (key.dptr != 0) {
1377 if (!is_special_key(&key))
1379 * For these maps want all timouts to be maximum
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);
1391 /* Update map TTL */
1392 update_map_ttl(&temp_map);
1394 /* Close temp DBM files */
1395 dbm_close(temp_entries_db);
1396 dbm_close(temp_ttl_db);
1398 /* Lock access to the map for copy */
1399 lock_map_ctrl(map);
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);
1405 /* Free file names */
1406 sfree(temp_entries);
1407 sfree(temp_ttl);
1409 /* Unlock map */
1410 unlock_map_ctrl(map);
1412 return (SUCCESS);