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