Skip to content
43 changes: 43 additions & 0 deletions lib/auth/webauthnwin/syscall_gen.go
Original file line number Diff line number Diff line change
@@ -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
99 changes: 11 additions & 88 deletions lib/auth/webauthnwin/webauthn_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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))
}
Expand All @@ -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)
Expand Down Expand Up @@ -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))
}
Expand All @@ -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)

Expand All @@ -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
}
Comment on lines -231 to -233
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: removing the explicit load was the cause of #36851.

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)
Expand All @@ -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
Expand All @@ -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
}
118 changes: 118 additions & 0 deletions lib/auth/webauthnwin/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.