xref: /netbsd-src/usr.sbin/sdpd/server.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
1 /*	$NetBSD: server.c,v 1.5 2009/01/15 23:17:00 plunky Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006 Itronix Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of Itronix Inc. may not be used to endorse
16  *    or promote products derived from this software without specific
17  *    prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*
32  * server.c
33  *
34  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  *
46  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
47  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
50  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56  * SUCH DAMAGE.
57  *
58  * $Id: server.c,v 1.5 2009/01/15 23:17:00 plunky Exp $
59  * $FreeBSD: src/usr.sbin/bluetooth/sdpd/server.c,v 1.2 2005/12/06 17:56:36 emax Exp $
60  */
61 
62 #include <sys/cdefs.h>
63 __RCSID("$NetBSD: server.c,v 1.5 2009/01/15 23:17:00 plunky Exp $");
64 
65 #include <sys/param.h>
66 #include <sys/select.h>
67 #include <sys/stat.h>
68 #include <sys/queue.h>
69 #include <sys/ucred.h>
70 #include <sys/un.h>
71 #include <netinet/in.h>
72 #include <arpa/inet.h>
73 #include <assert.h>
74 #include <bluetooth.h>
75 #include <errno.h>
76 #include <grp.h>
77 #include <pwd.h>
78 #include <sdp.h>
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <string.h>
82 #include <unistd.h>
83 #include "log.h"
84 #include "profile.h"
85 #include "provider.h"
86 #include "server.h"
87 
88 static void	server_accept_client		(server_p srv, int32_t fd);
89 static int32_t	server_process_request		(server_p srv, int32_t fd);
90 static int32_t	server_send_error_response	(server_p srv, int32_t fd,
91 						 uint16_t error);
92 static void	server_close_fd			(server_p srv, int32_t fd);
93 static int	server_auth_check		(server_p srv, struct sockcred *cred);
94 
95 /*
96  * Initialize server
97  */
98 
99 int32_t
100 server_init(server_p srv, char const *control, char const *sgroup)
101 {
102 	struct sockaddr_un	un;
103 	struct sockaddr_bt	l2;
104 	socklen_t		size;
105 	int32_t			unsock, l2sock;
106 	uint16_t		imtu;
107 	int			opt;
108 
109 	assert(srv != NULL);
110 	assert(control != NULL);
111 
112 	memset(srv, 0, sizeof(srv));
113 	srv->sgroup = sgroup;
114 
115 	/* Open control socket */
116 	if (unlink(control) < 0 && errno != ENOENT) {
117 		log_crit("Could not unlink(%s). %s (%d)",
118 			control, strerror(errno), errno);
119 		return (-1);
120 	}
121 
122 	unsock = socket(PF_LOCAL, SOCK_STREAM, 0);
123 	if (unsock < 0) {
124 		log_crit("Could not create control socket. %s (%d)",
125 			strerror(errno), errno);
126 		return (-1);
127 	}
128 
129 	opt = 1;
130 	if (setsockopt(unsock, 0, LOCAL_CREDS, &opt, sizeof(opt)) < 0)
131 		log_crit("Warning: No credential checks on control socket");
132 
133 	memset(&un, 0, sizeof(un));
134 	un.sun_len = sizeof(un);
135 	un.sun_family = AF_LOCAL;
136 	strlcpy(un.sun_path, control, sizeof(un.sun_path));
137 
138 	if (bind(unsock, (struct sockaddr *) &un, sizeof(un)) < 0) {
139 		log_crit("Could not bind control socket. %s (%d)",
140 			strerror(errno), errno);
141 		close(unsock);
142 		return (-1);
143 	}
144 
145 	if (chmod(control, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0) {
146 		log_crit("Could not change permissions on control socket. " \
147 			"%s (%d)", strerror(errno), errno);
148 		close(unsock);
149 		return (-1);
150 	}
151 
152 	if (listen(unsock, 10) < 0) {
153 		log_crit("Could not listen on control socket. %s (%d)",
154 			strerror(errno), errno);
155 		close(unsock);
156 		return (-1);
157 	}
158 
159 	/* Open L2CAP socket */
160 	l2sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
161 	if (l2sock < 0) {
162 		log_crit("Could not create L2CAP socket. %s (%d)",
163 			strerror(errno), errno);
164 		close(unsock);
165 		return (-1);
166 	}
167 
168 	size = sizeof(imtu);
169         if (getsockopt(l2sock, BTPROTO_L2CAP, SO_L2CAP_IMTU, &imtu, &size) < 0) {
170 		log_crit("Could not get L2CAP IMTU. %s (%d)",
171 			strerror(errno), errno);
172 		close(unsock);
173 		close(l2sock);
174 		return (-1);
175         }
176 
177 	memset(&l2, 0, sizeof(l2));
178 	l2.bt_len = sizeof(l2);
179 	l2.bt_family = AF_BLUETOOTH;
180 	l2.bt_psm = L2CAP_PSM_SDP;
181 	bdaddr_copy(&l2.bt_bdaddr, BDADDR_ANY);
182 
183 	if (bind(l2sock, (struct sockaddr *) &l2, sizeof(l2)) < 0) {
184 		log_crit("Could not bind L2CAP socket. %s (%d)",
185 			strerror(errno), errno);
186 		close(unsock);
187 		close(l2sock);
188 		return (-1);
189 	}
190 
191 	if (listen(l2sock, 10) < 0) {
192 		log_crit("Could not listen on L2CAP socket. %s (%d)",
193 			strerror(errno), errno);
194 		close(unsock);
195 		close(l2sock);
196 		return (-1);
197 	}
198 
199 	/* Allocate incoming buffer */
200 	srv->imtu = (imtu > SDP_LOCAL_MTU)? imtu : SDP_LOCAL_MTU;
201 	srv->req = (uint8_t *) calloc(srv->imtu, sizeof(srv->req[0]));
202 	if (srv->req == NULL) {
203 		log_crit("Could not allocate request buffer");
204 		close(unsock);
205 		close(l2sock);
206 		return (-1);
207 	}
208 
209 	/* Allocate memory for descriptor index */
210 	srv->fdidx = (fd_idx_p) calloc(FD_SETSIZE, sizeof(srv->fdidx[0]));
211 	if (srv->fdidx == NULL) {
212 		log_crit("Could not allocate fd index");
213 		free(srv->req);
214 		close(unsock);
215 		close(l2sock);
216 		return (-1);
217 	}
218 
219 	/* Register Service Discovery profile (attach it to control socket) */
220 	if (provider_register_sd(unsock) < 0) {
221 		log_crit("Could not register Service Discovery profile");
222 		free(srv->fdidx);
223 		free(srv->req);
224 		close(unsock);
225 		close(l2sock);
226 		return (-1);
227 	}
228 
229 	/*
230 	 * If we got here then everything is fine. Add both control sockets
231 	 * to the index.
232 	 */
233 
234 	FD_ZERO(&srv->fdset);
235 	srv->maxfd = (unsock > l2sock)? unsock : l2sock;
236 
237 	FD_SET(unsock, &srv->fdset);
238 	srv->fdidx[unsock].valid = 1;
239 	srv->fdidx[unsock].server = 1;
240 	srv->fdidx[unsock].control = 1;
241 	srv->fdidx[unsock].priv = 0;
242 	srv->fdidx[unsock].rsp_cs = 0;
243 	srv->fdidx[unsock].rsp_size = 0;
244 	srv->fdidx[unsock].rsp_limit = 0;
245 	srv->fdidx[unsock].omtu = SDP_LOCAL_MTU;
246 	srv->fdidx[unsock].rsp = NULL;
247 
248 	FD_SET(l2sock, &srv->fdset);
249 	srv->fdidx[l2sock].valid = 1;
250 	srv->fdidx[l2sock].server = 1;
251 	srv->fdidx[l2sock].control = 0;
252 	srv->fdidx[l2sock].priv = 0;
253 	srv->fdidx[l2sock].rsp_cs = 0;
254 	srv->fdidx[l2sock].rsp_size = 0;
255 	srv->fdidx[l2sock].rsp_limit = 0;
256 	srv->fdidx[l2sock].omtu = 0; /* unknown */
257 	srv->fdidx[l2sock].rsp = NULL;
258 
259 	return (0);
260 }
261 
262 /*
263  * Shutdown server
264  */
265 
266 void
267 server_shutdown(server_p srv)
268 {
269 	int	fd;
270 
271 	assert(srv != NULL);
272 
273 	for (fd = 0; fd < srv->maxfd + 1; fd ++)
274 		if (srv->fdidx[fd].valid)
275 			server_close_fd(srv, fd);
276 
277 	free(srv->req);
278 	free(srv->fdidx);
279 
280 	memset(srv, 0, sizeof(*srv));
281 }
282 
283 /*
284  * Do one server iteration
285  */
286 
287 int32_t
288 server_do(server_p srv)
289 {
290 	fd_set	fdset;
291 	int32_t	n, fd;
292 
293 	assert(srv != NULL);
294 
295 	/* Copy cached version of the fd set and call select */
296 	memcpy(&fdset, &srv->fdset, sizeof(fdset));
297 	n = select(srv->maxfd + 1, &fdset, NULL, NULL, NULL);
298 	if (n < 0) {
299 		if (errno == EINTR)
300 			return (0);
301 
302 		log_err("Could not select(%d, %p). %s (%d)",
303 			srv->maxfd + 1, &fdset, strerror(errno), errno);
304 
305 		return (-1);
306 	}
307 
308 	/* Process  descriptors */
309 	for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) {
310 		if (!FD_ISSET(fd, &fdset))
311 			continue;
312 
313 		assert(srv->fdidx[fd].valid);
314 		n --;
315 
316 		if (srv->fdidx[fd].server)
317 			server_accept_client(srv, fd);
318 		else if (server_process_request(srv, fd) != 0)
319 			server_close_fd(srv, fd);
320 	}
321 
322 	return (0);
323 
324 }
325 
326 /*
327  * Accept new client connection and register it with index
328  */
329 
330 static void
331 server_accept_client(server_p srv, int32_t fd)
332 {
333 	uint8_t		*rsp = NULL;
334 	socklen_t	 size;
335 	int32_t		 cfd;
336 	uint16_t	 omtu;
337 
338 	do {
339 		cfd = accept(fd, NULL, NULL);
340 	} while (cfd < 0 && errno == EINTR);
341 
342 	if (cfd < 0) {
343 		log_err("Could not accept connection on %s socket. %s (%d)",
344 			srv->fdidx[fd].control? "control" : "L2CAP",
345 			strerror(errno), errno);
346 		return;
347 	}
348 
349 	assert(!FD_ISSET(cfd, &srv->fdset));
350 	assert(!srv->fdidx[cfd].valid);
351 
352 	if (!srv->fdidx[fd].control) {
353 		/* Get local BD_ADDR */
354 		size = sizeof(srv->req_sa);
355 		if (getsockname(cfd,(struct sockaddr*)&srv->req_sa, &size) < 0) {
356 			log_err("Could not get local BD_ADDR. %s (%d)",
357 				strerror(errno), errno);
358 			close(cfd);
359 			return;
360 		}
361 
362 		/* Get outgoing MTU */
363 		size = sizeof(omtu);
364 	        if (getsockopt(cfd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &omtu, &size) < 0) {
365 			log_err("Could not get L2CAP OMTU. %s (%d)",
366 				strerror(errno), errno);
367 			close(cfd);
368 			return;
369 		}
370 
371 		/*
372 		 * The maximum size of the L2CAP packet is 65536 bytes.
373 		 * The minimum L2CAP MTU is 43 bytes. That means we need
374 		 * 65536 / 43 = ~1524 chunks to transfer maximum packet
375 		 * size with minimum MTU. The "rsp_cs" field in fd_idx_t
376 		 * is 11 bit wide that gives us upto 2048 chunks.
377 		 */
378 
379 		if (omtu < L2CAP_MTU_MINIMUM) {
380 			log_err("L2CAP OMTU is too small (%d bytes)", omtu);
381 			close(cfd);
382 			return;
383 		}
384 	} else {
385 		bdaddr_copy(&srv->req_sa.bt_bdaddr, BDADDR_ANY);
386 		omtu = srv->fdidx[fd].omtu;
387 	}
388 
389 	/*
390 	 * Allocate buffer. This is an overkill, but we can not know how
391 	 * big our reply is going to be.
392 	 */
393 
394 	rsp = (uint8_t *) calloc(L2CAP_MTU_MAXIMUM, sizeof(rsp[0]));
395 	if (rsp == NULL) {
396 		log_crit("Could not allocate response buffer");
397 		close(cfd);
398 		return;
399 	}
400 
401 	/* Add client descriptor to the index */
402 	FD_SET(cfd, &srv->fdset);
403 	if (srv->maxfd < cfd)
404 		srv->maxfd = cfd;
405 	srv->fdidx[cfd].valid = 1;
406 	srv->fdidx[cfd].server = 0;
407 	srv->fdidx[cfd].control = srv->fdidx[fd].control;
408 	srv->fdidx[cfd].priv = 0;
409 	srv->fdidx[cfd].rsp_cs = 0;
410 	srv->fdidx[cfd].rsp_size = 0;
411 	srv->fdidx[cfd].rsp_limit = 0;
412 	srv->fdidx[cfd].omtu = omtu;
413 	srv->fdidx[cfd].rsp = rsp;
414 }
415 
416 /*
417  * Process request from the client
418  */
419 
420 static int32_t
421 server_process_request(server_p srv, int32_t fd)
422 {
423 	uint8_t		ctl[128];
424 	sdp_pdu_p	pdu = (sdp_pdu_p) srv->req;
425 	struct msghdr	msg;
426 	struct iovec	iov;
427 	int32_t		len, error;
428 	struct cmsghdr	*cmsg;
429 
430 	assert(srv->imtu > 0);
431 	assert(srv->req != NULL);
432 	assert(FD_ISSET(fd, &srv->fdset));
433 	assert(srv->fdidx[fd].valid);
434 	assert(!srv->fdidx[fd].server);
435 	assert(srv->fdidx[fd].rsp != NULL);
436 	assert(srv->fdidx[fd].omtu >= L2CAP_MTU_MINIMUM);
437 
438 	iov.iov_base = srv->req;
439 	iov.iov_len = srv->imtu;
440 
441 	msg.msg_name = NULL;
442 	msg.msg_namelen = 0;
443 	msg.msg_iov = &iov;
444 	msg.msg_iovlen = 1;
445 	msg.msg_control = ctl;
446 	msg.msg_controllen = sizeof(ctl);
447 	msg.msg_flags = 0;
448 
449 	do {
450 		len = recvmsg(fd, &msg, 0);
451 	} while (len < 0 && errno == EINTR);
452 
453 	if (len < 0) {
454 		log_err("Could not receive SDP request from %s socket. %s (%d)",
455 			srv->fdidx[fd].control? "control" : "L2CAP",
456 			strerror(errno), errno);
457 		return (-1);
458 	}
459 	if (len == 0) {
460 		log_info("Client on %s socket has disconnected",
461 			srv->fdidx[fd].control? "control" : "L2CAP");
462 		return (-1);
463 	}
464 
465 	if (msg.msg_flags & MSG_TRUNC) {
466 		log_err("Truncated message on %s socket",
467 			srv->fdidx[fd].control? "control" : "L2CAP");
468 		return (-1);
469 	}
470 
471 	if ((msg.msg_flags & MSG_CTRUNC) == 0
472 	    && (cmsg = CMSG_FIRSTHDR(&msg)) != NULL
473 	    && cmsg->cmsg_level == SOL_SOCKET
474 	    && cmsg->cmsg_type == SCM_CREDS
475 	    && cmsg->cmsg_len >= CMSG_LEN(SOCKCREDSIZE(0)))
476 	    	srv->fdidx[fd].priv =
477 		    server_auth_check(srv, (struct sockcred *)CMSG_DATA(cmsg));
478 
479 	if (len >= sizeof(*pdu)
480 	    && (sizeof(*pdu) + (pdu->len = ntohs(pdu->len))) == len) {
481 		switch (pdu->pid) {
482 		case SDP_PDU_SERVICE_SEARCH_REQUEST:
483 			error = server_prepare_service_search_response(srv, fd);
484 			break;
485 
486 		case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST:
487 			error = server_prepare_service_attribute_response(srv, fd);
488 			break;
489 
490 		case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST:
491 			error = server_prepare_service_search_attribute_response(srv, fd);
492 			break;
493 
494 		case SDP_PDU_SERVICE_REGISTER_REQUEST:
495 			error = server_prepare_service_register_response(srv, fd);
496 			break;
497 
498 		case SDP_PDU_SERVICE_UNREGISTER_REQUEST:
499 			error = server_prepare_service_unregister_response(srv, fd);
500 			break;
501 
502 		case SDP_PDU_SERVICE_CHANGE_REQUEST:
503 			error = server_prepare_service_change_response(srv, fd);
504 			break;
505 
506 		default:
507 			error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
508 			break;
509 		}
510 	} else
511 		error = SDP_ERROR_CODE_INVALID_PDU_SIZE;
512 
513 	if (error == 0) {
514 		switch (pdu->pid) {
515 		case SDP_PDU_SERVICE_SEARCH_REQUEST:
516 			error = server_send_service_search_response(srv, fd);
517 			break;
518 
519 		case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST:
520 			error = server_send_service_attribute_response(srv, fd);
521 			break;
522 
523 		case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST:
524 			error = server_send_service_search_attribute_response(srv, fd);
525 			break;
526 
527 		case SDP_PDU_SERVICE_REGISTER_REQUEST:
528 			error = server_send_service_register_response(srv, fd);
529 			break;
530 
531 		case SDP_PDU_SERVICE_UNREGISTER_REQUEST:
532 			error = server_send_service_unregister_response(srv, fd);
533 			break;
534 
535 		case SDP_PDU_SERVICE_CHANGE_REQUEST:
536 			error = server_send_service_change_response(srv, fd);
537 			break;
538 
539 		default:
540 			error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
541 			break;
542 		}
543 
544 		if (error != 0)
545 			log_err("Could not send SDP response to %s socket, " \
546 				"pdu->pid=%d, pdu->tid=%d, error=%d",
547 				srv->fdidx[fd].control? "control" : "L2CAP",
548 				pdu->pid, ntohs(pdu->tid), error);
549 	} else {
550 		log_err("Could not process SDP request from %s socket, " \
551 			"pdu->pid=%d, pdu->tid=%d, pdu->len=%d, len=%d, " \
552 			"error=%d",
553 			srv->fdidx[fd].control? "control" : "L2CAP",
554 			pdu->pid, ntohs(pdu->tid), pdu->len, len, error);
555 
556 		error = server_send_error_response(srv, fd, error);
557 		if (error != 0)
558 			log_err("Could not send SDP error response to %s " \
559 				"socket, pdu->pid=%d, pdu->tid=%d, error=%d",
560 				srv->fdidx[fd].control? "control" : "L2CAP",
561 				pdu->pid, ntohs(pdu->tid), error);
562 	}
563 
564 	/* On error forget response (if any) */
565 	if (error != 0) {
566 		srv->fdidx[fd].rsp_cs = 0;
567 		srv->fdidx[fd].rsp_size = 0;
568 		srv->fdidx[fd].rsp_limit = 0;
569 	}
570 
571 	return (error);
572 }
573 
574 /*
575  * Send SDP_Error_Response PDU
576  */
577 
578 static int32_t
579 server_send_error_response(server_p srv, int32_t fd, uint16_t error)
580 {
581 	int32_t	size;
582 
583 	struct {
584 		sdp_pdu_t		pdu;
585 		uint16_t		error;
586 	} __packed	rsp;
587 
588 	/* Prepare and send SDP error response */
589 	rsp.pdu.pid = SDP_PDU_ERROR_RESPONSE;
590 	rsp.pdu.tid = ((sdp_pdu_p)(srv->req))->tid;
591 	rsp.pdu.len = htons(sizeof(rsp.error));
592 	rsp.error   = htons(error);
593 
594 	do {
595 		size = write(fd, &rsp, sizeof(rsp));
596 	} while (size < 0 && errno == EINTR);
597 
598 	return ((size < 0)? errno : 0);
599 }
600 
601 /*
602  * Close descriptor and remove it from index
603  */
604 
605 static void
606 server_close_fd(server_p srv, int32_t fd)
607 {
608 	provider_p	provider = NULL, provider_next = NULL;
609 
610 	assert(FD_ISSET(fd, &srv->fdset));
611 	assert(srv->fdidx[fd].valid);
612 
613 	close(fd);
614 
615 	FD_CLR(fd, &srv->fdset);
616 	if (fd == srv->maxfd)
617 		srv->maxfd --;
618 
619 	if (srv->fdidx[fd].rsp != NULL)
620 		free(srv->fdidx[fd].rsp);
621 
622 	memset(&srv->fdidx[fd], 0, sizeof(srv->fdidx[fd]));
623 
624 	for (provider = provider_get_first();
625 	     provider != NULL;
626 	     provider = provider_next) {
627 		provider_next = provider_get_next(provider);
628 
629 		if (provider->fd == fd)
630 			provider_unregister(provider);
631 	}
632 }
633 
634 static int
635 server_auth_check(server_p srv, struct sockcred *cred)
636 {
637 	struct group *grp;
638 	int n;
639 
640 	if (cred == NULL)
641 		return 0;
642 
643 	if (cred->sc_uid == 0 || cred->sc_euid == 0)
644 		return 1;
645 
646 	if (srv->sgroup == NULL)
647 		return 0;
648 
649 	grp = getgrnam(srv->sgroup);
650 	if (grp == NULL) {
651 		log_err("No gid for group '%s'", srv->sgroup);
652 		srv->sgroup = NULL;
653 		return 0;
654 	}
655 
656 	if (cred->sc_gid == grp->gr_gid || cred->sc_egid == grp->gr_gid)
657 		return 1;
658 
659 	for (n = 0 ; n < cred->sc_ngroups ; n++) {
660 		if (cred->sc_groups[n] == grp->gr_gid)
661 			return 1;
662 	}
663 
664 	return 0;
665 }
666