GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/rpc/svc.c Lines: 0 219 0.0 %
Date: 2017-11-13 Branches: 0 127 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: svc.c,v 1.29 2015/10/05 01:23:17 deraadt Exp $ */
2
3
/*
4
 * Copyright (c) 2010, Oracle America, Inc.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are
8
 * met:
9
 *
10
 *     * Redistributions of source code must retain the above copyright
11
 *       notice, this list of conditions and the following disclaimer.
12
 *     * Redistributions in binary form must reproduce the above
13
 *       copyright notice, this list of conditions and the following
14
 *       disclaimer in the documentation and/or other materials
15
 *       provided with the distribution.
16
 *     * Neither the name of the "Oracle America, Inc." nor the names of its
17
 *       contributors may be used to endorse or promote products derived
18
 *       from this software without specific prior written permission.
19
 *
20
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
 *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25
 *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
 *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27
 *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
 *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29
 *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30
 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 */
33
34
/*
35
 * svc.c, Server-side remote procedure call interface.
36
 *
37
 * There are two sets of procedures here.  The xprt routines are
38
 * for handling transport handles.  The svc routines handle the
39
 * list of service routines.
40
 */
41
42
#include <errno.h>
43
#include <stdlib.h>
44
#include <string.h>
45
46
#include <rpc/rpc.h>
47
#include <rpc/pmap_clnt.h>
48
49
static SVCXPRT **xports;
50
static int xportssize;
51
52
#define	RQCRED_SIZE	400		/* this size is excessive */
53
54
#define max(a, b) (a > b ? a : b)
55
56
/*
57
 * The services list
58
 * Each entry represents a set of procedures (an rpc program).
59
 * The dispatch routine takes request structs and runs the
60
 * appropriate procedure.
61
 */
62
static struct svc_callout {
63
	struct svc_callout *sc_next;
64
	u_long		    sc_prog;
65
	u_long		    sc_vers;
66
	void		    (*sc_dispatch)();
67
} *svc_head;
68
69
static struct svc_callout *svc_find(u_long, u_long, struct svc_callout **);
70
static int svc_fd_insert(int);
71
static int svc_fd_remove(int);
72
73
int __svc_fdsetsize = FD_SETSIZE;
74
fd_set *__svc_fdset = &svc_fdset;
75
static int svc_pollfd_size;		/* number of slots in svc_pollfd */
76
static int svc_used_pollfd;		/* number of used slots in svc_pollfd */
77
static int *svc_pollfd_freelist;	/* svc_pollfd free list */
78
static int svc_max_free;		/* number of used slots in free list */
79
80
/* ***************  SVCXPRT related stuff **************** */
81
82
/*
83
 * Activate a transport handle.
84
 */
85
void
86
xprt_register(SVCXPRT *xprt)
87
{
88
	/* ignore failure conditions */
89
	(void) __xprt_register(xprt);
90
}
91
92
/*
93
 * Activate a transport handle.
94
 */
95
int
96
__xprt_register(SVCXPRT *xprt)
97
{
98
	int sock = xprt->xp_sock;
99
100
	if (xports == NULL || sock + 1 > xportssize) {
101
		SVCXPRT **xp;
102
		int size = FD_SETSIZE;
103
104
		while (sock + 1 > size)
105
			size += FD_SETSIZE;
106
		xp = calloc(size, sizeof(SVCXPRT *));
107
		if (xp == NULL)
108
			return (0);
109
		if (xports) {
110
			memcpy(xp, xports, xportssize * sizeof(SVCXPRT *));
111
			free(xports);
112
		}
113
		xportssize = size;
114
		xports = xp;
115
	}
116
117
	if (!svc_fd_insert(sock))
118
		return (0);
119
	xports[sock] = xprt;
120
121
	return (1);
122
}
123
124
/*
125
 * Insert a socket into svc_pollfd, svc_fdset and __svc_fdset.
126
 * If we are out of space, we allocate ~128 more slots than we
127
 * need now for future expansion.
128
 * We try to keep svc_pollfd well packed (no holes) as possible
129
 * so that poll(2) is efficient.
130
 */
131
static int
132
svc_fd_insert(int sock)
133
{
134
	int slot;
135
136
	/*
137
	 * Find a slot for sock in svc_pollfd; four possible cases:
138
	 *  1) need to allocate more space for svc_pollfd
139
	 *  2) there is an entry on the free list
140
	 *  3) the free list is empty (svc_used_pollfd is the next slot)
141
	 */
142
	if (svc_pollfd == NULL || svc_used_pollfd == svc_pollfd_size) {
143
		struct pollfd *pfd;
144
		int new_size, *new_freelist;
145
146
		new_size = svc_pollfd ? svc_pollfd_size + 128 : FD_SETSIZE;
147
		pfd = reallocarray(svc_pollfd, new_size, sizeof(*svc_pollfd));
148
		if (pfd == NULL)
149
			return (0);			/* no changes */
150
		new_freelist = realloc(svc_pollfd_freelist, new_size / 2);
151
		if (new_freelist == NULL) {
152
			free(pfd);
153
			return (0);			/* no changes */
154
		}
155
		svc_pollfd = pfd;
156
		svc_pollfd_size = new_size;
157
		svc_pollfd_freelist = new_freelist;
158
		for (slot = svc_used_pollfd; slot < svc_pollfd_size; slot++) {
159
			svc_pollfd[slot].fd = -1;
160
			svc_pollfd[slot].events = svc_pollfd[slot].revents = 0;
161
		}
162
		slot = svc_used_pollfd;
163
	} else if (svc_max_free != 0) {
164
		/* there is an entry on the free list, use it */
165
		slot = svc_pollfd_freelist[--svc_max_free];
166
	} else {
167
		/* nothing on the free list but we have room to grow */
168
		slot = svc_used_pollfd;
169
	}
170
	if (sock + 1 > __svc_fdsetsize) {
171
		fd_set *fds;
172
		size_t bytes;
173
174
		bytes = howmany(sock + 128, NFDBITS) * sizeof(fd_mask);
175
		/* realloc() would be nicer but it gets tricky... */
176
		if ((fds = (fd_set *)mem_alloc(bytes)) != NULL) {
177
			memset(fds, 0, bytes);
178
			memcpy(fds, __svc_fdset,
179
			    howmany(__svc_fdsetsize, NFDBITS) * sizeof(fd_mask));
180
			if (__svc_fdset != &svc_fdset)
181
				free(__svc_fdset);
182
			__svc_fdset = fds;
183
			__svc_fdsetsize = bytes / sizeof(fd_mask) * NFDBITS;
184
		}
185
	}
186
187
	svc_pollfd[slot].fd = sock;
188
	svc_pollfd[slot].events = POLLIN;
189
	svc_used_pollfd++;
190
	if (svc_max_pollfd < slot + 1)
191
		svc_max_pollfd = slot + 1;
192
	if (sock < FD_SETSIZE)
193
		FD_SET(sock, &svc_fdset);
194
	if (sock < __svc_fdsetsize && __svc_fdset != &svc_fdset)
195
		FD_SET(sock, __svc_fdset);
196
	svc_maxfd = max(svc_maxfd, sock);
197
198
	return (1);
199
}
200
201
/*
202
 * Remove a socket from svc_pollfd, svc_fdset and __svc_fdset.
203
 * Freed slots are placed on the free list.  If the free list fills
204
 * up, we compact svc_pollfd (free list size == svc_pollfd_size /2).
205
 */
206
static int
207
svc_fd_remove(int sock)
208
{
209
	int slot;
210
211
	if (svc_pollfd == NULL)
212
		return (0);
213
214
	for (slot = 0; slot < svc_max_pollfd; slot++) {
215
		if (svc_pollfd[slot].fd == sock) {
216
			svc_pollfd[slot].fd = -1;
217
			svc_pollfd[slot].events = svc_pollfd[slot].revents = 0;
218
			svc_used_pollfd--;
219
			if (sock < FD_SETSIZE)
220
				FD_CLR(sock, &svc_fdset);
221
			if (sock < __svc_fdsetsize && __svc_fdset != &svc_fdset)
222
				FD_CLR(sock, __svc_fdset);
223
			if (sock == svc_maxfd) {
224
				for (svc_maxfd--; svc_maxfd >= 0; svc_maxfd--)
225
					if (xports[svc_maxfd])
226
						break;
227
			}
228
			if (svc_max_free == svc_pollfd_size / 2) {
229
				int i, j;
230
231
				/*
232
				 * Out of space in the free list; this means
233
				 * that svc_pollfd is half full.  Pack things
234
				 * such that svc_max_pollfd == svc_used_pollfd
235
				 * and svc_pollfd_freelist is empty.
236
				 */
237
				for (i = svc_used_pollfd, j = 0;
238
				    i < svc_max_pollfd && j < svc_max_free; i++) {
239
					if (svc_pollfd[i].fd == -1)
240
						continue;
241
					/* be sure to use a low-numbered slot */
242
					while (svc_pollfd_freelist[j] >=
243
					    svc_used_pollfd)
244
						j++;
245
					svc_pollfd[svc_pollfd_freelist[j++]] =
246
					    svc_pollfd[i];
247
					svc_pollfd[i].fd = -1;
248
					svc_pollfd[i].events =
249
					    svc_pollfd[i].revents = 0;
250
				}
251
				svc_max_pollfd = svc_used_pollfd;
252
				svc_max_free = 0;
253
				/* could realloc if svc_pollfd_size is big */
254
			} else {
255
				/* trim svc_max_pollfd from the end */
256
				while (svc_max_pollfd > 0 &&
257
				    svc_pollfd[svc_max_pollfd - 1].fd == -1)
258
					svc_max_pollfd--;
259
			}
260
			svc_pollfd_freelist[svc_max_free++] = slot;
261
262
			return (1);
263
		}
264
	}
265
	return (0);		/* not found, shouldn't happen */
266
}
267
268
/*
269
 * De-activate a transport handle.
270
 */
271
void
272
xprt_unregister(SVCXPRT *xprt)
273
{
274
	int sock = xprt->xp_sock;
275
276
	if (xports[sock] == xprt) {
277
		xports[sock] = NULL;
278
		svc_fd_remove(sock);
279
	}
280
}
281
DEF_WEAK(xprt_unregister);
282
283
284
/* ********************** CALLOUT list related stuff ************* */
285
286
/*
287
 * Add a service program to the callout list.
288
 * The dispatch routine will be called when a rpc request for this
289
 * program number comes in.
290
 */
291
bool_t
292
svc_register(SVCXPRT *xprt, u_long prog, u_long vers, void (*dispatch)(),
293
    int protocol)
294
{
295
	struct svc_callout *prev;
296
	struct svc_callout *s;
297
298
	if ((s = svc_find(prog, vers, &prev)) != NULL) {
299
		if (s->sc_dispatch == dispatch)
300
			goto pmap_it;  /* he is registering another xptr */
301
		return (FALSE);
302
	}
303
	s = (struct svc_callout *)mem_alloc(sizeof(struct svc_callout));
304
	if (s == NULL) {
305
		return (FALSE);
306
	}
307
	s->sc_prog = prog;
308
	s->sc_vers = vers;
309
	s->sc_dispatch = dispatch;
310
	s->sc_next = svc_head;
311
	svc_head = s;
312
pmap_it:
313
	/* now register the information with the local binder service */
314
	if (protocol) {
315
		return (pmap_set(prog, vers, protocol, xprt->xp_port));
316
	}
317
	return (TRUE);
318
}
319
DEF_WEAK(svc_register);
320
321
/*
322
 * Remove a service program from the callout list.
323
 */
324
void
325
svc_unregister(u_long prog, u_long vers)
326
{
327
	struct svc_callout *prev;
328
	struct svc_callout *s;
329
330
	if ((s = svc_find(prog, vers, &prev)) == NULL)
331
		return;
332
	if (prev == NULL) {
333
		svc_head = s->sc_next;
334
	} else {
335
		prev->sc_next = s->sc_next;
336
	}
337
	s->sc_next = NULL;
338
	mem_free((char *) s, (u_int) sizeof(struct svc_callout));
339
	/* now unregister the information with the local binder service */
340
	(void)pmap_unset(prog, vers);
341
}
342
343
/*
344
 * Search the callout list for a program number, return the callout
345
 * struct.
346
 */
347
static struct svc_callout *
348
svc_find(u_long prog, u_long vers, struct svc_callout **prev)
349
{
350
	struct svc_callout *s, *p;
351
352
	p = NULL;
353
	for (s = svc_head; s != NULL; s = s->sc_next) {
354
		if ((s->sc_prog == prog) && (s->sc_vers == vers))
355
			goto done;
356
		p = s;
357
	}
358
done:
359
	*prev = p;
360
	return (s);
361
}
362
363
/* ******************* REPLY GENERATION ROUTINES  ************ */
364
365
/*
366
 * Send a reply to an rpc request
367
 */
368
bool_t
369
svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results, caddr_t xdr_location)
370
{
371
	struct rpc_msg rply;
372
373
	rply.rm_direction = REPLY;
374
	rply.rm_reply.rp_stat = MSG_ACCEPTED;
375
	rply.acpted_rply.ar_verf = xprt->xp_verf;
376
	rply.acpted_rply.ar_stat = SUCCESS;
377
	rply.acpted_rply.ar_results.where = xdr_location;
378
	rply.acpted_rply.ar_results.proc = xdr_results;
379
	return (SVC_REPLY(xprt, &rply));
380
}
381
DEF_WEAK(svc_sendreply);
382
383
/*
384
 * No procedure error reply
385
 */
386
void
387
svcerr_noproc(SVCXPRT *xprt)
388
{
389
	struct rpc_msg rply;
390
391
	rply.rm_direction = REPLY;
392
	rply.rm_reply.rp_stat = MSG_ACCEPTED;
393
	rply.acpted_rply.ar_verf = xprt->xp_verf;
394
	rply.acpted_rply.ar_stat = PROC_UNAVAIL;
395
	SVC_REPLY(xprt, &rply);
396
}
397
398
/*
399
 * Can't decode args error reply
400
 */
401
void
402
svcerr_decode(SVCXPRT *xprt)
403
{
404
	struct rpc_msg rply;
405
406
	rply.rm_direction = REPLY;
407
	rply.rm_reply.rp_stat = MSG_ACCEPTED;
408
	rply.acpted_rply.ar_verf = xprt->xp_verf;
409
	rply.acpted_rply.ar_stat = GARBAGE_ARGS;
410
	SVC_REPLY(xprt, &rply);
411
}
412
DEF_WEAK(svcerr_decode);
413
414
/*
415
 * Some system error
416
 */
417
void
418
svcerr_systemerr(SVCXPRT *xprt)
419
{
420
	struct rpc_msg rply;
421
422
	rply.rm_direction = REPLY;
423
	rply.rm_reply.rp_stat = MSG_ACCEPTED;
424
	rply.acpted_rply.ar_verf = xprt->xp_verf;
425
	rply.acpted_rply.ar_stat = SYSTEM_ERR;
426
	SVC_REPLY(xprt, &rply);
427
}
428
429
/*
430
 * Authentication error reply
431
 */
432
void
433
svcerr_auth(SVCXPRT *xprt, enum auth_stat why)
434
{
435
	struct rpc_msg rply;
436
437
	rply.rm_direction = REPLY;
438
	rply.rm_reply.rp_stat = MSG_DENIED;
439
	rply.rjcted_rply.rj_stat = AUTH_ERROR;
440
	rply.rjcted_rply.rj_why = why;
441
	SVC_REPLY(xprt, &rply);
442
}
443
DEF_WEAK(svcerr_auth);
444
445
/*
446
 * Auth too weak error reply
447
 */
448
void
449
svcerr_weakauth(SVCXPRT *xprt)
450
{
451
452
	svcerr_auth(xprt, AUTH_TOOWEAK);
453
}
454
455
/*
456
 * Program unavailable error reply
457
 */
458
void
459
svcerr_noprog(SVCXPRT *xprt)
460
{
461
	struct rpc_msg rply;
462
463
	rply.rm_direction = REPLY;
464
	rply.rm_reply.rp_stat = MSG_ACCEPTED;
465
	rply.acpted_rply.ar_verf = xprt->xp_verf;
466
	rply.acpted_rply.ar_stat = PROG_UNAVAIL;
467
	SVC_REPLY(xprt, &rply);
468
}
469
DEF_WEAK(svcerr_noprog);
470
471
/*
472
 * Program version mismatch error reply
473
 */
474
void
475
svcerr_progvers(SVCXPRT *xprt, u_long low_vers, u_long high_vers)
476
{
477
	struct rpc_msg rply;
478
479
	rply.rm_direction = REPLY;
480
	rply.rm_reply.rp_stat = MSG_ACCEPTED;
481
	rply.acpted_rply.ar_verf = xprt->xp_verf;
482
	rply.acpted_rply.ar_stat = PROG_MISMATCH;
483
	rply.acpted_rply.ar_vers.low = low_vers;
484
	rply.acpted_rply.ar_vers.high = high_vers;
485
	SVC_REPLY(xprt, &rply);
486
}
487
DEF_WEAK(svcerr_progvers);
488
489
/* ******************* SERVER INPUT STUFF ******************* */
490
491
/*
492
 * Get server side input from some transport.
493
 *
494
 * Statement of authentication parameters management:
495
 * This function owns and manages all authentication parameters, specifically
496
 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
497
 * the "cooked" credentials (rqst->rq_clntcred).
498
 * However, this function does not know the structure of the cooked
499
 * credentials, so it make the following assumptions:
500
 *   a) the structure is contiguous (no pointers), and
501
 *   b) the cred structure size does not exceed RQCRED_SIZE bytes.
502
 * In all events, all three parameters are freed upon exit from this routine.
503
 * The storage is trivially management on the call stack in userland, but
504
 * is mallocated in kernel land.
505
 */
506
507
void
508
svc_getreq(int rdfds)
509
{
510
	int bit;
511
512
	for (; (bit = ffs(rdfds)); rdfds ^= (1 << (bit - 1)))
513
		svc_getreq_common(bit - 1);
514
}
515
DEF_WEAK(svc_getreq);
516
517
void
518
svc_getreqset(fd_set *readfds)
519
{
520
	svc_getreqset2(readfds, FD_SETSIZE);
521
}
522
523
void
524
svc_getreqset2(fd_set *readfds, int width)
525
{
526
	fd_mask mask, *maskp;
527
	int bit, sock;
528
529
	maskp = readfds->fds_bits;
530
	for (sock = 0; sock < width; sock += NFDBITS) {
531
		for (mask = *maskp++; (bit = ffs(mask));
532
		    mask ^= (1 << (bit - 1)))
533
			svc_getreq_common(sock + bit - 1);
534
	}
535
}
536
DEF_WEAK(svc_getreqset2);
537
538
void
539
svc_getreq_poll(struct pollfd *pfd, const int nready)
540
{
541
	int i, n;
542
543
	for (n = nready, i = 0; n > 0; i++) {
544
		if (pfd[i].fd == -1)
545
			continue;
546
		if (pfd[i].revents != 0)
547
			n--;
548
		if ((pfd[i].revents & (POLLIN | POLLHUP)) == 0)
549
			continue;
550
		svc_getreq_common(pfd[i].fd);
551
	}
552
}
553
DEF_WEAK(svc_getreq_poll);
554
555
void
556
svc_getreq_common(int fd)
557
{
558
	enum xprt_stat stat;
559
	struct rpc_msg msg;
560
	int prog_found;
561
	u_long low_vers;
562
	u_long high_vers;
563
	struct svc_req r;
564
	SVCXPRT *xprt;
565
	char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE];
566
567
	msg.rm_call.cb_cred.oa_base = cred_area;
568
	msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
569
	r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]);
570
571
	/* sock has input waiting */
572
	xprt = xports[fd];
573
	if (xprt == NULL)
574
		/* But do we control the fd? */
575
		return;
576
	/* now receive msgs from xprtprt (support batch calls) */
577
	do {
578
		if (SVC_RECV(xprt, &msg)) {
579
			/* find the exported program and call it */
580
			struct svc_callout *s;
581
			enum auth_stat why;
582
583
			r.rq_xprt = xprt;
584
			r.rq_prog = msg.rm_call.cb_prog;
585
			r.rq_vers = msg.rm_call.cb_vers;
586
			r.rq_proc = msg.rm_call.cb_proc;
587
			r.rq_cred = msg.rm_call.cb_cred;
588
			/* first authenticate the message */
589
			if ((why= _authenticate(&r, &msg)) != AUTH_OK) {
590
				svcerr_auth(xprt, why);
591
				goto call_done;
592
			}
593
			/* now match message with a registered service*/
594
			prog_found = FALSE;
595
			low_vers = (u_long) -1;
596
			high_vers = 0;
597
			for (s = svc_head; s != NULL; s = s->sc_next) {
598
				if (s->sc_prog == r.rq_prog) {
599
					if (s->sc_vers == r.rq_vers) {
600
						(*s->sc_dispatch)(&r, xprt);
601
						goto call_done;
602
					}  /* found correct version */
603
					prog_found = TRUE;
604
					if (s->sc_vers < low_vers)
605
						low_vers = s->sc_vers;
606
					if (s->sc_vers > high_vers)
607
						high_vers = s->sc_vers;
608
				}   /* found correct program */
609
			}
610
			/*
611
			 * if we got here, the program or version
612
			 * is not served ...
613
			 */
614
			if (prog_found)
615
				svcerr_progvers(xprt, low_vers, high_vers);
616
			else
617
				 svcerr_noprog(xprt);
618
			/* Fall through to ... */
619
		}
620
	call_done:
621
		if ((stat = SVC_STAT(xprt)) == XPRT_DIED){
622
			SVC_DESTROY(xprt);
623
			break;
624
		}
625
	} while (stat == XPRT_MOREREQS);
626
}
627
DEF_WEAK(svc_getreq_common);