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