зеркало из https://github.com/mozilla/pjs.git
220 строки
6.6 KiB
Objective-C
220 строки
6.6 KiB
Objective-C
//
|
|
// NSFileManager+Authentication.m
|
|
// Sparkle
|
|
//
|
|
// Created by Andy Matuschak on 3/9/06.
|
|
// Copyright 2006 Andy Matuschak. All rights reserved.
|
|
//
|
|
|
|
// This code based on generous contribution from Allan Odgaard. Thanks, Allan!
|
|
|
|
#import "sys/stat.h"
|
|
#import <Security/Security.h>
|
|
|
|
#import <unistd.h>
|
|
#import <sys/stat.h>
|
|
#import <dirent.h>
|
|
|
|
#import "NSFileManager+ExtendedAttributes.h"
|
|
|
|
// TN OV02 says that the range between 1000 and 9999 can be used for
|
|
// application-defined errors. errToolFailedError will indicate that an
|
|
// executed program crashed or indicated failure with a nonzero exit status.
|
|
static const OSStatus errToolFailedError = 1000;
|
|
|
|
static OSStatus AuthorizationExecuteWithPrivilegesAndWait(
|
|
AuthorizationRef authorization,
|
|
const char* executablePath,
|
|
AuthorizationFlags options,
|
|
const char* const* arguments) {
|
|
sig_t oldSigChildHandler = signal(SIGCHLD, SIG_DFL);
|
|
OSStatus ret;
|
|
ret = AuthorizationExecuteWithPrivileges(authorization,
|
|
executablePath,
|
|
options,
|
|
(char* const*)arguments,
|
|
NULL);
|
|
if (ret == errAuthorizationSuccess) {
|
|
int status;
|
|
pid_t pid = wait(&status);
|
|
if (pid == -1 ||
|
|
!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
|
ret = errToolFailedError;
|
|
}
|
|
}
|
|
signal(SIGCHLD, oldSigChildHandler);
|
|
return ret;
|
|
}
|
|
|
|
@implementation NSFileManager (SUAuthenticationAdditions)
|
|
|
|
- (BOOL)currentUserOwnsPath:(NSString *)oPath
|
|
{
|
|
char *path = (char *)[oPath fileSystemRepresentation];
|
|
unsigned int uid = getuid();
|
|
bool res = false;
|
|
struct stat sb;
|
|
if(stat(path, &sb) == 0)
|
|
{
|
|
if(sb.st_uid == uid)
|
|
{
|
|
res = true;
|
|
if(sb.st_mode & S_IFDIR)
|
|
{
|
|
DIR* dir = opendir(path);
|
|
struct dirent* entry = NULL;
|
|
while(res && (entry = readdir(dir)))
|
|
{
|
|
if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
|
continue;
|
|
|
|
char descend[strlen(path) + 1 + entry->d_namlen + 1];
|
|
strcpy(descend, path);
|
|
strcat(descend, "/");
|
|
strcat(descend, entry->d_name);
|
|
res = [self currentUserOwnsPath:[NSString stringWithUTF8String:descend]];
|
|
}
|
|
closedir(dir);
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
- (BOOL)_movePathWithForcedAuthentication:(NSString *)src toPath:(NSString *)dst
|
|
{
|
|
NSString *tmp = [[[dst stringByDeletingPathExtension] stringByAppendingString:@".old"] stringByAppendingPathExtension:[dst pathExtension]];
|
|
|
|
const char* srcPath = [src fileSystemRepresentation];
|
|
const char* tmpPath = [tmp fileSystemRepresentation];
|
|
const char* dstPath = [dst fileSystemRepresentation];
|
|
|
|
struct stat sb, dstSB;
|
|
if ((stat(srcPath, &sb) != 0) ||
|
|
(stat(tmpPath, &sb) == 0) ||
|
|
(stat(dstPath, &dstSB) != 0)) {
|
|
return NO;
|
|
}
|
|
|
|
AuthorizationRef auth;
|
|
OSStatus authStat = errAuthorizationDenied;
|
|
while (authStat == errAuthorizationDenied) {
|
|
authStat = AuthorizationCreate(NULL,
|
|
kAuthorizationEmptyEnvironment,
|
|
kAuthorizationFlagDefaults,
|
|
&auth);
|
|
}
|
|
|
|
BOOL res = NO;
|
|
if (authStat == errAuthorizationSuccess) {
|
|
res = YES;
|
|
|
|
char uidgid[42];
|
|
snprintf(uidgid, sizeof(uidgid), "%d:%d",
|
|
dstSB.st_uid, dstSB.st_gid);
|
|
|
|
const char* executables[] = {
|
|
"/bin/mv",
|
|
"/bin/mv",
|
|
"/bin/rm",
|
|
NULL, // pause here and do some housekeeping before
|
|
// continuing
|
|
"/usr/sbin/chown",
|
|
NULL // stop here for real
|
|
};
|
|
|
|
// 4 is the maximum number of arguments to any command,
|
|
// including the NULL that signals the end of an argument
|
|
// list.
|
|
const char* const argumentLists[][4] = {
|
|
{ "-f", dstPath, tmpPath, NULL }, // mv
|
|
{ "-f", srcPath, dstPath, NULL }, // mv
|
|
{ "-rf", tmpPath, NULL }, // rm
|
|
{ NULL }, // pause
|
|
{ "-R", uidgid, dstPath, NULL }, // chown
|
|
{ NULL } // stop
|
|
};
|
|
|
|
// Process the commands up until the first NULL
|
|
int commandIndex = 0;
|
|
for (; executables[commandIndex] != NULL; ++commandIndex) {
|
|
if (res) {
|
|
res = (
|
|
AuthorizationExecuteWithPrivilegesAndWait(
|
|
auth,
|
|
executables[commandIndex],
|
|
kAuthorizationFlagDefaults,
|
|
argumentLists[commandIndex]) ==
|
|
errAuthorizationSuccess);
|
|
}
|
|
}
|
|
|
|
// If the currently-running application is trusted, the new
|
|
// version should be trusted as well. Remove it from the
|
|
// quarantine to avoid a delay at launch, and to avoid
|
|
// presenting the user with a confusing trust dialog.
|
|
//
|
|
// This needs to be done after the application is moved to its
|
|
// new home with "mv" in case it's moved across filesystems: if
|
|
// that happens, "mv" actually performs a copy and may result
|
|
// in the application being quarantined. It also needs to be
|
|
// done before "chown" changes ownership, because the ownership
|
|
// change will almost certainly make it impossible to change
|
|
// attributes to release the files from the quarantine.
|
|
if (res) {
|
|
[self releaseFromQuarantine:dst];
|
|
}
|
|
|
|
// Now move past the NULL we found and continue executing
|
|
// commands from the list.
|
|
++commandIndex;
|
|
|
|
for (; executables[commandIndex] != NULL; ++commandIndex) {
|
|
if (res) {
|
|
res = (
|
|
AuthorizationExecuteWithPrivilegesAndWait(
|
|
auth,
|
|
executables[commandIndex],
|
|
kAuthorizationFlagDefaults,
|
|
argumentLists[commandIndex]) ==
|
|
errAuthorizationSuccess);
|
|
}
|
|
}
|
|
|
|
AuthorizationFree(auth, 0);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
- (BOOL)movePathWithAuthentication:(NSString *)src toPath:(NSString *)dst
|
|
{
|
|
if ([[NSFileManager defaultManager] isWritableFileAtPath:dst] && [[NSFileManager defaultManager] isWritableFileAtPath:[dst stringByDeletingLastPathComponent]])
|
|
{
|
|
int tag = 0;
|
|
BOOL result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:[dst stringByDeletingLastPathComponent] destination:@"" files:[NSArray arrayWithObject:[dst lastPathComponent]] tag:&tag];
|
|
result &= [[NSFileManager defaultManager] movePath:src toPath:dst handler:NULL];
|
|
|
|
// If the currently-running application is trusted, the new
|
|
// version should be trusted as well. Remove it from the
|
|
// quarantine to avoid a delay at launch, and to avoid
|
|
// presenting the user with a confusing trust dialog.
|
|
//
|
|
// This needs to be done after the application is moved to its
|
|
// new home in case it's moved across filesystems: if that
|
|
// happens, the move is actually a copy, and it may result
|
|
// in the application being quarantined.
|
|
if (result) {
|
|
[self releaseFromQuarantine:dst];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return [self _movePathWithForcedAuthentication:src toPath:dst];
|
|
}
|
|
}
|
|
|
|
@end
|