// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package paths import ( "os" "unsafe" "golang.org/x/sys/windows" ) func getTokenInfo(token windows.Token, infoClass uint32) ([]byte, error) { var desiredLen uint32 err := windows.GetTokenInformation(token, infoClass, nil, 0, &desiredLen) if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { return nil, err } buf := make([]byte, desiredLen) actualLen := desiredLen err = windows.GetTokenInformation(token, infoClass, &buf[0], desiredLen, &actualLen) return buf, err } func getTokenUserInfo(token windows.Token) (*windows.Tokenuser, error) { buf, err := getTokenInfo(token, windows.TokenUser) if err != nil { return nil, err } return (*windows.Tokenuser)(unsafe.Pointer(&buf[0])), nil } func getTokenPrimaryGroupInfo(token windows.Token) (*windows.Tokenprimarygroup, error) { buf, err := getTokenInfo(token, windows.TokenPrimaryGroup) if err != nil { return nil, err } return (*windows.Tokenprimarygroup)(unsafe.Pointer(&buf[0])), nil } type userSids struct { User *windows.SID PrimaryGroup *windows.SID } func getCurrentUserSids() (*userSids, error) { token, err := windows.OpenCurrentProcessToken() if err != nil { return nil, err } defer token.Close() userInfo, err := getTokenUserInfo(token) if err != nil { return nil, err } primaryGroup, err := getTokenPrimaryGroupInfo(token) if err != nil { return nil, err } return &userSids{userInfo.User.Sid, primaryGroup.PrimaryGroup}, nil } // ensureStateDirPerms applies a restrictive ACL to the directory specified by dirPath. // It sets the following security attributes on the directory: // Owner: The user for the current process; // Primary Group: The primary group for the current process; // DACL: Full control to the current user and to the Administrators group. // (We include Administrators so that admin users may still access logs; // granting access exclusively to LocalSystem would require admins to use // special tools to access the Log directory) // Inheritance: The directory does not inherit the ACL from its parent. // However, any directories and/or files created within this // directory *do* inherit the ACL that we are setting. func ensureStateDirPerms(dirPath string) error { fi, err := os.Stat(dirPath) if err != nil { return err } if !fi.IsDir() { return os.ErrInvalid } // We need the info for our current user as SIDs sids, err := getCurrentUserSids() if err != nil { return err } // We also need the SID for the Administrators group so that admins may // easily access logs. adminGroupSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid) if err != nil { return err } // Munge the SIDs into the format required by EXPLICIT_ACCESS. userTrustee := windows.TRUSTEE{nil, windows.NO_MULTIPLE_TRUSTEE, windows.TRUSTEE_IS_SID, windows.TRUSTEE_IS_USER, windows.TrusteeValueFromSID(sids.User)} adminTrustee := windows.TRUSTEE{nil, windows.NO_MULTIPLE_TRUSTEE, windows.TRUSTEE_IS_SID, windows.TRUSTEE_IS_WELL_KNOWN_GROUP, windows.TrusteeValueFromSID(adminGroupSid)} // We declare our access rights via this array of EXPLICIT_ACCESS structures. // We set full access to our user and to Administrators. // We configure the DACL such that any files or directories created within // dirPath will also inherit this DACL. explicitAccess := []windows.EXPLICIT_ACCESS{ windows.EXPLICIT_ACCESS{ windows.GENERIC_ALL, windows.SET_ACCESS, windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT, userTrustee, }, windows.EXPLICIT_ACCESS{ windows.GENERIC_ALL, windows.SET_ACCESS, windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT, adminTrustee, }, } dacl, err := windows.ACLFromEntries(explicitAccess, nil) if err != nil { return err } // We now reset the file's owner, primary group, and DACL. // We also must pass PROTECTED_DACL_SECURITY_INFORMATION so that our new ACL // does not inherit any ACL entries from the parent directory. const flags = windows.OWNER_SECURITY_INFORMATION | windows.GROUP_SECURITY_INFORMATION | windows.DACL_SECURITY_INFORMATION | windows.PROTECTED_DACL_SECURITY_INFORMATION return windows.SetNamedSecurityInfo(dirPath, windows.SE_FILE_OBJECT, flags, sids.User, sids.PrimaryGroup, dacl, nil) }