Files
teleport/lib/devicetrust/native/device_darwin.go
T
Alan Parra d5e329e9ea Add tsh device debug commands (#19166)
Add `tsh device collect` and `tsh device keyget`, which are useful to debug
device trust functionality.

gravitational/teleport.e#514
2022-12-09 10:38:52 -03:00

145 lines
4.0 KiB
Go

// Copyright 2022 Gravitational, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package native
// #cgo CFLAGS: -Wall -xobjective-c -fblocks -fobjc-arc -mmacosx-version-min=10.13
// #cgo LDFLAGS: -framework CoreFoundation -framework Foundation -framework IOKit -framework Security
// #include <stdint.h>
// #include <stdlib.h>
// #include "device_darwin.h"
import "C"
import (
"crypto/sha256"
"crypto/x509"
"unsafe"
"github.com/google/uuid"
"github.com/gravitational/trace"
"google.golang.org/protobuf/types/known/timestamppb"
devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1"
"github.com/gravitational/teleport/lib/darwin"
)
func enrollDeviceInit() (*devicepb.EnrollDeviceInit, error) {
cred, err := deviceKeyGetOrCreate()
if err != nil {
return nil, trace.Wrap(err)
}
cd, err := collectDeviceData()
if err != nil {
return nil, trace.Wrap(err, "collecting device data")
}
return &devicepb.EnrollDeviceInit{
CredentialId: cred.Id,
DeviceData: cd,
Macos: &devicepb.MacOSEnrollPayload{
PublicKeyDer: cred.PublicKeyDer,
},
}, nil
}
func deviceKeyGetOrCreate() (*devicepb.DeviceCredential, error) {
newID := uuid.NewString()
newIDC := C.CString(newID)
defer C.free(unsafe.Pointer(newIDC))
var pubKeyC C.PublicKey
defer func() {
C.free(unsafe.Pointer(pubKeyC.id))
C.free(unsafe.Pointer(pubKeyC.pub_key))
}()
if res := C.DeviceKeyGetOrCreate(newIDC, &pubKeyC); res != 0 {
return nil, trace.Wrap(statusErrorFromC(res))
}
id := C.GoString(pubKeyC.id)
pubKeyRaw := C.GoBytes(unsafe.Pointer(pubKeyC.pub_key), C.int(pubKeyC.pub_key_len))
return pubKeyToCredential(id, pubKeyRaw)
}
func pubKeyToCredential(id string, pubKeyRaw []byte) (*devicepb.DeviceCredential, error) {
ecPubKey, err := darwin.ECDSAPublicKeyFromRaw(pubKeyRaw)
if err != nil {
return nil, trace.Wrap(err)
}
pubKeyDER, err := x509.MarshalPKIXPublicKey(ecPubKey)
if err != nil {
return nil, trace.Wrap(err)
}
return &devicepb.DeviceCredential{
Id: id,
PublicKeyDer: pubKeyDER,
}, nil
}
func collectDeviceData() (*devicepb.DeviceCollectedData, error) {
var dd C.DeviceData
defer func() { C.free(unsafe.Pointer(dd.serial_number)) }()
if res := C.DeviceCollectData(&dd); res != 0 {
return nil, trace.Wrap(statusErrorFromC(res))
}
return &devicepb.DeviceCollectedData{
CollectTime: timestamppb.Now(),
OsType: devicepb.OSType_OS_TYPE_MACOS,
SerialNumber: C.GoString(dd.serial_number),
}, nil
}
func signChallenge(chal []byte) (sig []byte, err error) {
h := sha256.Sum256(chal)
digC := C.Digest{
data: (*C.uint8_t)(C.CBytes(h[:])),
data_len: (C.size_t)(len(h)),
}
defer func() { C.free(unsafe.Pointer(digC.data)) }()
var sigC C.Signature
defer func() { C.free(unsafe.Pointer(sigC.data)) }()
if res := C.DeviceKeySign(digC, &sigC); res != 0 {
return nil, trace.Wrap(statusErrorFromC(res))
}
sig = C.GoBytes(unsafe.Pointer(sigC.data), C.int(sigC.data_len))
return sig, err
}
func getDeviceCredential() (*devicepb.DeviceCredential, error) {
var pubKeyC C.PublicKey
defer func() {
C.free(unsafe.Pointer(pubKeyC.id))
C.free(unsafe.Pointer(pubKeyC.pub_key))
}()
if res := C.DeviceKeyGet(&pubKeyC); res != 0 {
return nil, trace.Wrap(statusErrorFromC(res))
}
id := C.GoString(pubKeyC.id)
pubKeyRaw := C.GoBytes(unsafe.Pointer(pubKeyC.pub_key), C.int(pubKeyC.pub_key_len))
return pubKeyToCredential(id, pubKeyRaw)
}
func statusErrorFromC(res C.int32_t) error {
return &statusError{status: int32(res)}
}