1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* 29 * This module contains the private function __rpc_get_time_offset() 30 * which will return the difference in seconds between the local system's 31 * notion of time and a remote server's notion of time. This must be 32 * possible without calling any functions that may invoke the name 33 * service. (netdir_getbyxxx, getXbyY, etc). The function is used in the 34 * synchronize call of the authdes code to synchronize clocks between 35 * NIS+ clients and their servers. 36 * 37 * Note to minimize the amount of duplicate code, portions of the 38 * synchronize() function were folded into this code, and the synchronize 39 * call becomes simply a wrapper around this function. Further, if this 40 * function is called with a timehost it *DOES* recurse to the name 41 * server so don't use it in that mode if you are doing name service code. 42 * 43 * Side effects : 44 * When called a client handle to a RPCBIND process is created 45 * and destroyed. Two strings "netid" and "uaddr" are malloc'd 46 * and returned. The SIGALRM processing is modified only if 47 * needed to deal with TCP connections. 48 */ 49 50 #pragma ident "%Z%%M% %I% %E% SMI" 51 52 #include "mt.h" 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <unistd.h> 56 #include <syslog.h> 57 #include <netdir.h> 58 #include <string.h> 59 #include <strings.h> 60 #include <netconfig.h> 61 #include <netdb.h> 62 #include <signal.h> 63 #include <sys/errno.h> 64 #include <sys/poll.h> 65 #include <rpc/rpc.h> 66 #include <rpc/nettype.h> 67 #undef NIS 68 #include <rpcsvc/nis.h> 69 70 71 extern void __nis_netconfig2ep(struct netconfig *, endpoint *); 72 extern bool_t __nis_netconfig_matches_ep(struct netconfig *, endpoint *); 73 74 #ifdef TESTING 75 #define msg(x) printf("ERROR: %s\n", x) 76 /* #define msg(x) syslog(LOG_ERR, "%s", x) */ 77 #else 78 #define msg(x) 79 #endif 80 81 static int saw_alarm = 0; 82 83 /* ARGSUSED */ 84 static void 85 alarm_hndler(int s) 86 { 87 saw_alarm = 1; 88 } 89 90 /* 91 * The internet time server defines the epoch to be Jan 1, 1900 92 * whereas UNIX defines it to be Jan 1, 1970. To adjust the result 93 * from internet time-service time, into UNIX time we subtract the 94 * following offset : 95 */ 96 #define NYEARS (1970 - 1900) 97 #define TOFFSET ((uint_t)60*60*24*(365*NYEARS + (NYEARS/4))) 98 99 /* 100 * free_eps() 101 * 102 * Free the strings that were strduped into the eps structure. 103 */ 104 static void 105 free_eps(endpoint eps[], int num) 106 { 107 int i; 108 109 for (i = 0; i < num; i++) { 110 free(eps[i].uaddr); 111 free(eps[i].proto); 112 free(eps[i].family); 113 } 114 } 115 116 /* 117 * get_server() 118 * 119 * This function constructs a nis_server structure description for the 120 * indicated hostname. 121 */ 122 static nis_server * 123 get_server(char *host, nis_server *srv, endpoint eps[], int maxep) 124 { 125 int num_ep = 0, i; 126 struct netconfig *nc; 127 void *nch; 128 struct nd_hostserv hs; 129 struct nd_addrlist *addrs; 130 131 if (! host) 132 return (NULL); 133 hs.h_host = host; 134 hs.h_serv = "rpcbind"; 135 nch = setnetconfig(); 136 while (nc = getnetconfig(nch)) { 137 if ((nc->nc_flag & NC_VISIBLE) == 0) 138 continue; 139 if (! netdir_getbyname(nc, &hs, &addrs)) { 140 for (i = 0; (i < (addrs->n_cnt)) && (num_ep < maxep); 141 i++, num_ep++) { 142 eps[num_ep].uaddr = 143 taddr2uaddr(nc, &(addrs->n_addrs[i])); 144 __nis_netconfig2ep(nc, &(eps[num_ep])); 145 } 146 netdir_free((char *)addrs, ND_ADDRLIST); 147 } 148 } 149 (void) endnetconfig(nch); 150 151 srv->name = (nis_name) host; 152 srv->ep.ep_len = num_ep; 153 srv->ep.ep_val = eps; 154 srv->key_type = NIS_PK_NONE; 155 srv->pkey.n_bytes = NULL; 156 srv->pkey.n_len = 0; 157 return (srv); 158 } 159 160 #define MEP(ep, prot) (strcasecmp(ep.proto, prot) == 0) 161 #define MAX_ENDPOINTS 32 162 163 /* 164 * __rpc_get_time_offset() 165 * 166 * This function uses a nis_server structure to contact the a remote 167 * machine (as named in that structure) and returns the offset in time 168 * between that machine and this one. This offset is returned in seconds 169 * and may be positive or negative. 170 * 171 * The first time through, a lot of fiddling is done with the netconfig 172 * stuff to find a suitable transport. The function is very aggressive 173 * about choosing UDP or at worst TCP if it can. This is because 174 * those transports support both the RCPBIND call and the internet 175 * time service. 176 * 177 * Once through, *uaddr is set to the universal address of 178 * the machine and *netid is set to the local netid for the transport 179 * that uaddr goes with. On the second call, the netconfig stuff 180 * is skipped and the uaddr/netid pair are used to fetch the netconfig 181 * structure and to then contact the machine for the time. 182 * 183 * td = "server" - "client" 184 */ 185 int 186 __rpc_get_time_offset(struct timeval *td, nis_server *srv, 187 char *thost, char **uaddr, char **netid) 188 { 189 CLIENT *clnt; /* Client handle */ 190 struct netbuf *addr = 0; /* address */ 191 void *nc_handle; /* Netconfig "state" */ 192 struct netconfig *nc; /* Various handles */ 193 endpoint *ep; /* useful endpoints */ 194 char *useua = NULL, /* uaddr of selected xp */ 195 *useid = NULL; /* netid of selected xp */ 196 int epl, i; /* counters */ 197 enum clnt_stat status; /* result of clnt_call */ 198 uint_t thetime; 199 ulong_t delta; 200 int needfree = 0; 201 struct timeval tv; 202 int rtime_fd = -1, time_valid, flag = 0; 203 int a1, a2, a3, a4; 204 char ut[INET6_ADDRSTRLEN]; 205 char ipuaddr[INET6_ADDRSTRLEN]; 206 endpoint teps[MAX_ENDPOINTS], 207 *epcand[MAX_ENDPOINTS], 208 *nonipcand[MAX_ENDPOINTS], 209 supplied; 210 uint32_t epc, nonip; 211 nis_server tsrv; 212 void (*oldsig)() = NULL; /* old alarm handler */ 213 char *dot = NULL; /* tmp pointer */ 214 215 216 217 nc = NULL; 218 td->tv_sec = 0; 219 td->tv_usec = 0; 220 221 /* 222 * First check to see if we need to find and address for this 223 * server. 224 */ 225 if (*uaddr == NULL) { 226 if ((srv != NULL) && (thost != NULL)) { 227 msg("both timehost and srv pointer used!"); 228 return (0); 229 } 230 if (! srv) { 231 srv = get_server(thost, &tsrv, teps, 32); 232 if (! srv) { 233 msg("unable to contruct server data."); 234 return (0); 235 } 236 needfree = 1; /* need to free data in endpoints */ 237 } 238 239 nc_handle = (void *) setnetconfig(); 240 if (! nc_handle) { 241 msg("unable to get netconfig info."); 242 if (needfree) 243 free_eps(teps, tsrv.ep.ep_len); 244 return (0); 245 } 246 247 ep = srv->ep.ep_val; 248 epl = srv->ep.ep_len; 249 for (i = 0; i < sizeof (epcand)/sizeof (epcand[0]); i++) { 250 epcand[i] = 0; 251 nonipcand[i] = 0; 252 } 253 epc = 0; 254 nonip = 0; 255 256 /* 257 * Build the list of endpoint candidates. We prefer transports 258 * that we know are IP, but let /etc/netconfig determine the 259 * ordering among the IP transports. 260 * 261 * Note: We assume that the endpoint 'proto' field contains 262 * the netid of the transport. 263 */ 264 while ((nc = getnetconfig(nc_handle)) != NULL) { 265 266 /* Is it a visible transport ? */ 267 if ((nc->nc_flag & NC_VISIBLE) == 0) 268 continue; 269 270 /* Check against the end points */ 271 for (i = 0; i < epl; i++) { 272 if (__nis_netconfig_matches_ep(nc, &(ep[i]))) { 273 if (MEP(ep[i], "udp") || 274 MEP(ep[i], "udp6") || 275 MEP(ep[i], "tcp") || 276 MEP(ep[i], "tcp6")) { 277 epcand[epc++] = &(ep[i]); 278 } else { 279 nonipcand[nonip++] = &ep[i]; 280 } 281 break; 282 } 283 } 284 } 285 286 (void) endnetconfig(nc_handle); 287 288 /* 289 * epcand[] now contains the candidate transports. If there 290 * were non-IP transports as well, add them to the end of the 291 * candidate list. 292 */ 293 for (i = 0; i < nonip; i++) { 294 epcand[epc++] = nonipcand[i]; 295 } 296 297 if (epc == 0) { 298 msg("no acceptable transport endpoints."); 299 if (needfree) 300 free_eps(teps, tsrv.ep.ep_len); 301 return (0); 302 } 303 } else { 304 /* Caller supplied a uaddr. Fake an endpoint. */ 305 if (*netid != 0) { 306 supplied.proto = *netid; 307 /* Is it one of the known IP transports ? */ 308 if (strcmp("udp", supplied.proto) && 309 strcmp("udp6", supplied.proto) && 310 strcmp("tcp", supplied.proto) && 311 strcmp("tcp6", supplied.proto)) { 312 /* No, it's not */ 313 nonip = 1; 314 } else { 315 nonip = 0; 316 } 317 } else { 318 supplied.proto = (strchr(*uaddr, ':') != 0) ? 319 "udp6" : "udp"; 320 nonip = 0; 321 } 322 supplied.uaddr = *uaddr; 323 supplied.family = (strchr(*uaddr, ':') != 0) ? 324 "inet6" : "inet"; 325 epcand[0] = &supplied; 326 epc = 1; 327 nonip = 0; 328 } 329 330 nc = 0; 331 clnt = 0; 332 status = RPC_FAILED; /* Anything except RPC_SUCCESS */ 333 334 /* 335 * Loop over the endpoint candidates. Defer error reporting (except 336 * for the netconfig entry) until we've looked at all candidates. 337 */ 338 for (i = 0; i < epc; i++) { 339 340 if (nc != 0) 341 freenetconfigent(nc); 342 nc = getnetconfigent(epcand[i]->proto); 343 344 if (nc == 0) { 345 msg("unable to locate netconfig info for netid."); 346 if (needfree) 347 free_eps(teps, tsrv.ep.ep_len); 348 return (0); 349 } 350 351 /* 352 * Add the appropriate port number to the uaddr 353 */ 354 useua = epcand[i]->uaddr; 355 useid = epcand[i]->proto; 356 if (strcasecmp(nc->nc_protofmly, NC_INET) == 0) { 357 (void) sscanf(useua, 358 "%d.%d.%d.%d.", &a1, &a2, &a3, &a4); 359 (void) sprintf(ipuaddr, "%d.%d.%d.%d.0.111", 360 a1, a2, a3, a4); 361 useua = &ipuaddr[0]; 362 } else if (strcasecmp(nc->nc_protofmly, NC_INET6) == 0) { 363 size_t len; 364 char *port = ".0.111"; 365 366 if (strlen(useua) >= sizeof (ipuaddr)) { 367 freenetconfigent(nc); 368 if (needfree) 369 free_eps(teps, tsrv.ep.ep_len); 370 return (0); 371 } 372 373 (void) strcpy(ipuaddr, useua); 374 375 /* get the IPv6 address out of the uaddr */ 376 if ((dot = strrchr(ipuaddr, '.')) != 0) { 377 *dot = '\0'; 378 if ((dot = strrchr(ipuaddr, '.')) != 0) 379 *dot = '\0'; 380 } 381 382 if (dot == 0 || 383 (len = strlen(ipuaddr))+strlen(port) >= 384 sizeof (ipuaddr)) { 385 freenetconfigent(nc); 386 if (needfree) 387 free_eps(teps, tsrv.ep.ep_len); 388 return (0); 389 } 390 391 /* now put in 0.111 */ 392 (void) strcat(ipuaddr + len, port); 393 useua = ipuaddr; 394 } 395 396 /* 397 * Create the client handle to rpcbind. Note we always try 398 * version 3 since that is the earliest version that supports 399 * the RPCB_GETTIME call. Also it is the version that comes 400 * standard with SVR4. Since most everyone supports TCP/IP 401 * we could consider trying the rtime call first. 402 */ 403 if (clnt != 0) 404 clnt_destroy(clnt); 405 clnt = __nis_clnt_create(RPC_ANYFD, nc, useua, 0, 0, RPCBPROG, 406 RPCBVERS, 0, 0); 407 if (! clnt) 408 continue; 409 410 tv.tv_sec = 5; 411 tv.tv_usec = 0; 412 time_valid = 0; 413 414 status = clnt_call(clnt, RPCBPROC_GETTIME, xdr_void, NULL, 415 xdr_u_int, (char *)&thetime, tv); 416 /* 417 * The only error we check for is anything but success. In 418 * fact we could have seen PROGMISMATCH if talking to a 4.1 419 * machine (pmap v2) or TIMEDOUT if the net was busy. 420 */ 421 if (status == RPC_SUCCESS) 422 break; 423 424 } 425 426 if (status == RPC_SUCCESS) { 427 time_valid = 1; 428 } else if (clnt == 0) { 429 msg("unable to create client handle to rpcbind."); 430 freenetconfigent(nc); 431 if (needfree) 432 free_eps(teps, tsrv.ep.ep_len); 433 return (0); 434 } else { 435 436 /* 437 * Try the timeservice port. This presumably only exists 438 * for IP transports, so we ignore the non-IP ones. 439 */ 440 441 for (i = 0; i < epc-nonip; i++) { 442 443 /* 444 * Convert PMAP address into timeservice address 445 * We take advantage of the fact that we "know" what 446 * a universal address looks like for inet transports. 447 * 448 * We also know that the internet timeservice is always 449 * listening on port 37. 450 */ 451 452 if (nc != 0) 453 freenetconfigent(nc); 454 nc = getnetconfigent(epcand[i]->proto); 455 456 if (nc == 0) { 457 msg("no netconfig info for netid."); 458 if (needfree) 459 free_eps(teps, tsrv.ep.ep_len); 460 return (0); 461 } 462 463 useua = epcand[i]->uaddr; 464 useid = epcand[i]->proto; 465 466 if (strcasecmp(nc->nc_protofmly, NC_INET) == 0) { 467 (void) sscanf(useua, 468 "%d.%d.%d.%d.", &a1, &a2, &a3, &a4); 469 (void) sprintf(ut, "%d.%d.%d.%d.0.37", 470 a1, a2, a3, a4); 471 } else if (strcasecmp(nc->nc_protofmly, NC_INET6) == 472 0) { 473 size_t len; 474 char *port = ".0.37"; 475 476 if (strlen(useua) >= sizeof (ut)) { 477 goto error; 478 } 479 480 (void) strcpy(ut, useua); 481 482 /* get the IPv6 address out of the uaddr */ 483 if ((dot = strrchr(ut, '.')) != 0) { 484 *dot = '\0'; 485 if ((dot = strrchr(ut, '.')) != 0) 486 *dot = '\0'; 487 } 488 489 if (dot == 0) { 490 goto error; 491 } 492 493 if ((len = strlen(ut))+strlen(port) >= 494 sizeof (ut)) { 495 goto error; 496 } 497 498 (void) strcat(ut + len, port); 499 500 } 501 502 addr = uaddr2taddr(nc, ut); 503 if (! addr) { 504 msg("timeservice uaddr to taddr failed."); 505 goto error; 506 } 507 508 rtime_fd = t_open(nc->nc_device, O_RDWR, NULL); 509 if (rtime_fd == -1) { 510 msg("unable to open fd to network."); 511 goto error; 512 } 513 514 if (t_bind(rtime_fd, NULL, NULL) < 0) { 515 msg("unable to bind an endpoint to fd."); 516 goto error; 517 } 518 519 /* 520 * Now depending on whether or not we're talking to 521 * UDP we set a timeout or not. 522 */ 523 if (nc->nc_semantics == NC_TPI_CLTS) { 524 struct t_unitdata tu_data; 525 struct pollfd pfd; 526 int res; 527 528 tu_data.addr = *addr; 529 tu_data.udata.buf = (char *)&thetime; 530 tu_data.udata.len = (uint_t)sizeof (thetime); 531 tu_data.udata.maxlen = tu_data.udata.len; 532 tu_data.opt.len = 0; 533 tu_data.opt.maxlen = 0; 534 if (t_sndudata(rtime_fd, &tu_data) == -1) { 535 msg("udp : t_sndudata failed."); 536 goto error; 537 } 538 pfd.fd = rtime_fd; 539 pfd.events = 540 POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; 541 542 do { 543 res = poll(&pfd, 1, 10000); 544 } while (res < 0); 545 if ((res <= 0) || (pfd.revents & POLLNVAL)) 546 goto error; 547 if (t_rcvudata(rtime_fd, &tu_data, &flag) < 548 0) { 549 msg("t_rvcdata failed on udp trpt."); 550 goto error; 551 } 552 time_valid = 1; 553 } else { 554 struct t_call sndcall; 555 556 sndcall.addr = *addr; 557 sndcall.opt.len = sndcall.opt.maxlen = 0; 558 sndcall.udata.len = sndcall.udata.maxlen = 0; 559 560 oldsig = (void (*)())signal(SIGALRM, 561 alarm_hndler); 562 saw_alarm = 0; /* global tracking the alarm */ 563 (void) alarm(20); /* only wait 20 seconds */ 564 if (t_connect(rtime_fd, &sndcall, NULL) == 565 -1) { 566 msg("connect tcp endpoint failedd."); 567 goto error; 568 } 569 if (saw_alarm) { 570 msg("alarm caught it; unreachable."); 571 goto error; 572 } 573 if (t_rcv(rtime_fd, (char *)&thetime, 574 (uint_t)sizeof (thetime), &flag) != 575 (uint_t)sizeof (thetime)) { 576 if (saw_alarm) { 577 /*EMPTY*/ 578 msg("timed out TCP call."); 579 } else { 580 /*EMPTY*/ 581 msg("wrong size results"); 582 } 583 goto error; 584 } 585 time_valid = 1; 586 } 587 if (time_valid) { 588 thetime = ntohl(thetime); 589 /* adjust to UNIX time */ 590 thetime = thetime - TOFFSET; 591 } else 592 thetime = 0; 593 } 594 } 595 596 error: 597 /* 598 * clean up our allocated data structures. 599 */ 600 if (addr) 601 netdir_free((char *)(addr), ND_ADDR); 602 603 if (rtime_fd != -1) 604 (void) t_close(rtime_fd); 605 606 if (clnt) 607 clnt_destroy(clnt); 608 609 if (nc) 610 freenetconfigent(nc); 611 612 if (oldsig) { 613 (void) alarm(0); /* reset that alarm if its outstanding */ 614 (void) signal(SIGALRM, oldsig); 615 } 616 617 /* 618 * note, don't free uaddr strings until after we've made a 619 * copy of them. 620 */ 621 if (time_valid) { 622 if (! *netid) { 623 *netid = strdup(useid); 624 if (! *netid) { 625 msg("__rpc_get_time_offset: strdup failed."); 626 if (needfree) 627 free_eps(teps, tsrv.ep.ep_len); 628 return (0); 629 } 630 631 *uaddr = strdup(useua); 632 if (! *uaddr) { 633 msg("__rpc_get_time_offset: strdup failed."); 634 if (*netid) 635 free(*netid); 636 if (needfree) 637 free_eps(teps, tsrv.ep.ep_len); 638 return (0); 639 } 640 } 641 642 (void) gettimeofday(&tv, 0); 643 644 /* Round to the nearest second */ 645 tv.tv_sec += (tv.tv_sec > 500000) ? 1 : 0; 646 delta = (thetime > tv.tv_sec) ? thetime - tv.tv_sec : 647 tv.tv_sec - thetime; 648 td->tv_sec = (thetime < tv.tv_sec) ? - delta : delta; 649 td->tv_usec = 0; 650 } else { 651 /*EMPTY*/ 652 msg("unable to get the server's time."); 653 } 654 655 if (needfree) 656 free_eps(teps, tsrv.ep.ep_len); 657 658 return (time_valid); 659 } 660