├── LICENSE.txt ├── README.md ├── api ├── acl.go ├── acl_test.go ├── api.go ├── posix.go ├── secinfo.go ├── secinfo_test.go ├── sid.go └── sid_test.go ├── apply.go ├── apply_test.go ├── appveyor.yml ├── chmod.go ├── chmod_test.go ├── go.mod ├── go.sum ├── posix.go └── util.go /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nathan Osman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## go-acl 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/rbdyu7c39o2j0ru9?svg=true)](https://ci.appveyor.com/project/nathan-osman/go-acl) 4 | [![GoDoc](https://godoc.org/github.com/hectane/go-acl?status.svg)](https://godoc.org/github.com/hectane/go-acl) 5 | [![MIT License](http://img.shields.io/badge/license-MIT-9370d8.svg?style=flat)](http://opensource.org/licenses/MIT) 6 | 7 | Manipulating ACLs (Access Control Lists) on Windows is difficult. go-acl wraps the Windows API functions that control access to objects, simplifying the process. 8 | 9 | ### Using the Package 10 | 11 | To use the package add the following imports: 12 | 13 | import ( 14 | "github.com/hectane/go-acl" 15 | "golang.org/x/sys/windows" 16 | ) 17 | 18 | ### Examples 19 | 20 | Probably the most commonly used function in this package is `Chmod`: 21 | 22 | if err := acl.Chmod("C:\\path\\to\\file.txt", 0755); err != nil { 23 | panic(err) 24 | } 25 | 26 | To grant read access to user "Alice" and deny write access to user "Bob": 27 | 28 | if err := acl.Apply( 29 | "C:\\path\\to\\file.txt", 30 | false, 31 | false, 32 | acl.GrantName(windows.GENERIC_READ, "Alice"), 33 | acl.DenyName(windows.GENERIC_WRITE, "Bob"), 34 | ); err != nil { 35 | panic(err) 36 | } 37 | 38 | ### Using the API Directly 39 | 40 | go-acl's `api` package exposes the individual Windows API functions that are used to manipulate ACLs. For example, to retrieve the current owner of a file: 41 | 42 | import ( 43 | "github.com/hectane/go-acl/api" 44 | "golang.org/x/sys/windows" 45 | ) 46 | 47 | var ( 48 | owner *windows.SID 49 | secDesc windows.Handle 50 | ) 51 | err := api.GetNamedSecurityInfo( 52 | "C:\\path\\to\\file.txt", 53 | api.SE_FILE_OBJECT, 54 | api.OWNER_SECURITY_INFORMATION, 55 | &owner, 56 | nil, 57 | nil, 58 | nil, 59 | &secDesc, 60 | ) 61 | if err != nil { 62 | panic(err) 63 | } 64 | defer windows.LocalFree(secDesc) 65 | 66 | `owner` will then point to the SID for the owner of the file. 67 | -------------------------------------------------------------------------------- /api/acl.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package api 4 | 5 | import ( 6 | "golang.org/x/sys/windows" 7 | 8 | "unsafe" 9 | ) 10 | 11 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379284.aspx 12 | const ( 13 | NO_MULTIPLE_TRUSTEE = iota 14 | TRUSTEE_IS_IMPERSONATE 15 | ) 16 | 17 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379638.aspx 18 | const ( 19 | TRUSTEE_IS_SID = iota 20 | TRUSTEE_IS_NAME 21 | TRUSTEE_BAD_FORM 22 | TRUSTEE_IS_OBJECTS_AND_SID 23 | TRUSTEE_IS_OBJECTS_AND_NAME 24 | ) 25 | 26 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379639.aspx 27 | const ( 28 | TRUSTEE_IS_UNKNOWN = iota 29 | TRUSTEE_IS_USER 30 | TRUSTEE_IS_GROUP 31 | TRUSTEE_IS_DOMAIN 32 | TRUSTEE_IS_ALIAS 33 | TRUSTEE_IS_WELL_KNOWN_GROUP 34 | TRUSTEE_IS_DELETED 35 | TRUSTEE_IS_INVALID 36 | TRUSTEE_IS_COMPUTER 37 | ) 38 | 39 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa374899.aspx 40 | const ( 41 | NOT_USED_ACCESS = iota 42 | GRANT_ACCESS 43 | SET_ACCESS 44 | DENY_ACCESS 45 | REVOKE_ACCESS 46 | SET_AUDIT_SUCCESS 47 | SET_AUDIT_FAILURE 48 | ) 49 | 50 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa446627.aspx 51 | const ( 52 | NO_INHERITANCE = 0x0 53 | SUB_OBJECTS_ONLY_INHERIT = 0x1 54 | SUB_CONTAINERS_ONLY_INHERIT = 0x2 55 | SUB_CONTAINERS_AND_OBJECTS_INHERIT = 0x3 56 | INHERIT_NO_PROPAGATE = 0x4 57 | INHERIT_ONLY = 0x8 58 | 59 | OBJECT_INHERIT_ACE = 0x1 60 | CONTAINER_INHERIT_ACE = 0x2 61 | NO_PROPAGATE_INHERIT_ACE = 0x4 62 | INHERIT_ONLY_ACE = 0x8 63 | ) 64 | 65 | var ( 66 | procSetEntriesInAclW = advapi32.MustFindProc("SetEntriesInAclW") 67 | ) 68 | 69 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379636.aspx 70 | type Trustee struct { 71 | MultipleTrustee *Trustee 72 | MultipleTrusteeOperation int32 73 | TrusteeForm int32 74 | TrusteeType int32 75 | Name *uint16 76 | } 77 | 78 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa446627.aspx 79 | type ExplicitAccess struct { 80 | AccessPermissions uint32 81 | AccessMode int32 82 | Inheritance uint32 83 | Trustee Trustee 84 | } 85 | 86 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379576.aspx 87 | func SetEntriesInAcl(entries []ExplicitAccess, oldAcl windows.Handle, newAcl *windows.Handle) error { 88 | ret, _, _ := procSetEntriesInAclW.Call( 89 | uintptr(len(entries)), 90 | uintptr(unsafe.Pointer(&entries[0])), 91 | uintptr(oldAcl), 92 | uintptr(unsafe.Pointer(newAcl)), 93 | ) 94 | if ret != 0 { 95 | return windows.Errno(ret) 96 | } 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /api/acl_test.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package api 4 | 5 | import ( 6 | "golang.org/x/sys/windows" 7 | 8 | "testing" 9 | ) 10 | 11 | func TestSetEntriesInAcl(t *testing.T) { 12 | var ( 13 | entries = []ExplicitAccess{ 14 | { 15 | AccessPermissions: windows.GENERIC_READ, 16 | AccessMode: GRANT_ACCESS, 17 | Inheritance: NO_INHERITANCE, 18 | Trustee: Trustee{ 19 | TrusteeForm: TRUSTEE_IS_NAME, 20 | Name: windows.StringToUTF16Ptr("CURRENT_USER"), 21 | }, 22 | }, 23 | } 24 | acl windows.Handle 25 | ) 26 | if err := SetEntriesInAcl( 27 | entries, 28 | 0, 29 | &acl, 30 | ); err != nil { 31 | t.Fatal(err) 32 | } 33 | defer windows.LocalFree(acl) 34 | } 35 | -------------------------------------------------------------------------------- /api/api.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | // Windows API functions for manipulating ACLs. 4 | package api 5 | 6 | import ( 7 | "golang.org/x/sys/windows" 8 | ) 9 | 10 | var advapi32 = windows.MustLoadDLL("advapi32.dll") 11 | -------------------------------------------------------------------------------- /api/posix.go: -------------------------------------------------------------------------------- 1 | //+build !windows 2 | 3 | package api 4 | -------------------------------------------------------------------------------- /api/secinfo.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package api 4 | 5 | import ( 6 | "golang.org/x/sys/windows" 7 | 8 | "unsafe" 9 | ) 10 | 11 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379593.aspx 12 | const ( 13 | SE_UNKNOWN_OBJECT_TYPE = iota 14 | SE_FILE_OBJECT 15 | SE_SERVICE 16 | SE_PRINTER 17 | SE_REGISTRY_KEY 18 | SE_LMSHARE 19 | SE_KERNEL_OBJECT 20 | SE_WINDOW_OBJECT 21 | SE_DS_OBJECT 22 | SE_DS_OBJECT_ALL 23 | SE_PROVIDER_DEFINED_OBJECT 24 | SE_WMIGUID_OBJECT 25 | SE_REGISTRY_WOW64_32KEY 26 | ) 27 | 28 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379573.aspx 29 | const ( 30 | OWNER_SECURITY_INFORMATION = 0x00001 31 | GROUP_SECURITY_INFORMATION = 0x00002 32 | DACL_SECURITY_INFORMATION = 0x00004 33 | SACL_SECURITY_INFORMATION = 0x00008 34 | LABEL_SECURITY_INFORMATION = 0x00010 35 | ATTRIBUTE_SECURITY_INFORMATION = 0x00020 36 | SCOPE_SECURITY_INFORMATION = 0x00040 37 | PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00080 38 | BACKUP_SECURITY_INFORMATION = 0x10000 39 | 40 | PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000 41 | PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000 42 | UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000 43 | UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000 44 | ) 45 | 46 | var ( 47 | procGetNamedSecurityInfoW = advapi32.MustFindProc("GetNamedSecurityInfoW") 48 | procSetNamedSecurityInfoW = advapi32.MustFindProc("SetNamedSecurityInfoW") 49 | ) 50 | 51 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa446645.aspx 52 | func GetNamedSecurityInfo(objectName string, objectType int32, secInfo uint32, owner, group **windows.SID, dacl, sacl, secDesc *windows.Handle) error { 53 | ret, _, _ := procGetNamedSecurityInfoW.Call( 54 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(objectName))), 55 | uintptr(objectType), 56 | uintptr(secInfo), 57 | uintptr(unsafe.Pointer(owner)), 58 | uintptr(unsafe.Pointer(group)), 59 | uintptr(unsafe.Pointer(dacl)), 60 | uintptr(unsafe.Pointer(sacl)), 61 | uintptr(unsafe.Pointer(secDesc)), 62 | ) 63 | if ret != 0 { 64 | return windows.Errno(ret) 65 | } 66 | return nil 67 | } 68 | 69 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379579.aspx 70 | func SetNamedSecurityInfo(objectName string, objectType int32, secInfo uint32, owner, group *windows.SID, dacl, sacl windows.Handle) error { 71 | ret, _, _ := procSetNamedSecurityInfoW.Call( 72 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(objectName))), 73 | uintptr(objectType), 74 | uintptr(secInfo), 75 | uintptr(unsafe.Pointer(owner)), 76 | uintptr(unsafe.Pointer(group)), 77 | uintptr(dacl), 78 | uintptr(sacl), 79 | ) 80 | if ret != 0 { 81 | return windows.Errno(ret) 82 | } 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /api/secinfo_test.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package api 4 | 5 | import ( 6 | "golang.org/x/sys/windows" 7 | 8 | "io/ioutil" 9 | "os" 10 | "testing" 11 | ) 12 | 13 | func TestGetNamedSecurityInfo(t *testing.T) { 14 | f, err := ioutil.TempFile(os.TempDir(), "") 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | defer os.Remove(f.Name()) 19 | var ( 20 | secDesc windows.Handle 21 | ) 22 | if err = GetNamedSecurityInfo( 23 | f.Name(), 24 | SE_FILE_OBJECT, 25 | 0, 26 | nil, 27 | nil, 28 | nil, 29 | nil, 30 | &secDesc, 31 | ); err != nil { 32 | t.Fatal(err) 33 | } 34 | defer windows.LocalFree(secDesc) 35 | } 36 | 37 | func TestSetNamedSecurityInfo(t *testing.T) { 38 | f, err := ioutil.TempFile(os.TempDir(), "") 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | defer os.Remove(f.Name()) 43 | if err = SetNamedSecurityInfo( 44 | f.Name(), 45 | SE_FILE_OBJECT, 46 | DACL_SECURITY_INFORMATION, 47 | nil, 48 | nil, 49 | 0, 50 | 0, 51 | ); err != nil { 52 | t.Fatal(err) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /api/sid.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package api 4 | 5 | import ( 6 | "golang.org/x/sys/windows" 7 | 8 | "unsafe" 9 | ) 10 | 11 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ee207397.aspx 12 | const ( 13 | SECURITY_MAX_SID_SIZE = 68 14 | ) 15 | 16 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379650.aspx 17 | const ( 18 | WinNullSid = 0 19 | WinWorldSid = 1 20 | WinLocalSid = 2 21 | WinCreatorOwnerSid = 3 22 | WinCreatorGroupSid = 4 23 | WinCreatorOwnerServerSid = 5 24 | WinCreatorGroupServerSid = 6 25 | WinNtAuthoritySid = 7 26 | WinDialupSid = 8 27 | WinNetworkSid = 9 28 | WinBatchSid = 10 29 | WinInteractiveSid = 11 30 | WinServiceSid = 12 31 | WinAnonymousSid = 13 32 | WinProxySid = 14 33 | WinEnterpriseControllersSid = 15 34 | WinSelfSid = 16 35 | WinAuthenticatedUserSid = 17 36 | WinRestrictedCodeSid = 18 37 | WinTerminalServerSid = 19 38 | WinRemoteLogonIdSid = 20 39 | WinLogonIdsSid = 21 40 | WinLocalSystemSid = 22 41 | WinLocalServiceSid = 23 42 | WinNetworkServiceSid = 24 43 | WinBuiltinDomainSid = 25 44 | WinBuiltinAdministratorsSid = 26 45 | WinBuiltinUsersSid = 27 46 | WinBuiltinGuestsSid = 28 47 | WinBuiltinPowerUsersSid = 29 48 | WinBuiltinAccountOperatorsSid = 30 49 | WinBuiltinSystemOperatorsSid = 31 50 | WinBuiltinPrintOperatorsSid = 32 51 | WinBuiltinBackupOperatorsSid = 33 52 | WinBuiltinReplicatorSid = 34 53 | WinBuiltinPreWindows2000CompatibleAccessSid = 35 54 | WinBuiltinRemoteDesktopUsersSid = 36 55 | WinBuiltinNetworkConfigurationOperatorsSid = 37 56 | WinAccountAdministratorSid = 38 57 | WinAccountGuestSid = 39 58 | WinAccountKrbtgtSid = 40 59 | WinAccountDomainAdminsSid = 41 60 | WinAccountDomainUsersSid = 42 61 | WinAccountDomainGuestsSid = 43 62 | WinAccountComputersSid = 44 63 | WinAccountControllersSid = 45 64 | WinAccountCertAdminsSid = 46 65 | WinAccountSchemaAdminsSid = 47 66 | WinAccountEnterpriseAdminsSid = 48 67 | WinAccountPolicyAdminsSid = 49 68 | WinAccountRasAndIasServersSid = 50 69 | WinNTLMAuthenticationSid = 51 70 | WinDigestAuthenticationSid = 52 71 | WinSChannelAuthenticationSid = 53 72 | WinThisOrganizationSid = 54 73 | WinOtherOrganizationSid = 55 74 | WinBuiltinIncomingForestTrustBuildersSid = 56 75 | WinBuiltinPerfMonitoringUsersSid = 57 76 | WinBuiltinPerfLoggingUsersSid = 58 77 | WinBuiltinAuthorizationAccessSid = 59 78 | WinBuiltinTerminalServerLicenseServersSid = 60 79 | WinBuiltinDCOMUsersSid = 61 80 | WinBuiltinIUsersSid = 62 81 | WinIUserSid = 63 82 | WinBuiltinCryptoOperatorsSid = 64 83 | WinUntrustedLabelSid = 65 84 | WinLowLabelSid = 66 85 | WinMediumLabelSid = 67 86 | WinHighLabelSid = 68 87 | WinSystemLabelSid = 69 88 | WinWriteRestrictedCodeSid = 70 89 | WinCreatorOwnerRightsSid = 71 90 | WinCacheablePrincipalsGroupSid = 72 91 | WinNonCacheablePrincipalsGroupSid = 73 92 | WinEnterpriseReadonlyControllersSid = 74 93 | WinAccountReadonlyControllersSid = 75 94 | WinBuiltinEventLogReadersGroup = 76 95 | WinNewEnterpriseReadonlyControllersSid = 77 96 | WinBuiltinCertSvcDComAccessGroup = 78 97 | WinMediumPlusLabelSid = 79 98 | WinLocalLogonSid = 80 99 | WinConsoleLogonSid = 81 100 | WinThisOrganizationCertificateSid = 82 101 | WinApplicationPackageAuthoritySid = 83 102 | WinBuiltinAnyPackageSid = 84 103 | WinCapabilityInternetClientSid = 85 104 | WinCapabilityInternetClientServerSid = 86 105 | WinCapabilityPrivateNetworkClientServerSid = 87 106 | WinCapabilityPicturesLibrarySid = 88 107 | WinCapabilityVideosLibrarySid = 89 108 | WinCapabilityMusicLibrarySid = 90 109 | WinCapabilityDocumentsLibrarySid = 91 110 | WinCapabilitySharedUserCertificatesSid = 92 111 | WinCapabilityEnterpriseAuthenticationSid = 93 112 | WinCapabilityRemovableStorageSid = 94 113 | ) 114 | 115 | var ( 116 | procCreateWellKnownSid = advapi32.MustFindProc("CreateWellKnownSid") 117 | ) 118 | 119 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa446585.aspx 120 | func CreateWellKnownSid(sidType int32, sidDomain, sid *windows.SID, sidLen *uint32) error { 121 | ret, _, err := procCreateWellKnownSid.Call( 122 | uintptr(sidType), 123 | uintptr(unsafe.Pointer(sidDomain)), 124 | uintptr(unsafe.Pointer(sid)), 125 | uintptr(unsafe.Pointer(sidLen)), 126 | ) 127 | if ret == 0 { 128 | return err 129 | } 130 | return nil 131 | } 132 | -------------------------------------------------------------------------------- /api/sid_test.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package api 4 | 5 | import ( 6 | "golang.org/x/sys/windows" 7 | 8 | "testing" 9 | "unsafe" 10 | ) 11 | 12 | func TestSIDLookup(t *testing.T) { 13 | var ( 14 | sid = make([]byte, SECURITY_MAX_SID_SIZE) 15 | sidLen = uint32(unsafe.Sizeof(sid)) 16 | ) 17 | if err := CreateWellKnownSid( 18 | WinNullSid, 19 | nil, 20 | (*windows.SID)(unsafe.Pointer(&sid[0])), 21 | &sidLen, 22 | ); err != nil { 23 | t.Fatal(err) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /apply.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package acl 4 | 5 | import ( 6 | "github.com/hectane/go-acl/api" 7 | "golang.org/x/sys/windows" 8 | 9 | "unsafe" 10 | ) 11 | 12 | // Apply the provided access control entries to a file. If the replace 13 | // parameter is true, existing entries will be overwritten. If the inherit 14 | // parameter is true, the file will inherit ACEs from its parent. 15 | func Apply(name string, replace, inherit bool, entries ...api.ExplicitAccess) error { 16 | var oldAcl windows.Handle 17 | if !replace { 18 | var secDesc windows.Handle 19 | api.GetNamedSecurityInfo( 20 | name, 21 | api.SE_FILE_OBJECT, 22 | api.DACL_SECURITY_INFORMATION, 23 | nil, 24 | nil, 25 | &oldAcl, 26 | nil, 27 | &secDesc, 28 | ) 29 | defer windows.LocalFree(secDesc) 30 | } 31 | var acl windows.Handle 32 | if err := api.SetEntriesInAcl( 33 | entries, 34 | oldAcl, 35 | &acl, 36 | ); err != nil { 37 | return err 38 | } 39 | defer windows.LocalFree((windows.Handle)(unsafe.Pointer(acl))) 40 | var secInfo uint32 41 | if !inherit { 42 | secInfo = api.PROTECTED_DACL_SECURITY_INFORMATION 43 | } else { 44 | secInfo = api.UNPROTECTED_DACL_SECURITY_INFORMATION 45 | } 46 | return api.SetNamedSecurityInfo( 47 | name, 48 | api.SE_FILE_OBJECT, 49 | api.DACL_SECURITY_INFORMATION|secInfo, 50 | nil, 51 | nil, 52 | acl, 53 | 0, 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /apply_test.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package acl 4 | 5 | import ( 6 | "golang.org/x/sys/windows" 7 | 8 | "errors" 9 | "io/ioutil" 10 | "os" 11 | "testing" 12 | ) 13 | 14 | func TestApply(t *testing.T) { 15 | f, err := ioutil.TempFile(os.TempDir(), "") 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | defer os.Remove(f.Name()) 20 | if err := Apply( 21 | f.Name(), 22 | true, 23 | true, 24 | DenyName(windows.GENERIC_ALL, "CREATOR OWNER"), 25 | ); err != nil { 26 | t.Fatal(err) 27 | } 28 | r, err := os.Open(f.Name()) 29 | if err == nil { 30 | r.Close() 31 | t.Fatal("owner able to access file") 32 | } 33 | } 34 | 35 | func TestError(t *testing.T) { 36 | if _, err := os.Stat(`C:\Folder\That\Doesnt\Exist`); !os.IsNotExist(err) { 37 | t.Skip(`Oh come on - C:\Folder\That\Doesnt\Exist exists`) 38 | } 39 | 40 | err := Apply( 41 | `C:\Folder\That\Doesnt\Exist`, 42 | true, 43 | true, 44 | DenyName(windows.GENERIC_ALL, "CREATOR OWNER"), 45 | ) 46 | if err == nil { 47 | t.Fatal("Error expected, none received") 48 | } 49 | t.Log(err) 50 | if !errors.Is(err, os.ErrNotExist) { 51 | t.Fatalf("Expected to receive an error that \"Is\" ErrNotExist, received %s", err) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | clone_folder: C:\gopath\src\github.com\hectane\go-acl 4 | 5 | environment: 6 | GOPATH: C:\gopath 7 | 8 | install: 9 | - go version 10 | - go env 11 | - go get -t -v ./... 12 | 13 | build: off 14 | 15 | test_script: 16 | - go test -v ./... 17 | -------------------------------------------------------------------------------- /chmod.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package acl 4 | 5 | import ( 6 | "os" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | // Change the permissions of the specified file. Only the nine 12 | // least-significant bytes are used, allowing access by the file's owner, the 13 | // file's group, and everyone else to be explicitly controlled. 14 | func Chmod(name string, fileMode os.FileMode) error { 15 | // https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems 16 | creatorOwnerSID, err := windows.StringToSid("S-1-3-0") 17 | if err != nil { 18 | return err 19 | } 20 | creatorGroupSID, err := windows.StringToSid("S-1-3-1") 21 | if err != nil { 22 | return err 23 | } 24 | everyoneSID, err := windows.StringToSid("S-1-1-0") 25 | if err != nil { 26 | return err 27 | } 28 | 29 | mode := uint32(fileMode) 30 | return Apply( 31 | name, 32 | true, 33 | false, 34 | GrantSid(((mode&0700)<<23)|((mode&0200)<<9), creatorOwnerSID), 35 | GrantSid(((mode&0070)<<26)|((mode&0020)<<12), creatorGroupSID), 36 | GrantSid(((mode&0007)<<29)|((mode&0002)<<15), everyoneSID), 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /chmod_test.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package acl 4 | 5 | import ( 6 | "io/ioutil" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | func TestChmod(t *testing.T) { 12 | f, err := ioutil.TempFile(os.TempDir(), "") 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | defer os.Remove(f.Name()) 17 | if err := Chmod(f.Name(), 0); err != nil { 18 | t.Fatal(err) 19 | } 20 | r, err := os.Open(f.Name()) 21 | if err == nil { 22 | r.Close() 23 | t.Fatal("owner able to access file", f.Name()) 24 | } 25 | if err := Chmod(f.Name(), 0400); err != nil { 26 | t.Fatal(err) 27 | } 28 | r, err = os.Open(f.Name()) 29 | if err != nil { 30 | t.Fatal("owner unable to access file") 31 | } 32 | r.Close() 33 | } 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hectane/go-acl 2 | 3 | go 1.12 4 | 5 | require golang.org/x/sys v0.0.0-20190529164535-6a60838ec259 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.0.0-20190529164535-6a60838ec259 h1:so6Hr/LodwSZ5UQDu/7PmQiDeS112WwtLvU3lpSPZTU= 2 | golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 3 | -------------------------------------------------------------------------------- /posix.go: -------------------------------------------------------------------------------- 1 | //+build !windows 2 | 3 | package acl 4 | 5 | import "os" 6 | 7 | // Chmod is os.Chmod. 8 | var Chmod = os.Chmod 9 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package acl 4 | 5 | import ( 6 | "github.com/hectane/go-acl/api" 7 | "golang.org/x/sys/windows" 8 | 9 | "unsafe" 10 | ) 11 | 12 | // Create an ExplicitAccess instance granting permissions to the provided SID. 13 | func GrantSid(accessPermissions uint32, sid *windows.SID) api.ExplicitAccess { 14 | return api.ExplicitAccess{ 15 | AccessPermissions: accessPermissions, 16 | AccessMode: api.GRANT_ACCESS, 17 | Inheritance: api.SUB_CONTAINERS_AND_OBJECTS_INHERIT, 18 | Trustee: api.Trustee{ 19 | TrusteeForm: api.TRUSTEE_IS_SID, 20 | Name: (*uint16)(unsafe.Pointer(sid)), 21 | }, 22 | } 23 | } 24 | 25 | // Create an ExplicitAccess instance granting permissions to the provided name. 26 | func GrantName(accessPermissions uint32, name string) api.ExplicitAccess { 27 | return api.ExplicitAccess{ 28 | AccessPermissions: accessPermissions, 29 | AccessMode: api.GRANT_ACCESS, 30 | Inheritance: api.SUB_CONTAINERS_AND_OBJECTS_INHERIT, 31 | Trustee: api.Trustee{ 32 | TrusteeForm: api.TRUSTEE_IS_NAME, 33 | Name: windows.StringToUTF16Ptr(name), 34 | }, 35 | } 36 | } 37 | 38 | // Create an ExplicitAccess instance denying permissions to the provided SID. 39 | func DenySid(accessPermissions uint32, sid *windows.SID) api.ExplicitAccess { 40 | return api.ExplicitAccess{ 41 | AccessPermissions: accessPermissions, 42 | AccessMode: api.DENY_ACCESS, 43 | Inheritance: api.SUB_CONTAINERS_AND_OBJECTS_INHERIT, 44 | Trustee: api.Trustee{ 45 | TrusteeForm: api.TRUSTEE_IS_SID, 46 | Name: (*uint16)(unsafe.Pointer(sid)), 47 | }, 48 | } 49 | } 50 | 51 | // Create an ExplicitAccess instance denying permissions to the provided name. 52 | func DenyName(accessPermissions uint32, name string) api.ExplicitAccess { 53 | return api.ExplicitAccess{ 54 | AccessPermissions: accessPermissions, 55 | AccessMode: api.DENY_ACCESS, 56 | Inheritance: api.SUB_CONTAINERS_AND_OBJECTS_INHERIT, 57 | Trustee: api.Trustee{ 58 | TrusteeForm: api.TRUSTEE_IS_NAME, 59 | Name: windows.StringToUTF16Ptr(name), 60 | }, 61 | } 62 | } 63 | --------------------------------------------------------------------------------