rharder / imagesnap

Capture Images from the Command Line
https://github.com/rharder/imagesnap
458 stars 49 forks source link

Mojave support #26

Closed bytesoverflow closed 3 years ago

bytesoverflow commented 6 years ago

ImageSnap doesn't seem to work with Mojave. Is it something to do with the new hardened runtime?

bdentino commented 6 years ago

+1 According to Console:

This app has crashed because it has a hardened runtime and attempted to access privacy-sensitive data without an entitlement indicating its intent to access this data. The app must have the 'com.apple.security.device.camera' entitlement.

bdentino commented 6 years ago

@tobydeh How are you trying to run this? I found that it works fine running in the built-in Terminal app, but has problems running in iTerm2

bytesoverflow commented 6 years ago

The problem occurs when imagesnap is run from another application. I've had to build a new tool which requests permission to use the camera (similar to location services).

bdentino commented 6 years ago

Do you have that in a repo somewhere? I'd be curious to see how it's done. For now I've just wrapped the binary in an automator app, and that seems to be working nicely.

bmorg commented 6 years ago

I am having the same problem. Running in Terminal I get a "Terminal wants to access your camera" dialogue on first execution. With iTerm2 I am not getting the dialogue and hence execution fails.

Camera access can be controlled in System Preferences > Security > Camera. But unlike "Full Disk Access" the "Camera" list has no option to add new applications to it.

bmorg commented 6 years ago

This has been addressed in iTerm2: https://gitlab.com/gnachman/iterm2/issues/7194

The latest beta re-prompts for the camera permission.

I think this resolves this issue.

0xmilan commented 6 years ago

It works for me when I run it in Terminal, since I've approved the pop-up to give access to the camera. However I doesn't work through ssh. I get the following error: Abort trap: 6 I thought this was because the keychain is not unlocked with an ssh connection. So I did: security unlock-keychain ~/Library/Keychains/login.keychain (enter password) eval `ssh-agent -s` but it didn't help. Now I see that it's most likely because I explicitly have to give access to an app to use the camera. Is there a way to do this for ssh connections?

0xmilan commented 6 years ago

I found a way to get it to work with ssh. Create imagesnap.sh with the following content and make it executable: imagesnap -w 1 macCam.png run: open -a Terminal.app imagesnap.sh This will create a new Terminal window each time you run the command. I've created a bash function to automatically kill the Terminal after the photo is taken.

function cam() {
    open -a Terminal.app imagesnap.sh
    sleep 3
    killall Terminal
}

The sleep is necessary, otherwise the Terminal window is killed before it can take the picture. The problem with this is it will close every other Terminal instance that you were working in, but I couldn't find a way to only close the last window.

JayBrown commented 5 years ago

You should use osascript -e 'tell application "Terminal" to quit" || killall Terminal instead of just killall Terminal. That way you'll try to gracefully quit the app before resorting to the bruteforce method.

Weird thing that I noticed is that imagesnap (v0.2.5, because v0.2.6 is broken) doesn't work with zsh, and with bash in iTerm it sometimes doesn't work either: an image file is created, but it's all black. Using imagesnap in bash in Terminal.app is fine, however.

Same goes for putting imagesnap into a shell script: an image file is created, but it's all black.

Not a good thing if you want to use imagesnap for taking stealth pictures, e.g. in case your Mac has been stolen. In those cases you can't open Terminal etc. This is so pathetic, Apple.

skjalmhvide commented 5 years ago

I used to take screenshots with imagesnap through ssh - but using the app approach forcing me to be logged in into the mac - that's problem - I really don't the think it's useful. I'm wonder why the programmer has not released a new version of imagesnap incorporating the right security entitlement - it should be enough?

JayBrown commented 5 years ago

There's an easy solution. Create an AppleScript in Script Editor, e.g. this one

do shell script "! [[ -d ~/.cache/Snapper ]] && /bin/mkdir -p ~/.cache/Snapper"
set theDate to (do shell script "/bin/date -u | /usr/bin/sed 's/ /-/g'")
do shell script "/usr/local/bin/imagesnap -w 2 ~/.cache/Snapper/snap-" & theDate & ".jpeg"

…then don't save, but export as an application, in this case with the filename "Snapper.app", but don't codesign yet.

Open the Info.plist, add key LSUIElement with boolean true, change the BundleID, if you want, change the bundle icon, if you want etc. Then save and codesign, e.g. with: codesign --sign "<YourCertificateID>" --force --deep -i "<BundleID>" /Applications/Snapper.app

Then you can run it manually: macOS will probably ask you for camera access. Then (I assume!) you can also run it while being remotely logged in with open -a Snapper or with open -b <BundleID> or using the full path with open /Applications/Snapper.app… and of course you can also make one of those into an alias, e.g. snapper.

PS: I've tested the same procedure with app bundles exported from Automator, which does not work. Running a Platypus application doesn't work either.

skjalmhvide commented 5 years ago

Hi JayBrown, Do I need to codesign it?

JayBrown commented 5 years ago

You can try it without code-signing first. But it's possible that macOS will then ask you for camera access every time it launches. But if you have a developer account under your Apple ID and Xcode installed, you can just use the development certificate ("Mac Developer: "), or (if you have paid for a real developer certificate), you can (and should) use that. Otherwise it's easy to create your own code signature in Keychain Access or with third-party tools like @chris2511's xca, my preferred tool, because you can create whole certificate chains, if you want. But you don't need a valid developer certificate by Apple. That might change in the future, but currently it seems to work by just signing it locally with a self-signed certificate or self-issued root.

PS: I haven't tested it with an adhoc-signature, for which you would have to execute codesign --sign - --force --deep /Applications/Snapper.app (you don't need an actual certificate for adhoc-signing, so maybe you want to try that first).

PPS: another thing… if codesign prints an error about detritus, you need to execute xattr -cr /Applications/Snapper.app first before code-signing.

JayBrown commented 5 years ago

One additional piece of information: my AppleScript wrapper didn't wind up in the camera access list until reboot. And I had to allow camera access again after reboot too. I'll test this again as soon as I can. EDIT: did a second reboot, and camera access is baked in for the wrapper, so after one reboot, this workaround should be sustainable, and the only thing to check now is if the wrapper also works after remote ssh login.

snap

skjalmhvide commented 5 years ago

Hi I get this error when trying signing - any ideas? codesign --sign - --force --deep ./snapper.app/ 2019-01-26 10:13:12.323 codesign[47659:7298941] There was an error parsing the Info.plist for the bundle at URL <0x7fe0cae003d0>: NSCocoaErrorDomain - 3840 2019-01-26 10:13:12.325 codesign[47659:7298941] There was an error parsing the Info.plist for the bundle at URL <0x7fe0cac0b1a0>: NSCocoaErrorDomain - 3840 xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun ./snapper.app/: the codesign_allocate helper tool cannot be found or used In subcomponent: /Users/avdr/snapper.app/Contents/MacOS/applet

JayBrown commented 5 years ago

You probably haven't installed Xcode or the Command Line Tools yet. For code-signing you only need the latter; install them with xcode-select --install. (It's a couple of MB that need to be downloaded.) You can always check your developer path with xcode-select -p. This path will change (or will have to be changed manually), if you also install Xcode at some point.

skjalmhvide commented 5 years ago

Then I get this: 019-01-26 15:37:30.686 codesign[49732:7484157] There was an error parsing the Info.plist for the bundle at URL <0x7fabb940c4c0>: NSCocoaErrorDomain - 3840 2019-01-26 15:37:30.688 codesign[49732:7484157] There was an error parsing the Info.plist for the bundle at URL <0x7fabb9412200>: NSCocoaErrorDomain - 3840 2019-01-26 15:37:30.702 codesign[49732:7484157] There was an error parsing the Info.plist for the bundle at URL <0x7fabb9412ac0>: NSCocoaErrorDomain - 3840

JayBrown commented 5 years ago

The reason could be that the Info.plist is corrupted or incorrectly formatted. That's why I tend to use GUI tools when editing Info.plist files (with PrefEdit by Marcel Bresink).

snap

This is my Info.plist xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleAllowMixedLocalizations</key>
    <true/>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>applet</string>
    <key>CFBundleIconFile</key>
    <string>applet</string>
    <key>CFBundleIdentifier</key>
    <string>com.apple.ScriptEditor.id.Snapper</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>Snapper</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleSignature</key>
    <string>aplt</string>
    <key>LSMinimumSystemVersionByArchitecture</key>
    <dict>
        <key>x86_64</key>
        <string>10.6</string>
    </dict>
    <key>LSRequiresCarbon</key>
    <true/>
    <key>NSAppleEventsUsageDescription</key>
    <string>This script needs to control other applications to run.</string>
    <key>NSAppleMusicUsageDescription</key>
    <string>This script needs access to your music to run.</string>
    <key>NSCalendarsUsageDescription</key>
    <string>This script needs access to your calendars to run.</string>
    <key>NSCameraUsageDescription</key>
    <string>This script needs access to your camera to run.</string>
    <key>NSContactsUsageDescription</key>
    <string>This script needs access to your contacts to run.</string>
    <key>NSHomeKitUsageDescription</key>
    <string>This script needs access to your HomeKit Home to run.</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>This script needs access to your microphone to run.</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>This script needs access to your photos to run.</string>
    <key>NSRemindersUsageDescription</key>
    <string>This script needs access to your reminders to run.</string>
    <key>NSSiriUsageDescription</key>
    <string>This script needs access to Siri to run.</string>
    <key>NSSystemAdministrationUsageDescription</key>
    <string>This script needs access to administer this system to run.</string>
    <key>WindowState</key>
    <dict>
        <key>bundleDividerCollapsed</key>
        <true/>
        <key>bundlePositionOfDivider</key>
        <real>0.0000000000000000</real>
        <key>dividerCollapsed</key>
        <false/>
        <key>eventLogLevel</key>
        <integer>2</integer>
        <key>name</key>
        <string>ScriptWindowState</string>
        <key>positionOfDivider</key>
        <real>388.0000000000000000</real>
        <key>savedFrame</key>
        <string>244 200 700 672 0 0 1680 1027 </string>
        <key>selectedTab</key>
        <string>description</string>
    </dict>
    <key>LSUIElement</key>
    <true/>
</dict>
</plist>
JayBrown commented 5 years ago

And? Has it worked out for you? Incl. ssh?

jkav77 commented 5 years ago

I think I am encountering the same problem when I run imagesnap in a LaunchDaemon. Running imagesnap in a script and running imagesnap directly work fine, but it fails when calling the script from a launch daemon.

I tried wrapping it in an automator .app and calling the .app from the LaunchDaemon but that didn't have any effect.

I opened a stackoverflow question about it but maybe it's more relevant here.

JayBrown commented 5 years ago

Global daemons run as root, and if you're correct that background root processes don't have access to the camera, at least not out of the gate, you might try to call the Automator app under the logged in user instead of root.

user_id=$(id -u $(who | awk '/console/{print $1}'))

will get you the ID of the logged-in user (only regular logins, i.e. "moved on console"), and then you can launch the Automator app with

launchctl asuser $user_id open -a Snapper.

But I can't promise it'll work, because I haven't tested it. Maybe the system will detect that the open command originally goes back to a root process, and then it will fail too. (?)

EDIT: in my case, using an Automator app didn't work anyway… it only worked with app exported from the ScriptEditor. (Maybe you should try that first.)

EDIT2: it might also fail, if the user has never run the imagesnap wrapper app before; then he would need to allow camera access first.

manuelfhp commented 5 years ago

One additional piece of information: my AppleScript wrapper didn't wind up in the camera access list until reboot. And I had to allow camera access again after reboot too. I'll test this again as soon as I can. EDIT: did a second reboot, and camera access is baked in for the wrapper, so after one reboot, this workaround should be sustainable, and the only thing to check now is if the wrapper also works after remote ssh login.

snap

I tried to run imagesnap remotely but It not works. Isight can blink green light but the prompt freeze.. So the image capture not works. Running locally it works perfect.

I think maybe some Mojave security rules are blocking running outside console.

Do you know how to run imagesnap from remote ssh ?

JayBrown commented 5 years ago

Did you call imagesnap directly, or did you run a wrapper application?

(Note: I haven't been able to test the ssh context yet.)

manuelfhp commented 5 years ago

Did you call imagesnap directly, or did you run a wrapper application?

(Note: I haven't been able to test the ssh context yet.)

I just did a remote ssh after that run ./imagesnap.... Isight blinks green but imagesnap process freeze. I have to perform a ctrl+c . There isn't any image captured.

Can you try it? Do have the same behavior ? Any clue how to run it remotely ?

rharder commented 5 years ago

Author here: I appreciate everyone trying to find a solution to the permissions problem. Obviously it's a privacy issue that we're working against. I'm not familiar with any new code-signing that Apple wants, but I'm open to any direction people have.

martinorob commented 5 years ago

Any news? Thanks

rharder commented 5 years ago

I can only confirm that imagesnap works "in person" but if I ssh into a box I get "Abort trap: 6" which I'm guessing is from this issue. The first time I run imagesnap "in person" (that is, running it in Terminal on the computer I'm in front of), osx prompts me if I want to give Terminal access to the camera. I haven't tracked down anything further.

ghost commented 5 years ago

10.14.6 (18G87) Here. I created an applescript application with the following: do shell script "/usr/local/bin/imagesnap -w 1 /Users/mogitzme/Desktop/[backtick]date +%m%d%y-%H%M%S[backtick].png" *(replaced actual backtick with '[backtick]' as to not confuse markup on this post.

Ran it locally and approved camera

open Desktop/ImageSnapper.app

Then I was able to run it from a remote ssh session with same command above. Anytime I modified the applescript application, it had to be re-approved.

Have not tried rebooting or logging out from the console yet to see if it breaks, but looks like the wrapper approach is workable.

JayBrown commented 4 years ago

I've now finally managed to confirm that this works: code-signed and TCC-approved AppleScript application calling imagesnap > ssh-login as admin… ran the app with open -a Snapper, and it worked flawlessly