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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27
28 #include <malloc.h>
29 #include <strings.h>
30 #include <string.h>
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <time.h>
34 #include "db_headers.h"
35 #include "db.h"
36 #include "db_mindex.h"
37 #include "db_pickle.h"
38 #include "nisdb_mt.h"
39 #include "nisdb_ldap.h"
40 #include "ldap_nisdbquery.h"
41 #include "ldap_map.h"
42 #include "ldap_ruleval.h"
43 #include "ldap_scheme.h"
44 #include "ldap_parse.h"
45 #include "nis_hashitem.h"
46 #include "nis_db.h"
47 #include "ldap_glob.h"
48
49 /* Pass through configuration information to the table */
50 bool_t
configure(char * tablePath)51 db_mindex::configure(char *tablePath) {
52 if (tablePath == NULL)
53 return (FALSE);
54
55 if (objPath.ptr != 0)
56 free(objPath.ptr);
57 objPath.ptr = strdup(tablePath);
58
59 if (table != NULL) {
60 return (table->configure(tablePath));
61 } else {
62 /* Defer table config until we have a table instance */
63 return (objPath.ptr != NULL);
64 }
65 }
66
67 /*
68 * The noWriteThrough flag is used to prevent modifies/updates to LDAP
69 * while we're incorporating log data into the in-memory tables.
70 */
71 void
setNoWriteThrough(void)72 db_mindex::setNoWriteThrough(void) {
73 ASSERTWHELD(this->mindex);
74 noWriteThrough.flag++;
75 }
76
77 void
clearNoWriteThrough(void)78 db_mindex::clearNoWriteThrough(void) {
79 ASSERTWHELD(this->mindex);
80 if (noWriteThrough.flag > 0)
81 noWriteThrough.flag--;
82 #ifdef NISDB_LDAP_DEBUG
83 else
84 abort();
85 #endif /* NISDB_LDAP_DEBUG */
86 }
87
88 /*
89 * The noLDAPquery flag is used to prevent recursive LDAP queries when
90 * satisfy_query() is re-entered as we add an entry from queryLDAP().
91 */
92 void
setNoLDAPquery(void)93 db_mindex::setNoLDAPquery(void) {
94 ASSERTWHELD(this->mindex);
95 noLDAPquery.flag++;
96 }
97
98 void
clearNoLDAPquery(void)99 db_mindex::clearNoLDAPquery(void) {
100 ASSERTWHELD(this->mindex);
101 if (noLDAPquery.flag > 0)
102 noLDAPquery.flag--;
103 #ifdef NISDB_LDAP_DEBUG
104 else
105 abort();
106 #endif /* NISDB_LDAP_DEBUG */
107 }
108
109 /*
110 * The initialLoad flag tells us if an add or remove is done as part of
111 * the initial load of data, in which case we should use the initial TTLs.
112 */
113 void
setInitialLoad(void)114 db_mindex::setInitialLoad(void) {
115 ASSERTWHELD(this->mindex);
116 initialLoad.flag++;
117 }
118
119 void
clearInitialLoad(void)120 db_mindex::clearInitialLoad(void) {
121 ASSERTWHELD(this->mindex);
122 if (initialLoad.flag > 0)
123 initialLoad.flag--;
124 #ifdef NISDB_LDAP_DEBUG
125 else
126 abort();
127 #endif /* NISDB_LDAP_DEBUG */
128 }
129
130 void
setDbPtr(void * ptr)131 db_mindex::setDbPtr(void *ptr) {
132 dbptr.ptr = ptr;
133 }
134
135 void *
getDbPtr(void)136 db_mindex::getDbPtr(void) {
137 return (dbptr.ptr);
138 }
139
140 db_table *
getTable(void)141 db_mindex::getTable(void) {
142 return (table);
143 }
144
145 static void setOid(nis_object *obj);
146
147 extern void db_free_result(db_result *);
148
149 zotypes
updateMappingObj(__nis_table_mapping_t * t,char ** objNameP,bool_t * isMasterP)150 updateMappingObj(__nis_table_mapping_t *t, char **objNameP,
151 bool_t *isMasterP) {
152 zotypes type = NIS_BOGUS_OBJ;
153 char *objName = 0;
154 const char *myself = "updateMappingObj";
155
156 if (t != 0)
157 objName = t->objName;
158 else if (objNameP != 0)
159 objName = *objNameP;
160 else
161 return (NIS_BOGUS_OBJ);
162
163 if (objName != 0) {
164 db_status stat;
165 int lstat = LDAP_SUCCESS;
166 nis_object *o = dbFindObject(objName, &stat);
167
168 /* If not found in the local DB, try LDAP */
169 if (o == 0) {
170 if (stat != DB_NOTFOUND) {
171 logmsg(MSG_NOTIMECHECK, LOG_INFO,
172 "%s: DB err %d for \"%s\"",
173 myself, stat, NIL(objName));
174 }
175 o = ldapFindObj(t, objName, &lstat);
176 /* If found, refresh/create the local copy */
177 if (o != 0) {
178 db_status rstat;
179 rstat = dbRefreshObj(objName, o);
180 if (rstat != DB_SUCCESS)
181 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
182 "%s: DB error %d refreshing \"%s\"",
183 myself, rstat, NIL(objName));
184 }
185 }
186
187 if (o != 0) {
188 type = o->zo_data.zo_type;
189 if (objNameP != 0) {
190 *objNameP = sdup(myself, T, objName);
191 if (*objNameP == 0) {
192 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
193 "%s: Unable to copy object name (\"%s\")",
194 myself, NIL(objName));
195 }
196 }
197 if (t != 0) {
198 if (!setMappingObjTypeEtc(t, o))
199 nis_destroy_object(o);
200
201 } else {
202 nis_destroy_object(o);
203 }
204 } else if (lstat != LDAP_SUCCESS) {
205 logmsg(MSG_NOTIMECHECK, LOG_INFO,
206 "%s: LDAP err %d for \"%s\"",
207 myself, lstat, NIL(objName));
208 }
209 }
210
211 return (type);
212 }
213
214 static __nis_table_mapping_t *
mappingFromObj(nis_object * obj,int * statP)215 mappingFromObj(nis_object *obj, int *statP) {
216 __nis_table_mapping_t *t;
217 __nis_buffer_t b = {0, 0};
218 char *objPath;
219 const char *myself = "mappingFromObj";
220
221 if (obj == 0 || obj->zo_data.zo_type == NIS_ENTRY_OBJ)
222 return (0);
223
224 /*
225 * Convert full object name to the db table path used as
226 * key for the mapping hash list.
227 */
228 bp2buf(myself, &b, "%s.%s",
229 NIL(obj->zo_name), NIL(obj->zo_domain));
230 objPath = internalTableName(b.buf);
231 sfree(b.buf);
232 if (slen(objPath) <= 0) {
233 if (statP != 0)
234 *statP = LDAP_OPERATIONS_ERROR;
235 sfree(objPath);
236 return (0);
237 }
238
239 t = (__nis_table_mapping_t *)__nis_find_item_mt(objPath,
240 &ldapMappingList, 0, 0);
241
242 sfree(objPath);
243
244 return (t);
245 }
246
247 static __nis_table_mapping_t *
selectMapping(db_table * table,nis_object * obj,db_query * qin,bool_t wantWrite,bool_t * asObjP,int * statP)248 selectMapping(db_table *table, nis_object *obj, db_query *qin,
249 bool_t wantWrite, bool_t *asObjP, int *statP) {
250 __nis_table_mapping_t *t;
251 __nis_buffer_t b = {0, 0};
252 bool_t doLDAP, asObj;
253 int stat = LDAP_SUCCESS;
254 char *objPath = 0, buf[MAXPATHLEN+NIS_MAXNAMELEN+1];
255 const char *myself = "db_mindex::selectMapping";
256
257 /*
258 * If 'table' is NULL, we try to find a mapping for 'obj'.
259 * We expect this to happen when our caller wants to write
260 * the object from a directory entry to LDAP.
261 */
262 if (table == 0) {
263 if (asObjP != 0)
264 *asObjP = TRUE;
265 if (statP != 0)
266 *statP = LDAP_SUCCESS;
267
268 t = mappingFromObj(obj, statP);
269
270 if (t == 0)
271 return (0);
272
273 /*
274 * Should the object type in the mapping be NIS_BOGUS_OBJ,
275 * we need to determine what kind of object this is.
276 */
277 if (t->objType == NIS_BOGUS_OBJ) {
278 t->objType = updateMappingObj(t, 0, 0);
279 if (t->objType == NIS_BOGUS_OBJ) {
280 if (statP != 0)
281 *statP = LDAP_OPERATIONS_ERROR;
282 return (0);
283 }
284 }
285
286 /*
287 * If caller wants a mapping suitable for writing,
288 * check that we're the master for this object.
289 */
290
291 return (t);
292 }
293
294 /*
295 * If the object type for the mapping is NIS_BOGUS_OBJ, then
296 * we haven't yet been able to determine what kind of object this
297 * is. Try to fix that now.
298 */
299 if (table->mapping.objType == NIS_BOGUS_OBJ) {
300 table->mapping.objType = updateMappingObj(table->mapping.tm,
301 &table->mapping.objName,
302 &table->mapping.isMaster);
303 table->mapping.expireType = table->mapping.objType;
304 }
305
306 /*
307 * Depending on the object type (table->mapping.objType):
308 *
309 * table Use table->mapping.tm to query LDAP
310 * for entries per 'qin'.
311 *
312 * directory Use 'qin' and table->mapping.objName
313 * to retrieve a mapping entry, and then
314 * query LDAP for the corresponding object.
315 * 'qin' == NULL means reading/writing the
316 * entire directory object, plus the names
317 * of the directory entries.
318 *
319 * bogus Not mapping this object. However, we may
320 * still be mapping the object 'obj'.
321 *
322 * other Shouldn't happen; illegal.
323 */
324 switch (table->mapping.objType) {
325 case NIS_TABLE_OBJ:
326 t = table->mapping.tm;
327 if (wantWrite)
328 doLDAP = table->mapping.isMaster &&
329 table->mapping.toLDAP;
330 else
331 doLDAP = table->mapping.fromLDAP;
332 asObj = FALSE;
333 break;
334 case NIS_DIRECTORY_OBJ: {
335 char *sub = 0;
336 int nqc, len = 0;
337 db_qcomp *qc;
338
339 t = 0;
340 doLDAP = FALSE;
341 asObj = TRUE;
342
343 /*
344 * We expect the query to have one component, containing
345 * the directory entry name. If there's no query, we want
346 * an enumeration of the entries in the directory. They're
347 * stored with the XDR:ed directory object in LDAP, so
348 * asObj should be TRUE.
349 */
350 if (qin == 0) {
351 t = table->mapping.tm;
352 if (wantWrite)
353 doLDAP = table->mapping.isMaster &&
354 table->mapping.toLDAP;
355 else
356 doLDAP = table->mapping.fromLDAP;
357 asObj = TRUE;
358 break;
359 }
360
361 nqc = qin->size();
362 if (nqc != 1 || (qc = qin->queryloc()) == 0 ||
363 qc[0].index_value == 0) {
364 stat = LDAP_PARAM_ERROR;
365 break;
366 }
367 qc[0].index_value->get_value(&sub, &len);
368 if (sub == 0 || len <= 0) {
369 stat = LDAP_PARAM_ERROR;
370 break;
371 }
372
373 /* Append directory name to dir entry name */
374 sbc2buf(myself, sub, len, &b);
375 bp2buf(myself, &b, ".%s", table->mapping.objName);
376
377 /* Convert to the DB internal name */
378 objPath = internal_table_name(b.buf, buf);
379 sfree(b.buf);
380 if (slen(objPath) <= 0) {
381 stat = LDAP_OPERATIONS_ERROR;
382 break;
383 }
384
385 /* Look for the corresponding table mapping */
386 t = (__nis_table_mapping_t *)__nis_find_item_mt(
387 objPath, &ldapMappingList, 0, 0);
388
389 if (t == 0)
390 break;
391
392 /* Update object mapping information */
393 if (t->objType == NIS_BOGUS_OBJ)
394 (void) updateMappingObj(t, 0, 0);
395
396 /*
397 * Should check the objectDN's in 't', but leave that to
398 * underlying functions.
399 */
400 if (wantWrite)
401 doLDAP = t->isMaster;
402 else
403 doLDAP = TRUE;
404
405 break;
406 }
407 case NIS_BOGUS_OBJ:
408 t = mappingFromObj(obj, statP);
409 doLDAP = TRUE;
410 asObj = TRUE;
411 break;
412 default:
413 t = 0;
414 doLDAP = FALSE;
415 asObj = TRUE;
416 break;
417 }
418
419 if (!doLDAP)
420 t = 0;
421
422 if (asObjP != 0)
423 *asObjP = asObj;
424
425 if (statP != 0)
426 *statP = stat;
427
428 return (t);
429 }
430
431 /*
432 * Replace or remove the table entry identified by 'e'. 'tableName' is
433 * the name of the table (which could be a directory) in which the entry
434 * resides. 'obj' is an un-XDR:ed copy of the object in 'e', optionally
435 * supplied to save re-doing unpacking of the entry object. 'tobj' is
436 * a pointer to the table object; needed for table entries, but not
437 * for directory entries.
438 *
439 * 'ttime' contains the current time, to be supplied for the trans log
440 * entry.
441 *
442 * Returns LDAP_SUCCESS when entry successfully added/modified/deleted,
443 * LDAP_COMPARE_TRUE if an entry to be added/modified was the same as
444 * an already existing one, and a suitable error otherwise.
445 */
446 int
updateTableEntry(entry_object * e,int replace,char * tableName,nis_object * obj,nis_object * tobj,uint32_t ttime,int * xid)447 db_mindex::updateTableEntry(entry_object *e, int replace, char *tableName,
448 nis_object *obj, nis_object *tobj, uint32_t ttime,
449 int *xid) {
450 int stat, freeObj = 0;
451 db_index_entry *dbie;
452 long count = 0;
453 bool_t valid = TRUE;
454 db_result *dbres;
455 db_query *qi;
456 nis_object *oldObj = 0;
457 const char *myself = "db_mindex::updateTableEntry";
458
459 if (table == 0 || e == 0)
460 return (LDAP_PARAM_ERROR);
461
462 qi = extract_index_values_from_object(e);
463 if (qi == 0) {
464 logmsg(MSG_NOMEM, LOG_ERR,
465 "%s: Out of memory for query index",
466 myself);
467 return (LDAP_NO_MEMORY);
468 }
469
470 dbie = satisfy_query(qi, &count, &valid, FALSE);
471 if (dbie != 0 && (count != 1 || !valid)) {
472 logmsg(MSG_NOTIMECHECK, LOG_INFO,
473 "%s: count=%d, valid=%s",
474 myself, count, valid ? "TRUE" : "FALSE");
475 delete qi;
476 return (LDAP_OPERATIONS_ERROR);
477 }
478
479 /*
480 * Need a copy of the old object in order to log a removal
481 * (this is true even if we're modifying an existing entry).
482 */
483 if (dbie != 0) {
484 oldObj = unmakePseudoEntryObj(
485 table->get_entry(dbie->getlocation()), tobj);
486 if (oldObj == 0) {
487 logmsg(MSG_NOTIMECHECK, LOG_ERR,
488 "%s: Error getting object from old pseudo-entry for \"%s\" in \"%s\"",
489 myself, NIL(obj->zo_name),
490 NIL(tableName));
491 delete qi;
492 return (LDAP_OPERATIONS_ERROR);
493 }
494 }
495
496 if (replace) {
497 /* Need the object from the entry */
498 if (dbie != 0 && obj == 0) {
499 obj = unmakePseudoEntryObj(e, tobj);
500 if (obj == 0) {
501 logmsg(MSG_NOTIMECHECK, LOG_ERR,
502 "%s: Error getting object from pseudo-entry for \"%s\" in \"%s\"",
503 myself, NIL(obj->zo_name),
504 NIL(tableName));
505 delete qi;
506 nis_destroy_object(oldObj);
507 return (LDAP_OPERATIONS_ERROR);
508 }
509 freeObj = 1;
510 }
511
512 /* Is the new object a dup of the old ? */
513 if (dbie != 0 && sameNisPlusObj(oldObj, obj)) {
514 /* Yes, it's a dup, so just update the timestamp */
515 table->touchEntry(dbie->getlocation());
516 delete qi;
517 if (freeObj)
518 nis_destroy_object(obj);
519 nis_destroy_object(oldObj);
520 return (LDAP_COMPARE_TRUE);
521 } else {
522 /*
523 * Not a dup, so go ahead and add it. Provided
524 * that 'qi' isn't NULL (which we've already
525 * checked), DB_ADD(_NOSYNC) does the right
526 * thing even if matching entries already
527 * exist.
528 */
529 dbres = ((db *)dbptr.ptr)->log_action(DB_ADD_NOSYNC,
530 qi, e);
531 if (dbres == 0)
532 stat = LDAP_OPERATIONS_ERROR;
533 else if (dbres->status == DB_SUCCESS)
534 stat = LDAP_SUCCESS;
535 else
536 stat = LDAP_OPERATIONS_ERROR;
537 db_free_result(dbres);
538 }
539 } else { /* Removing */
540 /* If the object doesn't exist, we're done */
541 if (dbie == 0) {
542 delete qi;
543 return (LDAP_SUCCESS);
544 }
545
546 dbres = ((db *)dbptr.ptr)->log_action(DB_REMOVE_NOSYNC, qi, 0);
547 if (dbres == 0)
548 stat = LDAP_OPERATIONS_ERROR;
549 else if (dbres->status == DB_SUCCESS)
550 stat = LDAP_SUCCESS;
551 else
552 stat = LDAP_OPERATIONS_ERROR;
553 db_free_result(dbres);
554 }
555
556 /* Log the operation */
557 if (stat == LDAP_SUCCESS) {
558 int ret, numAttrs;
559 nis_attr *attr, attrbuf[NIS_MAXCOLUMNS];
560
561 /* If we haven't begun the transaction yet, do so now */
562 if (*xid == 0) {
563 *xid = beginTransaction();
564 if (*xid == 0) {
565 logmsg(MSG_NOTIMECHECK, LOG_ERR,
566 "%s: Error starting transaction for \"%s\"",
567 myself, NIL(tableName));
568 delete qi;
569 if (oldObj != 0)
570 nis_destroy_object(oldObj);
571 return (LDAP_OPERATIONS_ERROR);
572 }
573 }
574
575 if (replace && obj == 0) {
576 obj = unmakePseudoEntryObj(e, tobj);
577 if (obj == 0) {
578 logmsg(MSG_NOTIMECHECK, LOG_ERR,
579 "%s: Error getting object from pseudo-entry for \"%s\" in \"%s\"",
580 myself, NIL(obj->zo_name),
581 NIL(tableName));
582 delete qi;
583 if (oldObj != 0)
584 nis_destroy_object(oldObj);
585 return (LDAP_OPERATIONS_ERROR);
586 }
587 freeObj = 1;
588 }
589
590 /*
591 * The log stores nis_attr information, so we need to
592 * convert the scheme-query to a nis_attr array.
593 */
594 attr = schemeQuery2nisAttr(qi, attrbuf, scheme,
595 table->mapping.tm, &numAttrs);
596 if (attr == 0) {
597 logmsg(MSG_NOTIMECHECK, LOG_ERR,
598 "%s: Error converting index query to nis_attr for \"%s\" in \"%s\"",
599 myself, NIL(obj->zo_name), NIL(tableName));
600 if (freeObj)
601 nis_destroy_object(obj);
602 if (oldObj != 0)
603 nis_destroy_object(oldObj);
604 delete qi;
605 return (LDAP_OPERATIONS_ERROR);
606 }
607
608 if (replace) {
609 /*
610 * While the DB can handle a modify (replace)
611 * operation, the trans log stores this as a
612 * remove followed by an add (which allows
613 * backing out the change by removing the new
614 * object incarnation, and adding the old one).
615 */
616 if (oldObj != 0)
617 ret = addUpdate(REM_IBASE, tableName,
618 numAttrs, attr, oldObj, 0, ttime);
619 else
620 ret = 0;
621 if (ret == 0)
622 ret = addUpdate(ADD_IBASE, tableName,
623 numAttrs, attr, obj, 0, ttime);
624 } else { /* Removal */
625 ret = addUpdate(REM_IBASE, tableName, numAttrs, attr,
626 oldObj, 0, ttime);
627 }
628 if (ret != 0) {
629 logmsg(MSG_NOTIMECHECK, LOG_ERR,
630 "%s: Error adding trans log entry for \"%s\" in \"%s\"",
631 myself, NIL(obj->zo_name), NIL(tableName));
632 stat = LDAP_OPERATIONS_ERROR;
633 }
634 }
635
636 delete qi;
637
638 if (oldObj != 0)
639 nis_destroy_object(oldObj);
640 if (freeObj)
641 nis_destroy_object(obj);
642
643 return (stat);
644 }
645
646 bool_t
touchEntry(entry_object * e)647 db_mindex::touchEntry(entry_object *e) {
648 db_query *qi;
649 bool_t ret;
650
651 if (table == 0 || e == 0)
652 return (FALSE);
653
654 qi = extract_index_values_from_object(e);
655 if (qi == 0)
656 return (FALSE);
657
658 ret = touchEntry(qi);
659
660 delete qi;
661
662 return (ret);
663 }
664
665 bool_t
touchEntry(db_query * q)666 db_mindex::touchEntry(db_query *q) {
667 db_index_entry *dbie;
668 long count;
669 bool_t valid;
670
671 dbie = satisfy_query(q, &count, &valid, FALSE);
672 if (dbie != 0 && count == 1 && valid)
673 table->touchEntry(dbie->getlocation());
674 else
675 return (FALSE);
676
677 return (TRUE);
678 }
679
680 /*
681 * Compose an object name from column zero of 'e' and 't->objName',
682 * and return the mapping for that object, if any. Also set '*name'
683 * to point to the dir entry name in 'e'. Note that this is a pointer
684 * to existing data, and shouldn't be freed other than as part of
685 * freeing 'e'.
686 */
687 static __nis_table_mapping_t *
findDirEntryMapping(__nis_table_mapping_t * t,entry_object * e,char ** name)688 findDirEntryMapping(__nis_table_mapping_t *t, entry_object *e, char **name) {
689 __nis_table_mapping_t *x;
690 char *entryName;
691 const char *myself = "findDirEntryMapping";
692 __nis_buffer_t b = {0, 0};
693
694 if (e == 0 || e->en_cols.en_cols_len != 2 ||
695 e->en_cols.en_cols_val == 0)
696 return (0);
697
698 entryName = e->en_cols.en_cols_val[1].ec_value.ec_value_val;
699 if (name != 0)
700 *name = entryName;
701
702 if (t == 0 || entryName == 0 || t->objName == 0)
703 return (0);
704
705 bp2buf(myself, &b, "%s.%s", entryName, t->objName);
706 if (b.len == 0 || b.buf == 0)
707 return (0);
708
709 x = (__nis_table_mapping_t *)__nis_find_item_mt(b.buf,
710 &ldapMappingList, 0, 0);
711
712 sfree(b.buf);
713
714 return (x);
715 }
716
717 /*
718 * Query LDAP per the supplied (scheme-) query 'qin'. If 'doAsynch' is
719 * set, and the query is an enumeration (qin == 0), the query will be
720 * performed in a detached thread, and complete asynchronously. In this
721 * case, the return status reflects the setup and launch of the
722 * detached thread; the query will complete asynchronously.
723 *
724 * Returns an appropriate LDAP status code.
725 */
726 int
queryLDAP(db_query * qin,char * dbId,int doAsynch)727 db_mindex::queryLDAP(db_query *qin, char *dbId, int doAsynch) {
728 __nis_table_mapping_t *t;
729 int i, na, nq = 0, stat, stat2, numAttrs, ret;
730 int xid = 0;
731 long numEa;
732 bool_t asObj, doEnum;
733 db_query *q;
734 entry_object **ea;
735 nis_attr attr;
736 nis_object *dirObj;
737 db_status dstat;
738 const char *myself = "db_mindex::queryLDAP";
739
740 if (!useLDAPrespository || table == 0)
741 return (LDAP_SUCCESS);
742
743 /*
744 * Instances from the deferred dictionary shouldn't change,
745 * there's no point in querying LDAP.
746 */
747 if (table->mapping.isDeferredTable)
748 return (LDAP_SUCCESS);
749
750 t = selectMapping(table, 0, qin, FALSE, &asObj, &stat);
751
752 if (t == 0)
753 return (stat);
754
755 #ifdef NISDB_LDAP_DEBUG
756 printf("%s: %s (%s)\n",
757 myself, NIL(t->objName), (asObj ? "object" : "entry"));
758 #endif /* NISDB_LDAP_DEBUG */
759
760 if (qin != NULL) {
761 q = schemeQuery2Query(qin, scheme);
762 if (q == 0)
763 return (LDAP_PARAM_ERROR);
764 #ifdef NISDB_LDAP_DEBUG
765 q->print();
766 #endif /* NISDB_LDAP_DEBUG */
767 } else {
768 q = 0;
769 #ifdef NISDB_LDAP_DEBUG
770 printf("\tenumerating %s%s%s\n",
771 dbId ? dbId : "", dbId ? ":" : "", NIL(t->objName));
772 #endif /* NISDB_LDAP_DEBUG */
773 }
774
775 /*
776 * Do we have any active mappings for this particular query and
777 * dbId ? If not, we're done.
778 *
779 * Note that we don't care about the return value from
780 * selectTableMapping(), just wheter or not there are
781 * any valid mappings.
782 */
783 i = 0;
784 sfree(selectTableMapping(t, q, 0, asObj, dbId, &i));
785 if (i <= 0) {
786 freeQuery(q);
787 return (LDAP_SUCCESS);
788 }
789
790 /* Is the object a directory ? */
791 if (asObj) {
792 nis_object *o;
793 entry_object *e, eo;
794 entry_col ec[2];
795 int nea;
796
797 stat = objFromLDAP(t, &o, &ea, &nea);
798 numEa = nea;
799
800 if (stat == LDAP_NO_SUCH_OBJECT) {
801 /* Positive failure; remove the object */
802 dstat = dbDeleteObj(t->objName);
803 if (dstat == DB_SUCCESS || dstat == DB_NOTFOUND) {
804 stat = LDAP_SUCCESS;
805 } else {
806 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
807 "%s: DB error %d deleting \"%s\"",
808 myself, dstat, NIL(t->objName));
809 stat = LDAP_OPERATIONS_ERROR;
810 }
811
812 freeQuery(q);
813
814 return (stat);
815 } else if (stat != LDAP_SUCCESS) {
816 freeQuery(q);
817 return (stat);
818 } else if (o == 0) {
819 /* OK; this entry just isn't mapped */
820 freeQuery(q);
821 return (LDAP_SUCCESS);
822 }
823
824 if (q != 0) {
825 /*
826 * We're updating one particular entry (described
827 * by 't') in the directory 'table->mapping.tm'.
828 */
829
830 setOid(o);
831 dstat = dbRefreshObj(t->objName, o);
832 if (dstat == DB_SUCCESS) {
833 stat = LDAP_SUCCESS;
834 } else {
835 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
836 "%s: DB error %d updating \"%s\" in \"%s\"",
837 myself, NIL(t->objName),
838 NIL(table->mapping.tm->objName));
839 stat = LDAP_OPERATIONS_ERROR;
840 }
841
842 freeEntryObjArray(ea, numEa);
843 freeQuery(q);
844 nis_destroy_object(o);
845
846 return (stat);
847 }
848
849 dirObj = o;
850
851 /*
852 * q == 0, so we're enumerating. Update the list of
853 * directory entries.
854 */
855
856 /*
857 * Need to disable write-through to LDAP, for which we need
858 * a lock on our db_mindex ('this'); we're also updating the
859 * table, so we need a write lock on that as well.
860 */
861 WRITELOCKNR(this, stat, "w db_mindex::queryLDAP");
862 if (stat == 0) {
863 WRITELOCKNR(table, stat2,
864 "table w db_mindex::queryLDAP");
865 }
866 if (stat != 0 || stat2 != 0) {
867 nis_destroy_object(dirObj);
868 logmsg(MSG_NOTIMECHECK, LOG_ERR,
869 "%s: lock error %d", myself,
870 stat != 0 ? stat : stat2);
871 return (LDAP_OPERATIONS_ERROR);
872 }
873
874 setNoWriteThrough();
875 setNoLDAPquery();
876 table->setEnumMode(0);
877
878 for (i = 0, na = 0; i < numEa; i++) {
879 int st;
880 __nis_table_mapping_t *x;
881 char *name = 0;
882 entry_obj *e;
883
884 if (ea[i] == 0)
885 continue;
886
887 /*
888 * We've got a list of dir entries. In the general,
889 * case, some are new, and some already exist.
890 * We definitely want to add the new ones, and to
891 * that end, we need a copy of the object for the
892 * entry. By definition, if an entry is new, we
893 * don't yet have a copy of the object for it, so
894 * it's LDAP or nothing.
895 *
896 * If the entry already exists, try to update the
897 * entry object. In this case, we again only need
898 * to look in LDAP for the object; if there already
899 * is one in the DB, it's in the dir entry which we
900 * want to update.
901 *
902 * So, whether adding or replacing, try to get the
903 * object from LDAP.
904 *
905 * If we can't get a copy of the object, there's not
906 * much point in adding or updating (since a dir
907 * entry just consists of the entry object and name),
908 * so we continue to the next entry.
909 *
910 * However, in that case, we do need to touch the
911 * dir entry; otherwise, it will be removed later
912 * on.
913 */
914
915 x = findDirEntryMapping(t, ea[i], &name);
916 o = 0;
917 if (x == 0 || (st = objFromLDAP(x, &o, 0, 0)) !=
918 LDAP_SUCCESS) {
919 if (x != 0)
920 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
921 "%s: Unable to obtain object for \"%s\" in \"%s\": %s",
922 myself, NIL(name), NIL(t->objName),
923 ldap_err2string(st));
924 if (o != 0)
925 nis_destroy_object(o);
926 if (!touchEntry(ea[i])) {
927 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
928 "%s: Inconsistency: LDAP-derived directory \"%s\" "
929 "contains entry \"%s\", which is unknown locally, "
930 "and has no LDAP mapping",
931 myself, NIL(t->objName),
932 NIL(name));
933 }
934 continue;
935 }
936
937 if (ea[i]->en_cols.en_cols_len != 2 ||
938 ea[i]->en_cols.en_cols_val == 0 ||
939 ea[i]->en_cols.en_cols_val[0].
940 ec_value.ec_value_val != 0 ||
941 ea[i]->en_cols.en_cols_val[0].
942 ec_value.ec_value_len != 0) {
943 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
944 "%s: Illegal entry_obj col 0 for \"%s\" in \"%s\"",
945 myself, NIL(name), NIL(t->objName));
946 nis_destroy_object(o);
947 touchEntry(ea[i]);
948 continue;
949 }
950
951 setOid(o);
952 e = makePseudoEntryObj(o, ea[i], 0);
953 if (e == 0) {
954 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
955 "%s: Unable to create pseudo entry object for \"%s\" in \"%s\"",
956 myself, NIL(name), NIL(t->objName));
957 nis_destroy_object(o);
958 touchEntry(ea[i]);
959 continue;
960 }
961
962 st = updateTableEntry(e, 1, t->objName, o, 0,
963 o->zo_oid.mtime, &xid);
964 if (st == LDAP_SUCCESS) {
965 na++;
966 } else if (st == LDAP_COMPARE_TRUE) {
967 /* OK, same as existing entry */
968 st = LDAP_SUCCESS;
969 } else {
970 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
971 "%s: Error updating directory entry for \"%s\" in \"%s\": %s",
972 myself, NIL(name), NIL(t->objName),
973 ldap_err2string(st));
974 if (stat == LDAP_SUCCESS)
975 stat = st;
976 }
977
978 /* Free the XDR buffer */
979 sfree(e->en_cols.en_cols_val[0].
980 ec_value.ec_value_val);
981 /* Restore ea[i] */
982 ea[i]->en_cols.en_cols_val[0].
983 ec_value.ec_value_val = 0;
984 ea[i]->en_cols.en_cols_val[0].
985 ec_value.ec_value_len = 0;
986 nis_destroy_object(o);
987 }
988
989 freeEntryObjArray(ea, numEa);
990
991 /* Get list of entries to remove */
992 ea = table->endEnumMode(&numEa);
993 if (ea != 0) {
994 uint32_t nowt = time(0);
995
996 for (i = 0; i < numEa; i++) {
997 int st;
998
999 if (ea[i] == 0)
1000 continue;
1001
1002 st = updateTableEntry(ea[i], 0, t->objName, 0,
1003 0, nowt, &xid);
1004 if (st == LDAP_SUCCESS) {
1005 na++;
1006 } else {
1007 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1008 "%s: Error removing directory entry for \"%s\": %s",
1009 myself, NIL(t->objName),
1010 ldap_err2string(st));
1011 if (stat == LDAP_SUCCESS)
1012 stat = st;
1013 }
1014 }
1015 }
1016
1017 if (stat == LDAP_SUCCESS) {
1018 struct timeval now;
1019 (void) gettimeofday(&now, 0);
1020 table->mapping.enumExpire = now.tv_sec +
1021 table->mapping.ttl;
1022 }
1023
1024 if (na > 0)
1025 (void) ((db *)dbptr.ptr)->sync_log();
1026
1027 if (xid != 0 && na > 0 && stat == LDAP_SUCCESS) {
1028 ret = endTransaction(xid, dirObj);
1029 if (ret != 0) {
1030 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1031 "%s: Error ending transaction for \"%s\"",
1032 myself, NIL(t->objName));
1033 stat = LDAP_OPERATIONS_ERROR;
1034 }
1035 } else if (xid != 0) {
1036 ret = abort_transaction(xid);
1037 if (ret != 0) {
1038 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1039 "%s: Error aborting transaction for \"%s\"",
1040 myself, NIL(t->objName));
1041 }
1042 }
1043 nis_destroy_object(dirObj);
1044
1045 sfree(ea);
1046
1047 clearNoLDAPquery();
1048 clearNoWriteThrough();
1049
1050 WRITEUNLOCK2(table, this,
1051 stat, stat,
1052 "table wu db_mindex::queryLDAP",
1053 "wu db_mindex::queryLDAP");
1054
1055 return (stat);
1056 }
1057
1058 /*
1059 * In order to ping replicas, if any, we need to find the
1060 * directory containing the table to be updated. If we
1061 * can't find the directory object, we're sunk, so let's
1062 * start with that.
1063 */
1064 if (t->isMaster) {
1065 dirObj = findObj(t->obj->zo_domain, &dstat, &stat);
1066 if (dirObj == 0) {
1067 if (stat == LDAP_SUCCESS)
1068 stat = LDAP_OPERATIONS_ERROR;
1069 return (stat);
1070 }
1071 } else {
1072 dirObj = 0;
1073 }
1074
1075 stat = entriesFromLDAP(t, qin, q, dbId, dirObj, doAsynch);
1076
1077 return (stat);
1078 }
1079
1080 extern db *tableDB(char *);
1081
1082 /*
1083 * Remove the LDAP entry/entries corresponding to 'qin'/'obj'.
1084 */
1085 int
removeLDAP(db_query * qin,nis_object * obj)1086 db_mindex::removeLDAP(db_query *qin, nis_object *obj) {
1087 __nis_table_mapping_t *t;
1088 db_query *q;
1089 bool_t asObj;
1090 int stat;
1091
1092 if (!useLDAPrespository || table == 0)
1093 return (LDAP_SUCCESS);
1094
1095 /* Instances from the deferred dictionary should not update LDAP */
1096 if (table->mapping.isDeferredTable)
1097 return (LDAP_SUCCESS);
1098
1099 t = selectMapping(table, 0, qin, TRUE, &asObj, &stat);
1100 if (t == 0 && stat != LDAP_SUCCESS)
1101 return (stat);
1102
1103 #ifdef NISDB_LDAP_DEBUG
1104 if (t != 0)
1105 printf("removeLDAP: %s\n", NIL(t->objName));
1106 #endif /* NISDB_LDAP_DEBUG */
1107
1108 if (qin != NULL) {
1109 if (asObj) {
1110 /*
1111 * selectMapping() gave us the mapping for the
1112 * directory entry. However, if 't' is NULL, this
1113 * could be due to the directory itself not being
1114 * mapped, in which case we must obtain the mapping
1115 * info from 'obj'.
1116 */
1117 if (t == 0) {
1118 t = selectMapping(0, obj, 0, TRUE, &asObj,
1119 &stat);
1120 if (t == 0 && stat != LDAP_SUCCESS)
1121 return (stat);
1122 }
1123
1124 if (t != 0) {
1125 stat = deleteLDAPobj(t);
1126 /*
1127 * If we were successful, update the object
1128 * stored with the mapping.
1129 */
1130 if (stat == LDAP_SUCCESS)
1131 (void) replaceMappingObj(t, 0);
1132 else
1133 return (stat);
1134 }
1135
1136 /*
1137 * Since it's a directory entry we've removed, we also
1138 * need to update the directory object itself.
1139 */
1140 stat = storeLDAP(0, 0, 0, 0, 0);
1141 } else {
1142 q = schemeQuery2Query(qin, scheme);
1143 if (q == 0)
1144 return (LDAP_PARAM_ERROR);
1145 #ifdef NISDB_LDAP_DEBUG
1146 q->print();
1147 #endif /* NISDB_LDAP_DEBUG */
1148 stat = mapToLDAP(t, 1, &q, 0, 0, 0, 0);
1149 freeQuery(q);
1150 }
1151 } else {
1152 /*
1153 * This isn't the way to remove the LDAP entries
1154 * corresponding to the entire table.
1155 */
1156 #ifdef NISDB_LDAP_DEBUG
1157 abort();
1158 #endif /* NISDB_LDAP_DEBUG */
1159 stat = LDAP_PARAM_ERROR;
1160 }
1161
1162 return (stat);
1163 }
1164
1165 /*
1166 * Helper function for storeLDAP() which handles updates for objects
1167 * other than table entries.
1168 */
1169 int
storeObjLDAP(__nis_table_mapping_t * t,nis_object * o)1170 db_mindex::storeObjLDAP(__nis_table_mapping_t *t, nis_object *o) {
1171 int stat, assigned = 0;
1172 entry_object **ea;
1173 int numEa, doUnlock = 0;
1174 db *dbase = 0;
1175 db_mindex *dbm = 0;
1176 const char *myself = "db_mindex::storeObjLDAP";
1177
1178 if (t == 0 || o == 0)
1179 return (LDAP_SUCCESS);
1180
1181 /*
1182 * If the object to be stored is anything other than a
1183 * directory, we can just go ahead and write it to LDAP.
1184 * A directory object, however, also needs a directory
1185 * entry list, so we should to get hold of the db_table
1186 * that goes with the directory.
1187 */
1188 if (o->zo_data.zo_type == NIS_DIRECTORY_OBJ) {
1189 dbase = tableDB(t->objName);
1190 if (dbase != 0)
1191 dbm = dbase->mindex();
1192 if (dbase == 0 || dbm == 0 || dbm->table == 0) {
1193 /* By definition, no dir entries */
1194 ea = 0;
1195 numEa = 0;
1196 dbase = 0;
1197 } else {
1198 entry_object **tea;
1199 long i, ntea;
1200
1201 /*
1202 * Read-lock the table so that 'tab'
1203 * doesn't change while we're using it.
1204 */
1205 READLOCK(dbm->table, LDAP_OPERATIONS_ERROR,
1206 "r table db_mindex::storeLDAP");
1207 doUnlock = 1;
1208
1209 tea = dbm->table->gettab();
1210 ntea = dbm->table->getsize();
1211
1212 /*
1213 * There may be empty slots in the table 'tab'
1214 * array, so get rid of those.
1215 */
1216 if (tea != 0 && ntea > 0) {
1217 ea = (entry_object **)am(myself,
1218 ntea * sizeof (ea[0]));
1219 if (ea == 0) {
1220 READUNLOCK(dbm->table, LDAP_NO_MEMORY,
1221 "ru table db_mindex::storeLDAP");
1222 return (LDAP_NO_MEMORY);
1223 }
1224 for (i = 0, numEa = 0; i < ntea; i++) {
1225 if (tea[i] != 0) {
1226 ea[numEa] = tea[i];
1227 numEa++;
1228 }
1229 }
1230 if (numEa == 0) {
1231 /* No non-empty slots */
1232 sfree(ea);
1233 ea = 0;
1234 READUNLOCK(dbm->table,
1235 LDAP_OPERATIONS_ERROR,
1236 "ru table db_mindex::storeLDAP");
1237 doUnlock = 0;
1238 }
1239 } else {
1240 ea = 0;
1241 numEa = 0;
1242 READUNLOCK(dbm->table,
1243 LDAP_OPERATIONS_ERROR,
1244 "ru table db_mindex::storeLDAP");
1245 doUnlock = 0;
1246 }
1247 }
1248 } else {
1249 ea = 0;
1250 numEa = 0;
1251 }
1252
1253 stat = objToLDAP(t, o, ea, numEa);
1254
1255 if (ea != 0)
1256 sfree(ea);
1257 if (doUnlock) {
1258 READUNLOCK(dbm->table, stat,
1259 "ru table db_mindex::storeLDAP");
1260 }
1261
1262 return (stat);
1263 }
1264
1265
1266 /*
1267 * Store data specified by the index-query 'qin' to LDAP. If 'obj' is
1268 * non-null, it's a pointer to the pseudo-entry object corresponding to
1269 * 'qin'. As a short-cut/convenience, the caller can instead supply
1270 * the actual nis_object 'o'; if 'o' is NULL, it's derived from 'obj'.
1271 *
1272 * 'oldObj' is used for table entries if the store operation is
1273 * an update, and the corresponding NIS+ operation was a delete followed
1274 * by an add. In this case, oldObj contains the pre-delete incarnation of
1275 * the entry object to be modified.
1276 *
1277 * The 'dbId' string is used to select one dbId for mapping chains
1278 * that contain more than one.
1279 *
1280 * Returns an LDAP status code.
1281 */
1282 int
storeLDAP(db_query * qin,entry_object * obj,nis_object * o,entry_obj * oldObj,char * dbId)1283 db_mindex::storeLDAP(db_query *qin, entry_object *obj, nis_object *o,
1284 entry_obj *oldObj, char *dbId) {
1285 __nis_table_mapping_t *t;
1286 bool_t asObj;
1287 db_query *q, *qo, **qa;
1288 __nis_rule_value_t *rv = 0;
1289 int stat;
1290 const char *myself = "db_mindex::storeLDAP";
1291
1292 if (!useLDAPrespository || table == 0)
1293 return (LDAP_SUCCESS);
1294
1295 /* Instances from the deferred dictionary should not update LDAP */
1296 if (table->mapping.isDeferredTable)
1297 return (LDAP_SUCCESS);
1298
1299 t = selectMapping(table, 0, qin, TRUE, &asObj, &stat);
1300 if (t == 0 && stat != LDAP_SUCCESS)
1301 return (stat);
1302
1303 #ifdef NISDB_LDAP_DEBUG
1304 if (t != 0)
1305 printf("storeLDAP: %s%s%s\n",
1306 dbId ? dbId : "", dbId ? ":" : "", NIL(t->objName));
1307 #endif /* NISDB_LDAP_DEBUG */
1308
1309 /*
1310 * selectMapping() didn't have the object to look at, so we
1311 * must check if this is a directory entry or not.
1312 */
1313 if (asObj) {
1314 if (o != 0) {
1315 if (o->zo_data.zo_type == NIS_ENTRY_OBJ)
1316 asObj = FALSE;
1317 } else if (obj != 0) {
1318 if (obj->en_type == 0 ||
1319 strcmp(obj->en_type, "IN_DIRECTORY") != 0)
1320 asObj = FALSE;
1321 }
1322 }
1323
1324 if (asObj) {
1325 bool_t freeO = FALSE;
1326
1327 /*
1328 * If we don't have a mapping, that's probably because
1329 * the directory (represented by 'this') isn't mapped.
1330 * Try to get a mapping from 'o' or 'obj'.
1331 */
1332 if (t == 0) {
1333 if (o == 0 && obj != 0) {
1334 o = unmakePseudoEntryObj(obj, 0);
1335 if (o == 0)
1336 return (LDAP_OPERATIONS_ERROR);
1337 freeO = TRUE;
1338 }
1339 if (o != 0) {
1340 t = selectMapping(0, o, 0, TRUE, &asObj, &stat);
1341 if (t == 0) {
1342 if (freeO)
1343 nis_destroy_object(o);
1344 return (stat);
1345 }
1346 }
1347 }
1348
1349 /*
1350 * If we found a mapping for the 'table' in this db_mindex,
1351 * store the object.
1352 */
1353 if (t != 0) {
1354 if (o == 0) {
1355 if (obj != 0) {
1356 o = unmakePseudoEntryObj(obj, 0);
1357 freeO = TRUE;
1358 } else {
1359 db_status dstat;
1360
1361 o = dbFindObject(t->objName, &dstat);
1362 if (o == 0)
1363 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1364 "%s: DB error %d finding \"%s\"",
1365 myself,
1366 NIL(t->objName));
1367 freeO = TRUE;
1368 }
1369 }
1370 if (o == 0)
1371 return (LDAP_OPERATIONS_ERROR);
1372
1373 stat = storeObjLDAP(t, o);
1374
1375 /*
1376 * Store the object with the mapping. If 'o' was
1377 * supplied by the caller, we first need to make
1378 * a copy.
1379 */
1380 if (!freeO) {
1381 o = nis_clone_object(o, 0);
1382 if (o == 0)
1383 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1384 "%s: Unable to refresh mapping object for \"%s\"",
1385 myself, NIL(t->objName));
1386 }
1387 if (o != 0) {
1388 if (!replaceMappingObj(t, o))
1389 nis_destroy_object(o);
1390 }
1391
1392 /*
1393 * Object now either destroyed or stored in 't'.
1394 * Set pointer to NULL in order to avoid freeing
1395 * it below.
1396 */
1397 o = 0;
1398
1399 if (stat != LDAP_SUCCESS)
1400 return (stat);
1401 }
1402
1403 if (freeO && o != 0) {
1404 nis_destroy_object(o);
1405 o = 0;
1406 }
1407
1408 /*
1409 * If the entry object 'obj' has the type "IN_DIRECTORY",
1410 * then it's a directory entry, and we should check if
1411 * the directory is mapped to LDAP, and update the dir
1412 * entry list accordingly.
1413 */
1414 if (obj == 0 || obj->en_type == 0 ||
1415 strcmp(obj->en_type, "IN_DIRECTORY") != 0)
1416 return (LDAP_SUCCESS);
1417
1418 /* Does it have a mapping ? */
1419 t = selectMapping(table, 0, 0, TRUE, &asObj, &stat);
1420 if (t == 0)
1421 return (stat);
1422
1423 stat = storeObjLDAP(t, t->obj);
1424
1425 return (stat);
1426 }
1427
1428 /* Store table entries. If we don't have a mapping, we're done. */
1429 if (t == 0)
1430 return (LDAP_SUCCESS);
1431
1432 if (qin != NULL && obj != NULL) {
1433 db_index_entry *dbie;
1434 int i, size, nq = 0;
1435 long l, count;
1436 bool_t valid;
1437 db_query qbuf, **qold;
1438
1439 rv = (__nis_rule_value_t *)am(myself, sizeof (*rv));
1440 qa = (db_query **)am(myself, sizeof (qa[0]));
1441 if (oldObj != 0) {
1442 /*
1443 * Note that only qold[0] is a unique query pointer.
1444 * All the other qold[i]'s are copies of qa[i].
1445 * Hence, we only free qold[0], as well as qold
1446 * itself.
1447 */
1448 qold = (db_query **)am(myself, sizeof (qold[0]));
1449 } else {
1450 qold = 0;
1451 }
1452 if (rv == 0 || qa == 0 || (oldObj != 0 && qold == 0)) {
1453 sfree(rv);
1454 sfree(qa);
1455 sfree(qold);
1456 return (LDAP_NO_MEMORY);
1457 }
1458
1459 q = schemeQuery2Query(qin, scheme);
1460 if (q == 0) {
1461 sfree(rv);
1462 sfree(qa);
1463 return (LDAP_PARAM_ERROR);
1464 }
1465
1466 qa[0] = pseudoEntryObj2Query(obj, t->obj, &rv[0]);
1467 if (qa[0] == 0) {
1468 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1469 "%s: Unable to obtain query representation of new entry object for \"%s\"",
1470 myself, NIL(t->dbId));
1471 freeQuery(q);
1472 sfree(rv);
1473 sfree(qa);
1474 sfree(qold);
1475 return (LDAP_OPERATIONS_ERROR);
1476 }
1477 if (oldObj != 0) {
1478 qold[0] = pseudoEntryObj2Query(oldObj, t->obj, 0);
1479 if (qold[0] == 0) {
1480 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1481 "%s: Unable to obtain query representation of old entry object for \"%s\"",
1482 myself, NIL(t->dbId));
1483 freeQueries(qa, 1);
1484 freeQuery(q);
1485 sfree(rv);
1486 sfree(qa);
1487 sfree(qold);
1488 return (LDAP_OPERATIONS_ERROR);
1489 }
1490 }
1491
1492 nq++;
1493
1494 /*
1495 * In order to support many-to-one NIS+ to LDAP mapping,
1496 * we need to find all possible matches in the NIS+ DB,
1497 * and then merge to produce a single update. mapToLDAP()
1498 * takes care of the merging, so our job is to collect
1499 * the matches. Worst case is that we need to search
1500 * individually for each component in 'qin', so that's
1501 * what we'll do.
1502 *
1503 * mapToLDAP() has a mode that only performs an update
1504 * for the first DN, and that's what we want. In order
1505 * to make sure that it's the correct DN, we leave the
1506 * original query as the first one passed to mapToLDAP().
1507 */
1508
1509 size = qin->size();
1510
1511 /* For each component of 'qin' */
1512 for (i = 0; i < size; i++) {
1513 db_query *qc, **qat, **qoldt;
1514 long j;
1515 __nis_rule_value_t *rvt;
1516
1517 qc = queryFromComponent(qin, i, &qbuf);
1518 if (qc == 0)
1519 continue;
1520
1521 dbie = satisfy_query_dbonly(qc, &count, FALSE, &valid);
1522 if (dbie == 0 || !valid || count <= 0)
1523 continue;
1524
1525 rvt = (__nis_rule_value_t *)realloc(rv,
1526 (nq+count) * sizeof (rv[0]));
1527 qat = (db_query **)realloc(qa,
1528 (nq+count) * sizeof (qa[0]));
1529 if (qold != 0)
1530 qoldt = (db_query **)realloc(qold,
1531 (nq+count) * sizeof (qold[0]));
1532 if (rvt == 0 || qat == 0 || (qold != 0 && qoldt == 0)) {
1533 if (qat == 0)
1534 freeQueries(qa, nq);
1535 else
1536 freeQueries(qat, nq);
1537 if (rvt == 0)
1538 freeRuleValue(rv, nq);
1539 else
1540 freeRuleValue(rvt, nq);
1541 if (qold != 0) {
1542 if (qoldt == 0)
1543 freeQueries(qold, 1);
1544 else
1545 freeQueries(qoldt, 1);
1546 }
1547 freeQuery(q);
1548 (void) memset(&qbuf, 0, sizeof (qbuf));
1549 logmsg(MSG_NOMEM, LOG_ERR,
1550 "%s: realloc(%d) failed",
1551 myself, (nq+count) * sizeof (void *));
1552 return (LDAP_NO_MEMORY);
1553 }
1554
1555 rv = rvt;
1556 qa = qat;
1557
1558 (void) memset(&rv[nq], 0, count * sizeof (rv[0]));
1559 (void) memset(&qa[nq], 0, count * sizeof (qa[0]));
1560 if (qold != 0) {
1561 qold = qoldt;
1562 (void) memset(&qold[nq], 0,
1563 count * sizeof (qold[0]));
1564 }
1565
1566 for (j = 0; j < count; j++) {
1567 qa[nq] = pseudoEntryObj2Query(
1568 table->get_entry(dbie->getlocation()),
1569 t->obj, &rv[nq]);
1570 if (qa[nq] == 0) {
1571 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1572 "%s: Could not create query from entry obj for \"%s\"",
1573 myself, NIL(t->objName));
1574 freeQueries(qa, nq);
1575 freeQueries(qold, 1);
1576 freeRuleValue(rv, nq);
1577 freeQuery(q);
1578 (void) memset(&qbuf, 0, sizeof (qbuf));
1579 return (LDAP_PARAM_ERROR);
1580 }
1581 if (qold != 0)
1582 qold[nq] = qa[nq];
1583 nq++;
1584 dbie = dbie->getnextresult();
1585 if (dbie == 0)
1586 break;
1587 }
1588 }
1589
1590 stat = mapToLDAP(t, nq, (qold != 0 ? qold : qa), qa, rv, 1,
1591 dbId);
1592
1593 freeQueries(qa, nq);
1594 freeRuleValue(rv, nq);
1595 freeQuery(q);
1596 freeQueries(qold, 1);
1597 (void) memset(&qbuf, 0, sizeof (qbuf));
1598
1599 } else if (qin == 0 && obj == 0 && t->objType == NIS_TABLE_OBJ) {
1600 long i, j, ntab;
1601 entry_object **tab;
1602
1603 READLOCK(table, LDAP_OPERATIONS_ERROR,
1604 "r table db_mindex::storeLDAP");
1605
1606 tab = table->gettab();
1607 ntab = table->getsize();
1608 if (tab == 0 || ntab <= 0) {
1609 READUNLOCK(table, LDAP_OPERATIONS_ERROR,
1610 "ru table db_mindex::storeLDAP");
1611 return (LDAP_SUCCESS);
1612 }
1613
1614 qa = (db_query **)am(myself, ntab * sizeof (qa[0]));
1615 rv = (__nis_rule_value_t *)am(myself, ntab * sizeof (rv[0]));
1616 if (qa == 0 || rv == 0) {
1617 sfree(qa);
1618 sfree(rv);
1619 READUNLOCK(table, LDAP_OPERATIONS_ERROR,
1620 "ru table db_mindex::storeLDAP");
1621 return (LDAP_NO_MEMORY);
1622 }
1623
1624 for (i = 0; i < ntab; i++) {
1625 if (tab[i] == 0)
1626 continue;
1627
1628 qa[i] = pseudoEntryObj2Query(tab[i], t->obj, &rv[i]);
1629 if (qa[i] == 0) {
1630 freeQueries(qa, i);
1631 freeRuleValue(rv, i);
1632 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1633 "%s: Could not create query from entry for \"%s\"",
1634 myself, NIL(t->objName));
1635 READUNLOCK(table, LDAP_OPERATIONS_ERROR,
1636 "ru table db_mindex::storeLDAP");
1637 return (LDAP_OPERATIONS_ERROR);
1638 }
1639 }
1640
1641 stat = mapToLDAP(t, ntab, qa, qa, rv, 0, dbId);
1642
1643 freeQueries(qa, ntab);
1644 freeRuleValue(rv, ntab);
1645
1646 if (stat == LDAP_SUCCESS) {
1647 struct timeval now;
1648 int lstat, lck = 1;
1649 /*
1650 * Since we've just successfully uploaded everthing
1651 * in this table, we now consider our local copy
1652 * up-to-date as well.
1653 */
1654
1655 (void) gettimeofday(&now, 0);
1656 WRITELOCKNR(table, lstat,
1657 "w table db_mindex::storeLDAP");
1658 if (lstat == 0) {
1659 table->mapping.enumExpire = now.tv_sec +
1660 table->mapping.ttl;
1661 lck = 0;
1662 WRITEUNLOCKNR(table, lstat,
1663 "wu table db_mindex::storeLDAP");
1664 }
1665 if (lstat != 0) {
1666 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1667 "%s: %sock error %d for \"%s\"%s",
1668 myself, lck?"L":"Unl", lstat,
1669 NIL(t->objName),
1670 lck ?
1671 "; unable to update enumeration expiration":
1672 "");
1673 }
1674 }
1675
1676 READUNLOCK(table, stat,
1677 "ru table db_mindex::storeLDAP");
1678 }
1679
1680 return (stat);
1681 }
1682 /*
1683 * Sets the oid (i.e., the creation and modification times) for the
1684 * specified object. In order to avoid retrieving the old incarnation
1685 * (if any) from the DB first, we're punting and setting both mtime
1686 * and ctime to the current time.
1687 */
1688 static void
setOid(nis_object * obj)1689 setOid(nis_object *obj) {
1690 if (obj != 0) {
1691 obj->zo_oid.ctime = obj->zo_oid.mtime = time(0);
1692 }
1693 }
1694