Home | History | Annotate | Line # | Download | only in sdpd
      1  1.12      nia /*	$NetBSD: server.c,v 1.12 2021/08/08 20:54:49 nia Exp $	*/
      2   1.1  gdamore 
      3   1.1  gdamore /*-
      4   1.1  gdamore  * Copyright (c) 2006 Itronix Inc.
      5   1.1  gdamore  * All rights reserved.
      6   1.1  gdamore  *
      7   1.1  gdamore  * Redistribution and use in source and binary forms, with or without
      8   1.1  gdamore  * modification, are permitted provided that the following conditions
      9   1.1  gdamore  * are met:
     10   1.1  gdamore  * 1. Redistributions of source code must retain the above copyright
     11   1.1  gdamore  *    notice, this list of conditions and the following disclaimer.
     12   1.1  gdamore  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.1  gdamore  *    notice, this list of conditions and the following disclaimer in the
     14   1.1  gdamore  *    documentation and/or other materials provided with the distribution.
     15   1.1  gdamore  * 3. The name of Itronix Inc. may not be used to endorse
     16   1.1  gdamore  *    or promote products derived from this software without specific
     17   1.1  gdamore  *    prior written permission.
     18   1.1  gdamore  *
     19   1.1  gdamore  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
     20   1.1  gdamore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21   1.1  gdamore  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22   1.1  gdamore  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
     23   1.1  gdamore  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     24   1.1  gdamore  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25   1.1  gdamore  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     26   1.1  gdamore  * ON ANY THEORY OF LIABILITY, WHETHER IN
     27   1.1  gdamore  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28   1.1  gdamore  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29   1.1  gdamore  * POSSIBILITY OF SUCH DAMAGE.
     30   1.1  gdamore  */
     31   1.7   plunky /*-
     32   1.7   plunky  * Copyright (c) 2009 The NetBSD Foundation, Inc.
     33   1.1  gdamore  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>
     34   1.1  gdamore  * All rights reserved.
     35   1.1  gdamore  *
     36   1.1  gdamore  * Redistribution and use in source and binary forms, with or without
     37   1.1  gdamore  * modification, are permitted provided that the following conditions
     38   1.1  gdamore  * are met:
     39   1.1  gdamore  * 1. Redistributions of source code must retain the above copyright
     40   1.1  gdamore  *    notice, this list of conditions and the following disclaimer.
     41   1.1  gdamore  * 2. Redistributions in binary form must reproduce the above copyright
     42   1.1  gdamore  *    notice, this list of conditions and the following disclaimer in the
     43   1.1  gdamore  *    documentation and/or other materials provided with the distribution.
     44   1.1  gdamore  *
     45   1.1  gdamore  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     46   1.1  gdamore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     47   1.1  gdamore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     48   1.1  gdamore  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     49   1.1  gdamore  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     50   1.1  gdamore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     51   1.1  gdamore  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     52   1.1  gdamore  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     53   1.1  gdamore  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     54   1.1  gdamore  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     55   1.1  gdamore  * SUCH DAMAGE.
     56   1.1  gdamore  *
     57   1.1  gdamore  * $FreeBSD: src/usr.sbin/bluetooth/sdpd/server.c,v 1.2 2005/12/06 17:56:36 emax Exp $
     58   1.1  gdamore  */
     59   1.1  gdamore 
     60   1.1  gdamore #include <sys/cdefs.h>
     61  1.12      nia __RCSID("$NetBSD: server.c,v 1.12 2021/08/08 20:54:49 nia Exp $");
     62   1.1  gdamore 
     63   1.1  gdamore #include <sys/select.h>
     64   1.1  gdamore #include <sys/stat.h>
     65   1.1  gdamore #include <sys/ucred.h>
     66   1.1  gdamore #include <sys/un.h>
     67   1.7   plunky 
     68   1.1  gdamore #include <assert.h>
     69   1.1  gdamore #include <bluetooth.h>
     70   1.1  gdamore #include <errno.h>
     71   1.3   plunky #include <grp.h>
     72   1.1  gdamore #include <pwd.h>
     73   1.1  gdamore #include <sdp.h>
     74   1.1  gdamore #include <stdio.h>
     75   1.1  gdamore #include <stdlib.h>
     76   1.1  gdamore #include <string.h>
     77   1.1  gdamore #include <unistd.h>
     78   1.7   plunky 
     79   1.7   plunky #include "sdpd.h"
     80   1.7   plunky 
     81   1.7   plunky static bool	server_open_control	(server_t *, char const *);
     82   1.7   plunky static bool	server_open_l2cap	(server_t *);
     83   1.7   plunky static void	server_accept_client	(server_t *, int);
     84   1.7   plunky static bool	server_process_request	(server_t *, int);
     85   1.7   plunky static void	server_close_fd		(server_t *, int);
     86   1.7   plunky static bool	server_auth_check	(server_t *, void *);
     87   1.7   plunky 
     88   1.7   plunky /* number of groups we allocate space for in cmsg */
     89   1.7   plunky #define MAX_GROUPS	20
     90   1.1  gdamore 
     91   1.1  gdamore /*
     92   1.1  gdamore  * Initialize server
     93   1.1  gdamore  */
     94   1.7   plunky bool
     95   1.7   plunky server_init(server_t *srv, char const *control, char const *sgroup)
     96   1.1  gdamore {
     97   1.1  gdamore 
     98   1.1  gdamore 	assert(srv != NULL);
     99   1.1  gdamore 	assert(control != NULL);
    100   1.1  gdamore 
    101  1.10    joerg 	memset(srv, 0, sizeof(*srv));
    102   1.7   plunky 	FD_ZERO(&srv->fdset);
    103   1.3   plunky 	srv->sgroup = sgroup;
    104   1.1  gdamore 
    105   1.7   plunky 	srv->fdmax = -1;
    106   1.7   plunky 	srv->fdidx = calloc(FD_SETSIZE, sizeof(fd_idx_t));
    107   1.7   plunky 	if (srv->fdidx == NULL) {
    108   1.7   plunky 		log_crit("Failed to allocate fd index");
    109   1.7   plunky 		goto fail;
    110   1.7   plunky 	}
    111   1.7   plunky 
    112   1.7   plunky 	srv->ctllen = CMSG_SPACE(SOCKCREDSIZE(MAX_GROUPS));
    113   1.7   plunky 	srv->ctlbuf = malloc(srv->ctllen);
    114   1.7   plunky 	if (srv->ctlbuf == NULL) {
    115  1.11    joerg 		log_crit("Malloc cmsg buffer (len=%zu) failed.", srv->ctllen);
    116   1.7   plunky 		goto fail;
    117   1.7   plunky 	}
    118   1.7   plunky 
    119   1.7   plunky 	srv->imtu = SDP_LOCAL_MTU - sizeof(sdp_pdu_t);
    120   1.7   plunky 	srv->ibuf = malloc(srv->imtu);
    121   1.7   plunky 	if (srv->ibuf == NULL) {
    122   1.7   plunky 		log_crit("Malloc input buffer (imtu=%d) failed.", srv->imtu);
    123   1.7   plunky 		goto fail;
    124   1.7   plunky 	}
    125   1.7   plunky 
    126   1.7   plunky 	srv->omtu = L2CAP_MTU_DEFAULT - sizeof(sdp_pdu_t);
    127   1.7   plunky 	srv->obuf = malloc(srv->omtu);
    128   1.7   plunky 	if (srv->obuf == NULL) {
    129   1.7   plunky 		log_crit("Malloc output buffer (omtu=%d) failed.", srv->omtu);
    130   1.7   plunky 		goto fail;
    131   1.7   plunky 	}
    132   1.7   plunky 
    133   1.7   plunky 	if (db_init(srv)
    134   1.7   plunky 	    && server_open_control(srv, control)
    135   1.7   plunky 	    && server_open_l2cap(srv))
    136   1.7   plunky 		return true;
    137   1.7   plunky 
    138   1.7   plunky fail:
    139   1.7   plunky 	server_shutdown(srv);
    140   1.7   plunky 	return false;
    141   1.7   plunky }
    142   1.7   plunky 
    143   1.7   plunky /*
    144   1.7   plunky  * Open local control socket
    145   1.7   plunky  */
    146   1.7   plunky static bool
    147   1.7   plunky server_open_control(server_t *srv, char const *control)
    148   1.7   plunky {
    149   1.7   plunky 	struct sockaddr_un	un;
    150   1.7   plunky 	int			opt, fd;
    151   1.7   plunky 
    152   1.7   plunky 	if (unlink(control) == -1 && errno != ENOENT) {
    153   1.1  gdamore 		log_crit("Could not unlink(%s). %s (%d)",
    154   1.7   plunky 		    control, strerror(errno), errno);
    155   1.7   plunky 
    156   1.7   plunky 		return false;
    157   1.1  gdamore 	}
    158   1.1  gdamore 
    159   1.7   plunky 	fd = socket(PF_LOCAL, SOCK_STREAM, 0);
    160   1.7   plunky 	if (fd == -1) {
    161   1.1  gdamore 		log_crit("Could not create control socket. %s (%d)",
    162   1.7   plunky 		    strerror(errno), errno);
    163   1.7   plunky 
    164   1.7   plunky 		return false;
    165   1.1  gdamore 	}
    166   1.1  gdamore 
    167   1.1  gdamore 	opt = 1;
    168  1.12      nia 	if (setsockopt(fd, SOL_LOCAL, LOCAL_CREDS, &opt, sizeof(opt)) == -1)
    169   1.1  gdamore 		log_crit("Warning: No credential checks on control socket");
    170   1.1  gdamore 
    171   1.1  gdamore 	memset(&un, 0, sizeof(un));
    172   1.1  gdamore 	un.sun_len = sizeof(un);
    173   1.1  gdamore 	un.sun_family = AF_LOCAL;
    174   1.1  gdamore 	strlcpy(un.sun_path, control, sizeof(un.sun_path));
    175   1.1  gdamore 
    176   1.7   plunky 	if (bind(fd, (struct sockaddr *) &un, sizeof(un)) == -1) {
    177   1.1  gdamore 		log_crit("Could not bind control socket. %s (%d)",
    178   1.7   plunky 		    strerror(errno), errno);
    179   1.7   plunky 
    180   1.7   plunky 		close(fd);
    181   1.7   plunky 		return false;
    182   1.1  gdamore 	}
    183   1.1  gdamore 
    184   1.7   plunky 	if (chmod(control, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) {
    185   1.7   plunky 		log_crit("Could not set permissions on control socket. %s (%d)",
    186   1.7   plunky 		    strerror(errno), errno);
    187   1.7   plunky 
    188   1.7   plunky 		close(fd);
    189   1.7   plunky 		return false;
    190   1.1  gdamore 	}
    191   1.1  gdamore 
    192   1.7   plunky 	if (listen(fd, 5) == -1) {
    193   1.1  gdamore 		log_crit("Could not listen on control socket. %s (%d)",
    194   1.7   plunky 		    strerror(errno), errno);
    195   1.7   plunky 
    196   1.7   plunky 		close(fd);
    197   1.7   plunky 		return false;
    198   1.1  gdamore 	}
    199   1.1  gdamore 
    200   1.7   plunky 	/* Add control descriptor to index */
    201   1.7   plunky 	if (fd > srv->fdmax)
    202   1.7   plunky 		srv->fdmax = fd;
    203   1.7   plunky 
    204   1.7   plunky 	FD_SET(fd, &srv->fdset);
    205   1.7   plunky 	srv->fdidx[fd].valid = true;
    206   1.7   plunky 	srv->fdidx[fd].server = true;
    207   1.7   plunky 	srv->fdidx[fd].control = true;
    208   1.7   plunky 	srv->fdidx[fd].priv = false;
    209   1.7   plunky 	return true;
    210   1.7   plunky }
    211   1.7   plunky 
    212   1.7   plunky /*
    213   1.7   plunky  * Open L2CAP server socket
    214   1.7   plunky  */
    215   1.7   plunky static bool
    216   1.7   plunky server_open_l2cap(server_t *srv)
    217   1.7   plunky {
    218   1.7   plunky 	struct sockaddr_bt	sa;
    219   1.7   plunky 	int			fd;
    220   1.7   plunky 
    221   1.7   plunky 	fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
    222   1.7   plunky 	if (fd == -1) {
    223   1.1  gdamore 		log_crit("Could not create L2CAP socket. %s (%d)",
    224   1.7   plunky 		    strerror(errno), errno);
    225   1.7   plunky 
    226   1.7   plunky 		return false;
    227   1.1  gdamore 	}
    228   1.1  gdamore 
    229   1.7   plunky         if (setsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_IMTU,
    230   1.7   plunky 	    &srv->imtu, sizeof(srv->imtu)) == -1) {
    231   1.7   plunky 		log_crit("Could not set L2CAP Incoming MTU. %s (%d)",
    232   1.7   plunky 		    strerror(errno), errno);
    233   1.7   plunky 
    234   1.7   plunky 		close(fd);
    235   1.7   plunky 		return false;
    236   1.1  gdamore         }
    237   1.1  gdamore 
    238   1.7   plunky 	memset(&sa, 0, sizeof(sa));
    239   1.7   plunky 	sa.bt_len = sizeof(sa);
    240   1.7   plunky 	sa.bt_family = AF_BLUETOOTH;
    241   1.7   plunky 	sa.bt_psm = L2CAP_PSM_SDP;
    242   1.7   plunky 	bdaddr_copy(&sa.bt_bdaddr, BDADDR_ANY);
    243   1.1  gdamore 
    244   1.7   plunky 	if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
    245   1.1  gdamore 		log_crit("Could not bind L2CAP socket. %s (%d)",
    246   1.7   plunky 		    strerror(errno), errno);
    247   1.7   plunky 
    248   1.7   plunky 		close(fd);
    249   1.7   plunky 		return false;
    250   1.1  gdamore 	}
    251   1.1  gdamore 
    252   1.7   plunky 	if (listen(fd, 5) == -1) {
    253   1.1  gdamore 		log_crit("Could not listen on L2CAP socket. %s (%d)",
    254   1.7   plunky 		    strerror(errno), errno);
    255   1.7   plunky 
    256   1.7   plunky 		close(fd);
    257   1.7   plunky 		return false;
    258   1.1  gdamore 	}
    259   1.1  gdamore 
    260   1.7   plunky 	/* Add L2CAP descriptor to index */
    261   1.7   plunky 	if (fd > srv->fdmax)
    262   1.7   plunky 		srv->fdmax = fd;
    263   1.7   plunky 
    264   1.7   plunky 	FD_SET(fd, &srv->fdset);
    265   1.7   plunky 	srv->fdidx[fd].valid = true;
    266   1.7   plunky 	srv->fdidx[fd].server = true;
    267   1.7   plunky 	srv->fdidx[fd].control = false;
    268   1.7   plunky 	srv->fdidx[fd].priv = false;
    269   1.7   plunky 	return true;
    270   1.1  gdamore }
    271   1.1  gdamore 
    272   1.1  gdamore /*
    273   1.1  gdamore  * Shutdown server
    274   1.1  gdamore  */
    275   1.1  gdamore void
    276   1.7   plunky server_shutdown(server_t *srv)
    277   1.1  gdamore {
    278   1.7   plunky 	record_t *r;
    279   1.1  gdamore 	int	fd;
    280   1.1  gdamore 
    281   1.1  gdamore 	assert(srv != NULL);
    282   1.1  gdamore 
    283   1.7   plunky 	while ((r = LIST_FIRST(&srv->rlist)) != NULL) {
    284   1.7   plunky 		LIST_REMOVE(r, next);
    285   1.7   plunky 		free(r);
    286   1.7   plunky 	}
    287   1.7   plunky 
    288   1.7   plunky 	for (fd = 0; fd < srv->fdmax + 1; fd ++) {
    289   1.1  gdamore 		if (srv->fdidx[fd].valid)
    290   1.1  gdamore 			server_close_fd(srv, fd);
    291   1.7   plunky 	}
    292   1.1  gdamore 
    293   1.1  gdamore 	free(srv->fdidx);
    294   1.7   plunky 	free(srv->ctlbuf);
    295   1.7   plunky 	free(srv->ibuf);
    296   1.7   plunky 	free(srv->obuf);
    297   1.1  gdamore 
    298   1.1  gdamore 	memset(srv, 0, sizeof(*srv));
    299   1.1  gdamore }
    300   1.1  gdamore 
    301   1.1  gdamore /*
    302   1.1  gdamore  * Do one server iteration
    303   1.1  gdamore  */
    304   1.7   plunky bool
    305   1.7   plunky server_do(server_t *srv)
    306   1.1  gdamore {
    307   1.1  gdamore 	fd_set	fdset;
    308   1.7   plunky 	int	n, fd;
    309   1.1  gdamore 
    310   1.1  gdamore 	assert(srv != NULL);
    311   1.1  gdamore 
    312   1.1  gdamore 	memcpy(&fdset, &srv->fdset, sizeof(fdset));
    313   1.7   plunky 	n = select(srv->fdmax + 1, &fdset, NULL, NULL, NULL);
    314   1.7   plunky 	if (n == -1) {
    315   1.1  gdamore 		if (errno == EINTR)
    316   1.7   plunky 			return true;
    317   1.1  gdamore 
    318   1.1  gdamore 		log_err("Could not select(%d, %p). %s (%d)",
    319   1.7   plunky 		    srv->fdmax + 1, &fdset, strerror(errno), errno);
    320   1.1  gdamore 
    321   1.7   plunky 		return false;
    322   1.1  gdamore 	}
    323   1.1  gdamore 
    324   1.7   plunky 	for (fd = 0; fd < srv->fdmax + 1 && n > 0; fd++) {
    325   1.1  gdamore 		if (!FD_ISSET(fd, &fdset))
    326   1.1  gdamore 			continue;
    327   1.1  gdamore 
    328   1.1  gdamore 		assert(srv->fdidx[fd].valid);
    329   1.1  gdamore 
    330   1.1  gdamore 		if (srv->fdidx[fd].server)
    331   1.1  gdamore 			server_accept_client(srv, fd);
    332   1.7   plunky 		else if (!server_process_request(srv, fd))
    333   1.1  gdamore 			server_close_fd(srv, fd);
    334   1.7   plunky 
    335   1.7   plunky 		n--;
    336   1.1  gdamore 	}
    337   1.1  gdamore 
    338   1.7   plunky 	return true;
    339   1.1  gdamore 
    340   1.1  gdamore }
    341   1.1  gdamore 
    342   1.1  gdamore /*
    343   1.1  gdamore  * Accept new client connection and register it with index
    344   1.1  gdamore  */
    345   1.1  gdamore static void
    346   1.7   plunky server_accept_client(server_t *srv, int fd)
    347   1.1  gdamore {
    348   1.7   plunky 	struct sockaddr_bt	sa;
    349   1.7   plunky 	socklen_t		len;
    350   1.7   plunky 	int			cfd;
    351   1.7   plunky 	uint16_t		omtu;
    352   1.1  gdamore 
    353   1.1  gdamore 	do {
    354   1.1  gdamore 		cfd = accept(fd, NULL, NULL);
    355   1.7   plunky 	} while (cfd == -1 && errno == EINTR);
    356   1.1  gdamore 
    357   1.7   plunky 	if (cfd == -1) {
    358   1.1  gdamore 		log_err("Could not accept connection on %s socket. %s (%d)",
    359   1.7   plunky 		    srv->fdidx[fd].control ? "control" : "L2CAP",
    360   1.7   plunky 		    strerror(errno), errno);
    361   1.7   plunky 
    362   1.7   plunky 		return;
    363   1.7   plunky 	}
    364   1.7   plunky 
    365   1.7   plunky 	if (cfd >= FD_SETSIZE) {
    366   1.7   plunky 		log_crit("File descriptor too large");
    367   1.7   plunky 		close(cfd);
    368   1.1  gdamore 		return;
    369   1.1  gdamore 	}
    370   1.1  gdamore 
    371   1.1  gdamore 	assert(!FD_ISSET(cfd, &srv->fdset));
    372   1.1  gdamore 	assert(!srv->fdidx[cfd].valid);
    373   1.1  gdamore 
    374   1.7   plunky 	memset(&sa, 0, sizeof(sa));
    375   1.7   plunky 	omtu = srv->omtu;
    376   1.7   plunky 
    377   1.1  gdamore 	if (!srv->fdidx[fd].control) {
    378   1.7   plunky 		len = sizeof(sa);
    379   1.7   plunky 		if (getsockname(cfd, (struct sockaddr *)&sa, &len) == -1)
    380   1.7   plunky 			log_warning("getsockname failed, using BDADDR_ANY");
    381   1.7   plunky 
    382   1.7   plunky 		len = sizeof(omtu);
    383   1.7   plunky 	        if (getsockopt(cfd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &omtu, &len) == -1)
    384   1.7   plunky 			log_warning("Could not get L2CAP OMTU, using %d", omtu);
    385   1.7   plunky 		else
    386   1.7   plunky 			omtu -= sizeof(sdp_pdu_t);
    387   1.1  gdamore 	}
    388   1.1  gdamore 
    389   1.7   plunky 	/* Add client descriptor to the index */
    390   1.7   plunky 	if (cfd > srv->fdmax)
    391   1.7   plunky 		srv->fdmax = cfd;
    392   1.1  gdamore 
    393   1.1  gdamore 	FD_SET(cfd, &srv->fdset);
    394   1.7   plunky 	srv->fdidx[cfd].valid = true;
    395   1.7   plunky 	srv->fdidx[cfd].server = false;
    396   1.1  gdamore 	srv->fdidx[cfd].control = srv->fdidx[fd].control;
    397   1.7   plunky 	srv->fdidx[cfd].priv = false;
    398   1.7   plunky 	srv->fdidx[cfd].omtu = (omtu > srv->omtu) ? srv->omtu : omtu;
    399   1.7   plunky 	srv->fdidx[cfd].offset = 0;
    400   1.7   plunky 	bdaddr_copy(&srv->fdidx[cfd].bdaddr, &sa.bt_bdaddr);
    401   1.8   plunky 
    402   1.8   plunky 	log_debug("new %s client on fd#%d",
    403   1.8   plunky 	    srv->fdidx[cfd].control ? "control" : "L2CAP", cfd);
    404   1.1  gdamore }
    405   1.1  gdamore 
    406   1.1  gdamore /*
    407   1.1  gdamore  * Process request from the client
    408   1.1  gdamore  */
    409   1.7   plunky static bool
    410   1.7   plunky server_process_request(server_t *srv, int fd)
    411   1.1  gdamore {
    412   1.1  gdamore 	struct msghdr	msg;
    413   1.7   plunky 	struct iovec	iov[2];
    414   1.1  gdamore 	struct cmsghdr	*cmsg;
    415   1.7   plunky 	ssize_t		len;
    416   1.7   plunky 	uint16_t	error;
    417   1.1  gdamore 
    418   1.1  gdamore 	assert(FD_ISSET(fd, &srv->fdset));
    419   1.1  gdamore 	assert(srv->fdidx[fd].valid);
    420   1.1  gdamore 	assert(!srv->fdidx[fd].server);
    421   1.1  gdamore 
    422   1.7   plunky 	iov[0].iov_base = &srv->pdu;
    423   1.7   plunky 	iov[0].iov_len = sizeof(srv->pdu);
    424   1.7   plunky 	iov[1].iov_base = srv->ibuf;
    425   1.7   plunky 	iov[1].iov_len = srv->imtu;
    426   1.1  gdamore 
    427   1.1  gdamore 	msg.msg_name = NULL;
    428   1.1  gdamore 	msg.msg_namelen = 0;
    429   1.7   plunky 	msg.msg_iov = iov;
    430   1.7   plunky 	msg.msg_iovlen = __arraycount(iov);
    431   1.7   plunky 	msg.msg_control = srv->ctlbuf;
    432   1.7   plunky 	msg.msg_controllen = srv->ctllen;
    433   1.1  gdamore 	msg.msg_flags = 0;
    434   1.1  gdamore 
    435   1.1  gdamore 	do {
    436   1.1  gdamore 		len = recvmsg(fd, &msg, 0);
    437   1.7   plunky 	} while (len == -1 && errno == EINTR);
    438   1.7   plunky 
    439   1.7   plunky 	if (len == -1) {
    440   1.7   plunky 		log_err("Could not receive SDP request on %s socket. %s (%d)",
    441   1.7   plunky 		    srv->fdidx[fd].control ? "control" : "L2CAP",
    442   1.7   plunky 		    strerror(errno), errno);
    443   1.1  gdamore 
    444   1.7   plunky 		return false;
    445   1.1  gdamore 	}
    446   1.7   plunky 
    447   1.1  gdamore 	if (len == 0) {
    448   1.1  gdamore 		log_info("Client on %s socket has disconnected",
    449   1.7   plunky 		    srv->fdidx[fd].control ? "control" : "L2CAP");
    450   1.7   plunky 
    451   1.7   plunky 		return false;
    452   1.1  gdamore 	}
    453   1.1  gdamore 
    454   1.7   plunky 	if (msg.msg_flags & MSG_TRUNC)
    455   1.7   plunky 		log_info("Truncated message on %s socket",
    456   1.7   plunky 		    srv->fdidx[fd].control ? "control" : "L2CAP");
    457   1.5   plunky 
    458   1.7   plunky 	if ((cmsg = CMSG_FIRSTHDR(&msg)) != NULL
    459   1.1  gdamore 	    && cmsg->cmsg_level == SOL_SOCKET
    460   1.1  gdamore 	    && cmsg->cmsg_type == SCM_CREDS
    461   1.3   plunky 	    && cmsg->cmsg_len >= CMSG_LEN(SOCKCREDSIZE(0)))
    462   1.7   plunky 		srv->fdidx[fd].priv = server_auth_check(srv, CMSG_DATA(cmsg));
    463   1.2   plunky 
    464   1.7   plunky 	srv->pdu.len = be16toh(srv->pdu.len);
    465   1.7   plunky 
    466   1.7   plunky 	if ((uint32_t)len < sizeof(srv->pdu)
    467   1.7   plunky 	    || (uint32_t)len != sizeof(srv->pdu) + srv->pdu.len) {
    468   1.7   plunky 		error = SDP_ERROR_CODE_INVALID_PDU_SIZE;
    469   1.7   plunky 	} else {
    470   1.7   plunky 		switch (srv->pdu.pid) {
    471   1.1  gdamore 		case SDP_PDU_SERVICE_SEARCH_REQUEST:
    472   1.7   plunky 			error = service_search_request(srv, fd);
    473   1.1  gdamore 			break;
    474   1.1  gdamore 
    475   1.1  gdamore 		case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST:
    476   1.7   plunky 			error = service_attribute_request(srv, fd);
    477   1.1  gdamore 			break;
    478   1.1  gdamore 
    479   1.1  gdamore 		case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST:
    480   1.7   plunky 			error = service_search_attribute_request(srv, fd);
    481   1.1  gdamore 			break;
    482   1.1  gdamore 
    483   1.7   plunky #ifdef SDP_COMPAT
    484   1.1  gdamore 		case SDP_PDU_SERVICE_REGISTER_REQUEST:
    485   1.7   plunky 			error = compat_register_request(srv, fd);
    486   1.1  gdamore 			break;
    487   1.1  gdamore 
    488   1.1  gdamore 		case SDP_PDU_SERVICE_CHANGE_REQUEST:
    489   1.7   plunky 			error = compat_change_request(srv, fd);
    490   1.1  gdamore 			break;
    491   1.7   plunky #endif
    492   1.1  gdamore 
    493   1.7   plunky 		case SDP_PDU_RECORD_INSERT_REQUEST:
    494   1.7   plunky 			error = record_insert_request(srv, fd);
    495   1.1  gdamore 			break;
    496   1.1  gdamore 
    497   1.7   plunky 		case SDP_PDU_RECORD_UPDATE_REQUEST:
    498   1.7   plunky 			error = record_update_request(srv, fd);
    499   1.1  gdamore 			break;
    500   1.1  gdamore 
    501   1.7   plunky 		case SDP_PDU_RECORD_REMOVE_REQUEST:
    502   1.7   plunky 			error = record_remove_request(srv, fd);
    503   1.1  gdamore 			break;
    504   1.1  gdamore 
    505   1.1  gdamore 		default:
    506   1.1  gdamore 			error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    507   1.1  gdamore 			break;
    508   1.1  gdamore 		}
    509   1.1  gdamore 	}
    510   1.1  gdamore 
    511   1.1  gdamore 	if (error != 0) {
    512   1.7   plunky 		srv->fdidx[fd].offset = 0;
    513   1.7   plunky 		db_unselect(srv, fd);
    514   1.7   plunky 		srv->pdu.pid = SDP_PDU_ERROR_RESPONSE;
    515   1.7   plunky 		srv->pdu.len = sizeof(error);
    516   1.7   plunky 		be16enc(srv->obuf, error);
    517   1.8   plunky 		log_debug("sending ErrorResponse (error=0x%04x)", error);
    518   1.1  gdamore 	}
    519   1.1  gdamore 
    520   1.7   plunky 	iov[0].iov_base = &srv->pdu;
    521   1.7   plunky 	iov[0].iov_len = sizeof(srv->pdu);
    522   1.7   plunky 	iov[1].iov_base = srv->obuf;
    523   1.7   plunky 	iov[1].iov_len = srv->pdu.len;
    524   1.7   plunky 
    525   1.7   plunky 	srv->pdu.len = htobe16(srv->pdu.len);
    526   1.1  gdamore 
    527   1.7   plunky 	msg.msg_name = NULL;
    528   1.7   plunky 	msg.msg_namelen = 0;
    529   1.7   plunky 	msg.msg_iov = iov;
    530   1.7   plunky 	msg.msg_iovlen = __arraycount(iov);
    531   1.7   plunky 	msg.msg_control = NULL;
    532   1.7   plunky 	msg.msg_controllen = 0;
    533   1.7   plunky 	msg.msg_flags = 0;
    534   1.1  gdamore 
    535   1.7   plunky 	do {
    536   1.7   plunky 		len = sendmsg(fd, &msg, 0);
    537   1.7   plunky 	} while (len == -1 && errno == EINTR);
    538   1.1  gdamore 
    539   1.7   plunky 	if (len == -1) {
    540   1.7   plunky 		log_err("Could not send SDP response on %s socket. %s (%d)",
    541   1.7   plunky 		    srv->fdidx[fd].control ? "control" : "L2CAP",
    542   1.7   plunky 		    strerror(errno), errno);
    543   1.1  gdamore 
    544   1.7   plunky 		return false;
    545   1.7   plunky 	}
    546   1.1  gdamore 
    547   1.7   plunky 	return true;
    548   1.1  gdamore }
    549   1.1  gdamore 
    550   1.1  gdamore /*
    551   1.1  gdamore  * Close descriptor and remove it from index
    552   1.1  gdamore  */
    553   1.1  gdamore static void
    554   1.7   plunky server_close_fd(server_t *srv, int fd)
    555   1.1  gdamore {
    556   1.1  gdamore 
    557   1.1  gdamore 	assert(FD_ISSET(fd, &srv->fdset));
    558   1.1  gdamore 	assert(srv->fdidx[fd].valid);
    559   1.1  gdamore 
    560   1.7   plunky 	db_unselect(srv, fd);	/* release selected records */
    561   1.7   plunky 	db_release(srv, fd);	/* expire owned records */
    562   1.7   plunky 
    563   1.1  gdamore 	close(fd);
    564   1.1  gdamore 	FD_CLR(fd, &srv->fdset);
    565   1.7   plunky 	srv->fdidx[fd].valid = false;
    566   1.1  gdamore 
    567   1.9   plunky 	log_debug("client on fd#%d closed", fd);
    568   1.9   plunky 
    569   1.7   plunky 	if (fd == srv->fdmax) {
    570   1.7   plunky 		while (fd > 0 && !srv->fdidx[fd].valid)
    571   1.7   plunky 			fd--;
    572   1.1  gdamore 
    573   1.7   plunky 		srv->fdmax = fd;
    574   1.1  gdamore 	}
    575   1.1  gdamore }
    576   1.3   plunky 
    577   1.7   plunky /*
    578   1.7   plunky  * check credentials, return true when permitted to modify service records
    579   1.7   plunky  */
    580   1.7   plunky static bool
    581   1.7   plunky server_auth_check(server_t *srv, void *data)
    582   1.3   plunky {
    583   1.7   plunky 	struct sockcred *cred = data;
    584   1.3   plunky 	struct group *grp;
    585   1.3   plunky 	int n;
    586   1.3   plunky 
    587   1.3   plunky 	if (cred == NULL)
    588   1.7   plunky 		return false;
    589   1.3   plunky 
    590   1.3   plunky 	if (cred->sc_uid == 0 || cred->sc_euid == 0)
    591   1.7   plunky 		return true;
    592   1.3   plunky 
    593   1.3   plunky 	if (srv->sgroup == NULL)
    594   1.7   plunky 		return false;
    595   1.3   plunky 
    596   1.3   plunky 	grp = getgrnam(srv->sgroup);
    597   1.3   plunky 	if (grp == NULL) {
    598   1.3   plunky 		log_err("No gid for group '%s'", srv->sgroup);
    599   1.3   plunky 		srv->sgroup = NULL;
    600   1.7   plunky 		return false;
    601   1.3   plunky 	}
    602   1.3   plunky 
    603   1.3   plunky 	if (cred->sc_gid == grp->gr_gid || cred->sc_egid == grp->gr_gid)
    604   1.7   plunky 		return true;
    605   1.7   plunky 
    606   1.7   plunky 	if (cred->sc_ngroups > MAX_GROUPS) {
    607   1.7   plunky 		log_info("Credentials truncated, lost %d groups",
    608   1.7   plunky 		    MAX_GROUPS - cred->sc_ngroups);
    609   1.7   plunky 
    610   1.7   plunky 		cred->sc_ngroups = MAX_GROUPS;
    611   1.7   plunky 	}
    612   1.3   plunky 
    613   1.3   plunky 	for (n = 0 ; n < cred->sc_ngroups ; n++) {
    614   1.3   plunky 		if (cred->sc_groups[n] == grp->gr_gid)
    615   1.7   plunky 			return true;
    616   1.3   plunky 	}
    617   1.3   plunky 
    618   1.7   plunky 	return false;
    619   1.3   plunky }
    620