From 098cbab7e63125f23769a4ff9271ddbd0366af10 Mon Sep 17 00:00:00 2001
From: Eugene Burkov <e.burkov@adguard.com>
Date: Fri, 22 Nov 2024 17:03:09 +0300
Subject: [PATCH] Pull request 2305: 7400 Disable permcheck

Updates #7400.

Squashed commit of the following:

commit f6508d395288dfa5ed0b9aa2e714bc1eba72d243
Merge: aa7119648 d96e65cb0
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 22 15:43:27 2024 +0300

    Merge branch 'master' into 7400-disable-perm

commit aa7119648bae3f2ff6af96215c3c8eab70b379ea
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:51:37 2024 +0300

    next: add flag

commit c16b90918f5a9493876e3d16776fb18f67cbc09b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:42:47 2024 +0300

    home: fix help

commit 2e096c0e320599b9472f55a47c1abbc95e1a9f8e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:37:30 2024 +0300

    all: imp code, log changes

commit 368598819fc339cc1dbf788d092af9e4c5191f30
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 20 16:12:18 2024 +0300

    home: add permcheck option
---
 CHANGELOG.md             |  8 ++++++++
 internal/home/home.go    | 23 ++++++++++++++++-------
 internal/home/options.go | 13 +++++++++++++
 internal/next/cmd/opt.go | 22 +++++++++++++++++++---
 4 files changed, 56 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f248d42c..b260d138 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,14 @@ NOTE: Add new changes BELOW THIS COMMENT.
 
 - The release executables are now signed.
 
+### Added
+
+- The `--no-permcheck` command-line option to disable checking and migration of
+  permissions for the security-sensitive files and directories, which caused
+  issues on Windows ([#7400]).
+
+[#7400]: https://github.com/AdguardTeam/AdGuardHome/issues/7400
+
 [go-1.23.3]: https://groups.google.com/g/golang-announce/c/X5KodEJYuqI
 
 <!--
diff --git a/internal/home/home.go b/internal/home/home.go
index 4c2771e4..34620e2c 100644
--- a/internal/home/home.go
+++ b/internal/home/home.go
@@ -159,7 +159,7 @@ func setupContext(opts options) (err error) {
 
 	if Context.firstRun {
 		log.Info("This is the first time AdGuard Home is launched")
-		checkPermissions()
+		checkNetworkPermissions()
 
 		return nil
 	}
@@ -686,18 +686,26 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
 		}
 	}
 
-	if permcheck.NeedsMigration(confPath) {
-		permcheck.Migrate(Context.workDir, dataDir, statsDir, querylogDir, confPath)
+	if !opts.noPermCheck {
+		checkPermissions(Context.workDir, confPath, dataDir, statsDir, querylogDir)
 	}
 
-	permcheck.Check(Context.workDir, dataDir, statsDir, querylogDir, confPath)
-
 	Context.web.start()
 
 	// Wait for other goroutines to complete their job.
 	<-done
 }
 
+// checkPermissions checks and migrates permissions of the files and directories
+// used by AdGuard Home, if needed.
+func checkPermissions(workDir, confPath, dataDir, statsDir, querylogDir string) {
+	if permcheck.NeedsMigration(confPath) {
+		permcheck.Migrate(workDir, dataDir, statsDir, querylogDir, confPath)
+	}
+
+	permcheck.Check(workDir, dataDir, statsDir, querylogDir, confPath)
+}
+
 // initUsers initializes context auth module.  Clears config users field.
 func initUsers() (auth *Auth, err error) {
 	sessFilename := filepath.Join(Context.getDataDir(), "sessions.db")
@@ -757,8 +765,9 @@ func startMods(l *slog.Logger) (err error) {
 	return nil
 }
 
-// Check if the current user permissions are enough to run AdGuard Home
-func checkPermissions() {
+// checkNetworkPermissions checks if the current user permissions are enough to
+// use the required networking functionality.
+func checkNetworkPermissions() {
 	log.Info("Checking if AdGuard Home has necessary permissions")
 
 	if ok, err := aghnet.CanBindPrivilegedPorts(); !ok || err != nil {
diff --git a/internal/home/options.go b/internal/home/options.go
index 35f26303..dd7e53e4 100644
--- a/internal/home/options.go
+++ b/internal/home/options.go
@@ -78,6 +78,10 @@ type options struct {
 	// localFrontend forces AdGuard Home to use the frontend files from disk
 	// rather than the ones that have been compiled into the binary.
 	localFrontend bool
+
+	// noPermCheck disables checking and migration of permissions for the
+	// security-sensitive files.
+	noPermCheck bool
 }
 
 // initCmdLineOpts completes initialization of the global command-line option
@@ -305,6 +309,15 @@ var cmdLineOpts = []cmdLineOpt{{
 	description:     "Run in GL-Inet compatibility mode.",
 	longName:        "glinet",
 	shortName:       "",
+}, {
+	updateWithValue: nil,
+	updateNoValue:   func(o options) (options, error) { o.noPermCheck = true; return o, nil },
+	effect:          nil,
+	serialize:       func(o options) (val string, ok bool) { return "", o.noPermCheck },
+	description: "Skip checking and migration of permissions " +
+		"of security-sensitive files.",
+	longName:  "no-permcheck",
+	shortName: "",
 }, {
 	updateWithValue: nil,
 	updateNoValue:   nil,
diff --git a/internal/next/cmd/opt.go b/internal/next/cmd/opt.go
index 8e06a492..b565b9fc 100644
--- a/internal/next/cmd/opt.go
+++ b/internal/next/cmd/opt.go
@@ -89,6 +89,12 @@ type options struct {
 	// TODO(a.garipov): Use.
 	performUpdate bool
 
+	// noPermCheck, if true, instructs AdGuard Home to skip checking and
+	// migrating the permissions of its security-sensitive files.
+	//
+	// TODO(e.burkov):  Use.
+	noPermCheck bool
+
 	// verbose, if true, instructs AdGuard Home to enable verbose logging.
 	verbose bool
 
@@ -110,7 +116,8 @@ const (
 	disableUpdateIdx
 	glinetModeIdx
 	helpIdx
-	localFrontend
+	localFrontendIdx
+	noPermCheckIdx
 	performUpdateIdx
 	verboseIdx
 	versionIdx
@@ -214,7 +221,7 @@ var commandLineOptions = []*commandLineOption{
 		valueType:    "",
 	},
 
-	localFrontend: {
+	localFrontendIdx: {
 		defaultValue: false,
 		description:  "Use local frontend directories.",
 		long:         "local-frontend",
@@ -222,6 +229,14 @@ var commandLineOptions = []*commandLineOption{
 		valueType:    "",
 	},
 
+	noPermCheckIdx: {
+		defaultValue: false,
+		description:  "Skip checking the permissions of security-sensitive files.",
+		long:         "no-permcheck",
+		short:        "",
+		valueType:    "",
+	},
+
 	performUpdateIdx: {
 		defaultValue: false,
 		description:  "Update the current binary and restart the service in case it's installed.",
@@ -264,7 +279,8 @@ func parseOptions(cmdName string, args []string) (opts *options, err error) {
 		disableUpdateIdx: &opts.disableUpdate,
 		glinetModeIdx:    &opts.glinetMode,
 		helpIdx:          &opts.help,
-		localFrontend:    &opts.localFrontend,
+		localFrontendIdx: &opts.localFrontend,
+		noPermCheckIdx:   &opts.noPermCheck,
 		performUpdateIdx: &opts.performUpdate,
 		verboseIdx:       &opts.verbose,
 		versionIdx:       &opts.version,