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