Home | History | Annotate | Download | only in libnisdb
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  *	Copyright (c) 2001 by Sun Microsystems, Inc.
     24  *	All Rights Reserved.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <stdio.h>
     30 
     31 #include <malloc.h>
     32 #include <strings.h>
     33 #include <string.h>
     34 #include <sys/param.h>
     35 #include <sys/types.h>
     36 #include <time.h>
     37 #include "db_headers.h"
     38 #include "db.h"
     39 #include "db_mindex.h"
     40 #include "db_pickle.h"
     41 #include "nisdb_mt.h"
     42 #include "nisdb_ldap.h"
     43 #include "ldap_nisdbquery.h"
     44 #include "ldap_map.h"
     45 #include "ldap_ruleval.h"
     46 #include "ldap_scheme.h"
     47 #include "ldap_parse.h"
     48 #include "nis_hashitem.h"
     49 #include "nis_servlist.h"
     50 #include "ldap_nisplus.h"
     51 #include "nis_db.h"
     52 #include "ldap_glob.h"
     53 
     54 /* Pass through configuration information to the table */
     55 bool_t
     56 db_mindex::configure(char *tablePath) {
     57 	if (tablePath == NULL)
     58 		return (FALSE);
     59 
     60 	if (objPath.ptr != 0)
     61 		free(objPath.ptr);
     62 	objPath.ptr = strdup(tablePath);
     63 
     64 	if (table != NULL) {
     65 		return (table->configure(tablePath));
     66 	} else {
     67 		/* Defer table config until we have a table instance */
     68 		return (objPath.ptr != NULL);
     69 	}
     70 }
     71 
     72 /*
     73  * The noWriteThrough flag is used to prevent modifies/updates to LDAP
     74  * while we're incorporating log data into the in-memory tables.
     75  */
     76 void
     77 db_mindex::setNoWriteThrough(void) {
     78 	ASSERTWHELD(this->mindex);
     79 	noWriteThrough.flag++;
     80 }
     81 
     82 void
     83 db_mindex::clearNoWriteThrough(void) {
     84 	ASSERTWHELD(this->mindex);
     85 	if (noWriteThrough.flag > 0)
     86 		noWriteThrough.flag--;
     87 #ifdef	NISDB_LDAP_DEBUG
     88 	else
     89 		abort();
     90 #endif	/* NISDB_LDAP_DEBUG */
     91 }
     92 
     93 /*
     94  * The noLDAPquery flag is used to prevent recursive LDAP queries when
     95  * satisfy_query() is re-entered as we add an entry from queryLDAP().
     96  */
     97 void
     98 db_mindex::setNoLDAPquery(void) {
     99 	ASSERTWHELD(this->mindex);
    100 	noLDAPquery.flag++;
    101 }
    102 
    103 void
    104 db_mindex::clearNoLDAPquery(void) {
    105 	ASSERTWHELD(this->mindex);
    106 	if (noLDAPquery.flag > 0)
    107 		noLDAPquery.flag--;
    108 #ifdef	NISDB_LDAP_DEBUG
    109 	else
    110 		abort();
    111 #endif	/* NISDB_LDAP_DEBUG */
    112 }
    113 
    114 /*
    115  * The initialLoad flag tells us if an add or remove is done as part of
    116  * the initial load of data, in which case we should use the initial TTLs.
    117  */
    118 void
    119 db_mindex::setInitialLoad(void) {
    120 	ASSERTWHELD(this->mindex);
    121 	initialLoad.flag++;
    122 }
    123 
    124 void
    125 db_mindex::clearInitialLoad(void) {
    126 	ASSERTWHELD(this->mindex);
    127 	if (initialLoad.flag > 0)
    128 		initialLoad.flag--;
    129 #ifdef	NISDB_LDAP_DEBUG
    130 	else
    131 		abort();
    132 #endif	/* NISDB_LDAP_DEBUG */
    133 }
    134 
    135 void
    136 db_mindex::setDbPtr(void *ptr) {
    137 	dbptr.ptr = ptr;
    138 }
    139 
    140 void *
    141 db_mindex::getDbPtr(void) {
    142 	return (dbptr.ptr);
    143 }
    144 
    145 db_table *
    146 db_mindex::getTable(void) {
    147 	return (table);
    148 }
    149 
    150 extern void	db_free_result(db_result *);
    151 
    152 zotypes
    153 updateMappingObj(__nis_table_mapping_t *t, char **objNameP,
    154 		bool_t *isMasterP) {
    155 	zotypes	type = NIS_BOGUS_OBJ;
    156 	char	*objName = 0;
    157 	char	*myself = "updateMappingObj";
    158 
    159 	if (t != 0)
    160 		objName = t->objName;
    161 	else if (objNameP != 0)
    162 		objName = *objNameP;
    163 	else
    164 		return (NIS_BOGUS_OBJ);
    165 
    166 	if (objName != 0) {
    167 		db_status	stat;
    168 		int		lstat = LDAP_SUCCESS;
    169 		nis_object	*o = dbFindObject(objName, &stat);
    170 
    171 		/* If not found in the local DB, try LDAP */
    172 		if (o == 0) {
    173 			if (stat != DB_NOTFOUND) {
    174 				logmsg(MSG_NOTIMECHECK, LOG_INFO,
    175 					"%s: DB err %d for \"%s\"",
    176 					myself, stat, NIL(objName));
    177 			}
    178 			o = ldapFindObj(t, objName, &lstat);
    179 			/* If found, refresh/create the local copy */
    180 			if (o != 0) {
    181 				db_status	rstat;
    182 				rstat = dbRefreshObj(objName, o);
    183 				if (rstat != DB_SUCCESS)
    184 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    185 					"%s: DB error %d refreshing \"%s\"",
    186 					myself, rstat, NIL(objName));
    187 			}
    188 		}
    189 
    190 		if (o != 0) {
    191 			type = o->zo_data.zo_type;
    192 			if (objNameP != 0) {
    193 				*objNameP = sdup(myself, T, objName);
    194 				if (*objNameP == 0) {
    195 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    196 				"%s: Unable to copy object name (\"%s\")",
    197 						myself, NIL(objName));
    198 				}
    199 			}
    200 			if (t != 0) {
    201 				if (!setMappingObjTypeEtc(t, o))
    202 					nis_destroy_object(o);
    203 
    204 				if (isMasterP != 0)
    205 					*isMasterP = t->isMaster;
    206 			} else {
    207 				if (isMasterP != 0)
    208 					*isMasterP = isMaster(o);
    209 				nis_destroy_object(o);
    210 			}
    211 		} else if (lstat != LDAP_SUCCESS) {
    212 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
    213 				"%s: LDAP err %d for \"%s\"",
    214 				myself, lstat, NIL(objName));
    215 		}
    216 	}
    217 
    218 	return (type);
    219 }
    220 
    221 static __nis_table_mapping_t *
    222 mappingFromObj(nis_object *obj, int *statP) {
    223 	__nis_table_mapping_t	*t;
    224 	__nis_buffer_t		b = {0, 0};
    225 	char			*objPath;
    226 	char			*myself = "mappingFromObj";
    227 
    228 	if (obj == 0 || obj->zo_data.zo_type == NIS_ENTRY_OBJ)
    229 		return (0);
    230 
    231 	/*
    232 	 * Convert full object name to the db table path used as
    233 	 * key for the mapping hash list.
    234 	 */
    235 	bp2buf(myself, &b, "%s.%s",
    236 		NIL(obj->zo_name), NIL(obj->zo_domain));
    237 	objPath = internalTableName(b.buf);
    238 	sfree(b.buf);
    239 	if (slen(objPath) <= 0) {
    240 		if (statP != 0)
    241 			*statP = LDAP_OPERATIONS_ERROR;
    242 		sfree(objPath);
    243 		return (0);
    244 	}
    245 
    246 	t = (__nis_table_mapping_t *)__nis_find_item_mt(objPath,
    247 						&ldapMappingList, 0, 0);
    248 
    249 	sfree(objPath);
    250 
    251 	return (t);
    252 }
    253 
    254 static __nis_table_mapping_t *
    255 selectMapping(db_table *table, nis_object *obj, db_query *qin,
    256 		bool_t wantWrite, bool_t *asObjP, int *statP) {
    257 	__nis_table_mapping_t	*t;
    258 	__nis_buffer_t		b = {0, 0};
    259 	bool_t			doLDAP, asObj;
    260 	int			stat = LDAP_SUCCESS;
    261 	char			*objPath = 0, buf[MAXPATHLEN+NIS_MAXNAMELEN+1];
    262 	char			*myself = "db_mindex::selectMapping";
    263 
    264 	/*
    265 	 * If 'table' is NULL, we try to find a mapping for 'obj'.
    266 	 * We expect this to happen when our caller wants to write
    267 	 * the object from a directory entry to LDAP.
    268 	 */
    269 	if (table == 0) {
    270 		if (asObjP != 0)
    271 			*asObjP = TRUE;
    272 		if (statP != 0)
    273 			*statP = LDAP_SUCCESS;
    274 
    275 		t = mappingFromObj(obj, statP);
    276 
    277 		if (t == 0)
    278 			return (0);
    279 
    280 		/*
    281 		 * Should the object type in the mapping be NIS_BOGUS_OBJ,
    282 		 * we need to determine what kind of object this is.
    283 		 */
    284 		if (t->objType == NIS_BOGUS_OBJ) {
    285 			t->objType = updateMappingObj(t, 0, 0);
    286 			if (t->objType == NIS_BOGUS_OBJ) {
    287 				if (statP != 0)
    288 					*statP = LDAP_OPERATIONS_ERROR;
    289 				return (0);
    290 			}
    291 		}
    292 
    293 		/*
    294 		 * If caller wants a mapping suitable for writing,
    295 		 * check that we're the master for this object.
    296 		 */
    297 		if (wantWrite && !t->isMaster)
    298 			return (0);
    299 
    300 		return (t);
    301 	}
    302 
    303 	/*
    304 	 * If the object type for the mapping is NIS_BOGUS_OBJ, then
    305 	 * we haven't yet been able to determine what kind of object this
    306 	 * is. Try to fix that now.
    307 	 */
    308 	if (table->mapping.objType == NIS_BOGUS_OBJ) {
    309 		table->mapping.objType = updateMappingObj(table->mapping.tm,
    310 						&table->mapping.objName,
    311 						&table->mapping.isMaster);
    312 		table->mapping.expireType = table->mapping.objType;
    313 	}
    314 
    315 	/*
    316 	 * Depending on the object type (table->mapping.objType):
    317 	 *
    318 	 *	table		Use table->mapping.tm to query LDAP
    319 	 *			for entries per 'qin'.
    320 	 *
    321 	 *	directory	Use 'qin' and table->mapping.objName
    322 	 *			to retrieve a mapping entry, and then
    323 	 *			query LDAP for the corresponding object.
    324 	 *			'qin' == NULL means reading/writing the
    325 	 *			entire directory object, plus the names
    326 	 *			of the directory entries.
    327 	 *
    328 	 *	bogus		Not mapping this object. However, we may
    329 	 *			still be mapping the object 'obj'.
    330 	 *
    331 	 *	other		Shouldn't happen; illegal.
    332 	 */
    333 	switch (table->mapping.objType) {
    334 	case NIS_TABLE_OBJ:
    335 		t = table->mapping.tm;
    336 		if (wantWrite)
    337 			doLDAP = table->mapping.isMaster &&
    338 					table->mapping.toLDAP;
    339 		else
    340 			doLDAP = table->mapping.fromLDAP;
    341 		asObj = FALSE;
    342 		break;
    343 	case NIS_DIRECTORY_OBJ: {
    344 		char		*sub = 0;
    345 		int		nqc, len = 0;
    346 		db_qcomp	*qc;
    347 
    348 		t = 0;
    349 		doLDAP = FALSE;
    350 		asObj = TRUE;
    351 
    352 		/*
    353 		 * We expect the query to have one component, containing
    354 		 * the directory entry name. If there's no query, we want
    355 		 * an enumeration of the entries in the directory. They're
    356 		 * stored with the XDR:ed directory object in LDAP, so
    357 		 * asObj should be TRUE.
    358 		 */
    359 		if (qin == 0) {
    360 			t = table->mapping.tm;
    361 			if (wantWrite)
    362 				doLDAP = table->mapping.isMaster &&
    363 					table->mapping.toLDAP;
    364 			else
    365 				doLDAP = table->mapping.fromLDAP;
    366 			asObj = TRUE;
    367 			break;
    368 		}
    369 
    370 		nqc = qin->size();
    371 		if (nqc != 1 || (qc = qin->queryloc()) == 0 ||
    372 				qc[0].index_value == 0) {
    373 			stat = LDAP_PARAM_ERROR;
    374 			break;
    375 		}
    376 		qc[0].index_value->get_value(&sub, &len);
    377 		if (sub == 0 || len <= 0) {
    378 			stat = LDAP_PARAM_ERROR;
    379 			break;
    380 		}
    381 
    382 		/* Append directory name to dir entry name */
    383 		sbc2buf(myself, sub, len, &b);
    384 		bp2buf(myself, &b, ".%s", table->mapping.objName);
    385 
    386 		/* Convert to the DB internal name */
    387 		objPath = internal_table_name(b.buf, buf);
    388 		sfree(b.buf);
    389 		if (slen(objPath) <= 0) {
    390 			stat = LDAP_OPERATIONS_ERROR;
    391 			break;
    392 		}
    393 
    394 		/* Look for the corresponding table mapping */
    395 		t = (__nis_table_mapping_t *)__nis_find_item_mt(
    396 					objPath, &ldapMappingList, 0, 0);
    397 
    398 		if (t == 0)
    399 			break;
    400 
    401 		/* Update object mapping information */
    402 		if (t->objType == NIS_BOGUS_OBJ)
    403 			(void) updateMappingObj(t, 0, 0);
    404 
    405 		/*
    406 		 * Should check the objectDN's in 't', but leave that to
    407 		 * underlying functions.
    408 		 */
    409 		if (wantWrite)
    410 			doLDAP = t->isMaster;
    411 		else
    412 			doLDAP = TRUE;
    413 
    414 		break;
    415 	}
    416 	case NIS_BOGUS_OBJ:
    417 		t = mappingFromObj(obj, statP);
    418 		doLDAP = TRUE;
    419 		asObj = TRUE;
    420 		break;
    421 	default:
    422 		t = 0;
    423 		doLDAP = FALSE;
    424 		asObj = TRUE;
    425 		break;
    426 	}
    427 
    428 	if (!doLDAP)
    429 		t = 0;
    430 
    431 	if (asObjP != 0)
    432 		*asObjP = asObj;
    433 
    434 	if (statP != 0)
    435 		*statP = stat;
    436 
    437 	return (t);
    438 }
    439 
    440 /*
    441  * Replace or remove the table entry identified by 'e'. 'tableName' is
    442  * the name of the table (which could be a directory) in which the entry
    443  * resides. 'obj' is an un-XDR:ed copy of the object in 'e', optionally
    444  * supplied to save re-doing unpacking of the entry object. 'tobj' is
    445  * a pointer to the table object; needed for table entries, but not
    446  * for directory entries.
    447  *
    448  * 'ttime' contains the current time, to be supplied for the trans log
    449  * entry.
    450  *
    451  * Returns LDAP_SUCCESS when entry successfully added/modified/deleted,
    452  * LDAP_COMPARE_TRUE if an entry to be added/modified was the same as
    453  * an already existing one, and a suitable error otherwise.
    454  */
    455 int
    456 db_mindex::updateTableEntry(entry_object *e, int replace, char *tableName,
    457 		nis_object *obj, nis_object *tobj, uint32_t ttime,
    458 		int *xid) {
    459 	int			stat, freeObj = 0;
    460 	db_index_entry		*dbie;
    461 	long			count = 0;
    462 	bool_t			valid = TRUE;
    463 	db_result		*dbres;
    464 	db_query		*qi;
    465 	nis_object		*oldObj = 0;
    466 	char			*myself = "db_mindex::updateTableEntry";
    467 
    468 	if (table == 0 || e == 0)
    469 		return (LDAP_PARAM_ERROR);
    470 
    471 	qi = extract_index_values_from_object(e);
    472 	if (qi == 0) {
    473 		logmsg(MSG_NOMEM, LOG_ERR,
    474 				"%s: Out of memory for query index",
    475 				myself);
    476 		return (LDAP_NO_MEMORY);
    477 	}
    478 
    479 	dbie = satisfy_query(qi, &count, &valid, FALSE);
    480 	if (dbie != 0 && (count != 1 || !valid)) {
    481 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
    482 			"%s: count=%d, valid=%s",
    483 			myself, count, valid ? "TRUE" : "FALSE");
    484 		delete qi;
    485 		return (LDAP_OPERATIONS_ERROR);
    486 	}
    487 
    488 	/*
    489 	 * Need a copy of the old object in order to log a removal
    490 	 * (this is true even if we're modifying an existing entry).
    491 	 */
    492 	if (dbie != 0) {
    493 		oldObj = unmakePseudoEntryObj(
    494 				table->get_entry(dbie->getlocation()), tobj);
    495 		if (oldObj == 0) {
    496 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    497 	"%s: Error getting object from old pseudo-entry for \"%s\" in \"%s\"",
    498 					myself, NIL(obj->zo_name),
    499 					NIL(tableName));
    500 			delete qi;
    501 			return (LDAP_OPERATIONS_ERROR);
    502 		}
    503 	}
    504 
    505 	if (replace) {
    506 		/* Need the object from the entry */
    507 		if (dbie != 0 && obj == 0) {
    508 			obj = unmakePseudoEntryObj(e, tobj);
    509 			if (obj == 0) {
    510 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
    511 	"%s: Error getting object from pseudo-entry for \"%s\" in \"%s\"",
    512 					myself, NIL(obj->zo_name),
    513 					NIL(tableName));
    514 				delete qi;
    515 				nis_destroy_object(oldObj);
    516 				return (LDAP_OPERATIONS_ERROR);
    517 			}
    518 			freeObj = 1;
    519 		}
    520 
    521 		/* Is the new object a dup of the old ? */
    522 		if (dbie != 0 && sameNisPlusObj(oldObj, obj)) {
    523 			/* Yes, it's a dup, so just update the timestamp */
    524 			table->touchEntry(dbie->getlocation());
    525 			delete qi;
    526 			if (freeObj)
    527 				nis_destroy_object(obj);
    528 			nis_destroy_object(oldObj);
    529 			return (LDAP_COMPARE_TRUE);
    530 		} else {
    531 			/*
    532 			 * Not a dup, so go ahead and add it. Provided
    533 			 * that 'qi' isn't NULL (which we've already
    534 			 * checked), DB_ADD(_NOSYNC) does the right
    535 			 * thing even if matching entries already
    536 			 * exist.
    537 			 */
    538 			dbres = ((db *)dbptr.ptr)->log_action(DB_ADD_NOSYNC,
    539 								qi, e);
    540 			if (dbres == 0)
    541 				stat = LDAP_OPERATIONS_ERROR;
    542 			else if (dbres->status == DB_SUCCESS)
    543 				stat = LDAP_SUCCESS;
    544 			else
    545 				stat = LDAP_OPERATIONS_ERROR;
    546 			db_free_result(dbres);
    547 		}
    548 	} else {	/* Removing */
    549 		/* If the object doesn't exist, we're done */
    550 		if (dbie == 0) {
    551 			delete qi;
    552 			return (LDAP_SUCCESS);
    553 		}
    554 
    555 		dbres = ((db *)dbptr.ptr)->log_action(DB_REMOVE_NOSYNC, qi, 0);
    556 		if (dbres == 0)
    557 			stat = LDAP_OPERATIONS_ERROR;
    558 		else if (dbres->status == DB_SUCCESS)
    559 			stat = LDAP_SUCCESS;
    560 		else
    561 			stat = LDAP_OPERATIONS_ERROR;
    562 		db_free_result(dbres);
    563 	}
    564 
    565 	/* Log the operation */
    566 	if (stat == LDAP_SUCCESS) {
    567 		int		ret, numAttrs;
    568 		nis_attr	*attr, attrbuf[NIS_MAXCOLUMNS];
    569 
    570 		/* If we haven't begun the transaction yet, do so now */
    571 		if (*xid == 0) {
    572 			*xid = beginTransaction();
    573 			if (*xid == 0) {
    574 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
    575 				"%s: Error starting transaction for \"%s\"",
    576 					myself, NIL(tableName));
    577 				delete qi;
    578 				if (oldObj != 0)
    579 					nis_destroy_object(oldObj);
    580 				return (LDAP_OPERATIONS_ERROR);
    581 			}
    582 		}
    583 
    584 		if (replace && obj == 0) {
    585 			obj = unmakePseudoEntryObj(e, tobj);
    586 			if (obj == 0) {
    587 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
    588 	"%s: Error getting object from pseudo-entry for \"%s\" in \"%s\"",
    589 					myself, NIL(obj->zo_name),
    590 					NIL(tableName));
    591 				delete qi;
    592 				if (oldObj != 0)
    593 					nis_destroy_object(oldObj);
    594 				return (LDAP_OPERATIONS_ERROR);
    595 			}
    596 			freeObj = 1;
    597 		}
    598 
    599 		/*
    600 		 * The log stores nis_attr information, so we need to
    601 		 * convert the scheme-query to a nis_attr array.
    602 		 */
    603 		attr = schemeQuery2nisAttr(qi, attrbuf, scheme,
    604 				table->mapping.tm, &numAttrs);
    605 		if (attr == 0) {
    606 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    607 	"%s: Error converting index query to nis_attr for \"%s\" in \"%s\"",
    608 				myself, NIL(obj->zo_name), NIL(tableName));
    609 			if (freeObj)
    610 				nis_destroy_object(obj);
    611 			if (oldObj != 0)
    612 				nis_destroy_object(oldObj);
    613 			delete qi;
    614 			return (LDAP_OPERATIONS_ERROR);
    615 		}
    616 
    617 		if (replace) {
    618 			/*
    619 			 * While the DB can handle a modify (replace)
    620 			 * operation, the trans log stores this as a
    621 			 * remove followed by an add (which allows
    622 			 * backing out the change by removing the new
    623 			 * object incarnation, and adding the old one).
    624 			 */
    625 			if (oldObj != 0)
    626 				ret = addUpdate(REM_IBASE, tableName,
    627 					numAttrs, attr, oldObj, 0, ttime);
    628 			else
    629 				ret = 0;
    630 			if (ret == 0)
    631 				ret = addUpdate(ADD_IBASE, tableName,
    632 					numAttrs, attr, obj, 0, ttime);
    633 		} else {	/* Removal */
    634 			ret = addUpdate(REM_IBASE, tableName, numAttrs, attr,
    635 					oldObj, 0, ttime);
    636 		}
    637 		if (ret != 0) {
    638 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    639 		"%s: Error adding trans log entry for \"%s\" in \"%s\"",
    640 				myself, NIL(obj->zo_name), NIL(tableName));
    641 			stat = LDAP_OPERATIONS_ERROR;
    642 		}
    643 	}
    644 
    645 	delete qi;
    646 
    647 	if (oldObj != 0)
    648 		nis_destroy_object(oldObj);
    649 	if (freeObj)
    650 		nis_destroy_object(obj);
    651 
    652 	return (stat);
    653 }
    654 
    655 bool_t
    656 db_mindex::touchEntry(entry_object *e) {
    657 	db_query		*qi;
    658 	bool_t			ret;
    659 
    660 	if (table == 0 || e == 0)
    661 		return (FALSE);
    662 
    663 	qi = extract_index_values_from_object(e);
    664 	if (qi == 0)
    665 		return (FALSE);
    666 
    667 	ret = touchEntry(qi);
    668 
    669 	delete qi;
    670 
    671 	return (ret);
    672 }
    673 
    674 bool_t
    675 db_mindex::touchEntry(db_query *q) {
    676 	db_index_entry		*dbie;
    677 	long			count;
    678 	bool_t			valid;
    679 
    680 	dbie = satisfy_query(q, &count, &valid, FALSE);
    681 	if (dbie != 0 && count == 1 && valid)
    682 		table->touchEntry(dbie->getlocation());
    683 	else
    684 		return (FALSE);
    685 
    686 	return (TRUE);
    687 }
    688 
    689 /*
    690  * Compose an object name from column zero of 'e' and 't->objName',
    691  * and return the mapping for that object, if any. Also set '*name'
    692  * to point to the dir entry name in 'e'. Note that this is a pointer
    693  * to existing data, and shouldn't be freed other than as part of
    694  * freeing 'e'.
    695  */
    696 static __nis_table_mapping_t *
    697 findDirEntryMapping(__nis_table_mapping_t *t, entry_object *e, char **name) {
    698 	__nis_table_mapping_t	*x;
    699 	char			*entryName;
    700 	char			*myself = "findDirEntryMapping";
    701 	__nis_buffer_t		b = {0, 0};
    702 
    703 	if (e == 0 || e->en_cols.en_cols_len != 2 ||
    704 			e->en_cols.en_cols_val == 0)
    705 		return (0);
    706 
    707 	entryName = e->en_cols.en_cols_val[1].ec_value.ec_value_val;
    708 	if (name != 0)
    709 		*name = entryName;
    710 
    711 	if (t == 0 || entryName == 0 || t->objName == 0)
    712 		return (0);
    713 
    714 	bp2buf(myself, &b, "%s.%s", entryName, t->objName);
    715 	if (b.len == 0 || b.buf == 0)
    716 		return (0);
    717 
    718 	x = (__nis_table_mapping_t *)__nis_find_item_mt(b.buf,
    719 						&ldapMappingList, 0, 0);
    720 
    721 	sfree(b.buf);
    722 
    723 	return (x);
    724 }
    725 
    726 /*
    727  * Query LDAP per the supplied (scheme-) query 'qin'. If 'doAsynch' is
    728  * set, and the query is an enumeration (qin == 0), the query will be
    729  * performed in a detached thread, and complete asynchronously. In this
    730  * case, the return status reflects the setup and launch of the
    731  * detached thread; the query will complete asynchronously.
    732  *
    733  * Returns an appropriate LDAP status code.
    734  */
    735 int
    736 db_mindex::queryLDAP(db_query *qin, char *dbId, int doAsynch) {
    737 	__nis_table_mapping_t	*t;
    738 	int			i, na, nq = 0, stat, stat2, numAttrs, ret;
    739 	int			xid = 0;
    740 	long			numEa;
    741 	bool_t			asObj, doEnum;
    742 	db_query		*q;
    743 	entry_object		**ea;
    744 	nis_attr		attr;
    745 	nis_object		*dirObj;
    746 	db_status		dstat;
    747 	char			*myself = "db_mindex::queryLDAP";
    748 
    749 	if (!useLDAPrespository || table == 0)
    750 		return (LDAP_SUCCESS);
    751 
    752 	/*
    753 	 * Instances from the deferred dictionary shouldn't change,
    754 	 * there's no point in querying LDAP.
    755 	 */
    756 	if (table->mapping.isDeferredTable)
    757 		return (LDAP_SUCCESS);
    758 
    759 	t = selectMapping(table, 0, qin, FALSE, &asObj, &stat);
    760 
    761 	if (t == 0)
    762 		return (stat);
    763 
    764 #ifdef	NISDB_LDAP_DEBUG
    765 	printf("%s: %s (%s)\n",
    766 		myself, NIL(t->objName), (asObj ? "object" : "entry"));
    767 #endif	/* NISDB_LDAP_DEBUG */
    768 
    769 	if (qin != NULL) {
    770 		q = schemeQuery2Query(qin, scheme);
    771 		if (q == 0)
    772 			return (LDAP_PARAM_ERROR);
    773 #ifdef	NISDB_LDAP_DEBUG
    774 		q->print();
    775 #endif	/* NISDB_LDAP_DEBUG */
    776 	} else {
    777 		q = 0;
    778 #ifdef	NISDB_LDAP_DEBUG
    779 		printf("\tenumerating %s%s%s\n",
    780 			dbId ? dbId : "", dbId ? ":" : "", NIL(t->objName));
    781 #endif	/* NISDB_LDAP_DEBUG */
    782 	}
    783 
    784 	/*
    785 	 * Do we have any active mappings for this particular query and
    786 	 * dbId ?  If not, we're done.
    787 	 *
    788 	 * Note that we don't care about the return value from
    789 	 * selectTableMapping(), just wheter or not there are
    790 	 * any valid mappings.
    791 	 */
    792 	i = 0;
    793 	sfree(selectTableMapping(t, q, 0, asObj, dbId, &i));
    794 	if (i <= 0) {
    795 		freeQuery(q);
    796 		return (LDAP_SUCCESS);
    797 	}
    798 
    799 	/* Is the object a directory ? */
    800 	if (asObj) {
    801 		nis_object	*o;
    802 		entry_object	*e, eo;
    803 		entry_col	ec[2];
    804 		int		nea;
    805 
    806 		stat = objFromLDAP(t, &o, &ea, &nea);
    807 		numEa = nea;
    808 
    809 		if (stat == LDAP_NO_SUCH_OBJECT) {
    810 			/* Positive failure; remove the object */
    811 			dstat = dbDeleteObj(t->objName);
    812 			if (dstat == DB_SUCCESS || dstat == DB_NOTFOUND) {
    813 				stat = LDAP_SUCCESS;
    814 			} else {
    815 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    816 					"%s: DB error %d deleting \"%s\"",
    817 					myself, dstat, NIL(t->objName));
    818 				stat = LDAP_OPERATIONS_ERROR;
    819 			}
    820 
    821 			freeQuery(q);
    822 
    823 			return (stat);
    824 		} else if (stat != LDAP_SUCCESS) {
    825 			freeQuery(q);
    826 			return (stat);
    827 		} else if (o == 0) {
    828 			/* OK; this entry just isn't mapped */
    829 			freeQuery(q);
    830 			return (LDAP_SUCCESS);
    831 		}
    832 
    833 		if (q != 0) {
    834 			/*
    835 			 * We're updating one particular entry (described
    836 			 * by 't') in the directory 'table->mapping.tm'.
    837 			 */
    838 
    839 			setOid(o);
    840 			dstat = dbRefreshObj(t->objName, o);
    841 			if (dstat == DB_SUCCESS) {
    842 				stat = LDAP_SUCCESS;
    843 			} else {
    844 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    845 			"%s: DB error %d updating \"%s\" in \"%s\"",
    846 					myself, NIL(t->objName),
    847 					NIL(table->mapping.tm->objName));
    848 				stat = LDAP_OPERATIONS_ERROR;
    849 			}
    850 
    851 			freeEntryObjArray(ea, numEa);
    852 			freeQuery(q);
    853 			nis_destroy_object(o);
    854 
    855 			return (stat);
    856 		}
    857 
    858 		dirObj = o;
    859 
    860 		/*
    861 		 * q == 0, so we're enumerating. Update the list of
    862 		 * directory entries.
    863 		 */
    864 
    865 		/*
    866 		 * Need to disable write-through to LDAP, for which we need
    867 		 * a lock on our db_mindex ('this'); we're also updating the
    868 		 * table, so we need a write lock on that as well.
    869 		 */
    870 		WRITELOCKNR(this, stat, "w db_mindex::queryLDAP");
    871 		if (stat == 0) {
    872 			WRITELOCKNR(table, stat2,
    873 				"table w db_mindex::queryLDAP");
    874 		}
    875 		if (stat != 0 || stat2 != 0) {
    876 			nis_destroy_object(dirObj);
    877 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    878 				"%s: lock error %d", myself,
    879 				stat != 0 ? stat : stat2);
    880 			return (LDAP_OPERATIONS_ERROR);
    881 		}
    882 
    883 		setNoWriteThrough();
    884 		setNoLDAPquery();
    885 		table->setEnumMode(0);
    886 
    887 		for (i = 0, na = 0; i < numEa; i++) {
    888 			int			st;
    889 			__nis_table_mapping_t	*x;
    890 			char			*name = 0;
    891 			entry_obj		*e;
    892 
    893 			if (ea[i] == 0)
    894 				continue;
    895 
    896 			/*
    897 			 * We've got a list of dir entries. In the general,
    898 			 * case, some are new, and some already exist.
    899 			 * We definitely want to add the new ones, and to
    900 			 * that end, we need a copy of the object for the
    901 			 * entry. By definition, if an entry is new, we
    902 			 * don't yet have a copy of the object for it, so
    903 			 * it's LDAP or nothing.
    904 			 *
    905 			 * If the entry already exists, try to update the
    906 			 * entry object. In this case, we again only need
    907 			 * to look in LDAP for the object; if there already
    908 			 * is one in the DB, it's in the dir entry which we
    909 			 * want to update.
    910 			 *
    911 			 * So, whether adding or replacing, try to get the
    912 			 * object from LDAP.
    913 			 *
    914 			 * If we can't get a copy of the object, there's not
    915 			 * much point in adding or updating (since a dir
    916 			 * entry just consists of the entry object and name),
    917 			 * so we continue to the next entry.
    918 			 *
    919 			 * However, in that case, we do need to touch the
    920 			 * dir entry; otherwise, it will be removed later
    921 			 * on.
    922 			 */
    923 
    924 			x = findDirEntryMapping(t, ea[i], &name);
    925 			o = 0;
    926 			if (x == 0 || (st = objFromLDAP(x, &o, 0, 0)) !=
    927 					LDAP_SUCCESS) {
    928 				if (x != 0)
    929 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    930 			"%s: Unable to obtain object for \"%s\" in \"%s\": %s",
    931 					myself, NIL(name), NIL(t->objName),
    932 						ldap_err2string(st));
    933 				if (o != 0)
    934 					nis_destroy_object(o);
    935 				if (!touchEntry(ea[i])) {
    936 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    937 			"%s: Inconsistency: LDAP-derived directory \"%s\" "
    938 			"contains entry \"%s\", which is unknown locally, "
    939 			"and has no LDAP mapping",
    940 						myself, NIL(t->objName),
    941 						NIL(name));
    942 				}
    943 				continue;
    944 			}
    945 
    946 			if (ea[i]->en_cols.en_cols_len != 2 ||
    947 				ea[i]->en_cols.en_cols_val == 0 ||
    948 				ea[i]->en_cols.en_cols_val[0].
    949 					ec_value.ec_value_val != 0 ||
    950 				ea[i]->en_cols.en_cols_val[0].
    951 					ec_value.ec_value_len != 0) {
    952 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    953 			"%s: Illegal entry_obj col 0 for \"%s\" in \"%s\"",
    954 					myself, NIL(name), NIL(t->objName));
    955 				nis_destroy_object(o);
    956 				touchEntry(ea[i]);
    957 				continue;
    958 			}
    959 
    960 			setOid(o);
    961 			e = makePseudoEntryObj(o, ea[i], 0);
    962 			if (e == 0) {
    963 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    964 	"%s: Unable to create pseudo entry object for \"%s\" in \"%s\"",
    965 					myself, NIL(name), NIL(t->objName));
    966 				nis_destroy_object(o);
    967 				touchEntry(ea[i]);
    968 				continue;
    969 			}
    970 
    971 			st = updateTableEntry(e, 1, t->objName, o, 0,
    972 						o->zo_oid.mtime, &xid);
    973 			if (st == LDAP_SUCCESS) {
    974 				na++;
    975 			} else if (st == LDAP_COMPARE_TRUE) {
    976 				/* OK, same as existing entry */
    977 				st = LDAP_SUCCESS;
    978 			} else {
    979 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    980 		"%s: Error updating directory entry for \"%s\" in \"%s\": %s",
    981 					myself, NIL(name), NIL(t->objName),
    982 					ldap_err2string(st));
    983 				if (stat == LDAP_SUCCESS)
    984 					stat = st;
    985 			}
    986 
    987 			/* Free the XDR buffer */
    988 			sfree(e->en_cols.en_cols_val[0].
    989 					ec_value.ec_value_val);
    990 			/* Restore ea[i] */
    991 			ea[i]->en_cols.en_cols_val[0].
    992 					ec_value.ec_value_val = 0;
    993 			ea[i]->en_cols.en_cols_val[0].
    994 					ec_value.ec_value_len = 0;
    995 			nis_destroy_object(o);
    996 		}
    997 
    998 		freeEntryObjArray(ea, numEa);
    999 
   1000 		/* Get list of entries to remove */
   1001 		ea = table->endEnumMode(&numEa);
   1002 		if (ea != 0) {
   1003 			uint32_t	nowt = time(0);
   1004 
   1005 			for (i = 0; i < numEa; i++) {
   1006 				int	st;
   1007 
   1008 				if (ea[i] == 0)
   1009 					continue;
   1010 
   1011 				st = updateTableEntry(ea[i], 0, t->objName, 0,
   1012 							0, nowt, &xid);
   1013 				if (st == LDAP_SUCCESS) {
   1014 					na++;
   1015 				} else {
   1016 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
   1017 			"%s: Error removing directory entry for \"%s\": %s",
   1018 						myself, NIL(t->objName),
   1019 						ldap_err2string(st));
   1020 					if (stat == LDAP_SUCCESS)
   1021 						stat = st;
   1022 				}
   1023 			}
   1024 		}
   1025 
   1026 		if (stat == LDAP_SUCCESS) {
   1027 			struct timeval	now;
   1028 			(void) gettimeofday(&now, 0);
   1029 			table->mapping.enumExpire = now.tv_sec +
   1030 				table->mapping.ttl;
   1031 		}
   1032 
   1033 		if (na > 0)
   1034 			(void) ((db *)dbptr.ptr)->sync_log();
   1035 
   1036 		if (xid != 0 && na > 0 && stat == LDAP_SUCCESS) {
   1037 			ret = endTransaction(xid, dirObj);
   1038 			if (ret != 0) {
   1039 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
   1040 				"%s: Error ending transaction for \"%s\"",
   1041 					myself, NIL(t->objName));
   1042 				stat = LDAP_OPERATIONS_ERROR;
   1043 			}
   1044 		} else if (xid != 0) {
   1045 			ret = abort_transaction(xid);
   1046 			if (ret != 0) {
   1047 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
   1048 				"%s: Error aborting transaction for \"%s\"",
   1049 					myself, NIL(t->objName));
   1050 			}
   1051 		}
   1052 		nis_destroy_object(dirObj);
   1053 
   1054 		sfree(ea);
   1055 
   1056 		clearNoLDAPquery();
   1057 		clearNoWriteThrough();
   1058 
   1059 		WRITEUNLOCK2(table, this,
   1060 			stat, stat,
   1061 			"table wu db_mindex::queryLDAP",
   1062 			"wu db_mindex::queryLDAP");
   1063 
   1064 		return (stat);
   1065 	}
   1066 
   1067 	/*
   1068 	 * In order to ping replicas, if any, we need to find the
   1069 	 * directory containing the table to be updated. If we
   1070 	 * can't find the directory object, we're sunk, so let's
   1071 	 * start with that.
   1072 	 */
   1073 	if (t->isMaster) {
   1074 		dirObj = findObj(t->obj->zo_domain, &dstat, &stat);
   1075 		if (dirObj == 0) {
   1076 			if (stat == LDAP_SUCCESS)
   1077 				stat = LDAP_OPERATIONS_ERROR;
   1078 			return (stat);
   1079 		}
   1080 	} else {
   1081 		dirObj = 0;
   1082 	}
   1083 
   1084 	stat = entriesFromLDAP(t, qin, q, dbId, dirObj, doAsynch);
   1085 
   1086 	return (stat);
   1087 }
   1088 
   1089 extern db	*tableDB(char *);
   1090 
   1091 /*
   1092  * Remove the LDAP entry/entries corresponding to 'qin'/'obj'.
   1093  */
   1094 int
   1095 db_mindex::removeLDAP(db_query *qin, nis_object *obj) {
   1096 	__nis_table_mapping_t	*t;
   1097 	db_query		*q;
   1098 	bool_t			asObj;
   1099 	int			stat;
   1100 
   1101 	if (!useLDAPrespository || table == 0)
   1102 		return (LDAP_SUCCESS);
   1103 
   1104 	/* Instances from the deferred dictionary should not update LDAP */
   1105 	if (table->mapping.isDeferredTable)
   1106 		return (LDAP_SUCCESS);
   1107 
   1108 	t = selectMapping(table, 0, qin, TRUE, &asObj, &stat);
   1109 	if (t == 0 && stat != LDAP_SUCCESS)
   1110 		return (stat);
   1111 
   1112 #ifdef	NISDB_LDAP_DEBUG
   1113 	if (t != 0)
   1114 		printf("removeLDAP: %s\n", NIL(t->objName));
   1115 #endif	/* NISDB_LDAP_DEBUG */
   1116 
   1117 	if (qin != NULL) {
   1118 		if (asObj) {
   1119 			/*
   1120 			 * selectMapping() gave us the mapping for the
   1121 			 * directory entry. However, if 't' is NULL, this
   1122 			 * could be due to the directory itself not being
   1123 			 * mapped, in which case we must obtain the mapping
   1124 			 * info from 'obj'.
   1125 			 */
   1126 			if (t == 0) {
   1127 				t = selectMapping(0, obj, 0, TRUE, &asObj,
   1128 						&stat);
   1129 				if (t == 0 && stat != LDAP_SUCCESS)
   1130 					return (stat);
   1131 			}
   1132 
   1133 			if (t != 0) {
   1134 				stat = deleteLDAPobj(t);
   1135 				/*
   1136 				 * If we were successful, update the object
   1137 				 * stored with the mapping.
   1138 				 */
   1139 				if (stat == LDAP_SUCCESS)
   1140 					(void) replaceMappingObj(t, 0);
   1141 				else
   1142 					return (stat);
   1143 			}
   1144 
   1145 			/*
   1146 			 * Since it's a directory entry we've removed, we also
   1147 			 * need to update the directory object itself.
   1148 			 */
   1149 			stat = storeLDAP(0, 0, 0, 0, 0);
   1150 		} else {
   1151 			q = schemeQuery2Query(qin, scheme);
   1152 			if (q == 0)
   1153 				return (LDAP_PARAM_ERROR);
   1154 #ifdef	NISDB_LD