1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27/* All Rights Reserved */
28/*
29 * University Copyright- Copyright (c) 1982, 1986, 1988
30 * The Regents of the University of California
31 * All Rights Reserved
32 *
33 * University Acknowledgment- Portions of this document are derived from
34 * software developed by the University of California, Berkeley, and its
35 * contributors.
36 */
37
38#pragma ident	"%Z%%M%	%I%	%E% SMI"
39
40/*
41 * pmap_svc.c
42 * The server procedure for the version 2 portmaper.
43 * All the portmapper related interface from the portmap side.
44 */
45
46#ifdef PORTMAP
47#include <stdio.h>
48#include <alloca.h>
49#include <ucred.h>
50#include <rpc/rpc.h>
51#include <rpc/pmap_prot.h>
52#include <rpc/rpcb_prot.h>
53#include "rpcbind.h"
54
55#ifdef RPCBIND_DEBUG
56#include <netdir.h>
57#endif
58
59static PMAPLIST *find_service_pmap();
60static bool_t pmapproc_change();
61static bool_t pmapproc_getport();
62static bool_t pmapproc_dump();
63
64/*
65 * Called for all the version 2 inquiries.
66 */
67void
68pmap_service(rqstp, xprt)
69	register struct svc_req *rqstp;
70	register SVCXPRT *xprt;
71{
72	rpcbs_procinfo(RPCBVERS_2_STAT, rqstp->rq_proc);
73	switch (rqstp->rq_proc) {
74	case PMAPPROC_NULL:
75		/*
76		 * Null proc call
77		 */
78#ifdef RPCBIND_DEBUG
79		fprintf(stderr, "PMAPPROC_NULL\n");
80#endif
81		PMAP_CHECK(xprt, rqstp->rq_proc);
82
83		if ((!svc_sendreply(xprt, (xdrproc_t)xdr_void, NULL)) &&
84			debugging) {
85			if (doabort) {
86				rpcbind_abort();
87			}
88		}
89		break;
90
91	case PMAPPROC_SET:
92		/*
93		 * Set a program, version to port mapping
94		 */
95		pmapproc_change(rqstp, xprt, rqstp->rq_proc);
96		break;
97
98	case PMAPPROC_UNSET:
99		/*
100		 * Remove a program, version to port mapping.
101		 */
102		pmapproc_change(rqstp, xprt, rqstp->rq_proc);
103		break;
104
105	case PMAPPROC_GETPORT:
106		/*
107		 * Lookup the mapping for a program, version and return its
108		 * port number.
109		 */
110		pmapproc_getport(rqstp, xprt);
111		break;
112
113	case PMAPPROC_DUMP:
114		/*
115		 * Return the current set of mapped program, version
116		 */
117#ifdef RPCBIND_DEBUG
118		fprintf(stderr, "PMAPPROC_DUMP\n");
119#endif
120		PMAP_CHECK(xprt, rqstp->rq_proc);
121		pmapproc_dump(rqstp, xprt);
122		break;
123
124	case PMAPPROC_CALLIT:
125		/*
126		 * Calls a procedure on the local machine. If the requested
127		 * procedure is not registered this procedure does not return
128		 * error information!!
129		 * This procedure is only supported on rpc/udp and calls via
130		 * rpc/udp. It passes null authentication parameters.
131		 */
132		rpcbproc_callit_com(rqstp, xprt, PMAPPROC_CALLIT, PMAPVERS);
133		break;
134
135	default:
136		PMAP_CHECK(xprt, rqstp->rq_proc);
137		svcerr_noproc(xprt);
138		break;
139	}
140}
141
142/*
143 * returns the item with the given program, version number. If that version
144 * number is not found, it returns the item with that program number, so that
145 * the port number is now returned to the caller. The caller when makes a
146 * call to this program, version number, the call will fail and it will
147 * return with PROGVERS_MISMATCH. The user can then determine the highest
148 * and the lowest version number for this program using clnt_geterr() and
149 * use those program version numbers.
150 */
151static PMAPLIST *
152find_service_pmap(prog, vers, prot)
153	ulong_t prog;
154	ulong_t vers;
155	ulong_t prot;
156{
157	register PMAPLIST *hit = NULL;
158	register PMAPLIST *pml;
159
160	for (pml = list_pml; pml != NULL; pml = pml->pml_next) {
161		if ((pml->pml_map.pm_prog != prog) ||
162			(pml->pml_map.pm_prot != prot))
163			continue;
164		hit = pml;
165		if (pml->pml_map.pm_vers == vers)
166			break;
167	}
168	return (hit);
169}
170
171extern char *getowner(SVCXPRT *xprt, char *);
172
173/*ARGSUSED*/
174static bool_t
175pmapproc_change(rqstp, xprt, op)
176	struct svc_req *rqstp;
177	register SVCXPRT *xprt;
178	unsigned long op;
179{
180	PMAP reg;
181	RPCB rpcbreg;
182	int ans;
183	struct sockaddr_in *who;
184	extern bool_t map_set(), map_unset();
185	char owner[64];
186
187	if (!svc_getargs(xprt, (xdrproc_t)xdr_pmap, (char *)&reg)) {
188		svcerr_decode(xprt);
189		return (FALSE);
190	}
191	who = svc_getcaller(xprt);
192
193#ifdef RPCBIND_DEBUG
194	fprintf(stderr, "%s request for (%lu, %lu) : ",
195		op == PMAPPROC_SET ? "PMAP_SET" : "PMAP_UNSET",
196		reg.pm_prog, reg.pm_vers);
197#endif
198
199	/* Don't allow unset/set from remote. */
200	if (!localxprt(xprt, B_TRUE)) {
201		ans = FALSE;
202		goto done_change;
203	}
204
205	rpcbreg.r_owner = getowner(xprt, owner);
206
207	if ((op == PMAPPROC_SET) && (reg.pm_port < IPPORT_RESERVED) &&
208	    (ntohs(who->sin_port) >= IPPORT_RESERVED)) {
209		ans = FALSE;
210		goto done_change;
211	}
212	rpcbreg.r_prog = reg.pm_prog;
213	rpcbreg.r_vers = reg.pm_vers;
214
215	if (op == PMAPPROC_SET) {
216		char buf[32];
217
218		sprintf(buf, "0.0.0.0.%d.%d", (reg.pm_port >> 8) & 0xff,
219			reg.pm_port & 0xff);
220		rpcbreg.r_addr = buf;
221		if (reg.pm_prot == IPPROTO_UDP) {
222			rpcbreg.r_netid = udptrans;
223		} else if (reg.pm_prot == IPPROTO_TCP) {
224			rpcbreg.r_netid = tcptrans;
225		} else {
226			ans = FALSE;
227			goto done_change;
228		}
229		ans = map_set(&rpcbreg, rpcbreg.r_owner);
230	} else if (op == PMAPPROC_UNSET) {
231		bool_t ans1, ans2;
232
233		rpcbreg.r_addr = NULL;
234		rpcbreg.r_netid = tcptrans;
235		ans1 = map_unset(&rpcbreg, rpcbreg.r_owner);
236		rpcbreg.r_netid = udptrans;
237		ans2 = map_unset(&rpcbreg, rpcbreg.r_owner);
238		ans = ans1 || ans2;
239	} else {
240		ans = FALSE;
241	}
242done_change:
243	PMAP_LOG(ans, xprt, op, reg.pm_prog);
244
245	if ((!svc_sendreply(xprt, (xdrproc_t)xdr_long, (caddr_t)&ans)) &&
246	    debugging) {
247		fprintf(stderr, "portmap: svc_sendreply\n");
248		if (doabort) {
249			rpcbind_abort();
250		}
251	}
252#ifdef RPCBIND_DEBUG
253	fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed");
254#endif
255	if (op == PMAPPROC_SET)
256		rpcbs_set(RPCBVERS_2_STAT, ans);
257	else
258		rpcbs_unset(RPCBVERS_2_STAT, ans);
259	return (TRUE);
260}
261
262/* ARGSUSED */
263static bool_t
264pmapproc_getport(rqstp, xprt)
265	struct svc_req *rqstp;
266	register SVCXPRT *xprt;
267{
268	PMAP reg;
269	int port = 0;
270	PMAPLIST *fnd;
271#ifdef RPCBIND_DEBUG
272	char *uaddr;
273#endif
274
275	if (!svc_getargs(xprt, (xdrproc_t)xdr_pmap, (char *)&reg)) {
276		svcerr_decode(xprt);
277		return (FALSE);
278	}
279	PMAP_CHECK_RET(xprt, rqstp->rq_proc, FALSE);
280
281#ifdef RPCBIND_DEBUG
282	uaddr =  taddr2uaddr(rpcbind_get_conf(xprt->xp_netid),
283			    svc_getrpccaller(xprt));
284	fprintf(stderr, "PMAP_GETPORT request for (%lu, %lu, %s) from %s :",
285		reg.pm_prog, reg.pm_vers,
286		reg.pm_prot == IPPROTO_UDP ? "udp" : "tcp", uaddr);
287	free(uaddr);
288#endif
289	fnd = find_service_pmap(reg.pm_prog, reg.pm_vers, reg.pm_prot);
290	if (fnd) {
291		char serveuaddr[32], *ua;
292		int h1, h2, h3, h4, p1, p2;
293		char *netid;
294
295		if (reg.pm_prot == IPPROTO_UDP) {
296			ua = udp_uaddr;
297			netid = udptrans;
298		} else {
299			ua = tcp_uaddr; /* To get the len */
300			netid = tcptrans;
301		}
302		if (ua == NULL) {
303			goto sendreply;
304		}
305		if (sscanf(ua, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3,
306				&h4, &p1, &p2) == 6) {
307			p1 = (fnd->pml_map.pm_port >> 8) & 0xff;
308			p2 = (fnd->pml_map.pm_port) & 0xff;
309			sprintf(serveuaddr, "%d.%d.%d.%d.%d.%d",
310				h1, h2, h3, h4, p1, p2);
311			if (is_bound(netid, serveuaddr)) {
312				port = fnd->pml_map.pm_port;
313			} else { /* this service is dead; delete it */
314				delete_prog(reg.pm_prog);
315			}
316		}
317	}
318sendreply:
319	if ((!svc_sendreply(xprt, (xdrproc_t)xdr_long, (caddr_t)&port)) &&
320			debugging) {
321		(void) fprintf(stderr, "portmap: svc_sendreply\n");
322		if (doabort) {
323			rpcbind_abort();
324		}
325	}
326#ifdef RPCBIND_DEBUG
327	fprintf(stderr, "port = %d\n", port);
328#endif
329	rpcbs_getaddr(RPCBVERS_2_STAT, reg.pm_prog, reg.pm_vers,
330		reg.pm_prot == IPPROTO_UDP ? udptrans : tcptrans,
331		port ? udptrans : "");
332
333	return (TRUE);
334}
335
336/* ARGSUSED */
337static bool_t
338pmapproc_dump(rqstp, xprt)
339	struct svc_req *rqstp;
340	register SVCXPRT *xprt;
341{
342	if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL)) {
343		svcerr_decode(xprt);
344		return (FALSE);
345	}
346	if ((!svc_sendreply(xprt, (xdrproc_t)xdr_pmaplist_ptr,
347			(caddr_t)&list_pml)) && debugging) {
348		(void) fprintf(stderr, "portmap: svc_sendreply\n");
349		if (doabort) {
350			rpcbind_abort();
351		}
352	}
353	return (TRUE);
354}
355
356/*
357 * Is the transport local?  The original rpcbind code tried to
358 * figure out all the network interfaces but there can be a nearly
359 * infinite number of network interfaces.  And the number of interfaces can
360 * vary over time.
361 *
362 * Note that when we get here, we've already establised that we're
363 * dealing with a TCP/IP endpoint.
364 */
365boolean_t
366localxprt(SVCXPRT *transp, boolean_t forceipv4)
367{
368	struct sockaddr_gen *sgen = svc_getgencaller(transp);
369
370	switch (SGFAM(sgen)) {
371	case AF_INET:
372		break;
373	case AF_INET6:
374		if (forceipv4)
375			return (B_FALSE);
376		break;
377	default:
378		return (B_FALSE);
379	}
380
381	/*
382	 * Get the peer's uid; if it is known it is sufficiently
383	 * authenticated and considered local.  The magic behind this
384	 * call is all in libnsl.
385	 */
386	return (rpcb_caller_uid(transp) != -1);
387}
388#endif /* PORTMAP */
389