Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move compilation daemon portfile to homedir #6108

Merged
merged 2 commits into from
Oct 9, 2017
Merged

Conversation

adriaanm
Copy link
Contributor

@adriaanm adriaanm commented Oct 3, 2017

Store the compilation daemon's administrativia (port file, redirection)
under ~/.scalac/, instead of the less standard
/tmp/scala-devel/${USER:shared}/scalac-compile-server-port.

On creation, remove group- and other-permissions from these
private files, ditto for the repl's history file.

On Java 6 on Windows, opt in to compilation daemon using -nc:false.

Store the compilation daemon's administrativia (port file, redirection)
under `~/.scalac/`, instead of the less standard
`/tmp/scala-devel/${USER:shared}/scalac-compile-server-port`.

On creation, remove group- and other-permissions from these
private files, ditto for the repl's history file.

On Java 6 on Windows, opt in to compilation daemon using `-nc:false`.


trait OwnerOnlyChmod {
/** Remove group/other permisisons for `file`, it if exists */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo (permissions)


private def defaultUseCompdaemon = {
// can't reliably lock down permissions on the portfile in this environment => disable by default.
!scala.util.Properties.isWin || scala.util.Properties.isJavaAtLeast("7")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should print a warning here rather than silently giving different behavior

val secret = new SecureRandom().nextInt.toString
val secretBytes = new Array[Byte](16)
new SecureRandom().nextBytes(secretBytes)
val secretDigits = new BigInteger(secretBytes).toString().getBytes("UTF-8")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these 3 lines of code are quite mystifying. some comments explaining the motivation would be good

def chmod(file: File): Unit = if (file.exists()) {
def clearAndSetOwnerOnly(f: (Boolean, Boolean) => Boolean): Unit = {
def fail() = throw new IOException("Unable to modify permissions of " + file)
// attribute = false, ownerOwnly = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo here and 2 lines down: ownly -> only

val aclEntryPermissionValues = aclEntryPermission_class.getDeclaredMethod("values")
val aclEntryType_ALLOW = aclEntryType_class.getDeclaredField("ALLOW")
}
private val reflectors = try { new Reflectors } catch { case ex: Throwable => null }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we log the swallowed exception?

protected lazy val historyFile: File = FileBackedHistory.defaultFile
// For a history file in the standard location, always try to restrict permission,
// creating an empty file if none exists.
// For a user-specified location, only lock down permissions on if we're the ones
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing word in this sentence

try OwnerOnlyChmod().chmodOrCreateEmpty(p.jfile)
catch { case NonFatal(e) =>
if (interpreter.isReplDebug) e.printStackTrace()
interpreter.replinfo(s"Warning: history file ${p}'s permissions could not be restricted to owner-only.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is warning and continuing considered sufficiently secure?

@adriaanm
Copy link
Contributor Author

adriaanm commented Oct 6, 2017

@retronym, ready for final review

@adriaanm adriaanm added this to the 2.11.12 milestone Oct 6, 2017
@adriaanm
Copy link
Contributor Author

adriaanm commented Oct 6, 2017

@szeiger could you give this a go on your surface and any other windows desktops at your disposal ?

@adriaanm adriaanm merged commit f3419fc into scala:2.11.x Oct 9, 2017
} finally {
fos2.close()
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that creating a file then changing the permissions on the file allows a race condition where an attacker on the system can open the file between the file create and the permissions change. It is far safer to use open(2) to set the permissions restrictive while creating the file.

Furthermore if this code is ever run on a directory with shared writing permissions, it's possible for an attacker to unlink(file), set a symlink in place, and have the process change modes of any target that the process has privileges to modify. fchmod(2) is the safe way to change permissions on a file that is already open, though that won't fix the above race condition. So consider this just an aside.

Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Could you suggest how to do this on Java 6? I agree the race condition is a security hole, but couldn't figure out how to do this atomically with the API we have available. I think our NIO-based implementation on Java 8 is closer to what you're suggesting: https://github.com/scala/scala/pull/6120/files#diff-3578ac76088d22b5d9912e984dee3affR46.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review.

We implemented things that way (or at least, tried to) in the 2.12 branch where we could link against the Java 8 NIO API. Scala 2.11 still supports Java 6.

https://github.com/scala/scala/blob/2.12.x/src/reflect/scala/reflect/internal/util/OwnerOnlyChmod.scala

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adriaanm a minute ago
retronym 29 seconds ago

Speaking of race conditions :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The #diff-3578ac7.. link above looks like it may have the same race condition.

I don't know Java particularly well any longer, so it's hard to suggest a replacement. Two possibilities come to mind:

  • set the umask to 0077 (octal), create the file, set the modes, and then reset umask to whatever the user wanted
  • chmod 0700 (octal) the containing directory, create the file, set the file modes, then reset the directory to whatever permissions it should have.

I hope this helps. Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about: create the file with some temporary name/location, do what you need to do and then move it. file moves are atomic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants