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