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