source: rtems-libbsd/freebsd/lib/libc/rpc/auth_time.c @ f41a394

55-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since f41a394 was 60b1d40, checked in by Sebastian Huber <sebastian.huber@…>, on 06/09/16 at 08:23:57

RPC(3): Import from FreeBSD

  • Property mode set to 100644
File size: 12.6 KB
Line 
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
54extern 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
63static int saw_alarm = 0;
64
65static void
66alarm_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 */
88static 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 */
122static void
123free_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 */
148static nis_server *
149get_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 */
242int
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
471error:
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}
Note: See TracBrowser for help on using the repository browser.