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: src/lib/libc/rpc/auth_time.c,v 1.4 2000/01/27 23:06:35 jasone Exp $ |
---|
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 = _RPC_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)_RPC_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)_RPC_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 | } |
---|