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