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