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

Detect/disable translocation when moving app on macOS Sierra #56

Closed
bewebste opened this issue Jun 29, 2016 · 11 comments
Closed

Detect/disable translocation when moving app on macOS Sierra #56

bewebste opened this issue Jun 29, 2016 · 11 comments

Comments

@bewebste
Copy link

As outlined at http://weblog.rogueamoeba.com/2016/06/29/sierra-and-gatekeeper-path-randomization/, macOS Sierra introduces a new security feature called "Gatekeeper Path Randomization" (or "app translocation", as it's called on the API level). The basic gist is that if you download and run a Gatekeeper app from the Downloads folder, the OS will copy the app into a read-only disk image and run it from there instead. See the link above for more details.

This applies until the user moves the app to any other location (not just the Applications folder), after which the OS will just run the app normally. However, the move is only recognized if performed using the Finder. If you move the app another way, e.g. using "mv" in the Terminal, the app will continue to be translocated when run, even if it's in /Applications.

I've only gotten a little chance to fool around with this on the WWDC seed, but it does appear that the move performed by PFMoveApplication using NSFileManager doesn't disable translocation. So, the app will be moved successfully, but will still be run translocated, even when launching from /Applications. This will also cause PFMoveApplication to prompt the user to move the app a second time, since the app is in fact not being run from /Applications, but rather from its translocated read-only disk image.

I haven't had a chance to try any of this yet, but a new header in the 10.12 SDK at <Security/SecTranslocate.h> contains an API named SecTranslocateURLShouldRunTranslocated() which outlines the circumstances under which an app should be run as translocated. It reads:

@discussion The policy is as follows:
    1. If path is already on a nullfs mountpoint - no translocation
    2. No quarantine attributes - no translocation
    3. If QTN_FLAG_DO_NOT_TRANSLOCATE is set or QTN_FLAG_TRANSLOCATE is not set - no translocations
    4. Otherwise, if QTN_FLAG_TRANSLOCATE is set - translocation

So, I think what the Finder is doing is to set that QTN_FLAG_DO_NOT_TRANSLOCATE flag in the quarantine attributes for the file when the user moves it to a different location. I did some quick digging with xattr in the Terminal, and I do see a bit getting flipped in the com.apple.quarantine attribute when moving a downloaded app on Sierra. Assuming this is correct, then this should be fairly simple for PFMoveApplication to set that same quarantine attribute when it performs its move as well.

It might also be worth adding some logic to check and see whether the app is running from a translocated location (there is other API in SecTranslocate.h that lets you detect this). If it's already being translocated from /Applications, then instead of needing to recopy the entire app, we could potentially just set the quarantine flag on the existing app, so that it doesn't get translocated anymore.

@tempelmann
Copy link

I have more to add and a possible solution that works in my testing (with 10.12 beta 3):

First, let me state that I do not use LetsMove but have implemented my own solution using AppleScript.

Curiously, even using AppleScript, telling the Finder to move the app does not help to stop the translocation process.

Even weirder is this: My Applescript determines the path to the currently running app. It then quits the app and issues the move command, using the previously determined path. And that effectively moves the app from its current location, e.g. from the Downloads folder, to the new location (e.g. /Applications). What's weird, though, is that if I display the determined path or try to use it with a shell command (e.g. to remove the quarantine attributes), it turns out that the path is the randomized path (on which no modifying file operations are allowed). But if the path is the randomized one, it's surprising that the move command doesn't move the app at the randomized location but moves the original app, isn't it?

So, it seems that AppleScript or the Finder is aware of the randomized path and can reconnect it with the downloaded app's path when asking to move the item. But it can't be used to locate the original path in order to issue shell commands, as far as I've come. I wonder if this is special handling Apple added after realizing that many self-moving apps would break otherwise.

Now, my current solution for this whole mess is as follows:

My main app copies the compiled AppleScript app to a temp folder, launches it, but does not quit itself yet. The script then locates the app by referecing it by its id, and fetches the path to the app (which is the randomized path). It then tells my app to quit. Once the app has quit, the script tells the Finder to move the app to the Applications folder. This surprisingly works despite the randomized path that's passed to the move command. Once the move has succeeded, the script issues a shell command to remove the entire quarantine attributes (recursively, as it's attached to every folder and file inside the app bundle), and finally launches the app at the new location.

Which leads me to another issue that you probably have: If you're not using AppleScript but run a shell script, it won't be able to tell the original path of the app for the "mv" command.

And this would be an issue for the suggestion solution messing with the quarantine bits, too, I guess: How does the script tell where the original app is, so that it can move it? Using mv won't work, I guess. AppleScript curiously does.

Hope that helps.

@tempelmann
Copy link

tempelmann commented Jul 28, 2016

Actually, now that I've finally looked at the code of LetsMove, I see that it does remove the quarantine attribute after the move, the same way my solution does it. And since bewebste says that PFMoveApplication does successfully copy the app to the destination, I don't see why removing the quarantine attribute afterwards should not work either, as it does in my AppleScript based solution (which I tested with 10.12 dev beta 3).

So either my testing is flawed, bewebste's is, or the code in LetsMove still does someone differently that my AppleScript solution does better.

Since there are no comments here yet apart from my own, I wonder if anyone else but me is even monitoring this. Do others at least confirm that there's a problem with using LetsMove on 10.12?

Or maybe the issue that bewebste saw only occured with 10.12 b1 but not with the current b3, and thus this issue could be closed.

@tempelmann
Copy link

I've just done a test on 10.12 beta 3. It seems Apple has made some changes that fixes this issue.

I've built the Test app, codesigned it, zipped it using Finder, uploaded it to a web server, downloaded it on a Mac running 10.12 b3 (16A254g), and then unzipped (by double click) and launched it.
I then got the GateKeeper confirmation dialog to open this app.
While the app was running, asking to move itself, I verified with "lsof | grep -e 'LetsMove Test$'" that it's really running from a randomized path at that moment.
When I then click the app's button to move itself to the Apps folder, it succeeds in that and relaunches itself from the Apps folder, without a new prompt to move itself.
I then also verified using "ls -l@" that the app in the Apps folder does not have a quarantine attribute any more.

So, it seems this issue can be closed, and my proposed workaround is not needed.

@talk2neeraj
Copy link

Hi All,

I am trying to detect GPR enabled launch though below code and seems to be working fine for me.

Just wanted share and cross check if it can fail to detect.

-(BOOL) readonlyAccessCheck {
NSString *filePath = [[NSBundle mainBundle] bundlePath];
NSLog(@"bundlePath = %@", filePath);
if([[NSFileManager defaultManager] isWritableFileAtPath: filePath] == NO) {
NSLog(@"%@ is read only", filePath);
return YES;
}
return NO;
}

This is not exactly only GPR detection but it will help me to avoid my sparkle update failure due to GPR.

Cheers,
Neeraj

@tempelmann
Copy link

tempelmann commented Aug 10, 2016

Ugh - there's still one problem with this, after all: Under Sierra b5 (16A286a), LetsMove is able to copy itself to the Apps folder BUT it does not manage to delete itself from the Downloads folder afterwards.

Using AppleScript to move the app instead, would solve this problem, however, as I found in my earlier tests. So, the trick would be to leave most of the existing code intact, and only change the DeleteOrTrash() function to issue an applescript command to move the app to the trash, like this:

on run {filePath}
    set theFile to POSIX file filePath
    tell application "Finder"
        move theFile to trash
    end tell
end run

I've implemented this in my own app and can confirm that this works.

@tempelmann
Copy link

tempelmann commented Aug 17, 2016

BTW, I brought all this to Apple's attention in various bug reports.

This one is particular interesting: http://www.openradar.me/radar?id=5022734169931776
It says:

Creating a script to strip quarantine off an app next to it is dangerous and could end up getting blocked in the future.

Meaning Apple may break what LetsMove is doing in the future.

@bewebste
Copy link
Author

Thanks for all the additional info on this, I've only finally gotten around to investigating this further myself. It turns out that the reason I wasn't seeing the quarantine bit being removed after moving the app into /Applications is because I made a slight alternation to the standard code so that I would bring up the prompt the first time the user quit the app, rather than assaulting them with it when they first launch the app. When doing it this way, I don't bother calling the Relaunch() function (since they're about to quit, relaunching the app doesn't make sense), and it's inside that function where the quarantine bit gets stripped. I modified my code to strip the quarantine separately, and now it successfully moves without being re-translocated the next time it's launched. Anyone who's using LetsMove unmodified should still have it work OK for them, AFAICT, so I'm going to go ahead and close this.

@RolfKocherhans
Copy link

We have this problem with for example TextWrangler - deployed with DeployStudio on German systems.
TextWrangler (5.5.2) gets copied to the Application/Programme folder from the DeployStudio Repository via DeployStudio Runtime.
Now when we start TextWrangler it asks to move itself to the Application folder, but it is already
there !!!!

If we say "Yes" to the move the Program deletes itself and is then 0 bytes in size, the Icon is still there but the App does obviously not work anymore. How can we prevent this ? And why would TextWrangler want to relocate itself to the Application folder when it is already there ?
BTW this is with the release version of Sierra.

@tempelmann
Copy link

tempelmann commented Sep 22, 2016

Rolf, that's happening because the copied app still has the quarantine flags set and thus still runs in "App translocation" mode, i.e. the app, when started, will think it's running from a tmp folder. So, that code you use to copy the app to the Apps folder needs to be improved to include erasing the quarantine flag, e.g. using this cmd: "xattr -dr com.apple.quarantine path/to/the/copied/app".

The fact that it erases itself when trying to move itself again is to be considered a bug, though, IMO. Bug I think this deserves a new issue to be opened, not added to this one.

@andypotion
Copy link
Collaborator

andypotion commented Sep 22, 2016

@tempelmann Thank to your pointer of using AppleScript, I was able to make the app trash itself after copying to /Applications even when running in an App Translocation environment in Sierra. Thanks.

@felix-antony
Copy link

I was facing Android Studio 3.0 preview update error when I try to update the app which running from Downloads folder.
Then I moved Android Studio 3.0 preview from downloads to Application folder on Mac OS Sierra, then I was able to update latest Android Studio 3.0 preview version.

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

No branches or pull requests

6 participants