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]
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.
44 #include <sys/systeminfo.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"
61 #include "dit_access_utils.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 */
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.
89 * OUTPUTS: TRUE = Run in N2L mode
90 * FALSE = Run in traditional mode.
97 if (stat(YP_DEFAULTCONFFILE
, &filestat
) != -1)
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)
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
)
124 __nisdb_retry_t
*retrieveRetry
;
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
])
133 if (0 == strcasecmp(domain
, ypDomains
.domainLabels
[count
])) {
134 __nisdb_get_tsd()->domainContext
=
135 ypDomains
.domains
[count
];
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 */
149 if (LDAP_TIMEOUT
== res
) { /* Exceeded search timeout */
154 if (is_fatal_error(res
))
158 * Didn't work. If not the special case where no repeats are
161 if (0 != retrieveRetry
->attempts
)
162 (void) poll(NULL
, 0, retrieveRetry
->timeout
*1000);
165 /* Make sure returned pointer is NULL */
168 /* If we get here access failed work out what to return */
169 if (ldapDBTableMapping
.retrieveError
== use_cached
)
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
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
196 write_to_dit(char *map
, char *domain
, datum key
, datum value
,
197 bool_t replace
, bool_t ignore_map_errs
)
201 __nisdb_retry_t
*storeRetry
= &ldapDBTableMapping
.storeErrorRetry
;
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
])
210 if (0 == strcasecmp(domain
, ypDomains
.domainLabels
[count
])) {
211 __nisdb_get_tsd()->domainContext
=
212 ypDomains
.domains
[count
];
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
)
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)
233 if (ignore_map_errs
) {
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
:
248 if (ldapDBTableMapping
.storeError
!= sto_retry
)
252 * Didn't work. If not the special case where no repeats are
255 if (0 != storeRetry
->attempts
)
256 (void) poll(NULL
, 0, storeRetry
->timeout
*1000);
263 * FUNCTION : get_ttl_value()
265 * DESCRIPTION: Get the TTL value, derived from mapping file or DIT, for a
268 * GIVEN : Pointer to map
269 * A flag indication if TTL should be max, min or random
271 * RETURNS : TTL value in seconds.
275 get_ttl_value(map_ctrl
*map
, TTL_TYPE type
)
277 __nis_table_mapping_t
*table_map
;
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
);
293 return (table_map
->initTtlHi
);
296 return (table_map
->initTtlLo
);
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 */
304 interval
= table_map
->initTtlHi
- table_map
->initTtlLo
;
306 return (table_map
->initTtlLo
);
309 * Must get a random value. We assume srand48() got
310 * called at initialization.
312 return (lrand48() % interval
);
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
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.
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
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
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)
401 get_passwd_list(bool_t adjunct
, char *domain
)
403 char *myself
= "get_passwd_list";
404 __nis_hash_item_mt
*it
;
407 char *target
; /* What we are looking for */
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 *));
422 array_size
= ARRAY_CHUNK
;
426 target
= PASSWD_ADJUNCT_PREFIX
;
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
))
441 if (0 != strncmp(it
->name
, target
, target_len
))
444 /* Check right domain (minus trailing dot) */
445 if (strlen(domain
) >= strlen(it
->name
))
447 end_ptr
= it
->name
+ strlen(it
->name
) -
449 if (',' != *(end_ptr
- 1))
451 if (0 != strncmp(end_ptr
, domain
, strlen(domain
)))
454 /* Check if we need to enlarge array */
455 if ((res_count
+ 1) >= array_size
) {
456 array_size
+= ARRAY_CHUNK
;
458 res
= realloc(res
, array_size
*
461 res_old
[res_count
] = NULL
;
462 free_passwd_list(res_old
);
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
);
474 /* Copy from start to end_ptr */
475 (void) memcpy(res
[res_count
], it
->name
,
476 end_ptr
-it
->name
- 1);
481 /* Terminate array */
482 res
[res_count
] = NULL
;
487 * FUNCTION : free_passwd_list()
489 * DESCRIPTION: Frees a password list obtained with get_passwd_list()
491 * INPUTS : Address of list to free.
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.
511 free_null_terminated_list(char **list
)
515 /* Free all the strings */
516 for (index
= 0; NULL
!= list
[index
]; index
++)
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
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
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
;
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
);
562 /* Add secure and interdomain flags if required */
563 if (table_map
->securemap_flag
) {
564 addpair(db
, yp_secure
, "");
567 *secure_flag
= FALSE
;
569 if (table_map
->usedns_flag
)
570 addpair(db
, yp_interdomain
, "");
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
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
;
600 /* Name of temporary entries DBM file */
602 /* Name of temporary TTL DBM file */
604 /* Temporary DBM handles */
605 DBM
*temp_entries_db
;
609 char *myself
= "update_map_from_dit";
612 int next_print
= PRINT_FREQ
;
613 int search_flag
= SUCCESS
;
617 /* list of maps whose keys will be transliterated to lowercase */
618 char *xlate_to_lcase_maps
[] = {
623 bool_t xlate_to_lcase
= FALSE
;
625 if (!map
|| !map
->map_name
|| !map
->domain
) {
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
);
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
);
658 /* Create temp entry and TTL file */
659 if ((temp_entries_db
= dbm_open(temp_entries
, O_RDWR
| O_CREAT
, 0644))
661 logmsg(MSG_NOTIMECHECK
, LOG_ERR
, "%s: Could not open %s",
662 myself
, temp_entries
);
668 if ((temp_ttl_db
= dbm_open(temp_ttl
, O_RDWR
| O_CREAT
, 0644))
670 logmsg(MSG_NOTIMECHECK
, LOG_ERR
, "%s: Could not open %s",
672 dbm_close(temp_entries_db
);
673 delete_map(temp_entries
);
679 /* Initialize domainContext tsd */
680 __nisdb_get_tsd()->domainContext
= 0;
681 for (i
= 0; i
< ypDomains
.numDomains
; i
++) {
682 if (0 == ypDomains
.domainLabels
[i
])
684 if (0 == strcasecmp(map
->domain
, ypDomains
.domainLabels
[i
])) {
685 __nisdb_get_tsd()->domainContext
= ypDomains
.domains
[i
];
690 if (!(objname
= getFullMapName(map
->map_name
, map
->domain
))) {
692 dbm_close(temp_entries_db
);
694 dbm_close(temp_ttl_db
);
695 delete_map(temp_entries
);
697 delete_map(temp_ttl
);
703 * set xlate_to_lcase to TRUE if map_name is found in
704 * xlate_to_lcase_maps[]
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
;
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) {
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
);
732 /* Set flag to indicate update is enabled */
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
,
740 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
741 "%s: Failed to create "
742 "ldapSearch request for "
743 "%s (%s) for base %s",
746 objectDN
->read
.base
);
748 search_flag
= FAILURE
;
753 printf("Waiting for LDAP search results.\n");
757 nr
= (ls
->isDN
)?0:-1;
758 rv
= ldapSearch(ls
, &nr
, 0, &statP
);
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
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
);
776 search_flag
= FAILURE
;
781 printf("Processing search results.\n");
784 /* Obtain list of DNs for logging */
785 if ((dn
= findDNs(myself
, rv
, nr
, 0, &numDNs
)) == 0) {
787 search_flag
= FAILURE
;
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
],
796 if (statP
== MAP_INDEXLIST_ERROR
)
798 if (statP
!= SUCCESS
) {
799 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
800 "%s: Conversion error %d (LDAP to "
803 "%s (%s) for base %s",
804 myself
, statP
, NIL(dn
[i
]),
805 t
->dbId
, map
->map_name
,
806 objectDN
->read
.base
);
810 /* Obtain the datum for value */
811 datval
= ruleValueToDatum(t
, &rv
[i
], &statP
);
813 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
814 "%s: Conversion error %d "
815 "(name=value pairs to NIS)"
817 "%s (%s) for base %s",
818 myself
, statP
, NIL(dn
[i
]),
819 t
->dbId
, map
->map_name
,
820 objectDN
->read
.base
);
824 /* Obtain the datum for key */
825 datkey
= getKeyFromRuleValue(t
, &rv
[i
],
826 &nv
, &statP
, xlate_to_lcase
);
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
,
834 objectDN
->read
.base
);
840 /* Write to the temporary map */
841 for (j
= 0; j
< nv
; j
++, entry_count
++) {
842 if (datkey
[j
].dsize
== 0)
845 /* DBM_INSERT to match */
846 /* singleReadFromDIT */
847 if (dbm_store(temp_entries_db
,
852 * For some cases errno may
853 * still be 0 but dbm_error
854 * isn't informative at all.
856 logmsg(MSG_NOTIMECHECK
,
858 "%s: dbm store error "
860 "for (key=%s, value=%s) "
861 "for %s (%s) for base %s",
865 datval
->dptr
, t
->dbId
,
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
>=
875 printf("%d entries processed\n",
886 freeRuleValue(rv
, nr
);
888 } /* End of for over objectDN */
892 if (t
!= 0 || flag
== 0 || search_flag
== FAILURE
) {
894 dbm_close(temp_entries_db
);
896 dbm_close(temp_ttl_db
);
897 delete_map(temp_entries
);
899 delete_map(temp_ttl
);
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
,
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
,
923 key
= dbm_nextkey(temp_entries_db
);
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 */
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 */
951 unlock_map_ctrl(map
);
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.
969 get_mapping_map_list(char *domain
)
971 char *myself
= "get_mapping_map_list";
972 __nis_hash_item_mt
*it
;
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 *));
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
)
1000 /* Check right domain (minus trailing dot) */
1001 if (strlen(domain
) >= strlen(it
->name
))
1003 end_ptr
= it
->name
+ strlen(it
->name
) -
1005 if (',' != *(end_ptr
- 1))
1007 if (0 != strncmp(end_ptr
, domain
, strlen(domain
)))
1010 /* Check if we need to enlarge array */
1011 if ((res_count
+ 1) >= array_size
) {
1012 array_size
+= ARRAY_CHUNK
;
1014 res
= realloc(res
, array_size
*
1017 res_old
[res_count
] = NULL
;
1018 free_passwd_list(res_old
);
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
;
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
) {
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
]) {
1063 /* Copy from start to end_ptr */
1064 (void) memcpy(res
[i
], it
->name
, end_ptr
-it
->name
- 1);
1071 * FUNCTION : make_nis_container()
1073 * DESCRIPTION: Sets up container for map_name in the DIT.
1077 * Flag indicating if container should be created.
1079 * RETURNS : SUCCESS = It worked
1080 * FAILURE = There was a problem.
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
;
1087 char *myself
= "make_nis_container";
1089 if (!map_name
|| !domain
)
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
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
));
1115 /* Two times. One for readDN and other for writeDN */
1116 for (i
= 0; i
< 2; i
++) {
1118 dn
= t
->objectDN
->read
.base
;
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
);
1127 if (!strcasecmp(dn
, t
->objectDN
->write
.base
))
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
);
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
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
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
1186 update_netgroup_byxxx(map_ctrl
*map
) {
1187 /* Name of temporary entries DBM file */
1189 /* Name of temporary TTL DBM file */
1191 /* Temporary DBM handles */
1192 DBM
*temp_entries_db
;
1195 char *myself
= "update_netgroup_byxxx";
1199 map_ctrl
*netgroupmap
;
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
;
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
);
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
);
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 > ");
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
);
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
);
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
);
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
);
1321 sfree(temp_entries
);
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
);
1342 if ((temp_entries_db
= dbm_open(temp_entries
, O_RDWR
| O_CREAT
, 0644))
1344 logmsg(MSG_NOTIMECHECK
, LOG_ERR
, "%s: Could not open %s",
1345 myself
, temp_entries
);
1346 sfree(temp_entries
);
1351 if ((temp_ttl_db
= dbm_open(temp_ttl
, O_RDWR
| O_CREAT
, 0644))
1353 logmsg(MSG_NOTIMECHECK
, LOG_ERR
, "%s: Could not open %s",
1355 dbm_close(temp_entries_db
);
1356 sfree(temp_entries
);
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
,
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
,
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 */
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
);
1410 unlock_map_ctrl(map
);