Securely delete NSURL file and show percent in NSProgressIndicator - cocoa

I am trying to create a "document shredder" application for Mac. I have the NSURL of the files that the user selected, but how would I go about securely deleting said files and passing the progress along to a progress bar?

You want to look at the posix layer calls for file I/O, with those you can overwrite files and implement any algorithm for secure deletion you choose.
If you just need to securely delete the file and are not concerned over showing progress in a GUI you can invoke the srm (secure remove) command using NSTask. For details on the srm enter man srm in a Terminal window.
If you do want to show progress in a GUI then you can convert the output of that command, captured with NSTask and display it as you choose. Or Apple provide the source to srm, you can find it here on the Apple Open Source site and you can incorporate code/algorithms from that into your own code and provide progress indication in your GUI.
HTH

There is no direct way to "securely delete" files from the Cocoa API.
You can use NSFileManager to move files into the trash can, which then leaves it up to the user to delete, or securely delete, the files via the Finder menu.
You could also use NSFileManager to move files into the trash and then use AppleScript to send Finder a message to delete/securely delete files, however, this is generally not a good idea unless your user has specifically requested you empty their trash as you may delete things they are not ready to have deleted.
Otherwise, there does not seem to be an easy or direct way to accomplish a secure delete of files as with the standard API there is no guarantee that the bytes you write out to disk are going to overwrite the ones that existed before.
You may also want to check out this previous question here on SO in relation to why secure delete is not that secure:
how to write back to an existing file, ensuring the bits on the disk get overwritten in OS X

Related

Alternative to Windows Alternate Data Streams

I have the following need to implement on Windows: file with files.
Originally I was thinking to use directory with extension. Something like "folderA.myappext", so when user clicks on it in Explorer, my app is launched instead of folder being opened. Unfortunately, I was unable to find a way to do that. Then I tried to use Alternate Data Streams. This works just fine, but several problems with it:
It works only in NTFS, so no way to send it via email or FTP as is;
Only WinRAR can properly archive it, and you still have to do extra clicks in the UI for that;
The real file size (with all streams in it) is not shown in Explorer and does not participate in showing free/used space, which can very quickly lead to big problems for the user.
No, I can't use zip or any other way to combine files into one - this is high-performance app that also requires write streaming (i.e. it changes data all the time).
Any idea how else to achieve my need on Windows? I know on MacOS you can use 'package', but there is nothing like that on Windows. Any idea?
Something like "folderA.myappext", so when user clicks on it in Explorer, my app is launched instead of folder being opened.
You can't do it based on the extension because folders don't have extensions but you can do it with desktop.ini. Windows 7 and later supports custom verbs on folders.
A working example can be found here.

How to Trash items that are not owned by the current user, the same way the Finder does?

I am writing a tool that offers the option to trash selected items (files, folders). Usually, I'd call -[NSFileManager trashItemAtURL:...] for each of those items, as it's also explained in this and in this SO question.
However, these do not work when trying to delete files from a directory owned by a different user, such as root. My tool shall offer the same option as the Finder in this case, i.e. ask the user to authorize the operation by providing the credentials of an Admin user, and then my app would move the items to the Trash like the Finder does.
I've tried solving this by using a privileged helper, as outline by the EvenBetterAuthorizationSample example code, using launchd, SMJobBless and XPC Services.
The problem with that is, however, that the privileged helper runs as the root user, without knowledge of the current user my app runs under. The result is that, when it trashes a file, it ends up in the root user's Trash folder and not, as the Finder would do it, in the user's Trash folder.
How do I solve this, i.e. how do I move items not owned by the user to the current user's trash instead of the root user's Trash?
Is there some trick I can use that would let me keep using one of the existing trash oder recycle functions?
Doing the move myself is not going to work properly because for Put Back to work, the Trash's .DS_Store file would need to be updated, and there's no API for that, AFAIK.
I have almost found a solution:
Analysis
When the helper is run, e.g. from launchd, or via AuthorizationExecuteWithPrivileges (under macOS 10.15), it may be running as root, with no knowledge of the logged-in user, hence it cannot determine the user's Trash folder.
Oddly, the Environment variables (see man env) may even show the current user's name and home dir, yet the real user id, which one can query with getuid(), will return 0 (root), which also results in NSUserName()and NSHomeDirectory() returning the root user's information. And it appears that trashItemAtURL and related functions rely on NSHomeDirectory() to determine the Trash folder location.
Half-working solution
Fortunately, there is a way to change the real user id, with setreuid.
Therefore, in my testing, when I call setreuid (501, 0) (501 being the uid of the currently logged-in user), then trashItemAtURL does move the file to the user's Trash folder, indeed, along with automatic renaming where necessary.
However, this does not make the Put Back work, the way it would when trashing the same file using the Finder.
Making Put Back work
Looks like the reason for Put Back not working comes from a deeper issue: It appears to be a long-standing bug in the macOS framework, see this bug report.
Which basically means: This is the best we can get out of it until Apple fixes the underlying bug.
The only working alternative to make Put Back work is to ask the Finder to trash the items using AppleEvents / AppleScript.

Open file with app as duplicate

The idea is that I have a template of a file that needs to be opened but it should behave as if only the contents of the file were opened instead of the file itself.
The difference for the user should be, that he needs to select a place to save the file to instead of just overwriting the file that was opened.
Similar behaviour can be seen in the Preview app. When duplicating a function Preview will open the same file in a new window. Upon saving the file the user needs to specify where to save it to.
As far as I can see NSWorkspace does not support such behaviour out of the box. Does anybody know a workaround for that? I'd appreciate it!
EDIT
In my case I don't know where the user wants to save the file to when he or she is done with it. Currently I am only copying the file to a hidden folder in the user's home directory and then I open the copy so that the original file can not be overwritten.
The resulting behaviour is:
- the user does not see where the file is located
- upon saving and closing the application that the file was edited in the user will not find the file ever again.
Workaround: I guess for now I will ask the user where she wants to save it before opening it, which seems kind of redundant to me as it could very well be, that she will not want to save it. Hopefully I will find a better solution soon.
You may be able to achieve this by sending an "open contents" ('ocon'/kAEOpenContents) Apple Event to the target app. The easiest way is probably to use the Scripting Bridge.
The normal way to use the Scripting Bridge requires that you know in advance which app you want to target and generate an Objective-C header from its scripting interface definition. However, you should be able to use it "raw". For example, something like this:
SBApplication *app = [SBApplication applicationWith...:...]; // there are method to take a PID, a bundle ID, or a URL
[app activate];
NSAppleEventDescriptor *desc = [NSAppleEventDescriptor descriptorWithString:#"foobar"];
[app sendEvent:kCoreEventClass id:kAEOpenContents parameters:keyDirectObject, desc, nil];
The NSAppleEventDescriptor object is the contents payload, in this case the string "foobar". You can use other methods to create different types of payloads. If you use the generic one and need a descriptor type, you can look in the headers in /System/Library/Frameworks/CoreServices.framework/Frameworks/AE.framework/Headers (e.g. AEDataModel.h or AERegistry.h).

NSSavePanel for saving a file after sandboxing

I have a mac AVRecording app, which records a video and save it to a location selected via NSSavePanel. It was working fine till I sandboxed the app.
For sandboxing I have added the following entitlements
com.apple.security.files.user-selected.read-write
com.apple.security.assets.movies.read-write
com.apple.security.files.downloads.read-write
This enables saving to Downloads and movies folder only.
How is it possible to save my file to any desired location, Desktop, Documents etc ?
It's not clear from your question whether you are referring to saving a particular file (in which case you can use the NSSavePanel and manually copy the file using NSFileManager to write the file into the user-specified new file), or whether you are referring to having the user choose a location for all future downloads.
If you want to prompt the user for a location to use for future downloads, you'll need to use the secure bookmark entitlement and secure bookmarks to retain access to the folder.
There's another stackoverflow answer about sandboxing which covers the process of saving and using the secure bookmark.

Windows API hook, custom save as file dialog to save directly to webserver via POST

I want to write a custom save as dialog that is hooked into the File -> "Save As" of most Windows program. This custom dialog will allow the user to enter their username, password, destination folder and uploads the file to the web server via a POST. If the user clicks cancel, it will call the original file dialog.
I've been reading up about Windows API hooking and this is vaguely how I think I would approach this:
Intercept "Save As"
Display my custom dialog, return some temporary path on the drive
Let the program write file to the temporary path, assume it calls WINAPI CreateFile(...) for now
Read the temporary file and upload to web server
Clean up temporary file
But I still can't get my head around the steps required to pull this off. Assuming I can intercept the "Save As" and CreateFile function, how do I detect the CreateFile was called from a "Save As" and not just any random file creation? I can think of a hack where I keep track of the time difference of when the File dialog got open and CreateFile got called.
My alternative solution is to use the existing file dialog and create a special folder on the disk, that is constantly monitored. When a file gets written there it will call an external program that uploads the file. I haven't looked into how to do this yet. I suspect this is easier.
UPDATE
As a first baby step, I wrote a .NET task tray application that allows the user to enter their login details and a folder to monitor. Whenever a file gets dropped in there there it will upload to the web server. So far it seems to work. Now I just need to figure out how to add a nice shortcut to the left pane of the file dialog. Once that's done I think I got a solution I'm happy with.
There is no need to hook or patch anything. Create a shell namespace extension that supports IStorage::CreateStream and implements it by returning a stream that POSTs its data to the Web server. The user can then choose to save the file to your namespace extension in order to upload the file.
Hooking the standard save dialog requires you to inject a DLL into every running process and have it replace the import stub of the the Win32 API GetSaveFileName() function in the process's PE header (something anti-virus and anti-malware apps are not likely to be happy about).
Then there is the new-style save dialog that was introduced in Vista using the new IFileSaveDialog COM interface instead of GetSaveFileName(). For that, you would have to uninstall and replace Microsoft's default FileDialog COM object with a custom implementation.
That does not count custom-made save dialogs, which you are not likely to hook.
If, by some miracle, you can hook the dialog and have it return a custom path of your own creation, you don't need to hook CreateFile() itself, Just monitor the folder that you create for your purposes. Place it where it is unlikely that any other app (or user) besides you will write files to. You can create a custom subfolder in the user's or system'ss AppData folder for that purpose. You can use SHGetSpecialFolderPath() and/or SHGetKnownFolderPath() to find those folders.
The tricky part will be detecting when the file is finished being written to and has been closed. You will have to monitor the folder for changes, such as with ReadDirectoryChangesW() or SHChangeNotifyRegister(), and periodically open new/modified files for exclusive access. If a file is still open by someone else, you won't be able to open it yourself. But once you do open it, you can do whatever you want with it.

Resources