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 2001-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <sys/param.h> 32 #include <errno.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <ctype.h> 36 #include <malloc.h> 37 #include <stdlib.h> 38 #include <deflt.h> 39 #include <limits.h> 40 41 #include "ldap_parse.h" 42 #include "ldap_glob.h" 43 #include "ldap_attr.h" 44 #include "ldap_util.h" 45 #include "ldap_map.h" 46 #include "ldap_ruleval.h" 47 #include "nis_parse_ldap_conf.h" 48 49 int yp2ldap = 0; 50 /* 51 * List of mapping structures in original (i.e., as in config file) order. 52 * Lined on the 'seqNext' field. 53 */ 54 __nis_table_mapping_t *ldapMappingSeq = 0; 55 56 /* 57 * Call the parser for the config file 'ldapConfFile', and command line 58 * attribute settings per 'ldapCLA'. 59 * 60 * Returns 61 * 0 Success 62 * -1 Config file stat/open or parse error 63 * 1 No mapping should be used. 64 */ 65 int 66 parseConfig(char **ldapCLA, char *ldapConfFile) { 67 int ret; 68 69 /* 70 * Establish defaults for ldapDBTableMapping, so that we have 71 * valid values even if there's no mapping config to parse. 72 */ 73 ldapDBTableMapping.initTtlLo = (3600-1800); 74 ldapDBTableMapping.initTtlHi = (3600+1800); 75 ldapDBTableMapping.ttl = 3600; 76 ldapDBTableMapping.enumExpire = 0; 77 ldapDBTableMapping.fromLDAP = FALSE; 78 ldapDBTableMapping.toLDAP = FALSE; 79 ldapDBTableMapping.expire = 0; 80 81 ret = parse_ldap_migration((const char **)ldapCLA, ldapConfFile); 82 83 return (ret); 84 } 85 86 /* 87 * Convert the linked list of __nis_table_mapping_t's (produced by the 88 * attribute parser) to the 'ldapMappingList', keyed on the objPath. 89 * 90 * Once this function has returned, the 'tlist' is invalid, and must 91 * not be used in any way. 92 */ 93 int 94 linked2hash(__nis_table_mapping_t *tlist) { 95 __nis_hash_table_mt dbids; 96 __nis_table_mapping_t *t, *told, *x, **seqNext; 97 __nis_object_dn_t *o, *to; 98 char *myself = "linked2hash"; 99 #ifdef NISDB_LDAP_DEBUG 100 char *selectDbid = getenv("NISLDAPSELECTDBID"); 101 char **sdi, *s; 102 int i, nsdi; 103 #endif /* NISDB_LDAP_DEBUG */ 104 105 106 if (tlist == 0) 107 return (0); 108 109 /* proxyInfo.default_nis_domain must end in a dot */ 110 { 111 int len = slen(proxyInfo.default_nis_domain); 112 113 if (len > 0 && proxyInfo.default_nis_domain[len-1] != '.') { 114 char *domain = am(myself, len+2); 115 116 (void) memcpy(domain, proxyInfo.default_nis_domain, 117 len); 118 domain[len] = '.'; 119 domain[len+1] = '\0'; 120 sfree(proxyInfo.default_nis_domain); 121 proxyInfo.default_nis_domain = domain; 122 } 123 } 124 125 #ifdef NISDB_LDAP_DEBUG 126 for (nsdi = 0, s = selectDbid; s != 0 && *s != '\0'; s++) { 127 if (*s != ' ') { 128 nsdi++; 129 while (*s != ' ' && *s != '\0') 130 s++; 131 if (*s == '\0') 132 break; 133 } 134 } 135 if (nsdi > 0) { 136 sdi = am(myself, nsdi * sizeof (sdi[0])); 137 if (sdi == 0) 138 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 139 "%s: Memory alloc failure for dbId selection", 140 myself); 141 else { 142 for (i = 0, s = selectDbid; *s != '\0'; s++) { 143 if (*s != ' ') { 144 sdi[i++] = selectDbid; 145 while (*s != ' ' && *s != '\0') 146 s++; 147 if (*s != '\0') { 148 *s = '\0'; 149 s++; 150 } else 151 break; 152 selectDbid = s; 153 } 154 } 155 } 156 } 157 #endif /* NISDB_LDAP_DEBUG */ 158 159 __nis_init_hash_table(&dbids, 0); 160 161 seqNext = &ldapMappingSeq; 162 for (t = tlist; t != 0; t = told) { 163 int len; 164 165 #ifdef NISDB_LDAP_DEBUG 166 /* 167 * If the dbId doesn't match 'selectDbid', skip this 168 * mapping. Re-insert on 'tlist', in order to keep memory 169 * leak checking happy. Note that 'tlist' may end up pointing 170 * into the real mapping list, so it shouldn't be used once 171 * this routine has been called. 172 */ 173 if (nsdi > 0) { 174 for (i = 0; i < nsdi; i++) { 175 if (strcmp(sdi[i], t->dbId) == 0) 176 break; 177 } 178 if (i >= nsdi) { 179 told = t->next; 180 if (tlist != t) 181 t->next = tlist; 182 else 183 t->next = 0; 184 tlist = t; 185 continue; 186 } 187 } 188 #endif /* NISDB_LDAP_DEBUG */ 189 190 told = t->next; 191 t->next = 0; 192 193 /* Make sure t->item.name is set correctly */ 194 if (t->item.name == 0) 195 t->item.name = t->dbId; 196 197 /* Remove leading dot in object name, if any */ 198 len = slen(t->objName); 199 while (len > 0 && t->objName[0] == '.') { 200 (void) memmove(t->objName, &t->objName[1], len); 201 len -= 1; 202 } 203 204 /* 205 * Initialize the object path, which is what we'll 206 * rehash on. 207 */ 208 if (yp2ldap) { 209 t->objPath = internal_table_name(t->objName, 210 t->objPath); 211 if (!t->objPath) { 212 logmsg(MSG_NOTIMECHECK, LOG_ERR, 213 "%s: Failed to obtain internal table name for \"%s\"", 214 myself, t->objName); 215 return (-1); 216 } 217 } else { 218 t->objPath = am(myself, len + MAXPATHLEN + 1); 219 if (t->objPath == 0) 220 return (-1); 221 if (internal_table_name(t->objName, 222 t->objPath) == 0) { 223 logmsg(MSG_NOTIMECHECK, LOG_ERR, 224 "%s: Failed to obtain internal table name for \"%s\"", 225 myself, t->objName); 226 return (-1); 227 } 228 } 229 230 /* 231 * Initialize the column name array. 232 */ 233 if (!yp2ldap) { 234 if (setColumnsDuringConfig && setColumnNames(t)) { 235 logmsg(MSG_NOTIMECHECK, LOG_ERR, 236 "%s: Unable to find column names for \"%s\"", 237 myself, NIL(t->objName)); 238 return (-1); 239 } 240 } 241 242 /* 243 * If there are multiple mapping target containers, make 244 * each one into it's own mapping structure. They can all 245 * be minimal copies (i.e., share pointers to sub-structures 246 * other than the objectDN). 247 * 248 * If objectDN is NULL, we will never use this structure. 249 * In order to allow the rest of the mapping code to assume 250 * objectDN != NULL, skip the mapping (even if x == t). 251 */ 252 for (o = to = t->objectDN; o != 0; o = o->next) { 253 __nis_table_mapping_t *p; 254 255 if (o == to) { 256 x = t; 257 /* 258 * Only insert the first mapping for an 259 * object on the sequential list. 260 */ 261 *seqNext = t; 262 t->seqNext = 0; 263 seqNext = (__nis_table_mapping_t **)&t->seqNext; 264 } else { 265 x = am(myself, sizeof (*x)); 266 if (x == 0) { 267 /* 268 * This happens during rpc.nisd 269 * initialization, and it's an 270 * unrecoverable disaster, so don't 271 * bother cleaning up. 272 */ 273 return (-1); 274 } 275 memcpy(x, t, sizeof (*x)); 276 x->objectDN = o; 277 x->next = 0; 278 } 279 280 /* 281 * If x->objectDN->write.base is NULL, clone it from 282 * x->objectDN->read.base. 283 */ 284 if (x->objectDN->write.scope != LDAP_SCOPE_UNKNOWN) { 285 if (x->objectDN->write.base == 0 && 286 x->objectDN->read.base != 0) { 287 x->objectDN->write.base = 288 sdup(myself, T, 289 x->objectDN->read.base); 290 if (x->objectDN->write.base == 0) 291 return (-1); 292 } 293 if (x->objectDN->write.attrs == 0 && 294 x->objectDN->read.attrs != 0) { 295 x->objectDN->write.attrs = 296 sdup(myself, T, 297 x->objectDN->read.attrs); 298 if (x->objectDN->write.attrs == 0) 299 return (-1); 300 } 301 } 302 303 if (o != to) { 304 /* Insert last on the 't->next' list */ 305 for (p = t; p->next != 0; p = p->next); 306 p->next = x; 307 } 308 } 309 310 /* Insert on dbid hash list */ 311 if (t->objectDN != 0 && !__nis_insert_item_mt(t, &dbids, 0)) { 312 logmsg(MSG_NOTIMECHECK, LOG_ERR, 313 "%s: Error inserting mapping for \"%s\" on hash list", 314 myself, NIL(t->objName)); 315 #ifdef NISDB_LDAP_DEBUG 316 abort(); 317 #endif /* NISDB_LDAP_DEBUG */ 318 return (-1); 319 } 320 } 321 322 /* 323 * dbids2objs() will remove the entries on 'dbids', so no need 324 * to clean up that list from this function. 325 */ 326 return (dbids2objs(&dbids, &ldapMappingList)); 327 } 328 329 int 330 dbids2objs(__nis_hash_table_mt *dbids, __nis_hash_table_mt *objs) { 331 __nis_table_mapping_t *t, *o; 332 char *myself = "dbids2objs"; 333 334 335 while ((t = __nis_pop_item_mt(dbids)) != 0) { 336 /* Previous entry for this object ? */ 337 o = __nis_find_item_mt(t->objPath, objs, -1, 0); 338 if (o != 0) { 339 __nis_table_mapping_t *p = o; 340 /* 341 * Mapping already exists, so this is an alternate. 342 * Find the end of the list of any previous alt's, 343 * and insert there. 344 */ 345 while (p->next != 0) { 346 p = p->next; 347 } 348 p->next = t; 349 if (!__nis_release_item(o, objs, -1)) { 350 logmsg(MSG_NOTIMECHECK, LOG_ERR, 351 "%s: __nis_release_item error", 352 myself); 353 return (-1); 354 } 355 } else { 356 t->item.name = t->objPath; 357 if (!__nis_insert_item_mt(t, objs, 0)) { 358 logmsg(MSG_NOTIMECHECK, LOG_ERR, 359 "%s: __nis_insert_item error", 360 myself); 361 return (-1); 362 } 363 } 364 } 365 366 return (0); 367 } 368 369 /* 370 * internal_table_name() 371 * 372 * Removes the local domain part from a fully qualified name 373 * to create the internal table name for an object. These tables are 374 * stored in /var/nis/<hostname> 375 * 376 * Imported from rpc.nisd/nisdb.c. 377 */ 378 char * 379 internal_table_name(nis_name name, char *res) 380 { 381 char *s, *t; 382 int i, j; 383 384 if (yp2ldap) { 385 if (name == NULL) 386 return (NULL); 387 res = s_strndup(name, strlen(name)); 388 if (res == NULL) 389 return (NULL); 390 return (res); 391 } 392 393 if (res == NULL) 394 return (NULL); 395 /* pointer at the first character of the table name */ 396 s = relative_name(name); 397 398 /* 399 * If s == NULL then either this is a request for a lookup 400 * in our parents namespace (ILLEGAL), or we're the root 401 * server and this is a lookup in our namespace. 402 */ 403 if (s) { 404 strcpy(res, nis_data(s)); 405 free((void *)s); 406 } else if (nis_dir_cmp(name, __nis_rpc_domain()) == SAME_NAME) { 407 strcpy(res, nis_data("ROOT_DIR")); 408 } else { 409 return (NULL); 410 } 411 412 t = strrchr(res, '/'); 413 if (t) 414 t++; /* Point past the slash */ 415 /* Strip off the quotes if they were used here. */ 416 if (t[0] == '"') { 417 /* Check for simply a quoted quote. */ 418 if (t[1] != '"') { 419 j = strlen(t); 420 /* shift string left by one */ 421 for (i = 0; i < j; i++) 422 t[i] = t[i+1]; 423 t[j-2] = '\0'; /* Trounce trailing dquote */ 424 } 425 } 426 /* 427 * OK so now we have the unique name for the table. 428 * At this point we can fix it up to match local 429 * file system conventions if we so desire. Since it 430 * is only used in this form by _this_ server we can 431 * mangle it any way we want, as long as we are consistent 432 * about it. :-) 433 */ 434 __make_legal(res); 435 return (res); 436 } 437 438 /* 439 * SYSTEM DEPENDENT 440 * 441 * This function makes the table name "legal" for the underlying file system. 442 * 443 * Imported from rpc.nisd/nisdb.c. 444 */ 445 void 446 __make_legal(char *s) 447 { 448 while (*s) { 449 if (isupper(*s)) 450 *s = tolower(*s); 451 s++; 452 } 453 } 454 455 /* 456 * relative_name() 457 * This internal function will remove from the NIS name, the domain 458 * name of the current server, this will leave the unique part in 459 * the name this becomes the "internal" version of the name. If this 460 * function returns NULL then the name we were given to resolve is 461 * bad somehow. 462 * 463 * A dynamically-allocated string is returned. 464 * 465 * Imported from rpc.nisd/nis_log_common.c 466 */ 467 468 nis_name 469 relative_name(s) 470 char *s; /* string with the name in it. */ 471 { 472 char *d; 473 char *buf; 474 int dl, sl; 475 name_pos p; 476 477 if (s == NULL) 478 return (NULL); 479 480 d = __nis_rpc_domain(); 481 if (d == NULL) 482 return (NULL); 483 dl = strlen(d); /* _always dot terminated_ */ 484 485 buf = strdup(s); 486 if (buf == NULL) 487 return (NULL); 488 strcpy(buf, s); /* Make a private copy of 's' */ 489 sl = strlen(buf); 490 491 if (dl == 1) { /* We're the '.' directory */ 492 buf[sl-1] = '\0'; /* Lose the 'dot' */ 493 return (buf); 494 } 495 496 p = nis_dir_cmp(buf, d); 497 498 /* 's' is above 'd' in the tree */ 499 if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME)) { 500 free(buf); 501 return (NULL); 502 } 503 504 /* Insert a NUL where the domain name starts in the string */ 505 buf[(sl - dl) - 1] = '\0'; 506 507 /* Don't return a zero length name */ 508 if (buf[0] == '\0') { 509 free((void *)buf); 510 return (NULL); 511 } 512 513 return (buf); 514 } 515 516 /* 517 * Wrapper for internal_table_name() that allocates a large enough 518 * buffer for the internal name. Return value must be freed by caller. 519 * If the input 'name' is NULL, the name of the root directory table 520 * is returned. 521 */ 522 char * 523 internalTableName(char *name) { 524 char *buf, *res; 525 char *myself = "internalTableName"; 526 527 buf = (char *)am(myself, MAXPATHLEN + NIS_MAXNAMELEN + 1); 528 if (buf == 0) 529 return (0); 530 531 if (name == 0) { 532 (void) memcpy(buf, ROOTDIRFILE, slen(ROOTDIRFILE)); 533 return (buf); 534 } 535 536 res = internal_table_name(name, buf); 537 if (res != buf) { 538 sfree(buf); 539 buf = 0; 540 } 541 542 return (buf); 543 } 544 545 /* 546 * Return the object mapping for the object indicated either by the 547 * internal DB name ('intNameArg'; preferred), or the FQ object name 548 * 'name'. If 'asObj' is non-zero, the caller is interested in the 549 * object mapping proper, not a mapping of table entries. Optionally, 550 * also indicate if the object is mapped from (read) or to (write) LDAP. 551 * 552 * Note that there may be more than one mapping of the appropriate type. 553 * Use the selectTableMapping() function in ldap_map.c to get all 554 * alternatives. However, the function below works as a short-cut if: 555 * 556 * You only want an indication that _a_ mapping of the desired 557 * type exists, or 558 * 559 * You want the non-objectDN information for an object-mapping 560 * proper (i.e., _not_ the mapping for entries in a table). 561 */ 562 __nis_table_mapping_t * 563 getObjMapping(char *name, char *intNameArg, int asObj, 564 int *doRead, int *doWrite) { 565 __nis_table_mapping_t *t, *x; 566 char *intName; 567 int freeIntName = 0, rd, wr; 568 569 if (doRead != 0) 570 *doRead = 0; 571 if (doWrite != 0) 572 *doWrite = 0; 573 574 if (intNameArg == 0) { 575 if (name == 0) 576 return (0); 577 intName = internalTableName(name); 578 if (intName == 0) 579 return (0); 580 freeIntName = 1; 581 } else { 582 intName = intNameArg; 583 } 584 585 t = __nis_find_item_mt(intName, &ldapMappingList, 0, 0); 586 if (t == 0) { 587 if (freeIntName) 588 sfree(intName); 589 return (0); 590 } 591 592 rd = wr = 0; 593 for (x = t; x != 0; x = x->next) { 594 /* 595 * If we're looking for an object mapping, and this 596 * one's for entries in a table, skip it. 597 */ 598 if (asObj && x->objType == NIS_TABLE_OBJ && 599 x->numColumns > 0) 600 continue; 601 /* Check if we should read/write */ 602 if (x->objectDN->read.scope != LDAP_SCOPE_UNKNOWN) 603 rd++; 604 if (x->objectDN->write.scope != LDAP_SCOPE_UNKNOWN) 605 wr++; 606 } 607 608 if (doRead != 0) 609 *doRead = (rd > 0) ? 1 : 0; 610 if (doWrite != 0) 611 *doWrite = (wr > 0) ? 1 : 0; 612 613 if (freeIntName) 614 sfree(intName); 615 616 return (x); 617 } 618