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