1 #pragma ident "@(#)auth_time.c 1.4 92/11/10 SMI" 2 3 /* 4 * auth_time.c 5 * 6 * This module contains the private function __rpc_get_time_offset() 7 * which will return the difference in seconds between the local system's 8 * notion of time and a remote server's notion of time. This must be 9 * possible without calling any functions that may invoke the name 10 * service. (netdir_getbyxxx, getXbyY, etc). The function is used in the 11 * synchronize call of the authdes code to synchronize clocks between 12 * NIS+ clients and their servers. 13 * 14 * Note to minimize the amount of duplicate code, portions of the 15 * synchronize() function were folded into this code, and the synchronize 16 * call becomes simply a wrapper around this function. Further, if this 17 * function is called with a timehost it *DOES* recurse to the name 18 * server so don't use it in that mode if you are doing name service code. 19 * 20 * Copyright (c) 1992 Sun Microsystems Inc. 21 * All rights reserved. 22 * 23 * Side effects : 24 * When called a client handle to a RPCBIND process is created 25 * and destroyed. Two strings "netid" and "uaddr" are malloc'd 26 * and returned. The SIGALRM processing is modified only if 27 * needed to deal with TCP connections. 28 * 29 * NOTE: This code has had the crap beaten out it in order to convert 30 * it from TI-RPC back to TD-RPC for use on FreeBSD. 31 * 32 * $FreeBSD$ 33 */ 34 #include <stdio.h> 35 #include <syslog.h> 36 #include <string.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include <netdb.h> 40 #include <sys/signal.h> 41 #include <sys/errno.h> 42 #include <sys/socket.h> 43 #include <netinet/in.h> 44 #include <arpa/inet.h> 45 #include <rpc/rpc.h> 46 #include <rpc/rpc_com.h> 47 #undef NIS 48 #include <rpcsvc/nis.h> 49 50 /* 51 * FreeBSD currently uses RPC 4.0, which uses portmap rather than 52 * rpcbind. Consequently, we need to fake up these values here. 53 * Luckily, the RPCB_GETTIME procedure uses only base XDR data types 54 * so we don't need anything besides these magic numbers. 55 */ 56 #define RPCBPROG (u_long)100000 57 #define RPCBVERS (u_long)3 58 #define RPCBPROC_GETTIME (u_long)6 59 60 #ifdef TESTING 61 #define msg(x) printf("ERROR: %s\n", x) 62 /* #define msg(x) syslog(LOG_ERR, "%s", x) */ 63 #else 64 #define msg(x) 65 #endif 66 67 static int saw_alarm = 0; 68 69 static void 70 alarm_hndler(s) 71 int s; 72 { 73 saw_alarm = 1; 74 return; 75 } 76 77 /* 78 * The internet time server defines the epoch to be Jan 1, 1900 79 * whereas UNIX defines it to be Jan 1, 1970. To adjust the result 80 * from internet time-service time, into UNIX time we subtract the 81 * following offset : 82 */ 83 #define NYEARS (1970 - 1900) 84 #define TOFFSET ((u_long)60*60*24*(365*NYEARS + (NYEARS/4))) 85 86 87 /* 88 * Stolen from rpc.nisd: 89 * Turn a 'universal address' into a struct sockaddr_in. 90 * Bletch. 91 */ 92 static int uaddr_to_sockaddr(uaddr, sin) 93 #ifdef foo 94 endpoint *endpt; 95 #endif 96 char *uaddr; 97 struct sockaddr_in *sin; 98 { 99 unsigned char p_bytes[2]; 100 int i; 101 unsigned long a[6]; 102 103 i = sscanf(uaddr, "%lu.%lu.%lu.%lu.%lu.%lu", &a[0], &a[1], &a[2], 104 &a[3], &a[4], &a[5]); 105 106 if (i < 6) 107 return(1); 108 109 for (i = 0; i < 4; i++) 110 sin->sin_addr.s_addr |= (a[i] & 0x000000FF) << (8 * i); 111 112 p_bytes[0] = (unsigned char)a[4] & 0x000000FF; 113 p_bytes[1] = (unsigned char)a[5] & 0x000000FF; 114 115 sin->sin_family = AF_INET; /* always */ 116 bcopy((char *)&p_bytes, (char *)&sin->sin_port, 2); 117 118 return (0); 119 } 120 121 /* 122 * free_eps() 123 * 124 * Free the strings that were strduped into the eps structure. 125 */ 126 static void 127 free_eps(eps, num) 128 endpoint eps[]; 129 int num; 130 { 131 int i; 132 133 for (i = 0; i < num; i++) { 134 free(eps[i].uaddr); 135 free(eps[i].proto); 136 free(eps[i].family); 137 } 138 return; 139 } 140 141 /* 142 * get_server() 143 * 144 * This function constructs a nis_server structure description for the 145 * indicated hostname. 146 * 147 * NOTE: There is a chance we may end up recursing here due to the 148 * fact that gethostbyname() could do an NIS search. Ideally, the 149 * NIS+ server will call __rpc_get_time_offset() with the nis_server 150 * structure already populated. 151 */ 152 static nis_server * 153 get_server(sin, host, srv, eps, maxep) 154 struct sockaddr_in *sin; 155 char *host; /* name of the time host */ 156 nis_server *srv; /* nis_server struct to use. */ 157 endpoint eps[]; /* array of endpoints */ 158 int maxep; /* max array size */ 159 { 160 char hname[256]; 161 int num_ep = 0, i; 162 struct hostent *he; 163 struct hostent dummy; 164 char *ptr[2]; 165 166 if (host == NULL && sin == NULL) 167 return (NULL); 168 169 if (sin == NULL) { 170 he = gethostbyname(host); 171 if (he == NULL) 172 return(NULL); 173 } else { 174 he = &dummy; 175 ptr[0] = (char *)&sin->sin_addr.s_addr; 176 ptr[1] = NULL; 177 dummy.h_addr_list = ptr; 178 } 179 180 /* 181 * This is lame. We go around once for TCP, then again 182 * for UDP. 183 */ 184 for (i = 0; (he->h_addr_list[i] != NULL) && (num_ep < maxep); 185 i++, num_ep++) { 186 struct in_addr *a; 187 188 a = (struct in_addr *)he->h_addr_list[i]; 189 snprintf(hname, sizeof(hname), "%s.0.111", inet_ntoa(*a)); 190 eps[num_ep].uaddr = strdup(hname); 191 eps[num_ep].family = strdup("inet"); 192 eps[num_ep].proto = strdup("tcp"); 193 } 194 195 for (i = 0; (he->h_addr_list[i] != NULL) && (num_ep < maxep); 196 i++, num_ep++) { 197 struct in_addr *a; 198 199 a = (struct in_addr *)he->h_addr_list[i]; 200 snprintf(hname, sizeof(hname), "%s.0.111", inet_ntoa(*a)); 201 eps[num_ep].uaddr = strdup(hname); 202 eps[num_ep].family = strdup("inet"); 203 eps[num_ep].proto = strdup("udp"); 204 } 205 206 srv->name = (nis_name) host; 207 srv->ep.ep_len = num_ep; 208 srv->ep.ep_val = eps; 209 srv->key_type = NIS_PK_NONE; 210 srv->pkey.n_bytes = NULL; 211 srv->pkey.n_len = 0; 212 return (srv); 213 } 214 215 /* 216 * __rpc_get_time_offset() 217 * 218 * This function uses a nis_server structure to contact the a remote 219 * machine (as named in that structure) and returns the offset in time 220 * between that machine and this one. This offset is returned in seconds 221 * and may be positive or negative. 222 * 223 * The first time through, a lot of fiddling is done with the netconfig 224 * stuff to find a suitable transport. The function is very aggressive 225 * about choosing UDP or at worst TCP if it can. This is because 226 * those transports support both the RCPBIND call and the internet 227 * time service. 228 * 229 * Once through, *uaddr is set to the universal address of 230 * the machine and *netid is set to the local netid for the transport 231 * that uaddr goes with. On the second call, the netconfig stuff 232 * is skipped and the uaddr/netid pair are used to fetch the netconfig 233 * structure and to then contact the machine for the time. 234 * 235 * td = "server" - "client" 236 */ 237 int 238 __rpc_get_time_offset(td, srv, thost, uaddr, netid) 239 struct timeval *td; /* Time difference */ 240 nis_server *srv; /* NIS Server description */ 241 char *thost; /* if no server, this is the timehost */ 242 char **uaddr; /* known universal address */ 243 struct sockaddr_in *netid; /* known network identifier */ 244 { 245 CLIENT *clnt; /* Client handle */ 246 endpoint *ep, /* useful endpoints */ 247 *useep = NULL; /* endpoint of xp */ 248 char *useua = NULL; /* uaddr of selected xp */ 249 int epl, i; /* counters */ 250 enum clnt_stat status; /* result of clnt_call */ 251 u_long thetime, delta; 252 int needfree = 0; 253 struct timeval tv; 254 int time_valid; 255 int udp_ep = -1, tcp_ep = -1; 256 int a1, a2, a3, a4; 257 char ut[64], ipuaddr[64]; 258 endpoint teps[32]; 259 nis_server tsrv; 260 void (*oldsig)() = NULL; /* old alarm handler */ 261 struct sockaddr_in sin; 262 int s = RPC_ANYSOCK, len; 263 int type = 0; 264 265 td->tv_sec = 0; 266 td->tv_usec = 0; 267 268 /* 269 * First check to see if we need to find and address for this 270 * server. 271 */ 272 if (*uaddr == NULL) { 273 if ((srv != NULL) && (thost != NULL)) { 274 msg("both timehost and srv pointer used!"); 275 return (0); 276 } 277 if (! srv) { 278 srv = get_server(netid, thost, &tsrv, teps, 32); 279 if (srv == NULL) { 280 msg("unable to contruct server data."); 281 return (0); 282 } 283 needfree = 1; /* need to free data in endpoints */ 284 } 285 286 ep = srv->ep.ep_val; 287 epl = srv->ep.ep_len; 288 289 /* Identify the TCP and UDP endpoints */ 290 for (i = 0; 291 (i < epl) && ((udp_ep == -1) || (tcp_ep == -1)); i++) { 292 if (strcasecmp(ep[i].proto, "udp") == 0) 293 udp_ep = i; 294 if (strcasecmp(ep[i].proto, "tcp") == 0) 295 tcp_ep = i; 296 } 297 298 /* Check to see if it is UDP or TCP */ 299 if (tcp_ep > -1) { 300 useep = &ep[tcp_ep]; 301 useua = ep[tcp_ep].uaddr; 302 type = SOCK_STREAM; 303 } else if (udp_ep > -1) { 304 useep = &ep[udp_ep]; 305 useua = ep[udp_ep].uaddr; 306 type = SOCK_DGRAM; 307 } 308 309 if (useep == NULL) { 310 msg("no acceptable transport endpoints."); 311 if (needfree) 312 free_eps(teps, tsrv.ep.ep_len); 313 return (0); 314 } 315 } 316 317 /* 318 * Create a sockaddr from the uaddr. 319 */ 320 if (*uaddr != NULL) 321 useua = *uaddr; 322 323 /* Fixup test for NIS+ */ 324 sscanf(useua, "%d.%d.%d.%d.", &a1, &a2, &a3, &a4); 325 sprintf(ipuaddr, "%d.%d.%d.%d.0.111", a1, a2, a3, a4); 326 useua = &ipuaddr[0]; 327 328 bzero((char *)&sin, sizeof(sin)); 329 if (uaddr_to_sockaddr(useua, &sin)) { 330 msg("unable to translate uaddr to sockaddr."); 331 if (needfree) 332 free_eps(teps, tsrv.ep.ep_len); 333 return (0); 334 } 335 336 /* 337 * Create the client handle to rpcbind. Note we always try 338 * version 3 since that is the earliest version that supports 339 * the RPCB_GETTIME call. Also it is the version that comes 340 * standard with SVR4. Since most everyone supports TCP/IP 341 * we could consider trying the rtime call first. 342 */ 343 clnt = clnttcp_create(&sin, RPCBPROG, RPCBVERS, &s, 0, 0); 344 if (clnt == NULL) { 345 msg("unable to create client handle to rpcbind."); 346 if (needfree) 347 free_eps(teps, tsrv.ep.ep_len); 348 return (0); 349 } 350 351 tv.tv_sec = 5; 352 tv.tv_usec = 0; 353 time_valid = 0; 354 status = clnt_call(clnt, RPCBPROC_GETTIME, xdr_void, NULL, 355 xdr_u_long, (char *)&thetime, tv); 356 /* 357 * The only error we check for is anything but success. In 358 * fact we could have seen PROGMISMATCH if talking to a 4.1 359 * machine (pmap v2) or TIMEDOUT if the net was busy. 360 */ 361 if (status == RPC_SUCCESS) 362 time_valid = 1; 363 else { 364 int save; 365 366 /* Blow away possible stale CLNT handle. */ 367 if (clnt != NULL) { 368 clnt_destroy(clnt); 369 clnt = NULL; 370 } 371 372 /* 373 * Convert PMAP address into timeservice address 374 * We take advantage of the fact that we "know" what 375 * the universal address looks like for inet transports. 376 * 377 * We also know that the internet timeservice is always 378 * listening on port 37. 379 */ 380 sscanf(useua, "%d.%d.%d.%d.", &a1, &a2, &a3, &a4); 381 sprintf(ut, "%d.%d.%d.%d.0.37", a1, a2, a3, a4); 382 383 if (uaddr_to_sockaddr(ut, &sin)) { 384 msg("cannot convert timeservice uaddr to sockaddr."); 385 goto error; 386 } 387 388 s = socket(AF_INET, type, 0); 389 if (s == -1) { 390 msg("unable to open fd to network."); 391 goto error; 392 } 393 394 /* 395 * Now depending on whether or not we're talking to 396 * UDP we set a timeout or not. 397 */ 398 if (type == SOCK_DGRAM) { 399 struct timeval timeout = { 20, 0 }; 400 struct sockaddr_in from; 401 fd_set readfds; 402 int res; 403 404 if (sendto(s, &thetime, sizeof(thetime), 0, 405 (struct sockaddr *)&sin, sizeof(sin)) == -1) { 406 msg("udp : sendto failed."); 407 goto error; 408 } 409 do { 410 FD_ZERO(&readfds); 411 FD_SET(s, &readfds); 412 res = select(_rpc_dtablesize(), &readfds, 413 (fd_set *)NULL, (fd_set *)NULL, &timeout); 414 } while (res < 0 && errno == EINTR); 415 if (res <= 0) 416 goto error; 417 len = sizeof(from); 418 res = recvfrom(s, (char *)&thetime, sizeof(thetime), 0, 419 (struct sockaddr *)&from, &len); 420 if (res == -1) { 421 msg("recvfrom failed on udp transport."); 422 goto error; 423 } 424 time_valid = 1; 425 } else { 426 int res; 427 428 oldsig = (void (*)())signal(SIGALRM, alarm_hndler); 429 saw_alarm = 0; /* global tracking the alarm */ 430 alarm(20); /* only wait 20 seconds */ 431 res = connect(s, (struct sockaddr *)&sin, sizeof(sin)); 432 if (res == -1) { 433 msg("failed to connect to tcp endpoint."); 434 goto error; 435 } 436 if (saw_alarm) { 437 msg("alarm caught it, must be unreachable."); 438 goto error; 439 } 440 res = _read(s, (char *)&thetime, sizeof(thetime)); 441 if (res != sizeof(thetime)) { 442 if (saw_alarm) 443 msg("timed out TCP call."); 444 else 445 msg("wrong size of results returned"); 446 447 goto error; 448 } 449 time_valid = 1; 450 } 451 save = errno; 452 (void)_close(s); 453 errno = save; 454 s = RPC_ANYSOCK; 455 456 if (time_valid) { 457 thetime = ntohl(thetime); 458 thetime = thetime - TOFFSET; /* adjust to UNIX time */ 459 } else 460 thetime = 0; 461 } 462 463 gettimeofday(&tv, 0); 464 465 error: 466 /* 467 * clean up our allocated data structures. 468 */ 469 470 if (s != RPC_ANYSOCK) 471 (void)_close(s); 472 473 if (clnt != NULL) 474 clnt_destroy(clnt); 475 476 alarm(0); /* reset that alarm if its outstanding */ 477 if (oldsig) { 478 signal(SIGALRM, oldsig); 479 } 480 481 /* 482 * note, don't free uaddr strings until after we've made a 483 * copy of them. 484 */ 485 if (time_valid) { 486 if (*uaddr == NULL) 487 *uaddr = strdup(useua); 488 489 /* Round to the nearest second */ 490 tv.tv_sec += (tv.tv_sec > 500000) ? 1 : 0; 491 delta = (thetime > tv.tv_sec) ? thetime - tv.tv_sec : 492 tv.tv_sec - thetime; 493 td->tv_sec = (thetime < tv.tv_sec) ? - delta : delta; 494 td->tv_usec = 0; 495 } else { 496 msg("unable to get the server's time."); 497 } 498 499 if (needfree) 500 free_eps(teps, tsrv.ep.ep_len); 501 502 return (time_valid); 503 } 504