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 /* 23 * Copyright 2007 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 /* 30 * This module contains the subroutines used by the server to manipulate 31 * objects and names. 32 */ 33 #include "mt.h" 34 #include <pwd.h> 35 #include <grp.h> 36 #include <syslog.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <ctype.h> 40 #include <stdlib.h> 41 #include <unistd.h> 42 #include <sys/time.h> 43 #include <sys/fcntl.h> 44 #include <netinet/in.h> 45 #include <rpc/rpc.h> /* Must be ahead of rpcb_clnt.h */ 46 #include <rpc/svc.h> 47 #include <tiuser.h> 48 #include <netconfig.h> 49 #include <netdir.h> 50 #include <rpc/rpcb_clnt.h> 51 #include <rpc/pmap_clnt.h> 52 #include <rpcsvc/nis.h> 53 #include <rpcsvc/nis_dhext.h> 54 #include "nis_clnt.h" 55 #include <sys/systeminfo.h> 56 #include "nis_local.h" 57 #include <nsswitch.h> 58 59 #define MAXIPRINT (11) /* max length of printed integer */ 60 static char *PKTABLE = "cred.org_dir"; 61 #define PKTABLE_LEN 12 62 /* 63 * send and receive buffer size used for clnt_tli_create if not specified. 64 * This is only used for "UDP" connection. 65 * This limit can be changed from the application if this value is too 66 * small for the application. To use the maximum value for the transport, 67 * set this value to 0. 68 */ 69 int __nisipbufsize = 8192; 70 71 /* Error result returned by nis_make_error() when malloc fails */ 72 const nis_result __nomem_nis_result = {NIS_NOMEMORY, {0, 0}, {0, 0}, 73 0, 0, 0, 0}; 74 75 extern int __readColdStartFile(); 76 77 /* 78 * Static function prototypes. 79 */ 80 static struct local_names *__get_local_names(void); 81 static char *__map_addr(struct netconfig *, char *, rpcprog_t, rpcvers_t); 82 83 CLIENT * nis_make_rpchandle_uaddr(nis_server *, 84 int, rpcprog_t, rpcvers_t, uint_t, int, int, char *); 85 86 #define COMMA ',' /* Avoid cstyle bug */ 87 88 /* __nis_data_directory is READ ONLY, so no locking is needed */ 89 /* Note: We make it static, so external caller can not access it */ 90 /* i.e we make sure it stay read only */ 91 static char __nis_data_directory[1024] = {"/var/nis/"}; 92 93 /* These macros make the code easier to read */ 94 95 #ifdef NOTIME 96 #define __start_clock(n) 97 #define __stop_clock(n) n 98 #else 99 static struct timeval clocks[MAXCLOCKS]; 100 101 #define LOOP_UADDR "127.0.0.1.0.0" 102 103 /* 104 * __start_clock() 105 * 106 * This function will start the "stopwatch" on the function calls. 107 * It uses an array of time vals to keep track of the time. The 108 * sister call __stop_clock() will return the number of microseconds 109 * since the clock was started. This allows for keeping statistics 110 * on the NIS calls and tuning the service. If the clock in question 111 * is not "stopped" this function returns an error. 112 */ 113 int 114 __start_clock( 115 int clk) /* The clock we want to start */ 116 { 117 if ((clk >= MAXCLOCKS) || (clk < 0) || (clocks[clk].tv_sec)) 118 return (FALSE); 119 120 (void) gettimeofday(&clocks[clk], NULL); 121 return (TRUE); 122 } 123 124 uint32_t 125 __stop_clock(int clk) 126 { 127 struct timeval now; 128 uint32_t secs, micros; 129 130 if ((clk >= MAXCLOCKS) || (clk < 0) || (!clocks[clk].tv_sec)) 131 return (0); 132 (void) gettimeofday(&now, NULL); 133 secs = (int)(now.tv_sec - clocks[clk].tv_sec); 134 if (now.tv_usec < clocks[clk].tv_usec) { 135 micros = (int)((now.tv_usec + 1000000) - clocks[clk].tv_usec); 136 secs--; /* adjusted 'cuz we added a second above */ 137 } else { 138 micros = (int)(now.tv_usec - clocks[clk].tv_usec); 139 } 140 micros = micros + (secs * 1000000); /* All micros now */ 141 clocks[clk].tv_sec = 0; /* Stop the clock. */ 142 return (micros); 143 } 144 #endif /* no time */ 145 146 /* 147 * nis_dir_cmp() -- the results can be read as: 148 * "Name 'n1' is a $result than name 'n2'" 149 */ 150 name_pos 151 nis_dir_cmp( 152 nis_name n1, 153 nis_name n2) /* See if these are the same domain */ 154 { 155 size_t l1, l2; 156 name_pos result; 157 158 if ((n1 == NULL) || (n2 == NULL)) 159 return (BAD_NAME); 160 161 l1 = strlen(n1); 162 l2 = strlen(n2); 163 164 /* In this routine we're lenient and don't require a trailing '.' */ 165 /* so we need to ignore it if it does appear. */ 166 /* ==== That's what the previous version did so this one does */ 167 /* too, but why? Is this inconsistent with rest of system? */ 168 if (l1 != 0 && n1[l1 - 1] == '.') { 169 --l1; 170 } 171 if (l2 != 0 && n2[l2 - 1] == '.') { 172 --l2; 173 } 174 175 if (l1 > l2) { 176 result = LOWER_NAME; 177 } else if (l1 == l2) { 178 result = SAME_NAME; 179 } else /* (l1 < l2); swap l1/l2 and n1/n2 */ { 180 nis_name ntmp; 181 size_t ltmp; 182 ntmp = n1; n1 = n2; n2 = ntmp; 183 ltmp = l1; l1 = l2; l2 = ltmp; 184 185 result = HIGHER_NAME; 186 } 187 188 /* Now l1 >= l2 in all cases */ 189 if (l2 == 0) { 190 /* Special case for n2 == "." or "" */ 191 return (result); 192 } 193 if (l1 > l2) { 194 n1 += l1 - l2; 195 if (n1[-1] != '.') { 196 return (NOT_SEQUENTIAL); 197 } 198 } 199 if (strncasecmp(n1, n2, l2) == 0) { 200 return (result); 201 } 202 return (NOT_SEQUENTIAL); 203 } 204 205 #define LN_BUFSIZE (size_t)1024 206 207 struct principal_list { 208 uid_t uid; 209 char principal[LN_BUFSIZE]; 210 struct principal_list *next; 211 }; 212 213 214 struct local_names { 215 char domain[LN_BUFSIZE]; 216 char host[LN_BUFSIZE]; 217 char *rpcdomain; 218 struct principal_list *principal_map; 219 char group[LN_BUFSIZE]; 220 }; 221 222 static mutex_t ln_lock = DEFAULTMUTEX; /* lock level 2 */ 223 static struct local_names *ln = NULL; 224 static struct local_names *__get_local_names1(); 225 226 static struct local_names * 227 __get_local_names(void) 228 { 229 struct local_names *names; 230 231 sig_mutex_lock(&ln_lock); 232 names = __get_local_names1(); 233 sig_mutex_unlock(&ln_lock); 234 return (names); 235 } 236 237 static char * 238 get_nis_domain(void) 239 { 240 directory_obj dobj; 241 enum __nsw_parse_err pserr; 242 struct __nsw_switchconfig *conf; 243 static int checked_domain = 0; 244 static char *nisdomain = 0; 245 246 if (!checked_domain) { 247 checked_domain = 1; 248 /* 249 * Check that nisplus is first in nsswitch.conf for publickey. 250 */ 251 conf = __nsw_getconfig("publickey", &pserr); 252 if (conf == NULL) 253 return (NULL); 254 if (conf->num_lookups <= 0) 255 return (NULL); 256 if (strcasecmp(conf->lookups[0].service_name, "nisplus") != 0) 257 return (NULL); 258 259 /* 260 * Read cold-start file to determine directory where 261 * the machine's credentials are stored. 262 */ 263 if (!__readColdStartFile(&dobj)) 264 return (NULL); 265 nisdomain = strdup(dobj.do_name); 266 xdr_free((xdrproc_t)xdr_directory_obj, (char *)&dobj); 267 } 268 269 return (nisdomain); 270 } 271 272 static struct local_names * 273 __get_local_names1(void) 274 { 275 char *t; 276 277 if (ln != NULL) { 278 /* Second and subsequent calls go this way */ 279 return (ln); 280 } 281 /* First call goes this way */ 282 ln = calloc(1, sizeof (*ln)); 283 if (ln == NULL) { 284 syslog(LOG_ERR, "__get_local_names: Out of heap."); 285 return (NULL); 286 } 287 ln->principal_map = NULL; 288 289 if (sysinfo(SI_SRPC_DOMAIN, ln->domain, LN_BUFSIZE) < 0) 290 return (ln); 291 /* If no dot exists, add one. */ 292 if (ln->domain[strlen(ln->domain)-1] != '.') 293 (void) strcat(ln->domain, "."); 294 if (sysinfo(SI_HOSTNAME, ln->host, LN_BUFSIZE) < 0) 295 return (ln); 296 297 /* 298 * Check for fully qualified hostname. If it's a fully qualified 299 * hostname, strip off the domain part. We always use the local 300 * domainname for the host principal name. 301 */ 302 t = strchr(ln->host, '.'); 303 if (t) 304 *t = 0; 305 if (ln->domain[0] != '.') 306 (void) strcat(ln->host, "."); 307 if ((ln->rpcdomain = get_nis_domain()) != NULL) { 308 (void) strcat(ln->host, ln->rpcdomain); 309 } else { 310 ln->rpcdomain = strdup(ln->domain); 311 (void) strcat(ln->host, ln->domain); 312 } 313 314 t = getenv("NIS_GROUP"); 315 if (t == NULL) { 316 ln->group[0] = '\0'; 317 } else { 318 size_t maxlen = LN_BUFSIZE-1; /* max chars to copy */ 319 char *temp; /* temp marker */ 320 321 /* 322 * Copy <= maximum characters from NIS_GROUP; strncpy() 323 * doesn't terminate, so we do that manually. #1223323 324 * Also check to see if it's "". If it's the null string, 325 * we return because we don't want to add ".domain". 326 */ 327 (void) strncpy(ln->group, t, maxlen); 328 if (strcmp(ln->group, "") == 0) { 329 return (ln); 330 } 331 ln->group[maxlen] = '\0'; 332 333 /* Is the group name somewhat fully-qualified? */ 334 temp = strrchr(ln->group, '.'); 335 336 /* If not, we need to add ".domain" to the group */ 337 if ((temp == NULL) || (temp[1] != '\0')) { 338 339 /* truncate to make room for ".domain" */ 340 ln->group[maxlen - (strlen(ln->domain)+1)] = '\0'; 341 342 /* concat '.' if domain doesn't already have it */ 343 if (ln->domain[0] != '.') { 344 (void) strcat(ln->group, "."); 345 } 346 (void) strcat(ln->group, ln->domain); 347 } 348 } 349 return (ln); 350 } 351 352 /* 353 * nis_local_group() 354 * 355 * Return's the group name of the current user. 356 */ 357 nis_name 358 nis_local_group(void) 359 { 360 struct local_names *ln = __get_local_names(); 361 362 /* LOCK NOTE: Warning, after initialization, "ln" is expected */ 363 /* to stay constant, So no need to lock here. If this assumption */ 364 /* is changed, this code must be protected. */ 365 if (!ln) 366 return (NULL); 367 return (ln->group); 368 } 369 370 /* 371 * __nis_nextsep_of() 372 * 373 * This internal funtion will accept a pointer to a NIS name string and 374 * return a pointer to the next separator occurring in it (it will point 375 * just past the first label). It allows for labels to be "quoted" to 376 * prevent the the dot character within them to be interpreted as a 377 * separator, also the quote character itself can be quoted by using 378 * it twice. If the the name contains only one label and no trailing 379 * dot character, a pointer to the terminating NULL is returned. 380 */ 381 nis_name 382 __nis_nextsep_of(char *s) 383 { 384 char *d; 385 int in_quotes = FALSE, 386 quote_quote = FALSE; 387 388 if (!s) 389 return (NULL); 390 391 for (d = s; (in_quotes && (*d != '\0')) || 392 (!in_quotes && (*d != '.') && (*d != '\0')); d++) { 393 if (quote_quote && in_quotes && (*d != '"')) { 394 quote_quote = FALSE; 395 in_quotes = FALSE; 396 if (*d == '.') 397 break; 398 } else if (quote_quote && in_quotes && (*d == '"')) { 399 quote_quote = FALSE; 400 } else if (quote_quote && (*d != '"')) { 401 quote_quote = FALSE; 402 in_quotes = TRUE; 403 } else if (quote_quote && (*d == '"')) { 404 quote_quote = FALSE; 405 } else if (in_quotes && (*d == '"')) { 406 quote_quote = TRUE; 407 } else if (!in_quotes && (*d == '"')) { 408 quote_quote = TRUE; 409 } 410 } 411 412 if (quote_quote || in_quotes) { 413 syslog(LOG_DEBUG, "__nis_nextsep_of: " 414 "Mismatched quotes in %s", s); 415 } 416 417 return (d); 418 } 419 420 /* 421 * nis_domain_of() 422 * 423 * This internal funtion will accept a pointer to a NIS name string and 424 * return a pointer to the "domain" part of it. 425 * 426 * ==== We don't need nis_domain_of_r(), but should we provide one for 427 * uniformity? 428 */ 429 nis_name 430 nis_domain_of(char *s) 431 { 432 char *d; 433 434 d = __nis_nextsep_of(s); 435 if (d == NULL) 436 return (NULL); 437 if (*d == '.') 438 d++; 439 if (*d == '\0') /* Don't return a zero length string */ 440 return ("."); /* return root domain instead */ 441 return (d); 442 } 443 444 445 /* 446 * nis_leaf_of() 447 * 448 * Returns the first label of a name. (other half of __domain_of) 449 */ 450 nis_name 451 nis_leaf_of_r( 452 const nis_name s, 453 char *buf, 454 size_t bufsize) 455 { 456 size_t nchars; 457 const char *d = __nis_nextsep_of((char *)s); 458 459 if (d == 0) { 460 return (0); 461 } 462 nchars = d - s; 463 if (bufsize < nchars + 1) { 464 return (0); 465 } 466 (void) strncpy(buf, s, nchars); 467 buf[nchars] = '\0'; 468 return (buf); 469 } 470 471 static pthread_key_t buf_key = PTHREAD_ONCE_KEY_NP; 472 static char buf_main[LN_BUFSIZE]; 473 474 nis_name 475 nis_leaf_of(char *s) 476 { 477 char *buf = thr_main()? buf_main : 478 thr_get_storage(&buf_key, LN_BUFSIZE, free); 479 480 if (buf == NULL) 481 return (NULL); 482 return (nis_leaf_of_r(s, buf, LN_BUFSIZE)); 483 } 484 485 /* 486 * nis_name_of() 487 * This internal function will remove from the NIS name, the domain 488 * name of the current server, this will leave the unique part in 489 * the name this becomes the "internal" version of the name. If this 490 * function returns NULL then the name we were given to resolve is 491 * bad somehow. 492 * NB: Uses static storage and this is a no-no with threads. XXX 493 */ 494 495 nis_name 496 nis_name_of_r( 497 char *s, /* string with the name in it. */ 498 char *buf, 499 size_t bufsize) 500 { 501 char *d; 502 struct local_names *ln = __get_local_names(); 503 size_t dl, sl; 504 name_pos p; 505 506 #ifdef lint 507 bufsize = bufsize; 508 #endif /* lint */ 509 if ((!s) || (!ln)) 510 return (NULL); /* No string, this can't continue */ 511 512 d = &(ln->domain[0]); 513 dl = strlen(ln->domain); /* _always dot terminated_ */ 514 515 sl = strlen(s); 516 if (sl >= bufsize || (s[sl-1] != '.' && sl >= bufsize-1)) 517 return (NULL); 518 (void) strcpy(buf, s); /* Make a private copy of 's' */ 519 if (buf[sl-1] != '.') { /* Add a dot if necessary. */ 520 (void) strcat(buf, "."); 521 sl++; 522 } 523 524 if (dl == 1) { /* We're the '.' directory */ 525 buf[sl-1] = '\0'; /* Lose the 'dot' */ 526 return (buf); 527 } 528 529 p = nis_dir_cmp(buf, d); 530 531 /* 's' is above 'd' in the tree */ 532 if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL) || (p == SAME_NAME)) 533 return (NULL); 534 535 /* Insert a NUL where the domain name starts in the string */ 536 buf[(sl - dl) - 1] = '\0'; 537 538 /* Don't return a zero length name */ 539 if (buf[0] == '\0') 540 return (NULL); 541 542 return (buf); 543 } 544 545 nis_name 546 nis_name_of( 547 char *s) /* string with the name in it. */ 548 { 549 char *buf = thr_main()? buf_main : 550 thr_get_storage(&buf_key, LN_BUFSIZE, free); 551 552 if (!buf) 553 return (NULL); 554 return (nis_name_of_r(s, buf, LN_BUFSIZE)); 555 } 556 557 558 559 /* 560 * nis_local_directory() 561 * 562 * Return a pointer to a string with the local directory name in it. 563 */ 564 nis_name 565 nis_local_directory(void) 566 { 567 struct local_names *ln = __get_local_names(); 568 569 /* LOCK NOTE: Warning, after initialization, "ln" is expected */ 570 /* to stay constant, So no need to lock here. If this assumption */ 571 /* is changed, this code must be protected. */ 572 if (ln == NULL) 573 return (NULL); 574 return (ln->domain); 575 } 576 577 /* 578 * __nis_rpc_domain() 579 * 580 * Return a pointer to a string with the rpc domain name in it. 581 */ 582 nis_name 583 __nis_rpc_domain() 584 { 585 struct local_names *ln = __get_local_names(); 586 587 /* LOCK NOTE: Warning, after initialization, "ln" is expected */ 588 /* to stay constant, So no need to lock here. If this assumption */ 589 /* is changed, this code must be protected. */ 590 if (ln == NULL) 591 return (NULL); 592 return (ln->rpcdomain); 593 } 594 595 /* 596 * nis_getprincipal: 597 * Return the prinicipal name of the given uid in string supplied. 598 * Returns status obtained from nis+. 599 * 600 * Look up the LOCAL mapping in the local cred table. Note that if the 601 * server calls this, then the version of nis_list that will 602 * will be bound here is the 'safe' one in the server code. 603 * 604 * The USE_DGRAM + NO_AUTHINFO is required to prevent a 605 * recursion through the getnetname() interface which is 606 * called by authseccreate_pk and authdes_pk_create(). 607 * 608 * NOTE that if you really want to get the nis+ principal name, 609 * you should not use this call. You should do something similar 610 * but use an authenticated handle. 611 */ 612 613 614 int 615 __nis_principal(char *principal_name, uid_t uid, char *directory) 616 { 617 nis_result *res; 618 char buf[NIS_MAXNAMELEN]; 619 int status; 620 621 if ((strlen(directory)+MAXIPRINT+PKTABLE_LEN+32) > 622 (size_t)NIS_MAXNAMELEN) { 623 return (NIS_BADNAME); 624 } 625 626 (void) snprintf(buf, sizeof (buf), 627 "[auth_name=%d,auth_type=LOCAL],%s.%s", 628 (int)uid, PKTABLE, directory); 629 630 if (buf[strlen(buf)-1] != '.') 631 (void) strcat(buf, "."); 632 633 res = nis_list(buf, 634 USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, 635 NULL, NULL); 636 status = res->status; 637 if (status == NIS_SUCCESS || status == NIS_S_SUCCESS) { 638 if (res->objects.objects_len > 1) { 639 /* 640 * More than one principal with same uid? 641 * something wrong with cred table. Should be unique 642 * Warn user and continue. 643 */ 644 syslog(LOG_ERR, 645 "nis_principal: LOCAL entry for %d in directory %s not unique", 646 uid, directory); 647 } 648 (void) strcpy(principal_name, 649 ENTRY_VAL(res->objects.objects_val, 0)); 650 } 651 nis_freeresult(res); 652 653 return (status); 654 } 655 656 /* 657 * nis_local_principal() 658 * Generate the principal name for this user by looking it up its LOCAL 659 * entry in the cred table of the local direectory. 660 * Does not use an authenticated call (to prevent recursion because 661 * this is called by user2netname). 662 * 663 * NOTE: the principal strings returned by nis_local_principal are 664 * never changed and never freed, so there is no need to copy them. 665 * Also note that nis_local_principal can return NULL. 666 */ 667 nis_name 668 nis_local_principal(void) 669 { 670 struct local_names *ln = __get_local_names(); 671 uid_t uid; 672 int status; 673 char *dirname; 674 static mutex_t local_principal_lock = DEFAULTMUTEX; 675 struct principal_list *p; 676 677 if (ln == NULL) 678 return (NULL); 679 680 sig_mutex_lock(&local_principal_lock); 681 uid = geteuid(); 682 p = ln->principal_map; 683 while (p) { 684 if (p->uid == uid) { 685 ASSERT(*(p->principal) != 0); 686 sig_mutex_unlock(&local_principal_lock); 687 return (p->principal); 688 } 689 p = p->next; 690 } 691 if (uid == 0) { 692 sig_mutex_unlock(&local_principal_lock); 693 return (ln->host); 694 } 695 p = calloc(1, sizeof (*p)); 696 if (p == NULL) 697 return (NULL); 698 if (!ln->principal_map) { 699 ln->principal_map = p; 700 } 701 dirname = nis_local_directory(); 702 if ((dirname == NULL) || (dirname[0] == NULL)) { 703 (void) strcpy(p->principal, "nobody"); 704 p->uid = uid; 705 sig_mutex_unlock(&local_principal_lock); 706 return (p->principal); 707 } 708 switch (status = __nis_principal(p->principal, uid, dirname)) { 709 case NIS_SUCCESS: 710 case NIS_S_SUCCESS: 711 break; 712 case NIS_NOTFOUND: 713 case NIS_PARTIAL: 714 case NIS_NOSUCHNAME: 715 case NIS_NOSUCHTABLE: 716 (void) strcpy(p->principal, "nobody"); 717 break; 718 default: 719 /* 720 * XXX We should return 'nobody', but 721 * should we be remembering 'nobody' as our 722 * principal name here? Some errors might be 723 * transient. 724 */ 725 syslog(LOG_ERR, 726 "nis_local_principal: %s", 727 nis_sperrno(status)); 728 (void) strcpy(p->principal, "nobody"); 729 } 730 p->uid = uid; 731 sig_mutex_unlock(&local_principal_lock); 732 return (p->principal); 733 } 734 735 /* 736 * nis_local_host() 737 * Generate the principal name for this host, "hostname"+"domainname" 738 * unless the hostname already has "dots" in its name. 739 */ 740 nis_name 741 nis_local_host(void) 742 { 743 struct local_names *ln = __get_local_names(); 744 745 /* LOCK NOTE: Warning, after initialization, "ln" is expected */ 746 /* to stay constant, So no need to lock here. If this assumption */ 747 /* is changed, this code must be protected. */ 748 if (ln == NULL) 749 return (NULL); 750 751 return (ln->host); 752 } 753 754 /* 755 * nis_destroy_object() 756 * This function takes a pointer to a NIS object and deallocates it. This 757 * is the inverse of __clone_object below. It must be able to correctly 758 * deallocate partially allocated objects because __clone_object will call 759 * it if it runs out of memory and has to abort. Everything is freed, 760 * INCLUDING the pointer that is passed. 761 */ 762 void 763 nis_destroy_object(nis_object *obj) /* The object to clone */ 764 { 765 if (obj == 0) 766 return; 767 xdr_free(xdr_nis_object, (char *)obj); 768 free(obj); 769 } /* nis_destroy_object */ 770 771 static void 772 destroy_nis_sdata(void *p) 773 { 774 struct nis_sdata *ns = p; 775 776 if (ns->buf != 0) 777 free(ns->buf); 778 free(ns); 779 } 780 781 /* XXX Why are these static ? */ 782 /* static XDR in_xdrs, out_xdrs; */ 783 784 785 /* 786 * __clone_object_r() 787 * This function takes a pointer to a NIS object and clones it. This 788 * duplicate object is now available for use in the local context. 789 */ 790 nis_object * 791 nis_clone_object_r( 792 nis_object *obj, /* The object to clone */ 793 nis_object *dest, /* Use this pointer if non-null */ 794 struct nis_sdata *clone_buf_ptr) 795 { 796 nis_object *result; /* The clone itself */ 797 int status; /* a counter variable */ 798 XDR in_xdrs, out_xdrs; 799 800 if (!nis_get_static_storage(clone_buf_ptr, 1, 801 xdr_sizeof(xdr_nis_object, obj))) 802 return (NULL); 803 804 (void) memset(&in_xdrs, 0, sizeof (in_xdrs)); 805 (void) memset(&out_xdrs, 0, sizeof (out_xdrs)); 806 xdrmem_create(&in_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size, 807 XDR_ENCODE); 808 xdrmem_create(&out_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size, 809 XDR_DECODE); 810 811 /* Allocate a basic NIS object structure */ 812 if (dest) { 813 (void) memset(dest, 0, sizeof (nis_object)); 814 result = dest; 815 } else 816 result = calloc(1, sizeof (nis_object)); 817 818 if (result == NULL) 819 return (NULL); 820 821 /* Encode our object into the clone buffer */ 822 (void) xdr_setpos(&in_xdrs, 0); 823 status = xdr_nis_object(&in_xdrs, obj); 824 if (status == FALSE) 825 return (NULL); 826 827 /* Now decode the buffer into our result pointer ... */ 828 (void) xdr_setpos(&out_xdrs, 0); 829 status = xdr_nis_object(&out_xdrs, result); 830 if (status == FALSE) 831 return (NULL); 832 833 /* presto changeo, a new object */ 834 return (result); 835 } /* __clone_object_r */ 836 837 838 nis_object * 839 nis_clone_object( 840 nis_object *obj, /* The object to clone */ 841 nis_object *dest) /* Use this pointer if non-null */ 842 { 843 static pthread_key_t clone_buf_key = PTHREAD_ONCE_KEY_NP; 844 static struct nis_sdata clone_buf_main; 845 struct nis_sdata *clone_buf_ptr; 846 847 clone_buf_ptr = thr_main()? &clone_buf_main : 848 thr_get_storage(&clone_buf_key, sizeof (struct nis_sdata), 849 destroy_nis_sdata); 850 return (nis_clone_object_r(obj, dest, clone_buf_ptr)); 851 } /* __clone_object */ 852 853 854 /* 855 * __break_name() converts a NIS name into it's components, returns an 856 * array of char pointers pointing to the components and INVERTS there 857 * order so that they are root first, then down. The list is terminated 858 * with a null pointer. Returned memory can be freed by freeing the last 859 * pointer in the list and the pointer returned. 860 */ 861 char ** 862 __break_name( 863 nis_name name, 864 int *levels) 865 { 866 char **pieces; /* pointer to the pieces */ 867 char *s; /* Temporary */ 868 char *data; /* actual data and first piece pointer. */ 869 int components; /* Number of estimated components */ 870 size_t namelen; /* Length of the original name. */ 871 int i; 872 873 /* First check to see that name is not NULL */ 874 if (!name) 875 return (NULL); 876 if ((namelen = strlen(name)) == 0) 877 return (NULL); /* Null string */ 878 879 namelen = strlen(name); 880 881 data = strdup(name); 882 if (!data) 883 return (NULL); /* No memory! */ 884 885 /* Kill the optional trailing dot */ 886 if (*(data+namelen-1) == '.') { 887 *(data+namelen-1) = '\0'; 888 namelen--; 889 } 890 s = data; 891 components = 1; 892 while (*s != '\0') { 893 if (*s == '.') { 894 *s = '\0'; 895 components++; 896 s++; 897 } else if (*s == '"') { 898 if (*(s+1) == '"') { /* escaped quote */ 899 s += 2; 900 } else { 901 /* skip quoted string */ 902 s++; 903 while ((*s != '"') && (*s != '\0')) 904 s++; 905 if (*s == '"') { 906 s++; 907 } 908 } 909 } else { 910 s++; 911 } 912 } 913 pieces = calloc(components+1, sizeof (char *)); 914 if (!pieces) { 915 free(data); 916 return (NULL); 917 } 918 919 /* store in pieces in inverted order */ 920 for (i = (components-1), s = data; i > -1; i--) { 921 *(pieces+i) = s; 922 while (*s != '\0') 923 s++; 924 s++; 925 } 926 *(pieces+components) = NULL; 927 *levels = components; 928 929 return (pieces); 930 } 931 932 void 933 __free_break_name(char **components, int levels) 934 { 935 free(components[levels-1]); 936 free(components); 937 } 938 939 int 940 __name_distance( 941 char **targ, /* The target name */ 942 char **test) /* the test name */ 943 { 944 int distance = 0; 945 946 /* Don't count common components */ 947 while ((*targ && *test) && (strcasecmp(*targ, *test) == 0)) { 948 targ++; 949 test++; 950 } 951 952 /* count off the legs of each name */ 953 while (*test != NULL) { 954 test++; 955 distance++; 956 } 957 958 while (*targ != NULL) { 959 targ++; 960 distance++; 961 } 962 963 return (distance); 964 } 965 966 int 967 __dir_same(char **test, char **targ) 968 { 969 /* skip common components */ 970 while ((*targ && *test) && (strcasecmp(*targ, *test) == 0)) { 971 targ++; 972 test++; 973 } 974 975 return (*test == NULL && *targ == NULL); 976 } 977 978 void 979 __broken_name_print(char **name, int levels) 980 { 981 int i; 982 983 for (i = levels-1; i >= 0; --i) 984 (void) printf("%s.", name[i]); 985 } 986 987 988 /* 989 * For returning errors in a NIS result structure 990 */ 991 nis_result * 992 nis_make_error( 993 nis_error err, 994 uint32_t aticks, /* Profile information for client */ 995 uint32_t cticks, 996 uint32_t dticks, 997 uint32_t zticks) 998 { 999 nis_result *nres; 1000 1001 nres = malloc(sizeof (nis_result)); 1002 if (!nres) 1003 return ((nis_result *)&__nomem_nis_result); 1004 (void) memset(nres, 0, sizeof (nis_result)); 1005 nres->status = err; 1006 nres->aticks = aticks; 1007 nres->zticks = zticks; 1008 nres->dticks = dticks; 1009 nres->cticks = cticks; 1010 return (nres); 1011 } 1012 1013 #define ZVAL zattr_val.zattr_val_val 1014 #define ZLEN zattr_val.zattr_val_len 1015 1016 /* 1017 * __cvt2attr() 1018 * 1019 * This function converts a search criteria of the form : 1020 * [ <key>=<value>, <key>=<value>, ... ] 1021 * Into an array of nis_attr structures. 1022 */ 1023 1024 nis_attr * 1025 __cvt2attr( 1026 int *na, /* Number of attributes */ 1027 char **attrs) /* Strings associated with them */ 1028 1029 { 1030 int i; 1031 nis_attr *zattrs; 1032 char *s; 1033 1034 zattrs = calloc(*na, sizeof (nis_attr)); 1035 if (!zattrs) 1036 return (NULL); 1037 1038 for (i = 0; i < *na; i++) { 1039 zattrs[i].zattr_ndx = *(attrs+i); 1040 for (s = zattrs[i].zattr_ndx; *s != '\0'; s++) { 1041 if (*s == '=') { 1042 *s = '\0'; 1043 s++; 1044 zattrs[i].ZVAL = s; 1045 zattrs[i].ZLEN = (uint_t)strlen(s) + 1; 1046 break; 1047 } else if (*s == '"') { 1048 /* advance s to matching quote */ 1049 s++; 1050 while ((*s != '"') && (*s != '\0')) 1051 s++; 1052 if (*s == '\0') { 1053 /* unterminated quote */ 1054 free(zattrs); 1055 return (NULL); 1056 } 1057 } 1058 } 1059 /* 1060 * POLICY : Missing value for an index name is an 1061 * error. The other alternative is the missing 1062 * value means "is present" unfortunately there 1063 * is no standard "is present" indicator in the 1064 * existing databases. 1065 * ANSWER : Always return an error. 1066 */ 1067 if (!zattrs[i].ZVAL) { 1068 free(zattrs); 1069 return (NULL); 1070 } 1071 } 1072 return (zattrs); 1073 } 1074 1075 /* 1076 * nis_free_request() 1077 * 1078 * Free memory associated with a constructed list request. 1079 */ 1080 void 1081 nis_free_request(ib_request *req) 1082 { 1083 if (req->ibr_srch.ibr_srch_len) { 1084 /* free the string memory */ 1085 free(req->ibr_srch.ibr_srch_val[0].zattr_ndx); 1086 /* free the nis_attr array */ 1087 free(req->ibr_srch.ibr_srch_val); 1088 } 1089 1090 if (req->ibr_name) 1091 free(req->ibr_name); 1092 } 1093 1094 /* 1095 * nis_get_request() 1096 * 1097 * This function takes a NIS name, and converts it into an ib_request 1098 * structure. The request can then be used in a call to the nis service 1099 * functions. If the name wasn't parseable it returns an appropriate 1100 * error. This function ends up allocating an array of nis_attr structures 1101 * and a duplicate of the name string passed. To free this memory you 1102 * can call nis_free_request(), or you can simply free the first nis_attr 1103 * zattr_ndx pointer (the string memory) and the nis_attr pointer which 1104 * is the array. 1105 */ 1106 nis_error 1107 nis_get_request( 1108 nis_name name, /* search criteria + Table name */ 1109 nis_object *obj, /* Object for (rem/modify/add) */ 1110 netobj *cookie, /* Pointer to a cookie */ 1111 ib_request *req) /* Request structure to fill in */ 1112 { 1113 char *s, *t; /* Some string pointer temps */ 1114 char *p; /* temp var */ 1115 char **attr; /* Intermediate attributes */ 1116 int i; /* Counter variable */ 1117 char *data; /* pointer to malloc'd string */ 1118 int zn = 0; /* Count of attributes */ 1119 size_t datalen; /* length of malloc'd data */ 1120 char namebuf[NIS_MAXNAMELEN]; 1121 1122 uchar_t within_attr_val; 1123 /* 1124 * a boolean to indicate the current parse 1125 * location is within the attribute value 1126 * - so that we can stop deleting white 1127 * space within an attribute value 1128 */ 1129 1130 (void) memset(req, 0, sizeof (ib_request)); 1131 1132 /* 1133 * if we're passed an object but no name, use the name from 1134 * the object instead. 1135 */ 1136 if (obj && !name) { 1137 if ((strlen(obj->zo_name)+strlen(obj->zo_domain)+2) > 1138 sizeof (namebuf)) { 1139 return (NIS_BADNAME); 1140 } 1141 (void) snprintf(namebuf, sizeof (namebuf), 1142 "%s.%s", obj->zo_name, obj->zo_domain); 1143 name = namebuf; 1144 } 1145 if (!name || (name[0] == '\0')) 1146 return (NIS_BADNAME); 1147 1148 s = name; 1149 1150 /* Move to the start of the components */ 1151 while (isspace(*s)) 1152 s++; 1153 1154 if (*s == '[') { 1155 1156 s++; /* Point past the opening bracket */ 1157 1158 datalen = strlen(s); 1159 data = calloc(1, datalen+1); 1160 if (!data) 1161 return (NIS_NOMEMORY); 1162 1163 t = data; /* Point to the databuffer */ 1164 while ((*s != '\0') && (*s != ']')) { 1165 while (isspace(*s)) { 1166 s++; 1167 } 1168 /* Check to see if we finished off the string */ 1169 if ((*s == '\0') || (*s == ']')) 1170 break; 1171 1172 /* If *s == comma its a null criteria */ 1173 if (*s == COMMA) { 1174 s++; 1175 continue; 1176 } 1177 /* Not a space and not a comma, process an attr */ 1178 zn++; 1179 within_attr_val = 0; /* not within attr_val right now */ 1180 while ((*s != COMMA) && (*s != ']') && (*s != '\0')) { 1181 if (*s == '"') { 1182 if (*(s+1) == '"') { /* escaped quote */ 1183 *t++ = *s; /* copy one quote */ 1184 s += 2; 1185 } else { 1186 /* skip quoted string */ 1187 s++; 1188 while ((*s != '"') && 1189 (*s != '\0')) 1190 *t++ = *s++; 1191 if (*s == '"') { 1192 s++; 1193 } 1194 } 1195 } else if (*s == '=') { 1196 *t++ = *s++; 1197 within_attr_val = 1; 1198 } else if (isspace(*s) && !within_attr_val) { 1199 s++; 1200 } else 1201 *t++ = *s++; 1202 } 1203 *t++ = '\0'; /* terminate the attribute */ 1204 if (*s == COMMA) 1205 s++; 1206 } 1207 if (*s == '\0') { 1208 free(data); 1209 return (NIS_BADATTRIBUTE); 1210 } 1211 1212 /* It wasn't a '\0' so it must be the closing bracket. */ 1213 s++; 1214 /* Skip any intervening white space and "comma" */ 1215 while (isspace(*s) || (*s == COMMA)) { 1216 s++; 1217 } 1218 /* Copy the name into our malloc'd buffer */ 1219 (void) strcpy(t, s); 1220 1221 /* 1222 * If we found any attributes we process them, the 1223 * data string at this point is completely nulled 1224 * out except for attribute data. We recover this 1225 * data by scanning the string (we know how long it 1226 * is) and saving to each chunk of non-null data. 1227 */ 1228 if (zn) { 1229 /* Save this as the table name */ 1230 req->ibr_name = strdup(t); 1231 attr = calloc(zn+1, sizeof (char *)); 1232 if (!attr) { 1233 free(data); 1234 free(req->ibr_name); 1235 req->ibr_name = 0; 1236 return (NIS_NOMEMORY); 1237 } 1238 1239 /* store in pieces in attr array */ 1240 for (i = 0, s = data; i < zn; i++) { 1241 *(attr+i) = s; 1242 /* Advance s past this component */ 1243 while (*s != '\0') 1244 s++; 1245 s++; 1246 } 1247 *(attr+zn) = NULL; 1248 } else { 1249 free(data); 1250 req->ibr_name = strdup(s); 1251 } 1252 } else { 1253 /* Null search criteria */ 1254 req->ibr_name = strdup(s); 1255 data = NULL; 1256 } 1257 1258 if (zn) { 1259 req->ibr_srch.ibr_srch_len = zn; 1260 req->ibr_srch.ibr_srch_val = __cvt2attr(&zn, attr); 1261 free(attr); /* don't need this any more */ 1262 if (!(req->ibr_srch.ibr_srch_val)) { 1263 req->ibr_srch.ibr_srch_len = 0; 1264 free(req->ibr_name); 1265 req->ibr_name = 0; 1266 free(data); 1267 return (NIS_BADATTRIBUTE); 1268 } 1269 } 1270 1271 /* check for correct quotes in ibr_name (but leave them in) */ 1272 for (p = req->ibr_name; *p; p++) { 1273 if (*p == '"') { 1274 /* advance p to the matching quote */ 1275 p++; 1276 while (*p != '"' && *p != '\0') { 1277 p++; 1278 } 1279 if (*p == '\0') { 1280 req->ibr_srch.ibr_srch_len = 0; 1281 free(req->ibr_name); 1282 req->ibr_name = 0; 1283 free(data); 1284 return (NIS_BADNAME); 1285 } 1286 } 1287 } 1288 1289 if (obj) { 1290 req->ibr_obj.ibr_obj_len = 1; 1291 req->ibr_obj.ibr_obj_val = obj; 1292 } 1293 if (cookie) { 1294 req->ibr_cookie = *cookie; 1295 } 1296 return (NIS_SUCCESS); 1297 } 1298 1299 /* Various subroutines used by the server code */ 1300 nis_object * 1301 nis_read_obj(char *f) /* name of the object to read */ 1302 { 1303 FILE *rootfile; 1304 int status; /* Status of the XDR decoding */ 1305 XDR xdrs; /* An xdr stream handle */ 1306 nis_object *res; 1307 1308 res = calloc(1, sizeof (nis_object)); 1309 if (!res) 1310 return (NULL); 1311 1312 rootfile = fopen(f, "rF"); 1313 if (rootfile == NULL) { 1314 /* This is ok if we are the root of roots. */ 1315 free(res); 1316 return (NULL); 1317 } 1318 /* Now read in the object */ 1319 xdrstdio_create(&xdrs, rootfile, XDR_DECODE); 1320 status = xdr_nis_object(&xdrs, res); 1321 xdr_destroy(&xdrs); 1322 (void) fclose(rootfile); 1323 if (!status) { 1324 syslog(LOG_ERR, "Object file %s is corrupt!", f); 1325 xdr_free(xdr_nis_object, (char *)res); 1326 free(res); 1327 return (NULL); 1328 } 1329 return (res); 1330 } 1331 1332 int 1333 nis_write_obj( 1334 char *f, /* name of the object to read */ 1335 nis_object *o) /* The object to write */ 1336 { 1337 FILE *rootfile; 1338 int status; /* Status of the XDR decoding */ 1339 XDR xdrs; /* An xdr stream handle */ 1340 1341 rootfile = fopen(f, "wF"); 1342 if (rootfile == NULL) { 1343 return (0); 1344 } 1345 /* Now encode the object */ 1346 xdrstdio_create(&xdrs, rootfile, XDR_ENCODE); 1347 status = xdr_nis_object(&xdrs, o); 1348 xdr_destroy(&xdrs); 1349 (void) fclose(rootfile); 1350 return (status); 1351 } 1352 1353 /* 1354 * nis_make_rpchandle() 1355 * 1356 * This is a generic version of clnt_creat() for NIS. It localizes 1357 * _all_ of the changes needed to port to TLI RPC into this one 1358 * section of code. 1359 */ 1360 1361 /* 1362 * Transport INDEPENDENT RPC code. This code assumes you 1363 * are using the new RPC/tli code and will build 1364 * a ping handle on top of a datagram transport. 1365 */ 1366 1367 /* 1368 * __map_addr() 1369 * 1370 * This is our internal function that replaces rpcb_getaddr(). We 1371 * build our own to prevent calling netdir_getbyname() which could 1372 * recurse to the nameservice. 1373 */ 1374 static char * 1375 __map_addr( 1376 struct netconfig *nc, /* Our transport */ 1377 char *uaddr, /* RPCBIND address */ 1378 rpcprog_t prog, /* Name service Prog */ 1379 rpcvers_t ver) 1380 { 1381 CLIENT *client; 1382 RPCB parms; /* Parameters for RPC binder */ 1383 enum clnt_stat clnt_st; /* Result from the rpc call */ 1384 char *ua = NULL; /* Universal address of service */ 1385 char *res = NULL; /* Our result to the parent */ 1386 struct timeval tv; /* Timeout for our rpcb call */ 1387 int ilen, olen; /* buffer length for clnt_tli_create */ 1388 1389 /* 1390 * If using "udp", use __nisipbufsize if inbuf and outbuf are set to 0. 1391 */ 1392 if (strcmp(NC_UDP, nc->nc_proto) == 0) { 1393 /* for udp only */ 1394 ilen = olen = __nisipbufsize; 1395 } else { 1396 ilen = olen = 0; 1397 } 1398 client = __nis_clnt_create(RPC_ANYFD, nc, uaddr, 0, 0, 1399 RPCBPROG, RPCBVERS, ilen, olen); 1400 if (!client) 1401 return (NULL); 1402 1403 (void) clnt_control(client, CLSET_FD_CLOSE, NULL); 1404 1405 /* 1406 * Now make the call to get the NIS service address. 1407 * We set the retry timeout to 3 seconds so that we 1408 * will retry a few times. Retries should be rare 1409 * because we are usually only called when we know 1410 * a server is available. 1411 */ 1412 tv.tv_sec = 3; 1413 tv.tv_usec = 0; 1414 (void) clnt_control(client, CLSET_RETRY_TIMEOUT, (char *)&tv); 1415 1416 tv.tv_sec = 10; 1417 tv.tv_usec = 0; 1418 parms.r_prog = prog; 1419 parms.r_vers = ver; 1420 parms.r_netid = nc->nc_netid; /* not needed */ 1421 parms.r_addr = ""; /* not needed; just for xdring */ 1422 parms.r_owner = ""; /* not needed; just for xdring */ 1423 clnt_st = clnt_call(client, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms, 1424 xdr_wrapstring, (char *)&ua, tv); 1425 1426 if (clnt_st == RPC_SUCCESS) { 1427 clnt_destroy(client); 1428 if (*ua == '\0') { 1429 free(ua); 1430 return (NULL); 1431 } 1432 res = strdup(ua); 1433 xdr_free(xdr_wrapstring, (char *)&ua); 1434 return (res); 1435 } else if (((clnt_st == RPC_PROGVERSMISMATCH) || 1436 (clnt_st == RPC_PROGUNAVAIL)) && 1437 (strcmp(nc->nc_protofmly, NC_INET) == 0)) { 1438 /* 1439 * version 3 not available. Try version 2 1440 * The assumption here is that the netbuf 1441 * is arranged in the sockaddr_in 1442 * style for IP cases. 1443 * 1444 * Note: If the remote host doesn't support version 3, 1445 * we assume it doesn't know IPv6 either. 1446 */ 1447 ushort_t port; 1448 struct sockaddr_in *sa; 1449 struct netbuf remote; 1450 int protocol; 1451 char buf[32]; 1452 1453 (void) clnt_control(client, CLGET_SVC_ADDR, (char *)&remote); 1454 /* LINTED pointer cast */ 1455 sa = (struct sockaddr_in *)(remote.buf); 1456 protocol = strcmp(nc->nc_proto, NC_TCP) ? 1457 IPPROTO_UDP : IPPROTO_TCP; 1458 port = (ushort_t)pmap_getport(sa, prog, ver, protocol); 1459 1460 if (port != 0) { 1461 port = htons(port); 1462 (void) sprintf(buf, "%d.%d.%d.%d.%d.%d", 1463 (sa->sin_addr.s_addr >> 24) & 0xff, 1464 (sa->sin_addr.s_addr >> 16) & 0xff, 1465 (sa->sin_addr.s_addr >> 8) & 0xff, 1466 (sa->sin_addr.s_addr) & 0xff, 1467 (port >> 8) & 0xff, 1468 port & 0xff); 1469 res = strdup(buf); 1470 } else 1471 res = NULL; 1472 clnt_destroy(client); 1473 return (res); 1474 } 1475 if (clnt_st == RPC_TIMEDOUT) 1476 syslog(LOG_ERR, "NIS+ server not responding"); 1477 else 1478 syslog(LOG_ERR, "NIS+ server could not be contacted: %s", 1479 clnt_sperrno(clnt_st)); 1480 clnt_destroy(client); 1481 return (NULL); 1482 } 1483 1484 char * 1485 __nis_get_server_address(struct netconfig *nc, endpoint *ep) 1486 { 1487 return (__map_addr(nc, ep->uaddr, NIS_PROG, NIS_VERSION)); 1488 } 1489 1490 #define MAX_EP (20) 1491 1492 int 1493 __nis_get_callback_addresses(endpoint *ep, endpoint **ret_eps) 1494 { 1495 int i; 1496 int n; 1497 int st; 1498 int nep = 0; 1499 endpoint *eps; 1500 struct nd_hostserv hs; 1501 struct nd_addrlist *addrs; 1502 struct nd_mergearg ma; 1503 void *lh; 1504 void *nch; 1505 struct netconfig *nc; 1506 1507 eps = malloc(MAX_EP * sizeof (endpoint)); 1508 if (eps == 0) 1509 return (0); 1510 1511 hs.h_host = HOST_SELF; 1512 hs.h_serv = "rpcbind"; /* as good as any */ 1513 1514 lh = __inet_get_local_interfaces(); 1515 1516 nch = setnetconfig(); 1517 while ((nc = getnetconfig(nch)) != NULL) { 1518 if (strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0) 1519 continue; 1520 if (nc->nc_semantics != NC_TPI_COTS && 1521 nc->nc_semantics != NC_TPI_COTS_ORD) 1522 continue; 1523 st = netdir_getbyname(nc, &hs, &addrs); 1524 if (st != 0) 1525 continue; 1526 1527 /* 1528 * The netdir_merge code does not work very well 1529 * for inet if the client and server are not 1530 * on the same network. Instead, we try each local 1531 * address. 1532 * 1533 * For other protocol families and for servers on a 1534 * local network, we use the regular merge code. 1535 */ 1536 1537 if (strcmp(nc->nc_protofmly, NC_INET) == 0 && 1538 !__inet_uaddr_is_local(lh, nc, ep->uaddr)) { 1539 n = __inet_address_count(lh); 1540 for (i = 0; i < n; i++) { 1541 if (nep >= MAX_EP) { 1542 syslog(LOG_INFO, 1543 "__nis_get_callback_addresses: too many endpoints"); 1544 goto full; 1545 } 1546 eps[nep].uaddr = __inet_get_uaddr(lh, nc, i); 1547 if (eps[nep].uaddr == 0) 1548 continue; 1549 if (strcmp(eps[nep].uaddr, LOOP_UADDR) == 0) { 1550 free(eps[nep].uaddr); 1551 continue; 1552 } 1553 __nis_netconfig2ep(nc, &(eps[nep])); 1554 nep++; 1555 } 1556 } else { 1557 ma.s_uaddr = ep->uaddr; 1558 ma.c_uaddr = taddr2uaddr(nc, addrs->n_addrs); 1559 ma.m_uaddr = 0; 1560 (void) netdir_options(nc, ND_MERGEADDR, 0, (void *)&ma); 1561 free(ma.s_uaddr); 1562 free(ma.c_uaddr); 1563 1564 if (nep >= MAX_EP) { 1565 syslog(LOG_INFO, 1566 "__nis_get_callback_addresses: too many endpoints"); 1567 goto full; 1568 } 1569 eps[nep].uaddr = ma.m_uaddr; 1570 __nis_netconfig2ep(nc, &(eps[nep])); 1571 nep++; 1572 } 1573 netdir_free((void *)addrs, ND_ADDRLIST); 1574 } 1575 1576 full: 1577 (void) endnetconfig(nch); 1578 __inet_free_local_interfaces(lh); 1579 1580 *ret_eps = eps; 1581 return (nep); 1582 } 1583 1584 /* 1585 * Try to create a RPC GSS security context (flavor RPCSEC_GSS). 1586 * Returns auth handle on success, else NULL. Set flag 'try_auth_des' 1587 * to TRUE if the AUTH_DES compat line is found in the security conf file 1588 * or no valid mech entries are found in the conf file. 1589 */ 1590 static AUTH * 1591 create_rpcgss_secctx( 1592 CLIENT *clnt, /* out */ 1593 nis_server *srv, 1594 char *gss_svc, 1595 bool_t *try_auth_des) /* out */ 1596 { 1597 mechanism_t **mechs; /* list of mechanisms */ 1598 1599 *try_auth_des = FALSE; 1600 if (mechs = __nis_get_mechanisms(TRUE)) { 1601 mechanism_t **mpp; 1602 char svc_name[NIS_MAXNAMELEN+1] = {0}; 1603 1604 /* Check RPC GSS service name buf size. */ 1605 if ((strlen(gss_svc ? gss_svc : NIS_SVCNAME_NISD) + 1 1606 + strlen(srv->name) + 1) > sizeof (svc_name)) { 1607 syslog(LOG_ERR, 1608 "nis_make_rpchandle_gss_svc: RPC GSS service name too long"); 1609 __nis_release_mechanisms(mechs); 1610 return (NULL); 1611 } 1612 1613 /* RPC GSS service names are of the form svc@host.dom */ 1614 (void) snprintf(svc_name, sizeof (svc_name), 1615 "%s@%s", gss_svc ? gss_svc : NIS_SVCNAME_NISD, 1616 srv->name); 1617 1618 /* 1619 * Loop thru all the available mech entries until an 1620 * RPC GSS security context is established or until 1621 * the AUTH_DES compat entry is found. 1622 */ 1623 for (mpp = mechs; *mpp; mpp++) { 1624 mechanism_t *mp = *mpp; 1625 1626 if (AUTH_DES_COMPAT_CHK(mp)) { 1627 __nis_release_mechanisms(mechs); 1628 *try_auth_des = TRUE; 1629 return (NULL); 1630 } 1631 1632 if (!VALID_MECH_ENTRY(mp)) { 1633 syslog(LOG_ERR, 1634 "%s: invalid mechanism entry name '%s'", 1635 NIS_SEC_CF_PATHNAME, 1636 mp->mechname ? mp->mechname : "NULL"); 1637 continue; 1638 } 1639 1640 /* 1641 * If the mechanism is of the public key crypto 1642 * technology variety, let's make sure the server's 1643 * public key exists and the clients secret key is set 1644 * before going thru the expense of a RPC GSS security 1645 * context creation attempt. 1646 */ 1647 if (MECH_PK_TECH(mp) && 1648 ((srv->key_type == NIS_PK_DHEXT && 1649 !__nis_dhext_extract_pkey(&(srv->pkey), 1650 mp->keylen, mp->algtype)) || 1651 !key_secretkey_is_set_g(mp->keylen, 1652 mp->algtype))) { 1653 #ifdef DHEXT_DEBUG 1654 (void) fprintf(stderr, 1655 "nis_make_rpchandle_gss_svc: srv keytype = %d: No keys, skip mech '%s' ...\n", 1656 srv->key_type, 1657 mp->alias); 1658 #endif 1659 continue; 1660 } 1661 1662 clnt->cl_auth = rpc_gss_seccreate(clnt, svc_name, 1663 mp->mechname, mp->secserv, 1664 mp->qop, NULL, NULL); 1665 if (clnt->cl_auth) { 1666 __nis_release_mechanisms(mechs); 1667 return (clnt->cl_auth); /* we're in bizness */ 1668 #ifdef DHEXT_DEBUG 1669 } else { 1670 rpc_gss_error_t err; 1671 1672 rpc_gss_get_error(&err); 1673 (void) fprintf(stderr, 1674 "nis_make_rpchandle_gss_svc: RPCGSS_SecCreat fail: gerr = %d serr = %d\n", 1675 err.rpc_gss_error, err.system_error); 1676 #endif /* DHEXT_DEBUG */ 1677 } 1678 } 1679 __nis_release_mechanisms(mechs); 1680 } else { 1681 /* no valid mechs, fallback to AUTH_DES */ 1682 *try_auth_des = TRUE; 1683 } 1684 1685 return (NULL); 1686 } 1687 1688 1689 CLIENT * 1690 nis_make_rpchandle( 1691 nis_server *srv, /* NIS Server description */ 1692 int cback, /* Boolean indicating callback address */ 1693 rpcprog_t prog, /* Program number */ 1694 rpcvers_t ver, /* Version */ 1695 uint_t flags, /* Flags, {VC, DG, AUTH} */ 1696 int inbuf, /* Preferred buffer sizes */ 1697 int outbuf) /* for input and output */ 1698 { 1699 return (nis_make_rpchandle_uaddr(srv, cback, prog, ver, flags, 1700 inbuf, outbuf, 0)); 1701 } 1702 1703 CLIENT * 1704 nis_make_rpchandle_uaddr( 1705 nis_server *srv, /* NIS Server description */ 1706 int cback, /* Boolean indicating callback address */ 1707 rpcprog_t prog, /* Program number */ 1708 rpcvers_t ver, /* Version */ 1709 uint_t flags, /* Flags, {VC, DG, AUTH} */ 1710 int inbuf, /* Preferred buffer sizes */ 1711 int outbuf, /* for input and output */ 1712 char *uaddr) /* optional address of server */ 1713 { 1714 return (nis_make_rpchandle_gss_svc(srv, cback, prog, ver, flags, 1715 inbuf, outbuf, uaddr, NULL)); 1716 } 1717 1718 extern int __can_use_af(sa_family_t af); 1719 1720 CLIENT * 1721 __nis_clnt_create(int fd, struct netconfig *nc, char *uaddr, 1722 struct netbuf *addr, int domapaddr, 1723 int prog, int ver, int inbuf, int outbuf) { 1724 1725 char *svc_addr; 1726 CLIENT *clnt; 1727 int freeaddr = 0; 1728 1729 /* Sanity check */ 1730 if (nc == 0 || (addr == 0 && uaddr == 0)) { 1731 return (0); 1732 } 1733 1734 /* 1735 * Check if we have a useable interface for this address family. 1736 * This check properly belongs in RPC (or even further down), 1737 * but until they provide it, we roll our own. 1738 */ 1739 if (__can_use_af((strcmp(nc->nc_protofmly, NC_INET6) == 0) ? 1740 AF_INET6 : AF_INET) == 0) { 1741 return (0); 1742 } 1743 1744 if (domapaddr) { 1745 svc_addr = __map_addr(nc, uaddr, prog, ver); 1746 if (svc_addr == 0) 1747 return (0); 1748 addr = uaddr2taddr(nc, svc_addr); 1749 freeaddr = 1; 1750 free(svc_addr); 1751 } else if (addr == 0) { 1752 addr = uaddr2taddr(nc, uaddr); 1753 freeaddr = 1; 1754 } 1755 1756 if (addr == 0) { 1757 return (0); 1758 } 1759 1760 clnt = clnt_tli_create(fd, nc, addr, prog, ver, outbuf, inbuf); 1761 1762 if (clnt) { 1763 if (clnt_control(clnt, CLGET_FD, (char *)&fd)) 1764 /* make it "close on exec" */ 1765 (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 1766 (void) clnt_control(clnt, CLSET_FD_CLOSE, NULL); 1767 } 1768 1769 if (freeaddr) 1770 netdir_free(addr, ND_ADDR); 1771 1772 return (clnt); 1773 } 1774 1775 1776 typedef struct { 1777 endpoint *ep; 1778 struct netconfig *nc; 1779 } alt_ep_t; 1780 1781 /* 1782 * Construct an rpc handle. 1783 * 1784 * If the gss_svc arg is NULL, then default to "nisd" (rpc.nisd). 1785 */ 1786 static CLIENT * 1787 nis_make_rpchandle_gss_svc_ext( 1788 nis_server *srv, /* NIS Server description */ 1789 int cback, /* Boolean indicating callback address */ 1790 rpcprog_t prog, /* Program number */ 1791 rpcvers_t ver, /* Version */ 1792 uint_t flags, /* Flags, {VC, DG, AUTH} */ 1793 int inbuf, /* Preferred buffer sizes */ 1794 int outbuf, /* for input and output */ 1795 char *uaddr, /* optional address of server */ 1796 char *gss_svc, /* RPC GSS service name */ 1797 int use_realid) /* 1: Use REAL id, 0: use Eff. ids */ 1798 { 1799 CLIENT *clnt = 0; /* Client handle */ 1800 void *nc_handle; /* Netconfig "state" */ 1801 struct netconfig *nc; /* Various handles */ 1802 endpoint *ep; /* useful endpoints */ 1803 int epl, i; /* counters */ 1804 int uid, gid; /* Effective uid/gid */ 1805 char netname[MAXNETNAMELEN+1]; /* our netname */ 1806 char *hexkey = NULL; /* hex public key for DHEXT */ 1807 netobj xpkey = { NULL, 0}; 1808 bool_t try_auth_des; 1809 alt_ep_t *altep = 0; 1810 1811 1812 nc_handle = (void *) setnetconfig(); 1813 if (!nc_handle) 1814 return (NULL); 1815 1816 ep = srv->ep.ep_val; 1817 epl = srv->ep.ep_len; 1818 1819 if (uaddr) { 1820 1821 char *fmly = (strchr(uaddr, ':') == 0) ? NC_INET : NC_INET6; 1822 1823 while ((nc = getnetconfig(nc_handle)) != NULL) { 1824 /* Is it a visible transport ? */ 1825 if ((nc->nc_flag & NC_VISIBLE) == 0) 1826 continue; 1827 /* Does the protocol family match the uaddr ? */ 1828 if (strcmp(nc->nc_protofmly, fmly) != 0) 1829 continue; 1830 for (i = 0; i < epl; i++) { 1831 if (__nis_netconfig_matches_ep(nc, &ep[i])) { 1832 break; 1833 } 1834 } 1835 /* Did we find a matching endpoint ? */ 1836 if (i < epl) 1837 break; 1838 } 1839 if (nc == 0) { 1840 syslog(LOG_ERR, 1841 "nis_make_rpchandle: can't find netconfig entry for %s, %s", 1842 uaddr, fmly); 1843 return (0); 1844 } 1845 1846 clnt = __nis_clnt_create(RPC_ANYFD, nc, uaddr, 0, 0, prog, ver, 1847 inbuf, outbuf); 1848 1849 } else { 1850 1851 altep = calloc(epl, sizeof (*altep)); 1852 1853 /* 1854 * The transport policies : 1855 * Selected transport must be visible. 1856 * Must have requested or better semantics. 1857 * Must be correct protocol. 1858 */ 1859 while ((nc = getnetconfig(nc_handle)) != 0) { 1860 1861 /* Is it a visible transport ? */ 1862 if ((nc->nc_flag & NC_VISIBLE) == 0) 1863 continue; 1864 1865 /* If we asked for a virtual circuit, is it ? */ 1866 if (((flags & ZMH_VC) != 0) && 1867 (nc->nc_semantics != NC_TPI_COTS) && 1868 (nc->nc_semantics != NC_TPI_COTS_ORD)) 1869 continue; 1870 1871 /* Check to see is we talk this protofmly, protocol */ 1872 for (i = 0; i < epl; i++) { 1873 if (__nis_netconfig_matches_ep(nc, &(ep[i]))) 1874 break; 1875 } 1876 1877 /* Was it one of our transports ? */ 1878 if (i == epl) 1879 continue; /* No */ 1880 1881 /* 1882 * If it is one of our supported transports, but isn't 1883 * a datagram and we want a datagram, keep looking but 1884 * remember this one as a possibility. 1885 */ 1886 if (((flags & ZMH_DG) != 0) && 1887 (nc->nc_semantics != NC_TPI_CLTS) && 1888 altep != 0) { 1889 altep[i].nc = nc; 1890 altep[i].ep = &ep[i]; /* This endpoint */ 1891 continue; 1892 } 1893 1894 /* We've got a candidate; see if it works */ 1895 clnt = __nis_clnt_create(RPC_ANYFD, nc, ep[i].uaddr, 0, 1896 (cback == 0), prog, ver, inbuf, 1897 outbuf); 1898 1899 if (clnt != 0) 1900 break; 1901 } 1902 1903 if (altep != 0 && (!(flags & ZMH_NOFALLBACK))) { 1904 /* If primary choices failed, try the alternates */ 1905 for (i = 0; clnt == 0 && i < epl; i++) { 1906 if (altep[i].ep == 0) 1907 continue; 1908 clnt = __nis_clnt_create(RPC_ANYFD, 1909 altep[i].nc, altep[i].ep->uaddr, 0, 1910 (cback == 0), prog, ver, inbuf, 1911 outbuf); 1912 } 1913 free(altep); 1914 } 1915 1916 } 1917 1918 /* Done with the netconfig handle regardless */ 1919 (void) endnetconfig(nc_handle); 1920 1921 /* If we still don't have a client handle, we're sunk */ 1922 if (clnt == 0) { 1923 return (0); 1924 } 1925 1926 /* 1927 * No auth requested or it's a callback (which is not authenticated), 1928 * so we're done. 1929 */ 1930 if (!(flags & ZMH_AUTH) || cback) 1931 return (clnt); 1932 1933 /* 1934 * Setup authentication. Try the RPCSEC_GSS flavor first, then 1935 * fallback to AUTH_DES (if requested) and, if need be, AUTH_SYS. 1936 */ 1937 if (create_rpcgss_secctx(clnt, srv, gss_svc, &try_auth_des)) 1938 return (clnt); 1939 1940 if (!try_auth_des) 1941 /* XXXX what's the meaning of going into a switch stmt??? */ 1942 goto auth_sys; 1943 1944 switch (srv->key_type) { 1945 case NIS_PK_DHEXT : 1946 /* 1947 * We're doing AUTH_DES, but the server might 1948 * have multiple keys so let's get the 192-0 one. 1949 */ 1950 if ((hexkey = __nis_dhext_extract_pkey(&(srv->pkey), 1951 192, 0)) == NULL) 1952 goto auth_sys; 1953 xpkey.n_len = strlen(hexkey) + 1; 1954 xpkey.n_bytes = hexkey; 1955 /*FALLTHROUGH*/ 1956 case NIS_PK_DH : 1957 (void) host2netname(netname, srv->name, NULL); 1958 clnt->cl_auth = (AUTH *)authdes_pk_seccreate(netname, 1959 xpkey.n_len ? &xpkey : &(srv->pkey), 1960 15, NULL, NULL, srv); 1961 if (xpkey.n_len) 1962 free(xpkey.n_bytes); 1963 if (clnt->cl_auth) 1964 break; 1965 /*FALLTHROUGH*/ 1966 case NIS_PK_NONE : 1967 auth_sys: 1968 uid = use_realid ? getuid() : geteuid(); 1969 gid = use_realid ? getgid() : getegid(); 1970 1971 clnt->cl_auth = authsys_create(nis_local_host(), uid, gid, 0, NULL); 1972 if (clnt->cl_auth) 1973 break; 1974 /*FALLTHROUGH*/ 1975 default : 1976 clnt->cl_auth = authnone_create(); 1977 if (clnt->cl_auth) 1978 break; 1979 syslog(LOG_CRIT, 1980 "nis_make_rpchandle_uaddr: cannot create cred."); 1981 abort(); 1982 break; 1983 } 1984 1985 if (clnt->cl_auth) 1986 return (clnt); 1987 1988 clnt_destroy(clnt); 1989 return (NULL); 1990 } 1991 1992 CLIENT * 1993 nis_make_rpchandle_gss_svc(nis_server *srv, int cback, rpcprog_t prog, 1994 rpcvers_t ver, uint_t flags, int inbuf, int outbuf, char *uaddr, 1995 char *gss_svc) 1996 { 1997 return (nis_make_rpchandle_gss_svc_ext(srv, cback, prog, ver, flags, 1998 inbuf, outbuf, uaddr, gss_svc, 0)); 1999 } 2000 2001 CLIENT * 2002 nis_make_rpchandle_gss_svc_ruid(nis_server *srv, int cback, rpcprog_t prog, 2003 rpcvers_t ver, uint_t flags, int inbuf, int outbuf, char *uaddr, 2004 char *gss_svc) 2005 { 2006 return (nis_make_rpchandle_gss_svc_ext(srv, cback, prog, ver, flags, 2007 inbuf, outbuf, uaddr, gss_svc, 1)); 2008 } 2009 2010 static mutex_t __nis_ss_used_lock = DEFAULTMUTEX; /* lock level 3 */ 2011 int __nis_ss_used = 0; 2012 2013 /* 2014 * nis_get_static_storage() 2015 * 2016 * This function is used by various functions in their effort to minimize the 2017 * hassles of memory management in an RPC daemon. Because the service doesn't 2018 * implement any hard limits, this function allows people to get automatically 2019 * growing buffers that meet their storage requirements. It returns the 2020 * pointer in the nis_sdata structure. 2021 * 2022 */ 2023 void * 2024 nis_get_static_storage( 2025 struct nis_sdata *bs, /* User buffer structure */ 2026 uint_t el, /* Sizeof elements */ 2027 uint_t nel) /* Number of elements */ 2028 { 2029 uint_t sz; 2030 2031 sz = nel * el; 2032 if (!bs) 2033 return (NULL); 2034 2035 if (!bs->buf) { 2036 bs->buf = malloc(sz); 2037 if (!bs->buf) 2038 return (NULL); 2039 bs->size = sz; 2040 sig_mutex_lock(&__nis_ss_used_lock); 2041 __nis_ss_used += sz; 2042 sig_mutex_unlock(&__nis_ss_used_lock); 2043 } else if (bs->size < sz) { 2044 int size_delta; 2045 2046 free(bs->buf); 2047 size_delta = - (bs->size); 2048 bs->buf = malloc(sz); 2049 2050 /* check the result of malloc() first */ 2051 /* then update the statistic. */ 2052 if (!bs->buf) 2053 return (NULL); 2054 bs->size = sz; 2055 size_delta += sz; 2056 sig_mutex_lock(&__nis_ss_used_lock); 2057 __nis_ss_used += size_delta; 2058 sig_mutex_unlock(&__nis_ss_used_lock); 2059 } 2060 2061 (void) memset(bs->buf, 0, sz); /* SYSV version of bzero() */ 2062 return (bs->buf); 2063 } 2064 2065 char * 2066 nis_old_data_r( 2067 char *s, 2068 struct nis_sdata *bs_ptr) 2069 { 2070 char *buf; 2071 char temp[1024]; 2072 size_t len = 0; 2073 2074 buf = (char *)nis_get_static_storage(bs_ptr, 1, 1024); 2075 2076 if (!buf) 2077 return (NULL); 2078 2079 /* 2080 * this saving of 's' is because the routines that call nis_data() 2081 * are not very careful about what they pass in. Sometimes what they 2082 * pass in are 'static' returned from some of the routines called 2083 * below nis_leaf_of(), nis_local_host() and so on. 2084 */ 2085 if (s) { 2086 len = strlen(s) + 1; 2087 if (len >= sizeof (temp)) 2088 return (NULL); 2089 (void) snprintf(temp, sizeof (temp), "/%s", s); 2090 } 2091 if (len + strlen(__nis_data_directory) + 2092 strlen(nis_leaf_of(nis_local_host())) >= bs_ptr->size) 2093 return (NULL); 2094 (void) strcpy(buf, __nis_data_directory); 2095 (void) strcat(buf, nis_leaf_of(nis_local_host())); 2096 if (s) 2097 (void) strcat(buf, temp); 2098 2099 for (s = buf; *s; s++) { 2100 if (isupper(*s)) 2101 *s = tolower(*s); 2102 } 2103 2104 return (buf); 2105 } 2106 2107 char * 2108 nis_old_data(char *s) 2109 { 2110 static pthread_key_t bs_key = PTHREAD_ONCE_KEY_NP; 2111 static struct nis_sdata bs_main; 2112 struct nis_sdata *bs_ptr; 2113 2114 bs_ptr = thr_main()? &bs_main : 2115 thr_get_storage(&bs_key, sizeof (struct nis_sdata), 2116 destroy_nis_sdata); 2117 return (nis_old_data_r(s, bs_ptr)); 2118 } 2119 2120 2121 char * 2122 nis_data_r(char *s, struct nis_sdata *bs_ptr) 2123 { 2124 char *buf; 2125 char temp[1024]; 2126 size_t len = 0; 2127 2128 buf = (char *)nis_get_static_storage(bs_ptr, 1, 1024); 2129 2130 if (!buf) 2131 return (NULL); 2132 2133 /* 2134 * this saving of 's' is because the routines that call nis_data() 2135 * are not very careful about what they pass in. Sometimes what they 2136 * pass in are 'static' returned from some of the routines called 2137 * below nis_leaf_of(), nis_local_host() and so on. 2138 */ 2139 if (s) { 2140 len = strlen(s) + 1; 2141 if (len >= sizeof (temp)) 2142 return (NULL); 2143 (void) snprintf(temp, sizeof (temp), "/%s", s); 2144 } 2145 if (len + strlen(__nis_data_directory) + 2146 strlen(NIS_DIR) >= bs_ptr->size) 2147 return (NULL); 2148 (void) strcpy(buf, __nis_data_directory); 2149 (void) strcat(buf, NIS_DIR); 2150 if (s) 2151 (void) strcat(buf, temp); 2152 2153 for (s = buf; *s; s++) { 2154 if (isupper(*s)) 2155 *s = tolower(*s); 2156 } 2157 2158 return (buf); 2159 } 2160 2161 char * 2162 nis_data(char *s) 2163 { 2164 static pthread_key_t bs_key = PTHREAD_ONCE_KEY_NP; 2165 static struct nis_sdata bs_main; 2166 struct nis_sdata *bs_ptr; 2167 2168 bs_ptr = thr_main()? &bs_main : 2169 thr_get_storage(&bs_key, sizeof (struct nis_sdata), 2170 destroy_nis_sdata); 2171 return (nis_data_r(s, bs_ptr)); 2172 } 2173 2174 /* 2175 * Return the directory name of the root_domain of the caller's NIS+ 2176 * domain. 2177 * 2178 * This routine is a temporary implementation and should be 2179 * provided as part of the the NIS+ project. See RFE: 1103216 2180 * Required for root replication. 2181 * 2182 * XXX MT safing: local_root_lock protects the local_root structure. 2183 * 2184 * It tries to determine the root domain 2185 * name by "walking" the path up the NIS+ directory tree, starting 2186 * at nis_local_directory() until a NIS_NOSUCHNAME or NIS_NOTFOUND error 2187 * is obtained. Returns 0 on fatal errors obtained before this point, 2188 * or if it exhausts the domain name without ever obtaining one of 2189 * of these errors. 2190 */ 2191 2192 static nis_name local_root = 0; 2193 static mutex_t local_root_lock = DEFAULTMUTEX; 2194 2195 nis_name 2196 __nis_local_root(void) 2197 { 2198 char *dir; 2199 int found_root = 0; 2200 int try_count = 0; 2201 int fatal_error = 0; 2202 char *prev_testdir; 2203 char *testdir; 2204 2205 sig_mutex_lock(&local_root_lock); 2206 if (local_root) { 2207 sig_mutex_unlock(&local_root_lock); 2208 return (local_root); 2209 } 2210 local_root = calloc(1, LN_BUFSIZE); 2211 2212 if (!local_root) { 2213 sig_mutex_unlock(&local_root_lock); 2214 return (0); 2215 } 2216 /* walk up NIS+ tree till we find the root. */ 2217 dir = strdup(__nis_rpc_domain()); 2218 prev_testdir = dir; 2219 testdir = nis_domain_of(prev_testdir); 2220 2221 while (testdir && !found_root && !fatal_error) { 2222 /* try lookup */ 2223 nis_result* nis_ret = nis_lookup(testdir, 0); 2224 /* handle return status */ 2225 switch (nis_ret->status) { 2226 case NIS_SUCCESS: 2227 case NIS_S_SUCCESS: 2228 try_count = 0; 2229 prev_testdir = testdir; 2230 testdir = nis_domain_of(prev_testdir); 2231 break; 2232 case NIS_NOSUCHNAME: 2233 case NIS_NOTFOUND: 2234 case NIS_NOT_ME: 2235 case NIS_FOREIGNNS: 2236 found_root = 1; 2237 break; 2238 case NIS_TRYAGAIN: 2239 case NIS_CACHEEXPIRED: 2240 /* sleep 1 second and try same name again, up to 10 times */ 2241 /* REMIND: This is arbitrary! BAD! */ 2242 (void) sleep(1); 2243 fatal_error = (try_count++ > 9); 2244 break; 2245 case NIS_NAMEUNREACHABLE: 2246 case NIS_SYSTEMERROR: 2247 case NIS_RPCERROR: 2248 case NIS_NOMEMORY: 2249 default: 2250 fatal_error = 1; 2251 break; 2252 } 2253 if (nis_ret) nis_freeresult(nis_ret); 2254 } 2255 2256 if (!found_root) { 2257 free(dir); 2258 sig_mutex_unlock(&local_root_lock); 2259 return (0); 2260 } 2261 (void) strcpy(local_root, prev_testdir); 2262 free(dir); 2263 sig_mutex_unlock(&local_root_lock); 2264 return (local_root); 2265 } 2266 2267 extern void __pkey_cache_add(char *, char *, keylen_t, algtype_t); 2268 extern int bin2hex(int, unsigned char *, char *); 2269 2270 /* 2271 * __nis_cache_server_pkeys 2272 * 2273 * Add the public keys for the servers of the directory object to the 2274 * per-process public key cache. 2275 */ 2276 void 2277 __nis_cache_server_pkeys(directory_obj *dir) { 2278 2279 int i; 2280 nis_server *srv; 2281 char netname[MAXNETNAMELEN+1]; 2282 char pkey[MAX_NETOBJ_SZ+1]; 2283 extdhkey_t *key; 2284 uint_t s; 2285 2286 if (dir == NULL) 2287 return; 2288 2289 for (i = 0; i < dir->do_servers.do_servers_len; i++) { 2290 2291 srv = &(dir->do_servers.do_servers_val[i]); 2292 2293 switch (srv->key_type) { 2294 case NIS_PK_DH: 2295 if (srv->pkey.n_len < sizeof (pkey) && 2296 host2netname(netname, srv->name, NULL)) { 2297 (void) memcpy(pkey, srv->pkey.n_bytes, 2298 srv->pkey.n_len); 2299 pkey[srv->pkey.n_len] = '\0'; 2300 __pkey_cache_add(netname, pkey, 192, 0); 2301 } 2302 break; 2303 case NIS_PK_DHEXT: 2304 if (!host2netname(netname, srv->name, NULL)) 2305 break; 2306 for (s = 0; s < srv->pkey.n_len; ) { 2307 keylen_t k, kpadlen; 2308 algtype_t a; 2309 /* LINTED pointer cast */ 2310 key = (extdhkey_t *)&(srv->pkey.n_bytes[s]); 2311 k = ntohs(key->keylen); 2312 if (k == 0) 2313 break; 2314 kpadlen = ((((k+7)/8)+3)/4)*4; 2315 a = ntohs(key->algtype); 2316 if (kpadlen <= sizeof (pkey)) { 2317 (void) bin2hex((k+7)/8, key->key, pkey); 2318 __pkey_cache_add(netname, pkey, k, a); 2319 } 2320 s += 2*sizeof (ushort_t) + kpadlen; 2321 } 2322 break; 2323 default: 2324 break; 2325 } 2326 2327 } 2328 } 2329