diff --git a/lib/auth/webauthnwin/syscall_gen.go b/lib/auth/webauthnwin/syscall_gen.go new file mode 100644 index 0000000000000..0c5cec50503d0 --- /dev/null +++ b/lib/auth/webauthnwin/syscall_gen.go @@ -0,0 +1,43 @@ +// Copyright 2023 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. + +//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_gen.go + +// See https://learn.microsoft.com/en-us/windows/win32/api/webauthn/. + +//sys webAuthNGetApiVersionNumber() (ret int, err error) [failretval==0] = WebAuthn.WebAuthNGetApiVersionNumber +// https://github.com/microsoft/webauthn/blob/7ab979cc833bfab9a682ed51761309db57f56c8c/webauthn.h#L895-L897 + +//sys webAuthNIsUserVerifyingPlatformAuthenticatorAvailable(out *bool) (ret uintptr, err error) [failretval!=0] = WebAuthn.WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable +// https://github.com/microsoft/webauthn/blob/7ab979cc833bfab9a682ed51761309db57f56c8c/webauthn.h#L901 + +//sys webAuthNAuthenticatorMakeCredential(hwnd syscall.Handle, rp *webauthnRPEntityInformation, user *webauthnUserEntityInformation, pubKeyCredParams *webauthnCoseCredentialParameters, clientData *webauthnClientData, opts *webauthnAuthenticatorMakeCredentialOptions, out **webauthnCredentialAttestation) (ret uintptr, err error) [failretval!=0] = WebAuthn.WebAuthNAuthenticatorMakeCredential +// https://github.com/microsoft/webauthn/blob/7ab979cc833bfab9a682ed51761309db57f56c8c/webauthn.h#L907 + +//sys freeCredentialAttestation(in *webauthnCredentialAttestation) = WebAuthn.WebAuthNFreeCredentialAttestation +// https://github.com/microsoft/webauthn/blob/7ab979cc833bfab9a682ed51761309db57f56c8c/webauthn.h#L928 + +//sys webAuthNAuthenticatorGetAssertion(hwnd syscall.Handle, rpID *uint16, clientData *webauthnClientData, opts *webauthnAuthenticatorGetAssertionOptions, out **webauthnAssertion) (ret uintptr, err error) [failretval!=0] = WebAuthn.WebAuthNAuthenticatorGetAssertion +// https://github.com/microsoft/webauthn/blob/7ab979cc833bfab9a682ed51761309db57f56c8c/webauthn.h#L919 + +//sys freeAssertion(in *webauthnAssertion) = WebAuthn.WebAuthNFreeAssertion +// https://github.com/microsoft/webauthn/blob/7ab979cc833bfab9a682ed51761309db57f56c8c/webauthn.h#L933 + +//sys webAuthNGetErrorName(in uintptr) (ret uintptr) = WebAuthn.WebAuthNGetErrorName +// https://github.com/microsoft/webauthn/blob/7ab979cc833bfab9a682ed51761309db57f56c8c/webauthn.h#L982 + +//sys getForegroundWindow() (hwnd syscall.Handle, err error) [failretval==0] = user32.GetForegroundWindow +// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getforegroundwindow + +package webauthnwin diff --git a/lib/auth/webauthnwin/webauthn_windows.go b/lib/auth/webauthnwin/webauthn_windows.go index 49d08e4773b0f..9db32a0562893 100644 --- a/lib/auth/webauthnwin/webauthn_windows.go +++ b/lib/auth/webauthnwin/webauthn_windows.go @@ -29,23 +29,6 @@ import ( wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) -var ( - modWebAuthn = windows.NewLazySystemDLL("WebAuthn.dll") - - // For reference, see - // https://learn.microsoft.com/en-us/windows/win32/api/webauthn/. - procWebAuthNGetApiVersionNumber = modWebAuthn.NewProc("WebAuthNGetApiVersionNumber") - procWebAuthNIsUserVerifyingPlatformAuthenticatorAvailable = modWebAuthn.NewProc("WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable") - procWebAuthNAuthenticatorMakeCredential = modWebAuthn.NewProc("WebAuthNAuthenticatorMakeCredential") - procWebAuthNFreeCredentialAttestation = modWebAuthn.NewProc("WebAuthNFreeCredentialAttestation") - procWebAuthNAuthenticatorGetAssertion = modWebAuthn.NewProc("WebAuthNAuthenticatorGetAssertion") - procWebAuthNFreeAssertion = modWebAuthn.NewProc("WebAuthNFreeAssertion") - procWebAuthNGetErrorName = modWebAuthn.NewProc("WebAuthNGetErrorName") - - modUser32 = windows.NewLazySystemDLL("user32.dll") - procGetForegroundWindow = modUser32.NewProc("GetForegroundWindow") -) - var native nativeWebauthn = newNativeImpl() // nativeImpl keeps diagnostic informations about windows webauthn support. @@ -107,13 +90,7 @@ func (n *nativeImpl) GetAssertion(origin string, in *getAssertionRequest) (*want } var out *webauthnAssertion - ret, _, err := procWebAuthNAuthenticatorGetAssertion.Call( - uintptr(hwnd), - uintptr(unsafe.Pointer(in.rpID)), - uintptr(unsafe.Pointer(in.clientData)), - uintptr(unsafe.Pointer(in.opts)), - uintptr(unsafe.Pointer(&out)), - ) + ret, err := webAuthNAuthenticatorGetAssertion(hwnd, in.rpID, in.clientData, in.opts, &out) if ret != 0 { return nil, trace.Wrap(getErrorNameOrLastErr(ret, err)) } @@ -123,8 +100,7 @@ func (n *nativeImpl) GetAssertion(origin string, in *getAssertionRequest) (*want // Note that we need to copy bytes out of `out` if we want to free object. // That's why bytesFromCBytes is used. - // We don't care about free error so ignore it explicitly. - defer func() { _ = freeAssertion(out) }() + defer freeAssertion(out) authData := bytesFromCBytes(out.cbAuthenticatorData, out.pbAuthenticatorData) signature := bytesFromCBytes(out.cbSignature, out.pbSignature) @@ -164,15 +140,8 @@ func (n *nativeImpl) MakeCredential(origin string, in *makeCredentialRequest) (* } var out *webauthnCredentialAttestation - ret, _, err := procWebAuthNAuthenticatorMakeCredential.Call( - uintptr(hwnd), - uintptr(unsafe.Pointer(in.rp)), - uintptr(unsafe.Pointer(in.user)), - uintptr(unsafe.Pointer(in.credParameters)), - uintptr(unsafe.Pointer(in.clientData)), - uintptr(unsafe.Pointer(in.opts)), - uintptr(unsafe.Pointer(&out)), - ) + ret, err := webAuthNAuthenticatorMakeCredential( + hwnd, in.rp, in.user, in.credParameters, in.clientData, in.opts, &out) if ret != 0 { return nil, trace.Wrap(getErrorNameOrLastErr(ret, err)) } @@ -182,8 +151,7 @@ func (n *nativeImpl) MakeCredential(origin string, in *makeCredentialRequest) (* // Note that we need to copy bytes out of `out` if we want to free object. // That's why bytesFromCBytes is used. - // We don't care about free error so ignore it explicitly. - defer func() { _ = freeCredentialAttestation(out) }() + defer freeCredentialAttestation(out) credential := bytesFromCBytes(out.cbCredentialID, out.pbCredentialID) @@ -204,50 +172,15 @@ func (n *nativeImpl) MakeCredential(origin string, in *makeCredentialRequest) (* }, nil } -func freeCredentialAttestation(in *webauthnCredentialAttestation) error { - _, _, err := procWebAuthNFreeCredentialAttestation.Call( - uintptr(unsafe.Pointer(in)), - ) - if err != syscall.Errno(0) { - return err - } - return nil -} - -func freeAssertion(in *webauthnAssertion) error { - _, _, err := procWebAuthNFreeAssertion.Call( - uintptr(unsafe.Pointer(in)), - ) - if err != syscall.Errno(0) { - return err - } - return nil -} - // checkIfDLLExistsAndGetAPIVersionNumber checks if dll exists and tries to load // it's version via API call. This function makes sure to not panic if dll is // missing. func checkIfDLLExistsAndGetAPIVersionNumber() (int, error) { - if err := modWebAuthn.Load(); err != nil { - return 0, err - } - if err := procWebAuthNGetApiVersionNumber.Find(); err != nil { - return 0, err - } - // This is the only API call of Windows Webauthn API that returns non-zero - // value when everything went fine. - // https://github.com/microsoft/webauthn/blob/7ab979cc833bfab9a682ed51761309db57f56c8c/webauthn.h#L895-L897 - ret, _, err := procWebAuthNGetApiVersionNumber.Call() - if ret == 0 && err != syscall.Errno(0) { - return 0, err - } - return int(ret), nil + return webAuthNGetApiVersionNumber() } func getErrorNameOrLastErr(in uintptr, lastError error) error { - ret, _, _ := procWebAuthNGetErrorName.Call( - uintptr(int32(in)), - ) + ret := webAuthNGetErrorName(in) if ret == 0 { if lastError != syscall.Errno(0) { return fmt.Errorf("webauthn error code %v and syscall err: %v", in, lastError) @@ -259,14 +192,12 @@ func getErrorNameOrLastErr(in uintptr, lastError error) error { } func isUVPlatformAuthenticatorAvailable() (bool, error) { - var out uint32 - ret, _, err := procWebAuthNIsUserVerifyingPlatformAuthenticatorAvailable.Call( - uintptr(unsafe.Pointer(&out)), - ) - if ret != 0 { + var out bool + ret, err := webAuthNIsUserVerifyingPlatformAuthenticatorAvailable(&out) + if err != nil { return false, getErrorNameOrLastErr(ret, err) } - return out == 1, nil + return out, nil } // bytesFromCBytes gets slice of bytes from C type and copies it to new slice @@ -283,11 +214,3 @@ func bytesFromCBytes(size uint32, p *byte) []byte { copy(out, tmp) return out } - -func getForegroundWindow() (hwnd syscall.Handle, err error) { - r0, _, err := procGetForegroundWindow.Call() - if err != syscall.Errno(0) { - return syscall.InvalidHandle, err - } - return syscall.Handle(r0), nil -} diff --git a/lib/auth/webauthnwin/zsyscall_windows.go b/lib/auth/webauthnwin/zsyscall_windows.go new file mode 100644 index 0000000000000..c27df503f6ea5 --- /dev/null +++ b/lib/auth/webauthnwin/zsyscall_windows.go @@ -0,0 +1,118 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package webauthnwin + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modWebAuthn = windows.NewLazySystemDLL("WebAuthn.dll") + moduser32 = windows.NewLazySystemDLL("user32.dll") + + procWebAuthNAuthenticatorGetAssertion = modWebAuthn.NewProc("WebAuthNAuthenticatorGetAssertion") + procWebAuthNAuthenticatorMakeCredential = modWebAuthn.NewProc("WebAuthNAuthenticatorMakeCredential") + procWebAuthNFreeAssertion = modWebAuthn.NewProc("WebAuthNFreeAssertion") + procWebAuthNFreeCredentialAttestation = modWebAuthn.NewProc("WebAuthNFreeCredentialAttestation") + procWebAuthNGetApiVersionNumber = modWebAuthn.NewProc("WebAuthNGetApiVersionNumber") + procWebAuthNGetErrorName = modWebAuthn.NewProc("WebAuthNGetErrorName") + procWebAuthNIsUserVerifyingPlatformAuthenticatorAvailable = modWebAuthn.NewProc("WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable") + procGetForegroundWindow = moduser32.NewProc("GetForegroundWindow") +) + +func webAuthNAuthenticatorGetAssertion(hwnd syscall.Handle, rpID *uint16, clientData *webauthnClientData, opts *webauthnAuthenticatorGetAssertionOptions, out **webauthnAssertion) (ret uintptr, err error) { + r0, _, e1 := syscall.Syscall6(procWebAuthNAuthenticatorGetAssertion.Addr(), 5, uintptr(hwnd), uintptr(unsafe.Pointer(rpID)), uintptr(unsafe.Pointer(clientData)), uintptr(unsafe.Pointer(opts)), uintptr(unsafe.Pointer(out)), 0) + ret = uintptr(r0) + if ret != 0 { + err = errnoErr(e1) + } + return +} + +func webAuthNAuthenticatorMakeCredential(hwnd syscall.Handle, rp *webauthnRPEntityInformation, user *webauthnUserEntityInformation, pubKeyCredParams *webauthnCoseCredentialParameters, clientData *webauthnClientData, opts *webauthnAuthenticatorMakeCredentialOptions, out **webauthnCredentialAttestation) (ret uintptr, err error) { + r0, _, e1 := syscall.Syscall9(procWebAuthNAuthenticatorMakeCredential.Addr(), 7, uintptr(hwnd), uintptr(unsafe.Pointer(rp)), uintptr(unsafe.Pointer(user)), uintptr(unsafe.Pointer(pubKeyCredParams)), uintptr(unsafe.Pointer(clientData)), uintptr(unsafe.Pointer(opts)), uintptr(unsafe.Pointer(out)), 0, 0) + ret = uintptr(r0) + if ret != 0 { + err = errnoErr(e1) + } + return +} + +func freeAssertion(in *webauthnAssertion) { + syscall.Syscall(procWebAuthNFreeAssertion.Addr(), 1, uintptr(unsafe.Pointer(in)), 0, 0) + return +} + +func freeCredentialAttestation(in *webauthnCredentialAttestation) { + syscall.Syscall(procWebAuthNFreeCredentialAttestation.Addr(), 1, uintptr(unsafe.Pointer(in)), 0, 0) + return +} + +func webAuthNGetApiVersionNumber() (ret int, err error) { + r0, _, e1 := syscall.Syscall(procWebAuthNGetApiVersionNumber.Addr(), 0, 0, 0, 0) + ret = int(r0) + if ret == 0 { + err = errnoErr(e1) + } + return +} + +func webAuthNGetErrorName(in uintptr) (ret uintptr) { + r0, _, _ := syscall.Syscall(procWebAuthNGetErrorName.Addr(), 1, uintptr(in), 0, 0) + ret = uintptr(r0) + return +} + +func webAuthNIsUserVerifyingPlatformAuthenticatorAvailable(out *bool) (ret uintptr, err error) { + var _p0 uint32 + if *out { + _p0 = 1 + } + r0, _, e1 := syscall.Syscall(procWebAuthNIsUserVerifyingPlatformAuthenticatorAvailable.Addr(), 1, uintptr(unsafe.Pointer(&_p0)), 0, 0) + *out = _p0 != 0 + ret = uintptr(r0) + if ret != 0 { + err = errnoErr(e1) + } + return +} + +func getForegroundWindow() (hwnd syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall(procGetForegroundWindow.Addr(), 0, 0, 0, 0) + hwnd = syscall.Handle(r0) + if hwnd == 0 { + err = errnoErr(e1) + } + return +}