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