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