]> git.neil.brown.name Git - portmap.git/blob - portmap.c
Release version 6.0
[portmap.git] / portmap.c
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1990 The Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39
40 /*
41 @(#)portmap.c   2.3 88/08/11 4.0 RPCSRC
42 static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro";
43 */
44
45 /*
46  * portmap.c, Implements the program,version to port number mapping for
47  * rpc.
48  */
49
50 /*
51  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
52  * unrestricted use provided that this legend is included on all tape
53  * media and as a part of the software program in whole or part.  Users
54  * may copy or modify Sun RPC without charge, but are not authorized
55  * to license or distribute it to anyone else except as part of a product or
56  * program developed by the user.
57  * 
58  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
59  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
60  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
61  * 
62  * Sun RPC is provided with no support and without any obligation on the
63  * part of Sun Microsystems, Inc. to assist in its use, correction,
64  * modification or enhancement.
65  * 
66  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
67  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
68  * OR ANY PART THEREOF.
69  * 
70  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
71  * or profits or other special, indirect and consequential damages, even if
72  * Sun has been advised of the possibility of such damages.
73  * 
74  * Sun Microsystems, Inc.
75  * 2550 Garcia Avenue
76  * Mountain View, California  94043
77  */
78
79 #include <rpc/rpc.h>
80 #include <rpc/pmap_prot.h>
81 #include <stdio.h>
82 #include <unistd.h>
83 #include <syslog.h>
84 #include <netdb.h>
85 #include <errno.h>
86 #include <fcntl.h>
87 #include <sys/socket.h>
88 #include <sys/ioctl.h>
89 #include <sys/wait.h>
90 #include <sys/signal.h>
91 #include <sys/time.h>
92 #include <sys/resource.h>
93 #include <sys/types.h>
94 #ifdef SYSV40
95 #include <netinet/in.h>
96 #endif
97 #include <arpa/inet.h>
98
99 #include <stdlib.h>
100 #include <pwd.h>
101
102 #ifndef LOG_PERROR
103 #define LOG_PERROR 0
104 #endif
105
106 #ifndef LOG_DAEMON
107 #define LOG_DAEMON 0
108 #endif
109
110 /* Older SYSV. */
111 #if !defined(SIGCHLD) && defined(SIGCLD)
112 #define SIGCHLD      SIGCLD
113 #endif
114
115 #ifndef svc_getcaller           /* SYSV4 */
116 #  define svc_getcaller svc_getrpccaller
117 #endif
118
119 static void reg_service(struct svc_req *rqstp, SVCXPRT *xprt);
120 #ifndef IGNORE_SIGCHLD                  /* Lionel Cons <cons@dxcern.cern.ch> */
121 static void reap(int);
122 #endif
123 static void callit(struct svc_req *rqstp, SVCXPRT *xprt);
124 struct pmaplist *pmaplist;
125 int debugging = 0;
126 int store_fd = -1;
127 static void dump_table(void);
128 static void load_table(void);
129
130 #include "pmap_check.h"
131
132  /*
133   * How desperate can one be. It is possible to prevent an attacker from
134   * manipulating your portmapper tables from outside with requests that
135   * contain spoofed source address information. The countermeasure is to
136   * force all rpc servers to register and unregister with the portmapper via
137   * the loopback network interface, instead of via the primary network
138   * interface that every host can talk to. For this countermeasure to work it
139   * is necessary to #define LOOPBACK_SETUNSET, to disable source routing in
140   * the kernel, and to modify libc so that get_myaddress() chooses the
141   * loopback interface address.
142   */
143
144 #ifdef LOOPBACK_SETUNSET
145 static SVCXPRT *ludpxprt, *ltcpxprt;
146 static int on = 1;
147 #ifndef INADDR_LOOPBACK
148 #define INADDR_LOOPBACK ntohl(inet_addr("127.0.0.1"))
149 #endif
150 #endif
151
152 #ifdef DAEMON_UID
153 int daemon_uid = DAEMON_UID;
154 int daemon_gid = DAEMON_GID;
155 #else
156 int daemon_uid = 1;
157 int daemon_gid = 1;
158 #endif
159
160 /*
161  * We record with each registration a flag telling whether it was
162  * registered with a privilege port or not.
163  * If it was, it can only be unregistered with a privileged port.
164  * So that we can still use standard pmap xdr routines, we store
165  * this flag in a structure wrapped around a pmaplist.
166  */
167 struct flagged_pml {
168         struct pmaplist pml;
169         int priv;
170 };
171
172 int
173 main(int argc, char **argv)
174 {
175         SVCXPRT *xprt;
176         int sock, c;
177         struct sockaddr_in addr;
178         int len = sizeof(struct sockaddr_in);
179         struct pmaplist *pml;
180         struct flagged_pml *fpml;
181         char *chroot_path = NULL;
182         struct in_addr bindaddr;
183         int have_bindaddr = 0;
184         int foreground = 0;
185         int have_uid = 0;
186
187         while ((c = getopt(argc, argv, "Vdflt:vi:u:g:")) != EOF) {
188                 switch (c) {
189
190                 case 'V':
191                         printf("portmap version 6.0 - 2007-May-11\n");
192                         exit(1);
193
194                 case 'u':
195                         daemon_uid = atoi(optarg);
196                         if (daemon_uid <= 0) {
197                                 fprintf(stderr,
198                                         "portmap: illegal uid: %s\n", optarg);
199                                 exit(1);
200                         }
201                         have_uid = 1;
202                         break;
203                 case 'g':
204                         daemon_gid = atoi(optarg);
205                         if (daemon_gid <= 0) {
206                                 fprintf(stderr,
207                                         "portmap: illegal gid: %s\n", optarg);
208                                 exit(1);
209                         }
210                         have_uid = 1;
211                         break;
212                 case 'd':
213                         debugging = 1;
214                 case 'f':
215                         foreground = 1;
216                         break;
217
218                 case 't':
219                         chroot_path = optarg;
220                         break;
221
222                 case 'v':
223                         verboselog = 1;
224                         break;
225
226                 case 'l':
227                         optarg = "127.0.0.1";
228                         /* FALL THROUGH */
229                 case 'i':
230                         have_bindaddr = inet_aton(optarg, &bindaddr);
231                         break;
232                 default:
233                         fprintf(stderr,
234                                 "usage: %s [-dflv] [-t dir] [-i address] "
235                                 "[-u uid] [-g gid]\n",
236                                 argv[0]);
237                         fprintf(stderr, "-d: debugging mode\n");
238                         fprintf(stderr,
239                                 "-f: don't daemonize, log to standard error\n");
240                         fprintf(stderr, "-t dir: chroot into dir\n");
241                         fprintf(stderr, "-v: verbose logging\n");
242                         fprintf(stderr, "-i address: bind to address\n");
243                         fprintf(stderr, "-l: same as -i 127.0.0.1\n");
244                         fprintf(stderr, "-u uid : setuid to this uid\n");
245                         fprintf(stderr, "-g uid : setgid to this gid\n");
246                         exit(1);
247                 }
248         }
249
250         if (!foreground && daemon(0, 0)) {
251                 (void) fprintf(stderr, "portmap: fork: %s", strerror(errno));
252                 exit(1);
253         }
254
255 #ifdef LOG_DAEMON
256         openlog("portmap", LOG_PID|LOG_NDELAY | ( foreground ? LOG_PERROR : 0),
257             FACILITY);
258 #else
259         openlog("portmap", LOG_PID|LOG_NDELAY | ( foreground ? LOG_PERROR : 0));
260 #endif
261
262 #ifdef RPCUSER
263         if (!have_uid) {
264                 struct passwd *pwent;
265                 pwent = getpwnam(RPCUSER);
266                 if (pwent) {
267                         daemon_uid = pwent->pw_uid;
268                         daemon_gid = pwent->pw_gid;
269                 } else
270                         syslog(LOG_WARNING, "user '" RPCUSER
271                                "' not found, reverting to default uid");
272         }
273 #endif
274
275         if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
276                 syslog(LOG_ERR, "cannot create udp socket: %m");
277                 exit(1);
278         }
279 #ifdef LOOPBACK_SETUNSET
280         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
281 #endif
282
283         memset((char *) &addr, 0, sizeof(addr));
284         addr.sin_addr.s_addr = 0;
285         addr.sin_family = AF_INET;
286         addr.sin_port = htons(PMAPPORT);
287         if (have_bindaddr)
288                 memcpy(&addr.sin_addr, &bindaddr, sizeof(bindaddr));
289
290         if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
291                 syslog(LOG_ERR, "cannot bind udp: %m");
292                 exit(1);
293         }
294
295         if ((xprt = svcudp_create(sock)) == (SVCXPRT *)NULL) {
296                 syslog(LOG_ERR, "couldn't do udp_create");
297                 exit(1);
298         }
299         /* make an entry for ourself */
300         fpml = malloc(sizeof(struct flagged_pml));
301         pml = &fpml->pml;
302         fpml->priv = 1;
303         pml->pml_next = NULL;
304         pml->pml_map.pm_prog = PMAPPROG;
305         pml->pml_map.pm_vers = PMAPVERS;
306         pml->pml_map.pm_prot = IPPROTO_UDP;
307         pml->pml_map.pm_port = PMAPPORT;
308         pmaplist = pml;
309
310         if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
311                 syslog(LOG_ERR, "cannot create tcp socket: %m");
312                 exit(1);
313         }
314 #ifdef LOOPBACK_SETUNSET
315         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
316 #endif
317         if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
318                 syslog(LOG_ERR, "cannot bind tcp: %m");
319                 exit(1);
320         }
321         if ((xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE))
322             == (SVCXPRT *)NULL) {
323                 syslog(LOG_ERR, "couldn't do tcp_create");
324                 exit(1);
325         }
326         /* make an entry for ourself */
327         fpml = malloc(sizeof(struct flagged_pml));
328         pml = &fpml->pml;
329         fpml->priv = 1;
330         pml->pml_map.pm_prog = PMAPPROG;
331         pml->pml_map.pm_vers = PMAPVERS;
332         pml->pml_map.pm_prot = IPPROTO_TCP;
333         pml->pml_map.pm_port = PMAPPORT;
334         pml->pml_next = pmaplist;
335         pmaplist = pml;
336
337 #ifdef LOOPBACK_SETUNSET
338         if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
339                 syslog(LOG_ERR, "cannot create udp socket: %m");
340                 exit(1);
341         }
342         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
343
344         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
345         if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
346                 syslog(LOG_ERR, "cannot bind udp: %m");
347                 exit(1);
348         }
349
350         if ((ludpxprt = svcudp_create(sock)) == (SVCXPRT *)NULL) {
351                 syslog(LOG_ERR, "couldn't do udp_create");
352                 exit(1);
353         }
354         if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
355                 syslog(LOG_ERR, "cannot create tcp socket: %m");
356                 exit(1);
357         }
358         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
359         if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
360                 syslog(LOG_ERR, "cannot bind tcp: %m");
361                 exit(1);
362         }
363         if ((ltcpxprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE))
364             == (SVCXPRT *)NULL) {
365                 syslog(LOG_ERR, "couldn't do tcp_create");
366                 exit(1);
367         }
368 #endif
369
370         (void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE);
371
372         store_fd = open("/var/run/portmap_mapping", O_RDWR|O_CREAT, 0600);
373         load_table();
374
375         /* additional initializations */
376         if (chroot_path) {
377                 if (chroot(chroot_path) < 0) {
378                         syslog(LOG_ERR, "couldn't do chroot");
379                         exit(1);
380                 }
381         }
382         check_startup();
383 #ifdef IGNORE_SIGCHLD                   /* Lionel Cons <cons@dxcern.cern.ch> */
384         (void)signal(SIGCHLD, SIG_IGN);
385 #else
386         (void)signal(SIGCHLD, reap);
387 #endif
388         (void)signal(SIGPIPE, SIG_IGN);
389         svc_run();
390         syslog(LOG_ERR, "run_svc returned unexpectedly");
391         abort();
392 }
393
394 /* need to override perror calls in rpc library */
395 void perror(const char *what)
396 {
397
398         syslog(LOG_ERR, "%s: %m", what);
399 }
400
401 static struct pmaplist *
402 find_service(u_long prog, u_long vers, u_long prot)
403 {
404         struct pmaplist *hit = NULL;
405         struct pmaplist *pml;
406
407         for (pml = pmaplist; pml != NULL; pml = pml->pml_next) {
408                 if ((pml->pml_map.pm_prog != prog) ||
409                         (pml->pml_map.pm_prot != prot))
410                         continue;
411                 hit = pml;
412                 if (pml->pml_map.pm_vers == vers)
413                     break;
414         }
415         return (hit);
416 }
417
418 /* 
419  * 1 OK, 0 not
420  */
421 static void reg_service(struct svc_req *rqstp, SVCXPRT *xprt)
422 {
423         struct pmap reg;
424         struct pmaplist *pml, *prevpml, *fnd;
425         struct flagged_pml *fpml;
426         int ans, port;
427         caddr_t t;
428         
429         /*
430          * Later wrappers change the logging severity on the fly. Reset to
431          * defaults before handling the next request.
432          */
433         allow_severity = LOG_INFO;
434         deny_severity = LOG_WARNING;
435
436         if (debugging)
437                 (void) fprintf(stderr, "server: about do a switch\n");
438         switch (rqstp->rq_proc) {
439
440         case PMAPPROC_NULL:
441                 /*
442                  * Null proc call
443                  */
444                 /* remote host authorization check */
445                 check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
446                 if (!svc_sendreply(xprt, (xdrproc_t) xdr_void, (caddr_t)0)
447                     && debugging) {
448                         abort();
449                 }
450                 break;
451
452         case PMAPPROC_SET:
453                 /*
454                  * Set a program,version to port mapping
455                  */
456                 if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (caddr_t)&reg))
457                         svcerr_decode(xprt);
458                 else {
459                         /* reject non-local requests, protect priv. ports */
460                         if (!CHECK_SETUNSET(xprt, ludpxprt, ltcpxprt,
461                             rqstp->rq_proc, reg.pm_prog, reg.pm_port)) {
462                                 ans = 0;
463                                 goto done;
464                         } 
465                         /*
466                          * check to see if already used
467                          * find_service returns a hit even if
468                          * the versions don't match, so check for it
469                          */
470                         fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
471                         if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) {
472                                 if (fnd->pml_map.pm_port == reg.pm_port) {
473                                         ans = 1;
474                                         goto done;
475                                 }
476                                 else {
477                                         ans = 0;
478                                         goto done;
479                                 }
480                         } else {
481                                 /* 
482                                  * add to END of list
483                                  */
484                                 fpml = (struct flagged_pml *)
485                                     malloc((u_int)sizeof(struct flagged_pml));
486                                 pml = &fpml->pml;
487                                 fpml->priv =
488                                         (ntohs(svc_getcaller(xprt)->sin_port)
489                                          < IPPORT_RESERVED);
490                                 pml->pml_map = reg;
491                                 pml->pml_next = 0;
492
493                                 if (pmaplist == 0) {
494                                         pmaplist = pml;
495                                 } else {
496                                         for (fnd= pmaplist; fnd->pml_next != 0;
497                                             fnd = fnd->pml_next);
498                                         fnd->pml_next = pml;
499                                 }
500                                 ans = 1;
501                                 dump_table();
502                         }
503                 done:
504                         if ((!svc_sendreply(xprt, (xdrproc_t)xdr_int,
505                                             (caddr_t)&ans)) &&
506                             debugging) {
507                                 (void) fprintf(stderr, "svc_sendreply\n");
508                                 abort();
509                         }
510                 }
511                 break;
512
513         case PMAPPROC_UNSET:
514                 /*
515                  * Remove a program,version to port mapping.
516                  */
517                 if (!svc_getargs(xprt, (xdrproc_t)xdr_pmap, (caddr_t)&reg))
518                         svcerr_decode(xprt);
519                 else {
520                         ans = 0;
521                         /* reject non-local requests */
522                         if (!CHECK_SETUNSET(xprt, ludpxprt, ltcpxprt,
523                             rqstp->rq_proc, reg.pm_prog, (u_long) 0))
524                                 goto done;
525                         for (prevpml = NULL, pml = pmaplist; pml != NULL; ) {
526                                 if ((pml->pml_map.pm_prog != reg.pm_prog) ||
527                                         (pml->pml_map.pm_vers != reg.pm_vers)) {
528                                         /* both pml & prevpml move forwards */
529                                         prevpml = pml;
530                                         pml = pml->pml_next;
531                                         continue;
532                                 }
533                                 /* found it; pml moves forward, prevpml stays */
534                                 /* privileged port check */
535                                 if (!check_privileged_port(svc_getcaller(xprt), 
536                                     rqstp->rq_proc, 
537                                     reg.pm_prog, 
538                                     pml->pml_map.pm_port)) {
539                                         ans = 0;
540                                         break;
541                                 }
542                                 fpml = (struct flagged_pml*)pml;
543                                 if (fpml->priv &&
544                                     (ntohs(svc_getcaller(xprt)->sin_port)
545                                      >= IPPORT_RESERVED)) {
546                                         ans = 0;
547                                         break;
548                                 }
549
550                                 ans = 1;
551                                 t = (caddr_t)pml;
552                                 pml = pml->pml_next;
553                                 if (prevpml == NULL)
554                                         pmaplist = pml;
555                                 else
556                                         prevpml->pml_next = pml;
557                                 free(t);
558                                 dump_table();
559                         }
560                         if ((!svc_sendreply(xprt, (xdrproc_t)xdr_int,
561                                             (caddr_t)&ans)) &&
562                             debugging) {
563                                 (void) fprintf(stderr, "svc_sendreply\n");
564                                 abort();
565                         }
566                 }
567                 break;
568
569         case PMAPPROC_GETPORT:
570                 /*
571                  * Lookup the mapping for a program,version and return its port
572                  */
573                 if (!svc_getargs(xprt, (xdrproc_t)xdr_pmap, (caddr_t)&reg))
574                         svcerr_decode(xprt);
575                 else {
576                         /* remote host authorization check */
577                         if (!check_default(svc_getcaller(xprt), 
578                             rqstp->rq_proc, 
579                             reg.pm_prog)) {
580                                 ans = 0;
581                                 goto done;
582                         }
583                         fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
584                         if (fnd)
585                                 port = fnd->pml_map.pm_port;
586                         else
587                                 port = 0;
588                         if ((!svc_sendreply(xprt, (xdrproc_t)xdr_int,
589                                             (caddr_t)&port)) &&
590                             debugging) {
591                                 (void) fprintf(stderr, "svc_sendreply\n");
592                                 abort();
593                         }
594                 }
595                 break;
596
597         case PMAPPROC_DUMP:
598                 /*
599                  * Return the current set of mapped program,version
600                  */
601                 if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL))
602                         svcerr_decode(xprt);
603                 else {
604                         /* remote host authorization check */
605                         struct pmaplist *p;
606                         if (!check_default(svc_getcaller(xprt), 
607                             rqstp->rq_proc, (u_long) 0)) {
608                                 p = 0;  /* send empty list */
609                         } else {
610                                 p = pmaplist;
611                         }
612                         if ((!svc_sendreply(xprt, (xdrproc_t)xdr_pmaplist,
613                             (caddr_t)&p)) && debugging) {
614                                 (void) fprintf(stderr, "svc_sendreply\n");
615                                 abort();
616                         }
617                 }
618                 break;
619
620         case PMAPPROC_CALLIT:
621                 /*
622                  * Calls a procedure on the local machine.  If the requested
623                  * procedure is not registered this procedure does not return
624                  * error information!!
625                  * This procedure is only supported on rpc/udp and calls via 
626                  * rpc/udp.  It passes null authentication parameters.
627                  */
628                 callit(rqstp, xprt);
629                 break;
630
631         default:
632                 /* remote host authorization check */
633                 check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
634                 svcerr_noproc(xprt);
635                 break;
636         }
637 }
638
639
640 /*
641  * Stuff for the rmtcall service
642  */
643 #define ARGSIZE 9000
644
645 struct encap_parms {
646         u_int arglen;
647         char *args;
648 };
649
650 static bool_t
651 xdr_encap_parms(XDR *xdrs, struct encap_parms *epp)
652 {
653
654         return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE));
655 }
656
657 struct rmtcallargs {
658         u_long  rmt_prog;
659         u_long  rmt_vers;
660         u_long  rmt_port;
661         u_long  rmt_proc;
662         struct encap_parms rmt_args;
663 };
664
665 static bool_t
666 xdr_rmtcall_args(XDR *xdrs, struct rmtcallargs *cap)
667 {
668
669         /* does not get a port number */
670         if (xdr_u_long(xdrs, &(cap->rmt_prog)) &&
671             xdr_u_long(xdrs, &(cap->rmt_vers)) &&
672             xdr_u_long(xdrs, &(cap->rmt_proc))) {
673                 return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
674         }
675         return (FALSE);
676 }
677
678 static bool_t
679 xdr_rmtcall_result(XDR *xdrs, struct rmtcallargs *cap)
680 {
681         if (xdr_u_long(xdrs, &(cap->rmt_port)))
682                 return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
683         return (FALSE);
684 }
685
686 /*
687  * only worries about the struct encap_parms part of struct rmtcallargs.
688  * The arglen must already be set!!
689  */
690 static bool_t
691 xdr_opaque_parms(XDR *xdrs, struct rmtcallargs *cap)
692 {
693
694         return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
695 }
696
697 /*
698  * This routine finds and sets the length of incoming opaque paraters
699  * and then calls xdr_opaque_parms.
700  */
701 static bool_t
702 xdr_len_opaque_parms(XDR *xdrs, struct rmtcallargs *cap)
703 {
704         u_int beginpos, lowpos, highpos, currpos, pos;
705
706         beginpos = lowpos = pos = xdr_getpos(xdrs);
707         highpos = lowpos + ARGSIZE;
708         while ((int)(highpos - lowpos) >= 0) {
709                 currpos = (lowpos + highpos) / 2;
710                 if (xdr_setpos(xdrs, currpos)) {
711                         pos = currpos;
712                         lowpos = currpos + 1;
713                 } else {
714                         highpos = currpos - 1;
715                 }
716         }
717         xdr_setpos(xdrs, beginpos);
718         cap->rmt_args.arglen = pos - beginpos;
719         return (xdr_opaque_parms(xdrs, cap));
720 }
721
722 /*
723  * Call a remote procedure service
724  * This procedure is very quiet when things go wrong.
725  * The proc is written to support broadcast rpc.  In the broadcast case,
726  * a machine should shut-up instead of complain, less the requestor be
727  * overrun with complaints at the expense of not hearing a valid reply ...
728  *
729  * This now forks so that the program & process that it calls can call 
730  * back to the portmapper.
731  */
732 static void callit(struct svc_req *rqstp, SVCXPRT *xprt)
733 {
734         struct rmtcallargs a;
735         struct pmaplist *pml;
736         u_short port;
737         struct sockaddr_in me;
738         int pid, so = -1;
739         CLIENT *client;
740         struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred;
741         struct timeval timeout;
742         char buf[ARGSIZE];
743
744         timeout.tv_sec = 5;
745         timeout.tv_usec = 0;
746         a.rmt_args.args = buf;
747         if (!svc_getargs(xprt, (xdrproc_t)xdr_rmtcall_args, (caddr_t)&a))
748                 return;
749         /* host and service access control */
750         if (!check_callit(svc_getcaller(xprt), 
751             rqstp->rq_proc, a.rmt_prog, a.rmt_proc))
752                 return;
753         if ((pml = find_service(a.rmt_prog, a.rmt_vers,
754             (u_long)IPPROTO_UDP)) == NULL)
755                 return;
756         /*
757          * fork a child to do the work.  Parent immediately returns.
758          * Child exits upon completion.
759          */
760         if ((pid = fork()) != 0) {
761                 if (pid < 0)
762                         syslog(LOG_ERR, "CALLIT (prog %lu): fork: %m",
763                             a.rmt_prog);
764                 return;
765         }
766         port = pml->pml_map.pm_port;
767         get_myaddress(&me);
768         me.sin_port = htons(port);
769         client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &so);
770         if (client != (CLIENT *)NULL) {
771                 if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
772                         client->cl_auth = authunix_create(au->aup_machname,
773                            au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids);
774                 }
775                 a.rmt_port = (u_long)port;
776                 if (clnt_call(client, a.rmt_proc, (xdrproc_t)xdr_opaque_parms,
777                               (caddr_t)&a, (xdrproc_t)xdr_len_opaque_parms,
778                               (caddr_t)&a, timeout) == RPC_SUCCESS) {
779                         svc_sendreply(xprt, (xdrproc_t)xdr_rmtcall_result,
780                                       (caddr_t)&a);
781                 }
782                 AUTH_DESTROY(client->cl_auth);
783                 clnt_destroy(client);
784         }
785         (void)close(so);
786         exit(0);
787 }
788
789 #ifndef IGNORE_SIGCHLD                  /* Lionel Cons <cons@dxcern.cern.ch> */
790 static void reap(int ignore)
791 {
792         int save_errno = errno;
793         while (wait3((int *)NULL, WNOHANG, (struct rusage *)NULL) > 0);
794         errno = save_errno;
795 }
796 #endif
797
798 /* Dump and restore mapping table so that we can survive kill/restart.
799  * To cope with chroot, an fd is opened early and we just write to that.
800  * If we are killed while writing the file, we lose, but that isn't
801  * very likely...
802  */
803
804 static void dump_table(void)
805 {
806         FILE *f;
807         struct pmaplist *pml;
808
809         if (store_fd < 0)
810                 return;
811         ftruncate(store_fd, 0);
812         lseek(store_fd, 0, 0);
813         f = fdopen(dup(store_fd), "w");
814         if (!f)
815                 return;
816
817         for (pml = pmaplist ; pml ; pml = pml->pml_next) {
818                 struct flagged_pml *fpml = (struct flagged_pml*)pml;
819
820                 fprintf(f, "%lu %lu %lu %lu %d\n",
821                         pml->pml_map.pm_prog,
822                         pml->pml_map.pm_vers,
823                         pml->pml_map.pm_prot,
824                         pml->pml_map.pm_port,
825                         fpml->priv);
826         }
827         fclose(f);
828 }
829
830 static void load_table(void)
831 {
832         FILE *f;
833         struct pmaplist **ep;
834         struct flagged_pml fpml, *fpmlp;
835
836         ep = &pmaplist;
837         while ((*ep)->pml_next)
838                 ep = & (*ep)->pml_next;
839
840         if (store_fd < 0)
841                 return;
842         lseek(store_fd, 0, 0);
843         f = fdopen(dup(store_fd), "r");
844         if (f == NULL)
845                 return;
846
847         while (fscanf(f, "%lu %lu %lu %lu %d\n",
848                       &fpml.pml.pml_map.pm_prog,
849                       &fpml.pml.pml_map.pm_vers,
850                       &fpml.pml.pml_map.pm_prot,
851                       &fpml.pml.pml_map.pm_port,
852                       &fpml.priv) == 5) {
853                 if (fpml.pml.pml_map.pm_port == PMAPPORT)
854                         continue;
855                 fpmlp = malloc(sizeof(struct flagged_pml));
856                 if (!fpmlp)
857                         break;
858                 *fpmlp = fpml;
859                 *ep = &fpmlp->pml;
860                 ep = &fpmlp->pml.pml_next;
861                 *ep = NULL;
862         }
863         fclose(f);
864 }