From d41f190c7f164d2141d1c26eff3ffb8feda04618 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 21 May 2019 13:13:32 +0200 Subject: firewall: use service-specific SID --- manager/install.go | 9 ++++++ tunnel/firewall/blocker.go | 10 +++---- tunnel/firewall/helpers.go | 50 +++++++++++++++++++++++++++++--- tunnel/firewall/rules.go | 2 +- tunnel/firewall/syscall_windows.go | 16 +++++++--- tunnel/firewall/types_windows.go | 56 ++++++++++++++++++++++++++++++----- tunnel/firewall/zsyscall_windows.go | 58 ++++++++++++++++++++++--------------- 7 files changed, 155 insertions(+), 46 deletions(-) diff --git a/manager/install.go b/manager/install.go index 4a570297..2eddbbbe 100644 --- a/manager/install.go +++ b/manager/install.go @@ -9,6 +9,7 @@ import ( "errors" "os" "time" + "unsafe" "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc" @@ -165,6 +166,14 @@ func InstallTunnel(configPath string) error { if err != nil { return err } + sidType := uint32(windows.SERVICE_SID_TYPE_UNRESTRICTED) + err = windows.ChangeServiceConfig2(service.Handle, windows.SERVICE_CONFIG_SERVICE_SID_INFO, (*byte)(unsafe.Pointer(&sidType))) + if err != nil { + service.Delete() + service.Close() + return err + } + err = service.Start() go trackTunnelService(name, service) // Pass off reference to handle. return err diff --git a/tunnel/firewall/blocker.go b/tunnel/firewall/blocker.go index 8034935d..cdb656b0 100644 --- a/tunnel/firewall/blocker.go +++ b/tunnel/firewall/blocker.go @@ -124,6 +124,11 @@ func EnableFirewall(luid uint64, restrictToDNSServers []net.IP, restrictAll bool return wrapErr(err) } + err = permitWireGuardService(session, baseObjects, 15) + if err != nil { + return wrapErr(err) + } + if len(restrictToDNSServers) > 0 { err = blockDNS(restrictToDNSServers, session, baseObjects, 15, 14) if err != nil { @@ -143,11 +148,6 @@ func EnableFirewall(luid uint64, restrictToDNSServers []net.IP, restrictAll bool return wrapErr(err) } - err = permitWireGuardService(session, baseObjects, 12) - if err != nil { - return wrapErr(err) - } - if restrictAll { err = permitDHCPIPv4(session, baseObjects, 12) if err != nil { diff --git a/tunnel/firewall/helpers.go b/tunnel/firewall/helpers.go index e340b802..6bc71806 100644 --- a/tunnel/firewall/helpers.go +++ b/tunnel/firewall/helpers.go @@ -72,16 +72,58 @@ func wrapErr(err error) error { } func getCurrentProcessSecurityDescriptor() (*wtFwpByteBlob, error) { - procHandle, err := windows.GetCurrentProcess() + processToken, err := windows.OpenCurrentProcessToken() if err != nil { - panic(err) + return nil, wrapErr(err) + } + defer processToken.Close() + gs, err := processToken.GetTokenGroups() + if err != nil { + return nil, wrapErr(err) + } + var sid *windows.SID + groups := (*[(1 << 28) - 1]windows.SIDAndAttributes)(unsafe.Pointer(&gs.Groups[0]))[:gs.GroupCount] + for _, g := range groups { + if g.Attributes != windows.SE_GROUP_ENABLED|windows.SE_GROUP_ENABLED_BY_DEFAULT|windows.SE_GROUP_OWNER { + continue + } + if *(*byte)(unsafe.Pointer(g.Sid)) != 1 { // The revision. + continue + } + if *getSidIdentifierAuthority(g.Sid) != windows.SECURITY_NT_AUTHORITY { + continue + } + // We could be checking != 6, but hopefully Microsoft will update + // RtlCreateServiceSid to use SHA2, which will then likely bump + // this up. So instead just roll with a minimum. + if *getSidSubAuthorityCount(g.Sid) < 6 { + continue + } + if *getSidSubAuthority(g.Sid, 0) != 80 { + continue + } + + sid = g.Sid + break + } + if sid == nil { + return nil, wrapErr(windows.ERROR_NO_SUCH_GROUP) + } + + access := &wtExplicitAccess{ + accessPermissions: cFWP_ACTRL_MATCH_FILTER, + accessMode: cGRANT_ACCESS, + trustee: wtTrustee{ + trusteeForm: cTRUSTEE_IS_SID, + trusteeType: cTRUSTEE_IS_GROUP, + sid: sid, + }, } blob := &wtFwpByteBlob{} - err = getSecurityInfo(procHandle, cSE_KERNEL_OBJECT, cDACL_SECURITY_INFORMATION, nil, nil, nil, nil, (*uintptr)(unsafe.Pointer(&blob.data))) + err = buildSecurityDescriptor(nil, nil, 1, access, 0, nil, nil, &blob.size, &blob.data) if err != nil { return nil, wrapErr(err) } - blob.size = getSecurityDescriptorLength(uintptr(unsafe.Pointer(blob.data))) return blob, nil } diff --git a/tunnel/firewall/rules.go b/tunnel/firewall/rules.go index 76e2a85b..1f28d3ab 100644 --- a/tunnel/firewall/rules.go +++ b/tunnel/firewall/rules.go @@ -149,7 +149,7 @@ func permitWireGuardService(session uintptr, baseObjects *baseObjects, weight ui // // Second condition is the SECURITY_DESCRIPTOR of the current process. - // This prevents low privileged applications hosted in the same exe from matching this filter. + // This prevents other processes hosted in the same exe from matching this filter. // sd, err := getCurrentProcessSecurityDescriptor() if err != nil { diff --git a/tunnel/firewall/syscall_windows.go b/tunnel/firewall/syscall_windows.go index 5ec41b0b..0f247d85 100644 --- a/tunnel/firewall/syscall_windows.go +++ b/tunnel/firewall/syscall_windows.go @@ -35,8 +35,16 @@ package firewall // https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmprovideradd0 //sys fwpmProviderAdd0(engineHandle uintptr, provider *wtFwpmProvider0, sd uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmProviderAdd0 -// https://docs.microsoft.com/sv-se/windows/desktop/api/aclapi/nf-aclapi-getsecurityinfo -//sys getSecurityInfo(handle windows.Handle, objectType wtObjectType, si uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *uintptr, sacl *uintptr, securityDescriptor *uintptr) (err error) [failretval!=0] = advapi32.GetSecurityInfo +//TODO: Add these to x/sys/windows: -// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-getsecuritydescriptorlength -//sys getSecurityDescriptorLength(securityDescriptor uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength +// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-getsididentifierauthority +//sys getSidIdentifierAuthority(sid *windows.SID) (authority *windows.SidIdentifierAuthority) = advapi32.GetSidIdentifierAuthority + +// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-getsidsubauthoritycount +//sys getSidSubAuthorityCount(sid *windows.SID) (count *uint8) = advapi32.GetSidSubAuthorityCount + +// https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-getsidsubauthority +//sys getSidSubAuthority(sid *windows.SID, index uint32) (subAuthority *uint32) = advapi32.GetSidSubAuthority + +// https://docs.microsoft.com/en-us/windows/desktop/api/aclapi/nf-aclapi-buildsecuritydescriptorw +//sys buildSecurityDescriptor(owner *wtTrustee, group *wtTrustee, countAccessEntries uint32, accessEntries *wtExplicitAccess, countAuditEntries uint32, auditEntries *wtExplicitAccess, oldSd **byte, sizeNewSd *uint32, newSd **byte) (ret error) = advapi32.BuildSecurityDescriptorW diff --git a/tunnel/firewall/types_windows.go b/tunnel/firewall/types_windows.go index e06f7d2b..6f86571f 100644 --- a/tunnel/firewall/types_windows.go +++ b/tunnel/firewall/types_windows.go @@ -393,14 +393,6 @@ type wtFwpmProvider0 struct { serviceName *uint16 } -type wtObjectType uint32 - -const ( - cSE_KERNEL_OBJECT wtObjectType = 6 - - cDACL_SECURITY_INFORMATION = 4 -) - type wtIfType uint32 const ( @@ -415,3 +407,51 @@ const ( cIPPROTO_TCP wtIPProto = 6 cIPPROTO_UDP wtIPProto = 17 ) + +type wtExplicitAccess struct { + accessPermissions uint32 + accessMode uint32 + inheritance uint32 + trustee wtTrustee +} + +type wtTrustee struct { + multipleTrustee *wtTrustee + multipleTrusteeOperation uint32 + trusteeForm uint32 + trusteeType uint32 + sid *windows.SID +} + +const ( + cTRUSTEE_IS_UNKNOWN = iota + cTRUSTEE_IS_USER + cTRUSTEE_IS_GROUP + cTRUSTEE_IS_DOMAIN + cTRUSTEE_IS_ALIAS + cTRUSTEE_IS_WELL_KNOWN_GROUP + cTRUSTEE_IS_DELETED + cTRUSTEE_IS_INVALID + cTRUSTEE_IS_COMPUTER +) +const ( + cTRUSTEE_IS_SID = iota + cTRUSTEE_IS_NAME + cTRUSTEE_BAD_FORM + cTRUSTEE_IS_OBJECTS_AND_SID + cTRUSTEE_IS_OBJECTS_AND_NAME +) + +const ( + cNOT_USED_ACCESS = iota + cGRANT_ACCESS + cSET_ACCESS + cDENY_ACCESS + cREVOKE_ACCESS + cSET_AUDIT_SUCCESS + cSET_AUDIT_FAILURE +) + +const ( + cFWP_ACTRL_MATCH_FILTER = 1 +) diff --git a/tunnel/firewall/zsyscall_windows.go b/tunnel/firewall/zsyscall_windows.go index 15e72703..82129d97 100644 --- a/tunnel/firewall/zsyscall_windows.go +++ b/tunnel/firewall/zsyscall_windows.go @@ -40,18 +40,20 @@ var ( modfwpuclnt = windows.NewLazySystemDLL("fwpuclnt.dll") modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") - procFwpmEngineOpen0 = modfwpuclnt.NewProc("FwpmEngineOpen0") - procFwpmEngineClose0 = modfwpuclnt.NewProc("FwpmEngineClose0") - procFwpmSubLayerAdd0 = modfwpuclnt.NewProc("FwpmSubLayerAdd0") - procFwpmGetAppIdFromFileName0 = modfwpuclnt.NewProc("FwpmGetAppIdFromFileName0") - procFwpmFreeMemory0 = modfwpuclnt.NewProc("FwpmFreeMemory0") - procFwpmFilterAdd0 = modfwpuclnt.NewProc("FwpmFilterAdd0") - procFwpmTransactionBegin0 = modfwpuclnt.NewProc("FwpmTransactionBegin0") - procFwpmTransactionCommit0 = modfwpuclnt.NewProc("FwpmTransactionCommit0") - procFwpmTransactionAbort0 = modfwpuclnt.NewProc("FwpmTransactionAbort0") - procFwpmProviderAdd0 = modfwpuclnt.NewProc("FwpmProviderAdd0") - procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo") - procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") + procFwpmEngineOpen0 = modfwpuclnt.NewProc("FwpmEngineOpen0") + procFwpmEngineClose0 = modfwpuclnt.NewProc("FwpmEngineClose0") + procFwpmSubLayerAdd0 = modfwpuclnt.NewProc("FwpmSubLayerAdd0") + procFwpmGetAppIdFromFileName0 = modfwpuclnt.NewProc("FwpmGetAppIdFromFileName0") + procFwpmFreeMemory0 = modfwpuclnt.NewProc("FwpmFreeMemory0") + procFwpmFilterAdd0 = modfwpuclnt.NewProc("FwpmFilterAdd0") + procFwpmTransactionBegin0 = modfwpuclnt.NewProc("FwpmTransactionBegin0") + procFwpmTransactionCommit0 = modfwpuclnt.NewProc("FwpmTransactionCommit0") + procFwpmTransactionAbort0 = modfwpuclnt.NewProc("FwpmTransactionAbort0") + procFwpmProviderAdd0 = modfwpuclnt.NewProc("FwpmProviderAdd0") + procGetSidIdentifierAuthority = modadvapi32.NewProc("GetSidIdentifierAuthority") + procGetSidSubAuthorityCount = modadvapi32.NewProc("GetSidSubAuthorityCount") + procGetSidSubAuthority = modadvapi32.NewProc("GetSidSubAuthority") + procBuildSecurityDescriptorW = modadvapi32.NewProc("BuildSecurityDescriptorW") ) func fwpmEngineOpen0(serverName *uint16, authnService wtRpcCAuthN, authIdentity *uintptr, session *wtFwpmSession0, engineHandle unsafe.Pointer) (err error) { @@ -167,20 +169,28 @@ func fwpmProviderAdd0(engineHandle uintptr, provider *wtFwpmProvider0, sd uintpt return } -func getSecurityInfo(handle windows.Handle, objectType wtObjectType, si uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *uintptr, sacl *uintptr, securityDescriptor *uintptr) (err error) { - r1, _, e1 := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(securityDescriptor)), 0) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } +func getSidIdentifierAuthority(sid *windows.SID) (authority *windows.SidIdentifierAuthority) { + r0, _, _ := syscall.Syscall(procGetSidIdentifierAuthority.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0) + authority = (*windows.SidIdentifierAuthority)(unsafe.Pointer(r0)) return } -func getSecurityDescriptorLength(securityDescriptor uintptr) (len uint32) { - r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(securityDescriptor), 0, 0) - len = uint32(r0) +func getSidSubAuthorityCount(sid *windows.SID) (count *uint8) { + r0, _, _ := syscall.Syscall(procGetSidSubAuthorityCount.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0) + count = (*uint8)(unsafe.Pointer(r0)) + return +} + +func getSidSubAuthority(sid *windows.SID, index uint32) (subAuthority *uint32) { + r0, _, _ := syscall.Syscall(procGetSidSubAuthority.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(index), 0) + subAuthority = (*uint32)(unsafe.Pointer(r0)) + return +} + +func buildSecurityDescriptor(owner *wtTrustee, group *wtTrustee, countAccessEntries uint32, accessEntries *wtExplicitAccess, countAuditEntries uint32, auditEntries *wtExplicitAccess, oldSd **byte, sizeNewSd *uint32, newSd **byte) (ret error) { + r0, _, _ := syscall.Syscall9(procBuildSecurityDescriptorW.Addr(), 9, uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(countAccessEntries), uintptr(unsafe.Pointer(accessEntries)), uintptr(countAuditEntries), uintptr(unsafe.Pointer(auditEntries)), uintptr(unsafe.Pointer(oldSd)), uintptr(unsafe.Pointer(sizeNewSd)), uintptr(unsafe.Pointer(newSd))) + if r0 != 0 { + ret = syscall.Errno(r0) + } return } -- cgit v1.2.3-59-g8ed1b