From f8e06def74a13d2e4cc37023bdc2c11122c0dea6 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Tue, 23 Jun 2020 19:07:32 -0700 Subject: [PATCH] Add win32 named pipe security level option The sbtipcsocket by default restricts win32 named pipes to only allow connections from the same login session. This makes connecting to a remote server not work over ssh. We relax the default slightly in sbt to allow the owner of the pipe to connect over any logon shell. The user could restore the old behavior with: ``` Global / windowsServerSecurityLevel := Win32SecurityLevel.LOGON_DACL ``` or, if YOLO ``` Global / windowsServerSecurityLevel := Win32SecurityLevel.NO_SECURITY ``` --- main-command/src/main/scala/sbt/BasicKeys.scala | 7 +++++++ .../src/main/scala/sbt/internal/server/Server.scala | 9 ++++++++- main/src/main/scala/sbt/Defaults.scala | 2 ++ main/src/main/scala/sbt/Keys.scala | 1 + main/src/main/scala/sbt/Project.scala | 3 +++ main/src/main/scala/sbt/internal/CommandExchange.scala | 2 ++ 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index 942698870..bee82e9b5 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -76,6 +76,13 @@ object BasicKeys { 10000 ) + val windowsServerSecurityLevel = + AttributeKey[Int]( + "windowsServerSecurityLevel", + "Configures the security level of the named pipe. Values: 0 - No security; 1 - Logon user only; 2 - Process owner only", + 10000 + ) + // Unlike other BasicKeys, this is not used directly as a setting key, // and severLog / logLevel is used instead. private[sbt] val serverLogLevel = diff --git a/main-command/src/main/scala/sbt/internal/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index 775683130..e3ffbfe6b 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -64,7 +64,13 @@ private[sbt] object Server { connection.connectionType match { case ConnectionType.Local if isWindows => // Named pipe already has an exclusive lock. - addServerError(new Win32NamedPipeServerSocket(pipeName)) + addServerError( + new Win32NamedPipeServerSocket( + pipeName, + false, + connection.windowsServerSecurityLevel + ) + ) case ConnectionType.Local => val maxSocketLength = new UnixDomainSocketLibrary.SockaddrUn().sunPath.length - 1 val path = socketfile.getAbsolutePath @@ -228,6 +234,7 @@ private[sbt] case class ServerConnection( pipeName: String, bspConnectionFile: File, appConfiguration: AppConfiguration, + windowsServerSecurityLevel: Int ) { def shortName: String = { connectionType match { diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 90b6bc815..59e030808 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -17,6 +17,7 @@ import lmcoursier.CoursierDependencyResolution import lmcoursier.definitions.{ Configuration => CConfiguration } import org.apache.ivy.core.module.descriptor.ModuleDescriptor import org.apache.ivy.core.module.id.ModuleRevisionId +import org.scalasbt.ipcsocket.Win32SecurityLevel import sbt.Def.{ Initialize, ScopedKey, Setting, SettingsDefinition } import sbt.Keys._ import sbt.Project.{ @@ -386,6 +387,7 @@ object Defaults extends BuildCommon { else Set() }, serverHandlers :== Nil, + windowsServerSecurityLevel := Win32SecurityLevel.OWNER_DACL, // allows any owner logon session to access the server fullServerHandlers := Nil, insideCI :== sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI") || SysProp.ci, diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 54a3c5ee1..30ff9df2d 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -95,6 +95,7 @@ object Keys { val serverHost = SettingKey(BasicKeys.serverHost) val serverAuthentication = SettingKey(BasicKeys.serverAuthentication) val serverConnectionType = SettingKey(BasicKeys.serverConnectionType) + val windowsServerSecurityLevel = SettingKey(BasicKeys.windowsServerSecurityLevel) val fullServerHandlers = SettingKey(BasicKeys.fullServerHandlers) val serverHandlers = settingKey[Seq[ServerHandler]]("User-defined server handlers.") diff --git a/main/src/main/scala/sbt/Project.scala b/main/src/main/scala/sbt/Project.scala index 18cf13ddc..8ee5c2e4c 100755 --- a/main/src/main/scala/sbt/Project.scala +++ b/main/src/main/scala/sbt/Project.scala @@ -30,6 +30,7 @@ import Keys.{ serverConnectionType, fullServerHandlers, logLevel, + windowsServerSecurityLevel, } import Scope.{ Global, ThisScope } import Def.{ Flattened, Initialize, ScopedKey, Setting } @@ -523,9 +524,11 @@ object Project extends ProjectExtra { s.definedCommands, projectCommand ) + val winSecurityLevel = get(windowsServerSecurityLevel).getOrElse(2) val newAttrs = s.attributes .put(historyPath.key, history) + .put(windowsServerSecurityLevel.key, winSecurityLevel) .setCond(autoStartServer.key, startSvr) .setCond(serverPort.key, port) .setCond(serverHost.key, host) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index ad756d32d..36985a5c0 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -156,6 +156,7 @@ private[sbt] final class CommandExchange { s.get(serverAuthentication).getOrElse(Set(ServerAuthentication.Token)) lazy val connectionType = s.get(serverConnectionType).getOrElse(ConnectionType.Tcp) lazy val handlers = s.get(fullServerHandlers).getOrElse(Nil) + lazy val win32Level = s.get(windowsServerSecurityLevel).getOrElse(2) def onIncomingSocket(socket: Socket, instance: ServerInstance): Unit = { val name = newNetworkName @@ -193,6 +194,7 @@ private[sbt] final class CommandExchange { pipeName, bspConnectionFile, s.configuration, + win32Level, ) val serverInstance = Server.start(connection, onIncomingSocket, s.log) // don't throw exception when it times out