Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / portmap / portmap.c
1 /*-
2  * Copyright (c) 1990, 1993
3  *      The Regents of the University of California.  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 static const char copyright[] =
36 "@(#) Copyright (c) 1990, 1993\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)portmap.c   8.1 (Berkeley) 6/6/93";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD: src/usr.sbin/portmap/portmap.c,v 1.10.2.3 2002/05/06 18:18:21 dwmalone Exp $";
46 #endif /* not lint */
47
48 /*
49 @(#)portmap.c   2.3 88/08/11 4.0 RPCSRC
50 static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro";
51 */
52
53 /*
54  * portmap.c, Implements the program,version to port number mapping for
55  * rpc.
56  */
57
58 /*
59  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
60  * unrestricted use provided that this legend is included on all tape
61  * media and as a part of the software program in whole or part.  Users
62  * may copy or modify Sun RPC without charge, but are not authorized
63  * to license or distribute it to anyone else except as part of a product or
64  * program developed by the user.
65  *
66  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
67  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
68  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
69  *
70  * Sun RPC is provided with no support and without any obligation on the
71  * part of Sun Microsystems, Inc. to assist in its use, correction,
72  * modification or enhancement.
73  *
74  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
75  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
76  * OR ANY PART THEREOF.
77  *
78  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
79  * or profits or other special, indirect and consequential damages, even if
80  * Sun has been advised of the possibility of such damages.
81  *
82  * Sun Microsystems, Inc.
83  * 2550 Garcia Avenue
84  * Mountain View, California  94043
85  */
86
87 #include <err.h>
88 #include <errno.h>
89 #include <netdb.h>
90 #include <stdio.h>
91 #include <stdlib.h>
92 #include <string.h>
93 #include <syslog.h>
94 #include <unistd.h>
95 #include <rpc/rpc.h>
96 #include <rpc/pmap_prot.h>
97 #include <sys/socket.h>
98 #include <sys/ioctl.h>
99 #include <sys/wait.h>
100 #include <sys/signal.h>
101 #include <sys/resource.h>
102
103 #include "pmap_check.h"
104
105 static void reg_service __P((struct svc_req *, SVCXPRT *));
106 static void reap __P((int));
107 static void callit __P((struct svc_req *, SVCXPRT *));
108 static void usage __P((void));
109
110 struct pmaplist *pmaplist;
111 int debugging = 0;
112
113 int
114 main(argc, argv)
115         int argc;
116         char **argv;
117 {
118         SVCXPRT *xprt;
119         int sock, c;
120         char **hosts = NULL;
121         int nhosts = 0;
122         struct sockaddr_in addr;
123         int len = sizeof(struct sockaddr_in);
124         register struct pmaplist *pml;
125
126         while ((c = getopt(argc, argv, "dvh:")) != -1) {
127                 switch (c) {
128
129                 case 'd':
130                         debugging = 1;
131                         break;
132
133                 case 'v':
134                         verboselog = 1;
135                         break;
136
137                 case 'h':
138                         ++nhosts;
139                         hosts = realloc(hosts, nhosts * sizeof(char *));
140                         hosts[nhosts - 1] = optarg;
141                         break;
142
143                 default:
144                         usage();
145                 }
146         }
147
148         if (!debugging && daemon(0, 0))
149                 err(1, "fork");
150
151         openlog("portmap", debugging ? LOG_PID | LOG_PERROR : LOG_PID,
152             LOG_DAEMON);
153
154         bzero(&addr, sizeof(addr));
155         addr.sin_family = AF_INET;
156         addr.sin_port = htons(PMAPPORT);
157
158         /*
159          * If no hosts were specified, just bind to INADDR_ANY.  Otherwise
160          * make sure 127.0.0.1 is added to the list.
161          */
162         ++nhosts;
163         hosts = realloc(hosts, nhosts * sizeof(char *));
164         if (nhosts == 1)
165                 hosts[0] = "0.0.0.0";
166         else
167                 hosts[nhosts - 1] = "127.0.0.1";
168
169         /*
170          * Add UDP socket(s) - bind to specific IPs if asked to
171          */
172         while (nhosts > 0) {
173             --nhosts;
174
175             if (!inet_aton(hosts[nhosts], &addr.sin_addr)) {
176                     syslog(LOG_ERR, "bad IP address: %s", hosts[nhosts]);
177                     exit(1);
178             }
179             if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
180                     syslog(LOG_ERR, "cannot create udp socket: %m");
181                     exit(1);
182             }
183
184             if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
185                     syslog(LOG_ERR, "cannot bind udp: %m");
186                     exit(1);
187             }
188
189             if ((xprt = svcudp_create(sock)) == (SVCXPRT *)NULL) {
190                     syslog(LOG_ERR, "couldn't do udp_create");
191                     exit(1);
192             }
193         }
194         /* make an entry for ourself */
195         pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
196         pml->pml_next = 0;
197         pml->pml_map.pm_prog = PMAPPROG;
198         pml->pml_map.pm_vers = PMAPVERS;
199         pml->pml_map.pm_prot = IPPROTO_UDP;
200         pml->pml_map.pm_port = PMAPPORT;
201         pmaplist = pml;
202
203         /*
204          * Add TCP socket
205          */
206         addr.sin_addr.s_addr = 0;
207         if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
208                 syslog(LOG_ERR, "cannot create tcp socket: %m");
209                 exit(1);
210         }
211         if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
212                 syslog(LOG_ERR, "cannot bind tcp: %m");
213                 exit(1);
214         }
215         if ((xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE))
216             == (SVCXPRT *)NULL) {
217                 syslog(LOG_ERR, "couldn't do tcp_create");
218                 exit(1);
219         }
220         /* make an entry for ourself */
221         pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
222         pml->pml_map.pm_prog = PMAPPROG;
223         pml->pml_map.pm_vers = PMAPVERS;
224         pml->pml_map.pm_prot = IPPROTO_TCP;
225         pml->pml_map.pm_port = PMAPPORT;
226         pml->pml_next = pmaplist;
227         pmaplist = pml;
228
229         (void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE);
230
231         /* additional initializations */
232         check_startup();
233         (void)signal(SIGCHLD, reap);
234         svc_run();
235         syslog(LOG_ERR, "svc_run returned unexpectedly");
236         abort();
237 }
238
239 static void
240 usage()
241 {
242         fprintf(stderr, "usage: portmap [-dv] [-h bindip]\n");
243         exit(1);
244 }
245
246 #ifndef lint
247 /* need to override perror calls in rpc library */
248 void
249 perror(what)
250         const char *what;
251 {
252         syslog(LOG_ERR, "%s: %m", what);
253 }
254 #endif
255
256 static struct pmaplist *
257 find_service(prog, vers, prot)
258         u_long prog, vers, prot;
259 {
260         register struct pmaplist *hit = NULL;
261         register struct pmaplist *pml;
262
263         for (pml = pmaplist; pml != NULL; pml = pml->pml_next) {
264                 if ((pml->pml_map.pm_prog != prog) ||
265                         (pml->pml_map.pm_prot != prot))
266                         continue;
267                 hit = pml;
268                 if (pml->pml_map.pm_vers == vers)
269                     break;
270         }
271         return (hit);
272 }
273
274 /*
275  * 1 OK, 0 not
276  */
277 static void
278 reg_service(rqstp, xprt)
279         struct svc_req *rqstp;
280         SVCXPRT *xprt;
281 {
282         struct pmap reg;
283         struct pmaplist *pml, *prevpml, *fnd;
284         int ans, port;
285         caddr_t t;
286
287         /*
288          * Later wrappers change the logging severity on the fly. Reset to
289          * defaults before handling the next request.
290          */
291         allow_severity = LOG_INFO;
292         deny_severity = LOG_WARNING;
293
294         if (debugging)
295                 (void) fprintf(stderr, "server: about to do a switch\n");
296         switch (rqstp->rq_proc) {
297
298         case PMAPPROC_NULL:
299                 /*
300                  * Null proc call
301                  */
302                 /* remote host authorization check */
303                 check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
304                 if (!svc_sendreply(xprt, xdr_void, (caddr_t)0) && debugging) {
305                         abort();
306                 }
307                 break;
308
309         case PMAPPROC_SET:
310                 /*
311                  * Set a program,version to port mapping
312                  */
313                 if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
314                         svcerr_decode(xprt);
315                 else {
316                         /* reject non-local requests, protect priv. ports */
317                         if (!check_setunset(svc_getcaller(xprt),
318                             rqstp->rq_proc, reg.pm_prog, reg.pm_port)) {
319                                 ans = 0;
320                                 goto done;
321                         }
322                         /*
323                          * check to see if already used
324                          * find_service returns a hit even if
325                          * the versions don't match, so check for it
326                          */
327                         fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
328                         if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) {
329                                 if (fnd->pml_map.pm_port == reg.pm_port) {
330                                         ans = 1;
331                                         goto done;
332                                 }
333                                 else {
334                                         ans = 0;
335                                         goto done;
336                                 }
337                         } else {
338                                 /*
339                                  * add to END of list
340                                  */
341                                 pml = (struct pmaplist *)
342                                     malloc((u_int)sizeof(struct pmaplist));
343                                 pml->pml_map = reg;
344                                 pml->pml_next = 0;
345                                 if (pmaplist == 0) {
346                                         pmaplist = pml;
347                                 } else {
348                                         for (fnd= pmaplist; fnd->pml_next != 0;
349                                             fnd = fnd->pml_next);
350                                         fnd->pml_next = pml;
351                                 }
352                                 ans = 1;
353                         }
354                 done:
355                         if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
356                             debugging) {
357                                 (void) fprintf(stderr, "svc_sendreply\n");
358                                 abort();
359                         }
360                 }
361                 break;
362
363         case PMAPPROC_UNSET:
364                 /*
365                  * Remove a program,version to port mapping.
366                  */
367                 if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
368                         svcerr_decode(xprt);
369                 else {
370                         ans = 0;
371                         /* reject non-local requests */
372                         if (!check_setunset(svc_getcaller(xprt),
373                             rqstp->rq_proc, reg.pm_prog, (u_long) 0))
374                                 goto done;
375                         for (prevpml = NULL, pml = pmaplist; pml != NULL; ) {
376                                 if ((pml->pml_map.pm_prog != reg.pm_prog) ||
377                                         (pml->pml_map.pm_vers != reg.pm_vers)) {
378                                         /* both pml & prevpml move forwards */
379                                         prevpml = pml;
380                                         pml = pml->pml_next;
381                                         continue;
382                                 }
383                                 /* found it; pml moves forward, prevpml stays */
384                                 /* privileged port check */
385                                 if (!check_privileged_port(svc_getcaller(xprt),
386                                     rqstp->rq_proc,
387                                     reg.pm_prog,
388                                     pml->pml_map.pm_port)) {
389                                         ans = 0;
390                                         break;
391                                 }
392                                 ans = 1;
393                                 t = (caddr_t)pml;
394                                 pml = pml->pml_next;
395                                 if (prevpml == NULL)
396                                         pmaplist = pml;
397                                 else
398                                         prevpml->pml_next = pml;
399                                 free(t);
400                         }
401                         if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
402                             debugging) {
403                                 (void) fprintf(stderr, "svc_sendreply\n");
404                                 abort();
405                         }
406                 }
407                 break;
408
409         case PMAPPROC_GETPORT:
410                 /*
411                  * Lookup the mapping for a program,version and return its port
412                  */
413                 if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
414                         svcerr_decode(xprt);
415                 else {
416                         /* remote host authorization check */
417                         if (!check_default(svc_getcaller(xprt),
418                             rqstp->rq_proc,
419                             reg.pm_prog)) {
420                                 ans = 0;
421                                 goto done;
422                         }
423                         fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
424                         if (fnd)
425                                 port = fnd->pml_map.pm_port;
426                         else
427                                 port = 0;
428                         if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&port)) &&
429                             debugging) {
430                                 (void) fprintf(stderr, "svc_sendreply\n");
431                                 abort();
432                         }
433                 }
434                 break;
435
436         case PMAPPROC_DUMP:
437                 /*
438                  * Return the current set of mapped program,version
439                  */
440                 if (!svc_getargs(xprt, xdr_void, NULL))
441                         svcerr_decode(xprt);
442                 else {
443                         /* remote host authorization check */
444                         struct pmaplist *p;
445                         if (!check_default(svc_getcaller(xprt),
446                             rqstp->rq_proc, (u_long) 0)) {
447                                 p = 0;  /* send empty list */
448                         } else {
449                                 p = pmaplist;
450                         }
451                         if ((!svc_sendreply(xprt, xdr_pmaplist,
452                             (caddr_t)&p)) && debugging) {
453                                 (void) fprintf(stderr, "svc_sendreply\n");
454                                 abort();
455                         }
456                 }
457                 break;
458
459         case PMAPPROC_CALLIT:
460                 /*
461                  * Calls a procedure on the local machine.  If the requested
462                  * procedure is not registered this procedure does not return
463                  * error information!!
464                  * This procedure is only supported on rpc/udp and calls via
465                  * rpc/udp.  It passes null authentication parameters.
466                  */
467                 callit(rqstp, xprt);
468                 break;
469
470         default:
471                 /* remote host authorization check */
472                 check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
473                 svcerr_noproc(xprt);
474                 break;
475         }
476 }
477
478
479 /*
480  * Stuff for the rmtcall service
481  */
482 #define ARGSIZE 9000
483
484 struct encap_parms {
485         u_int arglen;
486         char *args;
487 };
488
489 static bool_t
490 xdr_encap_parms(xdrs, epp)
491         XDR *xdrs;
492         struct encap_parms *epp;
493 {
494
495         return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE));
496 }
497
498 struct rmtcallargs {
499         u_long  rmt_prog;
500         u_long  rmt_vers;
501         u_long  rmt_port;
502         u_long  rmt_proc;
503         struct encap_parms rmt_args;
504 };
505
506 static bool_t
507 xdr_rmtcall_args(xdrs, cap)
508         XDR *xdrs;
509         struct rmtcallargs *cap;
510 {
511
512         /* does not get a port number */
513         if (xdr_u_long(xdrs, &(cap->rmt_prog)) &&
514             xdr_u_long(xdrs, &(cap->rmt_vers)) &&
515             xdr_u_long(xdrs, &(cap->rmt_proc))) {
516                 return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
517         }
518         return (FALSE);
519 }
520
521 static bool_t
522 xdr_rmtcall_result(xdrs, cap)
523         XDR *xdrs;
524         struct rmtcallargs *cap;
525 {
526         if (xdr_u_long(xdrs, &(cap->rmt_port)))
527                 return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
528         return (FALSE);
529 }
530
531 /*
532  * only worries about the struct encap_parms part of struct rmtcallargs.
533  * The arglen must already be set!!
534  */
535 static bool_t
536 xdr_opaque_parms(xdrs, cap)
537         XDR *xdrs;
538         struct rmtcallargs *cap;
539 {
540         return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
541 }
542
543 /*
544  * This routine finds and sets the length of incoming opaque paraters
545  * and then calls xdr_opaque_parms.
546  */
547 static bool_t
548 xdr_len_opaque_parms(xdrs, cap)
549         XDR *xdrs;
550         struct rmtcallargs *cap;
551 {
552         register u_int beginpos, lowpos, highpos, currpos, pos;
553
554         beginpos = lowpos = pos = xdr_getpos(xdrs);
555         highpos = lowpos + ARGSIZE;
556         while ((int)(highpos - lowpos) >= 0) {
557                 currpos = (lowpos + highpos) / 2;
558                 if (xdr_setpos(xdrs, currpos)) {
559                         pos = currpos;
560                         lowpos = currpos + 1;
561                 } else {
562                         highpos = currpos - 1;
563                 }
564         }
565         xdr_setpos(xdrs, beginpos);
566         cap->rmt_args.arglen = pos - beginpos;
567         return (xdr_opaque_parms(xdrs, cap));
568 }
569
570 /*
571  * Call a remote procedure service
572  * This procedure is very quiet when things go wrong.
573  * The proc is written to support broadcast rpc.  In the broadcast case,
574  * a machine should shut-up instead of complain, less the requestor be
575  * overrun with complaints at the expense of not hearing a valid reply ...
576  *
577  * This now forks so that the program & process that it calls can call
578  * back to the portmapper.
579  */
580 static void
581 callit(rqstp, xprt)
582         struct svc_req *rqstp;
583         SVCXPRT *xprt;
584 {
585         struct rmtcallargs a;
586         struct pmaplist *pml;
587         u_short port;
588         struct sockaddr_in me;
589         int pid, so = -1;
590         CLIENT *client;
591         struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred;
592         struct timeval timeout;
593         char buf[ARGSIZE];
594
595         timeout.tv_sec = 5;
596         timeout.tv_usec = 0;
597         a.rmt_args.args = buf;
598         if (!svc_getargs(xprt, xdr_rmtcall_args, (caddr_t)&a))
599                 return;
600         /* host and service access control */
601         if (!check_callit(svc_getcaller(xprt),
602             rqstp->rq_proc, a.rmt_prog, a.rmt_proc))
603                 return;
604         if ((pml = find_service(a.rmt_prog, a.rmt_vers,
605             (u_long)IPPROTO_UDP)) == NULL)
606                 return;
607         /*
608          * fork a child to do the work.  Parent immediately returns.
609          * Child exits upon completion.
610          */
611         if ((pid = fork()) != 0) {
612                 if (pid < 0)
613                         syslog(LOG_ERR, "CALLIT (prog %lu): fork: %m",
614                             a.rmt_prog);
615                 return;
616         }
617         port = pml->pml_map.pm_port;
618         get_myaddress(&me);
619         me.sin_port = htons(port);
620         client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &so);
621         if (client != (CLIENT *)NULL) {
622                 if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
623                         client->cl_auth = authunix_create(au->aup_machname,
624                            au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids);
625                 }
626                 a.rmt_port = (u_long)port;
627                 if (clnt_call(client, a.rmt_proc, xdr_opaque_parms, &a,
628                     xdr_len_opaque_parms, &a, timeout) == RPC_SUCCESS) {
629                         svc_sendreply(xprt, xdr_rmtcall_result, (caddr_t)&a);
630                 }
631                 AUTH_DESTROY(client->cl_auth);
632                 clnt_destroy(client);
633         }
634         (void)close(so);
635         exit(0);
636 }
637
638 static void
639 reap(sig)
640         int sig;
641 {
642         int save_errno;
643
644         save_errno = errno;
645         while (wait3((int *)NULL, WNOHANG, (struct rusage *)NULL) > 0);
646         errno = save_errno;
647 }