1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * nis_subr.c 31 * 32 * This module contains the subroutines used by the server to manipulate 33 * objects and names. 34 */ 35 #include "mt.h" 36 #include <pwd.h> 37 #include <grp.h> 38 #include <syslog.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <ctype.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <sys/time.h> 45 #include <sys/fcntl.h> 46 #include <netinet/in.h> 47 #include <rpc/rpc.h> /* Must be ahead of rpcb_clnt.h */ 48 #include <rpc/svc.h> 49 #include <tiuser.h> 50 #include <netconfig.h> 51 #include <netdir.h> 52 #include <rpc/rpcb_clnt.h> 53 #include <rpc/pmap_clnt.h> 54 #include <rpcsvc/nis.h> 55 #include <rpcsvc/nis_dhext.h> 56 #include "nis_clnt.h" 57 #include <sys/systeminfo.h> 58 #include "nis_local.h" 59 #include <nsswitch.h> 60 61 #define MAXIPRINT (11) /* max length of printed integer */ 62 static char *PKTABLE = "cred.org_dir"; 63 #define PKTABLE_LEN 12 64 /* 65 * send and receive buffer size used for clnt_tli_create if not specified. 66 * This is only used for "UDP" connection. 67 * This limit can be changed from the application if this value is too 68 * small for the application. To use the maximum value for the transport, 69 * set this value to 0. 70 */ 71 int __nisipbufsize = 8192; 72 73 /* Error result returned by nis_make_error() when malloc fails */ 74 const nis_result __nomem_nis_result = {NIS_NOMEMORY, {0, 0}, {0, 0}, 75 0, 0, 0, 0}; 76 77 extern int __readColdStartFile(); 78 79 /* 80 * Static function prototypes. 81 */ 82 static struct local_names *__get_local_names(void); 83 static char *__map_addr(struct netconfig *, char *, rpcprog_t, rpcvers_t); 84 85 CLIENT * nis_make_rpchandle_uaddr(nis_server *, 86 int, rpcprog_t, rpcvers_t, uint_t, int, int, char *); 87 88 #define COMMA ',' /* Avoid cstyle bug */ 89 90 /* __nis_data_directory is READ ONLY, so no locking is needed */ 91 /* Note: We make it static, so external caller can not access it */ 92 /* i.e we make sure it stay read only */ 93 static char __nis_data_directory[1024] = {"/var/nis/"}; 94 95 /* These macros make the code easier to read */ 96 97 #ifdef NOTIME 98 #define __start_clock(n) 99 #define __stop_clock(n) n 100 #else 101 static struct timeval clocks[MAXCLOCKS]; 102 103 #define LOOP_UADDR "127.0.0.1.0.0" 104 105 /* 106 * __start_clock() 107 * 108 * This function will start the "stopwatch" on the function calls. 109 * It uses an array of time vals to keep track of the time. The 110 * sister call __stop_clock() will return the number of microseconds 111 * since the clock was started. This allows for keeping statistics 112 * on the NIS calls and tuning the service. If the clock in question 113 * is not "stopped" this function returns an error. 114 */ 115 int 116 __start_clock( 117 int clk) /* The clock we want to start */ 118 { 119 if ((clk >= MAXCLOCKS) || (clk < 0) || (clocks[clk].tv_sec)) 120 return (FALSE); 121 122 (void) gettimeofday(&clocks[clk], NULL); 123 return (TRUE); 124 } 125 126 uint32_t 127 __stop_clock(int clk) 128 { 129 struct timeval now; 130 uint32_t secs, micros; 131 132 if ((clk >= MAXCLOCKS) || (clk < 0) || (!clocks[clk].tv_sec)) 133 return (0); 134 (void) gettimeofday(&now, NULL); 135 secs = (int)(now.tv_sec - clocks[clk].tv_sec); 136 if (now.tv_usec < clocks[clk].tv_usec) { 137 micros = (int)((now.tv_usec + 1000000) - clocks[clk].tv_usec); 138 secs--; /* adjusted 'cuz we added a second above */ 139 } else { 140 micros = (int)(now.tv_usec - clocks[clk].tv_usec); 141 } 142 micros = micros + (secs * 1000000); /* All micros now */ 143 clocks[clk].tv_sec = 0; /* Stop the clock. */ 144 return (micros); 145 } 146 #endif /* no time */ 147 148 /* 149 * nis_dir_cmp() -- the results can be read as: 150 * "Name 'n1' is a $result than name 'n2'" 151 */ 152 name_pos 153 nis_dir_cmp( 154 nis_name n1, 155 nis_name n2) /* See if these are the same domain */ 156 { 157 size_t l1, l2; 158 name_pos result; 159 160 if ((n1 == NULL) || (n2 == NULL)) 161 return (BAD_NAME); 162 163 l1 = strlen(n1); 164 l2 = strlen(n2); 165 166 /* In this routine we're lenient and don't require a trailing '.' */ 167 /* so we need to ignore it if it does appear. */ 168 /* ==== That's what the previous version did so this one does */ 169 /* too, but why? Is this inconsistent with rest of system? */ 170 if (l1 != 0 && n1[l1 - 1] == '.') { 171 --l1; 172 } 173 if (l2 != 0 && n2[l2 - 1] == '.') { 174 --l2; 175 } 176 177 if (l1 > l2) { 178 result = LOWER_NAME; 179 } else if (l1 == l2) { 180 result = SAME_NAME; 181 } else /* (l1 < l2); swap l1/l2 and n1/n2 */ { 182 nis_name ntmp; 183 size_t ltmp; 184 ntmp = n1; n1 = n2; n2 = ntmp; 185 ltmp = l1; l1 = l2; l2 = ltmp; 186 187 result = HIGHER_NAME; 188 } 189 190 /* Now l1 >= l2 in all cases */ 191 if (l2 == 0) { 192 /* Special case for n2 == "." or "" */ 193 return (result); 194 } 195 if (l1 > l2) { 196 n1 += l1 - l2; 197 if (n1[-1] != '.') { 198 return (NOT_SEQUENTIAL); 199 } 200 } 201 if (strncasecmp(n1, n2, l2) == 0) { 202 return (result); 203 } 204 return (NOT_SEQUENTIAL); 205 } 206 207 #define LN_BUFSIZE (size_t)1024 208 209 struct principal_list { 210 uid_t uid; 211 char principal[LN_BUFSIZE]; 212 struct principal_list *next; 213 }; 214 215 216 struct local_names { 217 char domain[LN_BUFSIZE]; 218 char host[LN_BUFSIZE]; 219 char *rpcdomain; 220 struct principal_list *principal_map; 221 char group[LN_BUFSIZE]; 222 }; 223 224 static mutex_t ln_lock = DEFAULTMUTEX; /* lock level 2 */ 225 static struct local_names *ln = NULL; 226 static struct local_names *__get_local_names1(); 227 228 static struct local_names * 229 __get_local_names(void) 230 { 231 struct local_names *names; 232 233 sig_mutex_lock(&ln_lock); 234 names = __get_local_names1(); 235 sig_mutex_unlock(&ln_lock); 236 return (names); 237 } 238 239 static 240 char * 241 get_nis_domain() 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 = (struct local_names *)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 strcat(ln->host, ln->rpcdomain); 312 } else { 313 ln->rpcdomain = strdup(ln->domain); 314 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) sprintf(buf, "[auth_name=%d,auth_type=LOCAL],%s.%s", 630 uid, PKTABLE, directory); 631 632 if (buf[strlen(buf)-1] != '.') 633 (void) strcat(buf, "."); 634 635 res = nis_list(buf, 636 USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, 637 NULL, NULL); 638 status = res->status; 639 if (status == NIS_SUCCESS || status == NIS_S_SUCCESS) { 640 if (res->objects.objects_len > 1) { 641 /* 642 * More than one principal with same uid? 643 * something wrong with cred table. Should be unique 644 * Warn user and continue. 645 */ 646 syslog(LOG_ERR, 647 "nis_principal: LOCAL entry for %d in directory %s not unique", 648 uid, directory); 649 } 650 (void) strcpy(principal_name, 651 ENTRY_VAL(res->objects.objects_val, 0)); 652 } 653 nis_freeresult(res); 654 655 return (status); 656 } 657 658 /* 659 * nis_local_principal() 660 * Generate the principal name for this user by looking it up its LOCAL 661 * entry in the cred table of the local direectory. 662 * Does not use an authenticated call (to prevent recursion because 663 * this is called by user2netname). 664 * 665 * NOTE: the principal strings returned by nis_local_principal are 666 * never changed and never freed, so there is no need to copy them. 667 * Also note that nis_local_principal can return NULL. 668 */ 669 nis_name 670 nis_local_principal(void) 671 { 672 struct local_names *ln = __get_local_names(); 673 uid_t uid; 674 int status; 675 char *dirname; 676 static mutex_t local_principal_lock = DEFAULTMUTEX; 677 struct principal_list *p; 678 679 if (ln == NULL) 680 return (NULL); 681 682 sig_mutex_lock(&local_principal_lock); 683 uid = geteuid(); 684 p = ln->principal_map; 685 while (p) { 686 if (p->uid == uid) { 687 ASSERT(*(p->principal) != 0); 688 sig_mutex_unlock(&local_principal_lock); 689 return (p->principal); 690 } 691 p = p->next; 692 } 693 if (uid == 0) { 694 sig_mutex_unlock(&local_principal_lock); 695 return (ln->host); 696 } 697 p = (struct principal_list *)calloc(1, sizeof (*p)); 698 if (p == NULL) 699 return (NULL); 700 if (!ln->principal_map) { 701 ln->principal_map = p; 702 } 703 dirname = nis_local_directory(); 704 if ((dirname == NULL) || (dirname[0] == NULL)) { 705 (void) strcpy(p->principal, "nobody"); 706 p->uid = uid; 707 sig_mutex_unlock(&local_principal_lock); 708 return (p->principal); 709 } 710 switch (status = __nis_principal(p->principal, uid, dirname)) { 711 case NIS_SUCCESS: 712 case NIS_S_SUCCESS: 713 break; 714 case NIS_NOTFOUND: 715 case NIS_PARTIAL: 716 case NIS_NOSUCHNAME: 717 case NIS_NOSUCHTABLE: 718 (void) strcpy(p->principal, "nobody"); 719 break; 720 default: 721 /* 722 * XXX We should return 'nobody', but 723 * should we be remembering 'nobody' as our 724 * principal name here? Some errors might be 725 * transient. 726 */ 727 syslog(LOG_ERR, 728 "nis_local_principal: %s", 729 nis_sperrno(status)); 730 (void) strcpy(p->principal, "nobody"); 731 } 732 p->uid = uid; 733 sig_mutex_unlock(&local_principal_lock); 734 return (p->principal); 735 } 736 737 /* 738 * nis_local_host() 739 * Generate the principal name for this host, "hostname"+"domainname" 740 * unless the hostname already has "dots" in its name. 741 */ 742 nis_name 743 nis_local_host(void) 744 { 745 struct local_names *ln = __get_local_names(); 746 747 /* LOCK NOTE: Warning, after initialization, "ln" is expected */ 748 /* to stay constant, So no need to lock here. If this assumption */ 749 /* is changed, this code must be protected. */ 750 if (ln == NULL) 751 return (NULL); 752 753 return (ln->host); 754 } 755 756 /* 757 * nis_destroy_object() 758 * This function takes a pointer to a NIS object and deallocates it. This 759 * is the inverse of __clone_object below. It must be able to correctly 760 * deallocate partially allocated objects because __clone_object will call 761 * it if it runs out of memory and has to abort. Everything is freed, 762 * INCLUDING the pointer that is passed. 763 */ 764 void 765 nis_destroy_object(nis_object *obj) /* The object to clone */ 766 { 767 if (obj == 0) 768 return; 769 xdr_free(xdr_nis_object, (char *)obj); 770 free(obj); 771 } /* nis_destroy_object */ 772 773 static void 774 destroy_nis_sdata(void *p) 775 { 776 struct nis_sdata *ns = p; 777 778 if (ns->buf != 0) 779 free(ns->buf); 780 free(ns); 781 } 782 783 /* XXX Why are these static ? */ 784 /* static XDR in_xdrs, out_xdrs; */ 785 786 787 /* 788 * __clone_object_r() 789 * This function takes a pointer to a NIS object and clones it. This 790 * duplicate object is now available for use in the local context. 791 */ 792 nis_object * 793 nis_clone_object_r( 794 nis_object *obj, /* The object to clone */ 795 nis_object *dest, /* Use this pointer if non-null */ 796 struct nis_sdata *clone_buf_ptr) 797 { 798 nis_object *result; /* The clone itself */ 799 int status; /* a counter variable */ 800 XDR in_xdrs, out_xdrs; 801 802 if (! nis_get_static_storage(clone_buf_ptr, 1, 803 xdr_sizeof(xdr_nis_object, obj))) 804 return (NULL); 805 806 memset(&in_xdrs, 0, sizeof (in_xdrs)); 807 memset(&out_xdrs, 0, sizeof (out_xdrs)); 808 xdrmem_create(&in_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size, 809 XDR_ENCODE); 810 xdrmem_create(&out_xdrs, clone_buf_ptr->buf, clone_buf_ptr->size, 811 XDR_DECODE); 812 813 /* Allocate a basic NIS object structure */ 814 if (dest) { 815 (void) memset((char *)dest, 0, sizeof (nis_object)); 816 result = dest; 817 } else 818 result = (nis_object *)calloc(1, sizeof (nis_object)); 819 820 if (result == NULL) 821 return (NULL); 822 823 /* Encode our object into the clone buffer */ 824 (void) xdr_setpos(&in_xdrs, 0); 825 status = xdr_nis_object(&in_xdrs, obj); 826 if (status == FALSE) 827 return (NULL); 828 829 /* Now decode the buffer into our result pointer ... */ 830 (void) xdr_setpos(&out_xdrs, 0); 831 status = xdr_nis_object(&out_xdrs, result); 832 if (status == FALSE) 833 return (NULL); 834 835 /* presto changeo, a new object */ 836 return (result); 837 } /* __clone_object_r */ 838 839 840 nis_object * 841 nis_clone_object( 842 nis_object *obj, /* The object to clone */ 843 nis_object *dest) /* Use this pointer if non-null */ 844 { 845 static pthread_key_t clone_buf_key; 846 static struct nis_sdata clone_buf_main; 847 struct nis_sdata *clone_buf_ptr; 848 849 clone_buf_ptr = thr_main()? &clone_buf_main : 850 thr_get_storage(&clone_buf_key, sizeof (struct nis_sdata), 851 destroy_nis_sdata); 852 return (nis_clone_object_r(obj, dest, clone_buf_ptr)); 853 } /* __clone_object */ 854 855 856 /* 857 * __break_name() converts a NIS name into it's components, returns an 858 * array of char pointers pointing to the components and INVERTS there 859 * order so that they are root first, then down. The list is terminated 860 * with a null pointer. Returned memory can be freed by freeing the last 861 * pointer in the list and the pointer returned. 862 */ 863 char ** 864 __break_name( 865 nis_name name, 866 int *levels) 867 { 868 char **pieces; /* pointer to the pieces */ 869 char *s; /* Temporary */ 870 char *data; /* actual data and first piece pointer. */ 871 int components; /* Number of estimated components */ 872 size_t namelen; /* Length of the original name. */ 873 int i; 874 875 /* First check to see that name is not NULL */ 876 if (!name) 877 return (NULL); 878 if ((namelen = strlen(name)) == 0) 879 return (NULL); /* Null string */ 880 881 namelen = strlen(name); 882 883 data = strdup(name); 884 if (!data) 885 return (NULL); /* No memory! */ 886 887 /* Kill the optional trailing dot */ 888 if (*(data+namelen-1) == '.') { 889 *(data+namelen-1) = '\0'; 890 namelen--; 891 } 892 s = data; 893 components = 1; 894 while (*s != '\0') { 895 if (*s == '.') { 896 *s = '\0'; 897 components++; 898 s++; 899 } else if (*s == '"') { 900 if (*(s+1) == '"') { /* escaped quote */ 901 s += 2; 902 } else { 903 /* skip quoted string */ 904 s++; 905 while ((*s != '"') && (*s != '\0')) 906 s++; 907 if (*s == '"') { 908 s++; 909 } 910 } 911 } else { 912 s++; 913 } 914 } 915 pieces = (char **)calloc(components+1, sizeof (char *)); 916 if (! pieces) { 917 free(data); 918 return (NULL); 919 } 920 921 /* store in pieces in inverted order */ 922 for (i = (components-1), s = data; i > -1; i--) { 923 *(pieces+i) = s; 924 while (*s != '\0') 925 s++; 926 s++; 927 } 928 *(pieces+components) = NULL; 929 *levels = components; 930 931 return (pieces); 932 } 933 934 void 935 __free_break_name(char **components, int levels) 936 { 937 free(components[levels-1]); 938 free(components); 939 } 940 941 int 942 __name_distance( 943 char **targ, /* The target name */ 944 char **test) /* the test name */ 945 { 946 int distance = 0; 947 948 /* Don't count common components */ 949 while ((*targ && *test) && (strcasecmp(*targ, *test) == 0)) { 950 targ++; 951 test++; 952 } 953 954 /* count off the legs of each name */ 955 while (*test != NULL) { 956 test++; 957 distance++; 958 } 959 960 while (*targ != NULL) { 961 targ++; 962 distance++; 963 } 964 965 return (distance); 966 } 967 968 int 969 __dir_same(char **test, char **targ) 970 { 971 /* skip common components */ 972 while ((*targ && *test) && (strcasecmp(*targ, *test) == 0)) { 973 targ++; 974 test++; 975 } 976 977 return (*test == NULL && *targ == NULL); 978 } 979 980 void 981 __broken_name_print(char **name, int levels) 982 { 983 int i; 984 985 for (i = levels-1; i >= 0; --i) 986 (void) printf("%s.", name[i]); 987 } 988 989 990 /* 991 * For returning errors in a NIS result structure 992 */ 993 nis_result * 994 nis_make_error( 995 nis_error err, 996 uint32_t aticks, /* Profile information for client */ 997 uint32_t cticks, 998 uint32_t dticks, 999 uint32_t zticks) 1000 { 1001 nis_result *nres; 1002 1003 nres = (nis_result *)malloc(sizeof (nis_result)); 1004 if (!nres) 1005 return ((nis_result *)&__nomem_nis_result); 1006 (void) memset((char *)nres, 0, sizeof (nis_result)); 1007 nres->status = err; 1008 nres->aticks = aticks; 1009 nres->zticks = zticks; 1010 nres->dticks = dticks; 1011 nres->cticks = cticks; 1012 return (nres); 1013 } 1014 1015 #define ZVAL zattr_val.zattr_val_val 1016 #define ZLEN zattr_val.zattr_val_len 1017 1018 /* 1019 * __cvt2attr() 1020 * 1021 * This function converts a search criteria of the form : 1022 * [ <key>=<value>, <key>=<value>, ... ] 1023 * Into an array of nis_attr structures. 1024 */ 1025 1026 nis_attr * 1027 __cvt2attr( 1028 int *na, /* Number of attributes */ 1029 char **attrs) /* Strings associated with them */ 1030 1031 { 1032 int i; 1033 nis_attr *zattrs; 1034 char *s; 1035 1036 zattrs = (nis_attr *)calloc(*na, sizeof (nis_attr)); 1037 if (! zattrs) 1038 return (NULL); 1039 1040 for (i = 0; i < *na; i++) { 1041 zattrs[i].zattr_ndx = *(attrs+i); 1042 for (s = zattrs[i].zattr_ndx; *s != '\0'; s++) { 1043 if (*s == '=') { 1044 *s = '\0'; 1045 s++; 1046 zattrs[i].ZVAL = s; 1047 zattrs[i].ZLEN = (uint_t)strlen(s) + 1; 1048 break; 1049 } else if (*s == '"') { 1050 /* advance s to matching quote */ 1051 s++; 1052 while ((*s != '"') && (*s != '\0')) 1053 s++; 1054 if (*s == '\0') { 1055 /* unterminated quote */ 1056 free(zattrs); 1057 return (NULL); 1058 } 1059 } 1060 } 1061 /* 1062 * POLICY : Missing value for an index name is an 1063 * error. The other alternative is the missing 1064 * value means "is present" unfortunately there 1065 * is no standard "is present" indicator in the 1066 * existing databases. 1067 * ANSWER : Always return an error. 1068 */ 1069 if (! zattrs[i].ZVAL) { 1070 free(zattrs); 1071 return (NULL); 1072 } 1073 } 1074 return (zattrs); 1075 } 1076 1077 /* 1078 * nis_free_request() 1079 * 1080 * Free memory associated with a constructed list request. 1081 */ 1082 void 1083 nis_free_request(ib_request *req) 1084 { 1085 if (req->ibr_srch.ibr_srch_len) { 1086 /* free the string memory */ 1087 free(req->ibr_srch.ibr_srch_val[0].zattr_ndx); 1088 /* free the nis_attr array */ 1089 free(req->ibr_srch.ibr_srch_val); 1090 } 1091 1092 if (req->ibr_name) 1093 free(req->ibr_name); 1094 } 1095 1096 /* 1097 * nis_get_request() 1098 * 1099 * This function takes a NIS name, and converts it into an ib_request 1100 * structure. The request can then be used in a call to the nis service 1101 * functions. If the name wasn't parseable it returns an appropriate 1102 * error. This function ends up allocating an array of nis_attr structures 1103 * and a duplicate of the name string passed. To free this memory you 1104 * can call nis_free_request(), or you can simply free the first nis_attr 1105 * zattr_ndx pointer (the string memory) and the nis_attr pointer which 1106 * is the array. 1107 */ 1108 nis_error 1109 nis_get_request( 1110 nis_name name, /* search criteria + Table name */ 1111 nis_object *obj, /* Object for (rem/modify/add) */ 1112 netobj *cookie, /* Pointer to a cookie */ 1113 ib_request *req) /* Request structure to fill in */ 1114 { 1115 char *s, *t; /* Some string pointer temps */ 1116 char *p; /* temp var */ 1117 char **attr; /* Intermediate attributes */ 1118 int i; /* Counter variable */ 1119 char *data; /* pointer to malloc'd string */ 1120 int zn = 0; /* Count of attributes */ 1121 size_t datalen; /* length of malloc'd data */ 1122 char namebuf[NIS_MAXNAMELEN]; 1123 1124 uchar_t within_attr_val; 1125 /* 1126 * a boolean to indicate the current parse 1127 * location is within the attribute value 1128 * - so that we can stop deleting white 1129 * space within an attribute value 1130 */ 1131 1132 (void) memset((char *)req, 0, sizeof (ib_request)); 1133 1134 /* 1135 * if we're passed an object but no name, use the name from 1136 * the object instead. 1137 */ 1138 if (obj && !name) { 1139 if ((strlen(obj->zo_name)+strlen(obj->zo_domain)+2) > 1140 sizeof (namebuf)) { 1141 return (NIS_BADNAME); 1142 } 1143 (void) sprintf(namebuf, "%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 = (char *)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 = (char **)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 = (nis_object *)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 1405 (void) clnt_control(client, CLSET_FD_CLOSE, NULL); 1406 1407 /* 1408 * Now make the call to get the NIS service address. 1409 * We set the retry timeout to 3 seconds so that we 1410 * will retry a few times. Retries should be rare 1411 * because we are usually only called when we know 1412 * a server is available. 1413 */ 1414 tv.tv_sec = 3; 1415 tv.tv_usec = 0; 1416 (void) clnt_control(client, CLSET_RETRY_TIMEOUT, (char *)&tv); 1417 1418 tv.tv_sec = 10; 1419 tv.tv_usec = 0; 1420 parms.r_prog = prog; 1421 parms.r_vers = ver; 1422 parms.r_netid = nc->nc_netid; /* not needed */ 1423 parms.r_addr = ""; /* not needed; just for xdring */ 1424 parms.r_owner = ""; /* not needed; just for xdring */ 1425 clnt_st = clnt_call(client, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms, 1426 xdr_wrapstring, (char *)&ua, tv); 1427 1428 if (clnt_st == RPC_SUCCESS) { 1429 clnt_destroy(client); 1430 if (*ua == '\0') { 1431 mem_free(ua, 1); 1432 return (NULL); 1433 } 1434 res = strdup(ua); 1435 xdr_free(xdr_wrapstring, (char *)&ua); 1436 return (res); 1437 } else if (((clnt_st == RPC_PROGVERSMISMATCH) || 1438 (clnt_st == RPC_PROGUNAVAIL)) && 1439 (strcmp(nc->nc_protofmly, NC_INET) == 0)) { 1440 /* 1441 * version 3 not available. Try version 2 1442 * The assumption here is that the netbuf 1443 * is arranged in the sockaddr_in 1444 * style for IP cases. 1445 * 1446 * Note: If the remote host doesn't support version 3, 1447 * we assume it doesn't know IPv6 either. 1448 */ 1449 ushort_t port; 1450 struct sockaddr_in *sa; 1451 struct netbuf remote; 1452 int protocol; 1453 char buf[32]; 1454 1455 (void) clnt_control(client, CLGET_SVC_ADDR, (char *)&remote); 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 = (endpoint *)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) sprintf(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 _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 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 = (void *) 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 = (void *) 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) sprintf(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) sprintf(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 = (nis_name)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 _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 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 key = (extdhkey_t *)&(srv->pkey.n_bytes[s]); 2311 k = ntohs(key->keylen); 2312 if (k == 0) 2313 break; 2314 kpadlen = ((((k+7)/8)+3)/4)*4; 2315 a = ntohs(key->algtype); 2316 if (kpadlen <= sizeof (pkey)) { 2317 (void) bin2hex((k+7)/8, key->key, pkey); 2318 __pkey_cache_add(netname, pkey, k, a); 2319 } 2320 s += 2*sizeof (ushort_t) + kpadlen; 2321 } 2322 break; 2323 default: 2324 break; 2325 } 2326 2327 } 2328 } 2329