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