AdGuardHome/internal/permcheck/migrate_windows.go
Eugene Burkov 3895cfb4f0 Pull request 2312: 7400 Windows permcheck
Updates #7400.

Squashed commit of the following:

commit f50d7c200de545dc6c8ef70b39208f522033fb90
Merge: 47040a14c 37b16bcf7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Dec 3 18:09:23 2024 +0300

    Merge branch 'master' into 7400-chown-permcheck

commit 47040a14cd
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Dec 3 14:26:43 2024 +0300

    permcheck: fix nil entries

commit e1d21c576d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Dec 2 15:37:58 2024 +0300

    permcheck: fix nil owner

commit b1fc67c4d1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 18:07:15 2024 +0300

    permcheck: imp doc

commit 0b6a71326e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 17:16:24 2024 +0300

    permcheck: imp code

commit 7dfbeda179
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 29 14:28:17 2024 +0300

    permcheck: imp code

commit 3a5b6aced9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 28 19:21:03 2024 +0300

    all: imp code, docs

commit c076c93669
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 28 15:14:06 2024 +0300

    permcheck: imp code, docs

commit 09e4ae1ba1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 27 19:19:11 2024 +0300

    all: implement windows permcheck

commit b75ed7d4d3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Nov 25 18:01:47 2024 +0300

    all: revert permissions
2024-12-03 18:26:00 +03:00

135 lines
4 KiB
Go

//go:build windows
package permcheck
import (
"context"
"log/slog"
"github.com/AdguardTeam/golibs/logutil/slogutil"
"golang.org/x/sys/windows"
)
// needsMigration is the Windows-specific implementation of [NeedsMigration].
func needsMigration(ctx context.Context, l *slog.Logger, workDir, _ string) (ok bool) {
l = l.With("type", typeDir, "path", workDir)
dacl, owner, err := getSecurityInfo(workDir)
if err != nil {
l.ErrorContext(ctx, "getting security info", slogutil.KeyError, err)
return true
}
if !owner.IsWellKnown(windows.WinBuiltinAdministratorsSid) {
return true
}
err = rangeACEs(dacl, func(
hdr windows.ACE_HEADER,
mask windows.ACCESS_MASK,
sid *windows.SID,
) (cont bool) {
switch {
case hdr.AceType != windows.ACCESS_ALLOWED_ACE_TYPE:
// Skip non-allowed access control entries.
l.DebugContext(ctx, "skipping deny access control entry", "sid", sid)
case !sid.IsWellKnown(windows.WinBuiltinAdministratorsSid):
// Non-administrator access control entries should not have any
// access rights.
ok = mask > 0
default:
// Administrators should have full control.
ok = mask&fullControlMask != fullControlMask
}
// Stop ranging if the access control entry is unexpected.
return !ok
})
if err != nil {
l.ErrorContext(ctx, "checking access control entries", slogutil.KeyError, err)
return true
}
return ok
}
// migrate is the Windows-specific implementation of [Migrate].
//
// It sets the owner to administrators and adds a full control access control
// entry for the account. It also removes all non-administrator access control
// entries, and keeps deny access control entries. For any created or modified
// entry it sets the propagation flags to be inherited by child objects.
func migrate(ctx context.Context, logger *slog.Logger, workDir, _, _, _, _ string) {
l := logger.With("type", typeDir, "path", workDir)
dacl, owner, err := getSecurityInfo(workDir)
if err != nil {
l.ErrorContext(ctx, "getting security info", slogutil.KeyError, err)
return
}
admins, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
if err != nil {
l.ErrorContext(ctx, "creating administrators sid", slogutil.KeyError, err)
return
}
// TODO(e.burkov): Check for duplicates?
var accessEntries []windows.EXPLICIT_ACCESS
var setACL bool
// Iterate over the access control entries in DACL to determine if its
// migration is needed.
err = rangeACEs(dacl, func(
hdr windows.ACE_HEADER,
mask windows.ACCESS_MASK,
sid *windows.SID,
) (cont bool) {
switch {
case hdr.AceType != windows.ACCESS_ALLOWED_ACE_TYPE:
// Add non-allowed access control entries as is, since they specify
// the access restrictions, which shouldn't be lost.
l.InfoContext(ctx, "migrating deny access control entry", "sid", sid)
accessEntries = append(accessEntries, newDenyExplicitAccess(sid, mask))
setACL = true
case !sid.IsWellKnown(windows.WinBuiltinAdministratorsSid):
// Remove non-administrator ACEs, since such accounts should not
// have any access rights.
l.InfoContext(ctx, "removing access control entry", "sid", sid)
setACL = true
default:
// Administrators should have full control. Don't add a new entry
// here since it will be added later in case there are other
// required entries.
l.InfoContext(ctx, "migrating access control entry", "sid", sid, "mask", mask)
setACL = setACL || mask&fullControlMask != fullControlMask
}
return true
})
if err != nil {
l.ErrorContext(ctx, "ranging through access control entries", slogutil.KeyError, err)
return
}
if setACL {
accessEntries = append(accessEntries, newFullExplicitAccess(admins))
}
if !owner.IsWellKnown(windows.WinBuiltinAdministratorsSid) {
l.InfoContext(ctx, "migrating owner", "sid", owner)
owner = admins
} else {
l.DebugContext(ctx, "owner is already an administrator")
owner = nil
}
err = setSecurityInfo(workDir, owner, accessEntries)
if err != nil {
l.ErrorContext(ctx, "setting security info", slogutil.KeyError, err)
}
}