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 (c) 1992,1999 by Sun Microsystems Inc. 24 * All rights reserved. 25 * 26 * auth_time.c 27 * 28 * This module contains the private function __rpc_get_time_offset() 29 * which will return the difference in seconds between the local system's 30 * notion of time and a remote server's notion of time. This must be 31 * possible without calling any functions that may invoke the name 32 * service. (netdir_getbyxxx, getXbyY, etc). The function is used in the 33 * synchronize call of the authdes code to synchronize clocks between 34 * NIS+ clients and their servers. 35 * 36 * Note to minimize the amount of duplicate code, portions of the 37 * synchronize() function were folded into this code, and the synchronize 38 * call becomes simply a wrapper around this function. Further, if this 39 * function is called with a timehost it *DOES* recurse to the name 40 * server so don't use it in that mode if you are doing name service code. 41 * 42 * Side effects : 43 * When called a client handle to a RPCBIND process is created 44 * and destroyed. Two strings "netid" and "uaddr" are malloc'd 45 * and returned. The SIGALRM processing is modified only if 46 * needed to deal with TCP connections. 47 */ 48 49 #pragma ident "%Z%%M% %I% %E% SMI" 50 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 <netconfig.h> 58 #include <netdb.h> 59 #include <signal.h> 60 #include <sys/errno.h> 61 #include <sys/poll.h> 62 #include <rpc/rpc.h> 63 #include <rpc/nettype.h> 64 #undef NIS 65 #include <rpcsvc/nis.h> 66 67 68 extern void __nis_netconfig2ep(struct netconfig *, endpoint *); 69 extern bool_t __nis_netconfig_matches_ep(struct netconfig *, endpoint *); 70 71 #ifdef TESTING 72 #define msg(x) printf("ERROR: %s\n", x) 73 /* #define msg(x) syslog(LOG_ERR, "%s", x) */ 74 #else 75 #define msg(x) 76 #endif 77 78 extern int bzero(caddr_t dst, int len); 79 80 static int saw_alarm = 0; 81 82 static void 83 alarm_hndler(s) 84 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 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 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 sscanf(useua, "%d.%d.%d.%d.", &a1, &a2, &a3, &a4); 357 sprintf(ipuaddr, "%d.%d.%d.%d.0.111", a1, a2, a3, a4); 358 useua = &ipuaddr[0]; 359 } else if (strcasecmp(nc->nc_protofmly, NC_INET6) == 0) { 360 size_t len; 361 char *port = ".0.111"; 362 363 if (strlen(useua) >= sizeof (ipuaddr)) { 364 freenetconfigent(nc); 365 if (needfree) 366 free_eps(teps, tsrv.ep.ep_len); 367 return (0); 368 } 369 370 strcpy(ipuaddr, useua); 371 372 /* get the IPv6 address out of the uaddr */ 373 if ((dot = strrchr(ipuaddr, '.')) != 0) { 374 *dot = '\0'; 375 if ((dot = strrchr(ipuaddr, '.')) != 0) 376 *dot = '\0'; 377 } 378 379 if (dot == 0 || 380 (len = strlen(ipuaddr))+strlen(port) >= 381 sizeof (ipuaddr)) { 382 freenetconfigent(nc); 383 if (needfree) 384 free_eps(teps, tsrv.ep.ep_len); 385 return (0); 386 } 387 388 /* now put in 0.111 */ 389 strcat(ipuaddr + len, port); 390 useua = ipuaddr; 391 } 392 393 /* 394 * Create the client handle to rpcbind. Note we always try 395 * version 3 since that is the earliest version that supports 396 * the RPCB_GETTIME call. Also it is the version that comes 397 * standard with SVR4. Since most everyone supports TCP/IP 398 * we could consider trying the rtime call first. 399 */ 400 if (clnt != 0) 401 clnt_destroy(clnt); 402 clnt = __nis_clnt_create(RPC_ANYFD, nc, useua, 0, 0, RPCBPROG, 403 RPCBVERS, 0, 0); 404 if (! clnt) 405 continue; 406 407 tv.tv_sec = 5; 408 tv.tv_usec = 0; 409 time_valid = 0; 410 411 status = clnt_call(clnt, RPCBPROC_GETTIME, xdr_void, NULL, 412 xdr_u_int, (char *)&thetime, tv); 413 /* 414 * The only error we check for is anything but success. In 415 * fact we could have seen PROGMISMATCH if talking to a 4.1 416 * machine (pmap v2) or TIMEDOUT if the net was busy. 417 */ 418 if (status == RPC_SUCCESS) 419 break; 420 421 } 422 423 if (status == RPC_SUCCESS) { 424 time_valid = 1; 425 } else if (clnt == 0) { 426 msg("unable to create client handle to rpcbind."); 427 freenetconfigent(nc); 428 if (needfree) 429 free_eps(teps, tsrv.ep.ep_len); 430 return (0); 431 } else { 432 433 /* 434 * Try the timeservice port. This presumably only exists 435 * for IP transports, so we ignore the non-IP ones. 436 */ 437 438 for (i = 0; i < epc-nonip; i++) { 439 440 /* 441 * Convert PMAP address into timeservice address 442 * We take advantage of the fact that we "know" what 443 * a universal address looks like for inet transports. 444 * 445 * We also know that the internet timeservice is always 446 * listening on port 37. 447 */ 448 449 if (nc != 0) 450 freenetconfigent(nc); 451 nc = getnetconfigent(epcand[i]->proto); 452 453 if (nc == 0) { 454 msg("no netconfig info for netid."); 455 if (needfree) 456 free_eps(teps, tsrv.ep.ep_len); 457 return (0); 458 } 459 460 useua = epcand[i]->uaddr; 461 useid = epcand[i]->proto; 462 463 if (strcasecmp(nc->nc_protofmly, NC_INET) == 0) { 464 sscanf(useua, 465 "%d.%d.%d.%d.", &a1, &a2, &a3, &a4); 466 sprintf(ut, "%d.%d.%d.%d.0.37", 467 a1, a2, a3, a4); 468 } else if (strcasecmp(nc->nc_protofmly, NC_INET6) == 469 0) { 470 size_t len; 471 char *port = ".0.37"; 472 473 if (strlen(useua) >= sizeof (ut)) { 474 goto error; 475 } 476 477 strcpy(ut, useua); 478 479 /* get the IPv6 address out of the uaddr */ 480 if ((dot = strrchr(ut, '.')) != 0) { 481 *dot = '\0'; 482 if ((dot = strrchr(ut, '.')) != 0) 483 *dot = '\0'; 484 } 485 486 if (dot == 0) { 487 goto error; 488 } 489 490 if ((len = strlen(ut))+strlen(port) >= 491 sizeof (ut)) { 492 goto error; 493 } 494 495 strcat(ut + len, port); 496 497 } 498 499 addr = uaddr2taddr(nc, ut); 500 if (! addr) { 501 msg("timeservice uaddr to taddr failed."); 502 goto error; 503 } 504 505 rtime_fd = t_open(nc->nc_device, O_RDWR, NULL); 506 if (rtime_fd == -1) { 507 msg("unable to open fd to network."); 508 goto error; 509 } 510 511 if (t_bind(rtime_fd, NULL, NULL) < 0) { 512 msg("unable to bind an endpoint to fd."); 513 goto error; 514 } 515 516 /* 517 * Now depending on whether or not we're talking to 518 * UDP we set a timeout or not. 519 */ 520 if (nc->nc_semantics == NC_TPI_CLTS) { 521 struct t_unitdata tu_data; 522 struct pollfd pfd; 523 int res; 524 525 tu_data.addr = *addr; 526 tu_data.udata.buf = (char *)&thetime; 527 tu_data.udata.len = (uint_t)sizeof (thetime); 528 tu_data.udata.maxlen = tu_data.udata.len; 529 tu_data.opt.len = 0; 530 tu_data.opt.maxlen = 0; 531 if (t_sndudata(rtime_fd, &tu_data) == -1) { 532 msg("udp : t_sndudata failed."); 533 goto error; 534 } 535 pfd.fd = rtime_fd; 536 pfd.events = 537 POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; 538 539 do { 540 res = poll(&pfd, 1, 10000); 541 } while (res < 0); 542 if ((res <= 0) || (pfd.revents & POLLNVAL)) 543 goto error; 544 if (t_rcvudata(rtime_fd, &tu_data, &flag) < 545 0) { 546 msg("t_rvcdata failed on udp trpt."); 547 goto error; 548 } 549 time_valid = 1; 550 } else { 551 struct t_call sndcall; 552 553 sndcall.addr = *addr; 554 sndcall.opt.len = sndcall.opt.maxlen = 0; 555 sndcall.udata.len = sndcall.udata.maxlen = 0; 556 557 oldsig = (void (*)())signal(SIGALRM, 558 alarm_hndler); 559 saw_alarm = 0; /* global tracking the alarm */ 560 alarm(20); /* only wait 20 seconds */ 561 if (t_connect(rtime_fd, &sndcall, NULL) == 562 -1) { 563 msg("connect tcp endpoint failedd."); 564 goto error; 565 } 566 if (saw_alarm) { 567 msg("alarm caught it; unreachable."); 568 goto error; 569 } 570 if (t_rcv(rtime_fd, (char *)&thetime, 571 (uint_t)sizeof (thetime), &flag) != 572 (uint_t)sizeof (thetime)) { 573 if (saw_alarm) { 574 /*EMPTY*/ 575 msg("timed out TCP call."); 576 } else { 577 /*EMPTY*/ 578 msg("wrong size results"); 579 } 580 goto error; 581 } 582 time_valid = 1; 583 } 584 if (time_valid) { 585 thetime = ntohl(thetime); 586 /* adjust to UNIX time */ 587 thetime = thetime - TOFFSET; 588 } else 589 thetime = 0; 590 } 591 } 592 593 error: 594 /* 595 * clean up our allocated data structures. 596 */ 597 if (addr) 598 netdir_free((char *)(addr), ND_ADDR); 599 600 if (rtime_fd != -1) 601 (void) t_close(rtime_fd); 602 603 if (clnt) 604 clnt_destroy(clnt); 605 606 if (nc) 607 freenetconfigent(nc); 608 609 if (oldsig) { 610 alarm(0); /* reset that alarm if its outstanding */ 611 signal(SIGALRM, oldsig); 612 } 613 614 /* 615 * note, don't free uaddr strings until after we've made a 616 * copy of them. 617 */ 618 if (time_valid) { 619 if (! *netid) { 620 *netid = strdup(useid); 621 if (! *netid) { 622 msg("__rpc_get_time_offset: strdup failed."); 623 if (needfree) 624 free_eps(teps, tsrv.ep.ep_len); 625 return (0); 626 } 627 628 *uaddr = strdup(useua); 629 if (! *uaddr) { 630 msg("__rpc_get_time_offset: strdup failed."); 631 if (*netid) 632 free(*netid); 633 if (needfree) 634 free_eps(teps, tsrv.ep.ep_len); 635 return (0); 636 } 637 } 638 639 gettimeofday(&tv, 0); 640 641 /* Round to the nearest second */ 642 tv.tv_sec += (tv.tv_sec > 500000) ? 1 : 0; 643 delta = (thetime > tv.tv_sec) ? thetime - tv.tv_sec : 644 tv.tv_sec - thetime; 645 td->tv_sec = (thetime < tv.tv_sec) ? - delta : delta; 646 td->tv_usec = 0; 647 } else { 648 /*EMPTY*/ 649 msg("unable to get the server's time."); 650 } 651 652 if (needfree) 653 free_eps(teps, tsrv.ep.ep_len); 654 655 return (time_valid); 656 } 657