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