From 55324636021e4123f720327ad1f81596ee6ca023 Mon Sep 17 00:00:00 2001 From: MaMe82 Date: Thu, 18 Oct 2018 14:40:05 +0200 Subject: [PATCH] Set bluetooth back to SSP, to make HS and thus NAP work (no PIN) --- service/bluetooth.go | 133 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 19 deletions(-) diff --git a/service/bluetooth.go b/service/bluetooth.go index 843766a..1033948 100644 --- a/service/bluetooth.go +++ b/service/bluetooth.go @@ -10,6 +10,8 @@ import ( "regexp" "strconv" "strings" + "sync" + "time" ) const ( @@ -20,41 +22,131 @@ const ( type BtService struct { RootSvc *Service - ServiceAvailable bool + serviceAvailable bool Controller *bluetooth.Controller BrName string bridgeIfDeployed bool Agent *bluetooth.DefaultAgent + + serviceAvailableLock *sync.Mutex } -func NewBtService(rootService *Service) (res *BtService) { +//Notes: If a bluetooth controller could be found with `bluetooth.FindFirstAvailableController()` +// this also means that the bluetoothd is running, as the Bluez mgmt-api is used to check for controllers + +// P4wnP1 doesn't depend on late starting systemd services like DBus, bluetoothd etc. +// In order to assure that Bluetooth functionality is present, the P4wnP1 systemd service would have to depend +// on such "late starting" services. This again means the P4wnP1 daemon would load very late and functionality like +// USB gadgets wouldn't work till service the P4wnP1 systemd service gets started. Tests with current Kali releases have +// shown that P4wnP1 is up and reachable network after about 20 to 30 seconds. +// If the P4wnP1 systemd service is changed to depend on bluetooth.service this duration increases up to 2 minutes, +// which is NOT ACCEPTABLE. +// On the other hand, access to the bluez-stack and the hci device (if present) is already possible some seconds +// after the P4wnP1 service has started (doesn't take 2 minutes). +// To deal with that, the bluetooth subsystem keeps retrying to find a working bluetooth adapter till "retryTimeout" +// is reached (the argument is handed in to NewBtService). If the hci adapter gets deployed after some seconds (as it +// happens in my tests) this means service startup of P4wnP1 increases by this duration (about 6 seconds in tests) +// On a system with a mis-configured bluetooth stack or with missing bluetooth hardware (Pi0 without WiFi/Bluetooth) +// this would mean P4wnP1 service startup consumes the full retryTime (shouldn't be the case, as we target a RPi0W +// with a custom build Kali image, which assures correct bluetooth stack setup; Pi0 without WiFi isn't supported). +// +// The current behavior could be changed, if NewBtService gets wrapped into a go-routine. The shortcoming would be, +// that every call to to BtService functions (like StartNap) would fail, even if the missing adapter could show up some +// seconds later. +// +// A polished future solution would be, to combine creation of Bluetooth SubSystem and initial configuration deployment +// in a go routine and let RPC calls relying on bluetooth sub system fail, till bluetooth is usable. This would mean that +// an event has to be PUSHED to the webclient, once bluetooth is usable. + + + +func NewBtService(rootService *Service, retryTimeout time.Duration) (res *BtService) { res = &BtService{ RootSvc: rootService, - Agent: bluetooth.NewDefaultAgent("4321"), + Agent: bluetooth.NewDefaultAgent("1337"), BrName: BT_ETHERNET_BRIDGE_NAME, + serviceAvailableLock: &sync.Mutex{}, } - // ToDo Check if bluetooth service is loaded - if c, err := bluetooth.FindFirstAvailableController(); err == nil { - res.ServiceAvailable = true - res.Controller = c - } else { - res.ServiceAvailable = false - return - } + log.Println("Starting Bluetooth sub system...") if err := CheckBluezVersion(); err != nil { - fmt.Println(err) - res.ServiceAvailable = false + log.Println(err) + + res.setServiceAvailable(false) return } + go func() { + timeStart := time.Now() + for timeSinceStart := time.Since(timeStart); !res.serviceAvailable && (timeSinceStart < retryTimeout); timeSinceStart = time.Since(timeStart) { + if c, err := bluetooth.FindFirstAvailableController(); err == nil { + res.setServiceAvailable(true) + res.Controller = c + log.Printf("... bluetooth controller found '%s' after %v\n", res.Controller.DBusPath, timeSinceStart) + } else { + log.Printf("Re-check bluetooth adapter existence %v\n", timeSinceStart) + res.setServiceAvailable(false) + } + time.Sleep(time.Second * 1) + } + if !res.serviceAvailable { + log.Printf("No bluetooth adapter found after %v\n", retryTimeout) + } + }() + + return } +func (bt *BtService) setServiceAvailable(val bool) { + bt.serviceAvailableLock.Lock() + defer bt.serviceAvailableLock.Unlock() + bt.serviceAvailable = val +} + +func (bt *BtService) IsServiceAvailable() bool { + bt.serviceAvailableLock.Lock() + defer bt.serviceAvailableLock.Unlock() + return bt.serviceAvailable +} + + +// Notes: On Bluetooth settings +// P4wnP1 is meant to run headless, which has influence on Pairing mode. There's legacy pairing (outdated and insecure) +// which allows requesting a PIN from a remote device which wants to connect. The new Pairing mode is Secure Simple Pairing +// (SSP) which add in dynamic key creation on pairing, without static PINs. There are different ways two devices could be paired, +// the way is chosen depending on the capabilities of both devices. IT ISN'T POSSIBLE TO REQUEST A PREDEFINED PIN WITH +// SECURE SIMPLE PAIRING. Bonding (=Pairing) is handled with a random passkey or in just works mode. +// As P4wnP1 could not display a passkey or request user input for a confirmation (assuming interactive access solutions +// like webclient, cli_client or ssh aren't always used), we have to fall back to "just works" mode if we want to use SSP. +// Even if a static PIN is a security issue, using just works is even more insecure. +// On the other hand, the idea to disable SSP didn't work out either, because this won't allow to set the broadcom bluetooth +// adapter to high speed. Not having high speed enabled, ultimately results in connection issues for BNEP usage +// (in fact, if a NAP is turned on without high speed, a remote device is able to pair and connect, even to receive a DHCP +// lease from the server. The latter indicates that th IP connection (layer 3) is working, but follow up traffic doesn't +// work (neither on layer 3 -l ike ICMP requests, nor on layer 4 - like TCP based SSH connections) +// The behavior of not fully working connections has been observed using a Samsung Android phone as remote device. It has +// not been confirmed that connection issues exist on other devices. Anyways, not having a common device working means +// PIN based pairing without SSP isn't an option. +// In result SSP has to be turned on in "just works" mode (we describe P4wnP1 as a device with no input and no outpu capabilities). +// +// As P4wnP1 IS NOT DESIGNED WITH SECURITY IN MIND (at least no self-protection), a new issue arises: +// +// As soon as a bluetooth is turned on and set to be discoverable and pairable, EVERY REMOTE DEVICE IS ACCEPTED TO CONNECT AND PAIR. +// (While WiFi access could at least be protected with WPA2). Neither the the WebClient, nor the RPC server deploy any kind of +// MitM protection, access restriction or other security measures to protect themselves from other ways of compromise. +// A solution has to be found to deal with that issue (in a headless way)! Possible ideas +// - allow disabling/enabling PAIRING on demand by webclient / clie +// - use limited PAIRING (timeout till pairing is disabled again) on boot, f.e. with a 30 second time window. Disable pairing +// as soon as one device connected. This till leaves a race condition +// +// For now: Pairing in "just works" mode is always on! + // ToDo: Move all controller specific tasks to controller func (bt *BtService) StartNAP() (err error) { - if !bt.ServiceAvailable { + + if !bt.IsServiceAvailable() { return bluetooth.ErrBtSvcNotAvailable } log.Println("Bluetooth: starting NAP...") @@ -69,14 +161,15 @@ func (bt *BtService) StartNAP() (err error) { } // Register custom agent bt-agent with "No Input, No Output" capabilities + // Note: This results in "just works" mode with no MitM protection (see notes above) if err = bt.Agent.Start(toolz.AGENT_CAP_NO_INPUT_NO_OUTPUT); err != nil { return err } - // Disable simple secure pairing to make PIN requests work + // SSP and HS enabled, this disables PIN requests but is needed for NAP to work (see comments above) bt.Controller.SetPowered(false) - bt.Controller.SetSSP(true) //NAP doesn't work well without SSP - bt.Controller.SetHighSpeed(true) // Enable high speed mode + bt.Controller.SetSSP(true) //Couldn't use legacy mode (no Secure Simple Pairing, but PIN based pairing), otherwise HighSpeed couldn't be enabled + bt.Controller.SetHighSpeed(true) // Enable high speed mode, yeah (without high speed, NAP connections don't work as intended) bt.Controller.SetPowered(true) // Configure adapter @@ -99,6 +192,8 @@ func (bt *BtService) StartNAP() (err error) { } err = bt.Controller.SetPairable(true) + time.Sleep(time.Second) //give some time before registering NAP to SDP + // Enable PAN networking for bridge nw, err := toolz.NetworkServer(bt.Controller.DBusPath) if err != nil { @@ -118,7 +213,7 @@ func (bt *BtService) StartNAP() (err error) { } func (bt *BtService) StopNAP() (err error) { - if !bt.ServiceAvailable { + if !bt.IsServiceAvailable() { return bluetooth.ErrBtSvcNotAvailable } log.Println("Bluetooth: stopping NAP...") @@ -262,7 +357,7 @@ func CheckBluezVersion() (err error) { if err != nil { return err } - fmt.Printf("Bluez %d.%d found (minimum needed %d.%d)\n", major, minor, BT_MINIMUM_BLUEZ_VERSION_MAJOR, BT_MINIMUM_BLUEZ_VERSION_MINOR) + log.Printf("Bluez %d.%d found (minimum needed %d.%d)\n", major, minor, BT_MINIMUM_BLUEZ_VERSION_MAJOR, BT_MINIMUM_BLUEZ_VERSION_MINOR) if major > BT_MINIMUM_BLUEZ_VERSION_MAJOR { return nil }