paths_windows.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package paths
  4. import (
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "golang.org/x/sys/windows"
  9. "tailscale.com/util/winutil"
  10. )
  11. // ensureStateDirPerms applies a restrictive ACL to the directory specified by dirPath.
  12. // It sets the following security attributes on the directory:
  13. // Owner: The user for the current process;
  14. // Primary Group: The primary group for the current process;
  15. // DACL: Full control to the current user and to the Administrators group.
  16. //
  17. // (We include Administrators so that admin users may still access logs;
  18. // granting access exclusively to LocalSystem would require admins to use
  19. // special tools to access the Log directory)
  20. //
  21. // Inheritance: The directory does not inherit the ACL from its parent.
  22. //
  23. // However, any directories and/or files created within this
  24. // directory *do* inherit the ACL that we are setting.
  25. func ensureStateDirPerms(dirPath string) error {
  26. fi, err := os.Stat(dirPath)
  27. if err != nil {
  28. return err
  29. }
  30. if !fi.IsDir() {
  31. return os.ErrInvalid
  32. }
  33. if strings.ToLower(filepath.Base(dirPath)) != "tailscale" {
  34. return nil
  35. }
  36. // We need the info for our current user as SIDs
  37. sids, err := winutil.GetCurrentUserSIDs()
  38. if err != nil {
  39. return err
  40. }
  41. // We also need the SID for the Administrators group so that admins may
  42. // easily access logs.
  43. adminGroupSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
  44. if err != nil {
  45. return err
  46. }
  47. // Munge the SIDs into the format required by EXPLICIT_ACCESS.
  48. userTrustee := windows.TRUSTEE{nil, windows.NO_MULTIPLE_TRUSTEE,
  49. windows.TRUSTEE_IS_SID, windows.TRUSTEE_IS_USER,
  50. windows.TrusteeValueFromSID(sids.User)}
  51. adminTrustee := windows.TRUSTEE{nil, windows.NO_MULTIPLE_TRUSTEE,
  52. windows.TRUSTEE_IS_SID, windows.TRUSTEE_IS_WELL_KNOWN_GROUP,
  53. windows.TrusteeValueFromSID(adminGroupSid)}
  54. // We declare our access rights via this array of EXPLICIT_ACCESS structures.
  55. // We set full access to our user and to Administrators.
  56. // We configure the DACL such that any files or directories created within
  57. // dirPath will also inherit this DACL.
  58. explicitAccess := []windows.EXPLICIT_ACCESS{
  59. {
  60. windows.GENERIC_ALL,
  61. windows.SET_ACCESS,
  62. windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
  63. userTrustee,
  64. },
  65. {
  66. windows.GENERIC_ALL,
  67. windows.SET_ACCESS,
  68. windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
  69. adminTrustee,
  70. },
  71. }
  72. dacl, err := windows.ACLFromEntries(explicitAccess, nil)
  73. if err != nil {
  74. return err
  75. }
  76. // We now reset the file's owner, primary group, and DACL.
  77. // We also must pass PROTECTED_DACL_SECURITY_INFORMATION so that our new ACL
  78. // does not inherit any ACL entries from the parent directory.
  79. const flags = windows.OWNER_SECURITY_INFORMATION |
  80. windows.GROUP_SECURITY_INFORMATION |
  81. windows.DACL_SECURITY_INFORMATION |
  82. windows.PROTECTED_DACL_SECURITY_INFORMATION
  83. return windows.SetNamedSecurityInfo(dirPath, windows.SE_FILE_OBJECT, flags,
  84. sids.User, sids.PrimaryGroup, dacl, nil)
  85. }
  86. // LegacyStateFilePath returns the legacy path to the state file when it was stored under the
  87. // current user's %LocalAppData%.
  88. func LegacyStateFilePath() string {
  89. return filepath.Join(os.Getenv("LocalAppData"), "Tailscale", "server-state.conf")
  90. }