mirror of
https://github.com/MacPass/MacPass.git
synced 2025-12-13 22:52:26 +00:00
Renamed MPPluginManager to MPPluginHost
This commit is contained in:
@@ -213,8 +213,7 @@
|
||||
4CCFA13D1BF0CC7A0078E0A1 /* Test_Password_1234.kdb in Resources */ = {isa = PBXBuildFile; fileRef = 4CCFA1321BF0CC7A0078E0A1 /* Test_Password_1234.kdb */; };
|
||||
4CCFA13E1BF0CC7A0078E0A1 /* Test_Password_1234.kdbx in Resources */ = {isa = PBXBuildFile; fileRef = 4CCFA1331BF0CC7A0078E0A1 /* Test_Password_1234.kdbx */; };
|
||||
4CD034AA1BFE113B003C002C /* MPPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034A51BFE113B003C002C /* MPPlugin.m */; };
|
||||
4CD034AB1BFE113B003C002C /* MPPluginHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034A71BFE113B003C002C /* MPPluginHost.m */; };
|
||||
4CD034AC1BFE113B003C002C /* MPPluginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034A91BFE113B003C002C /* MPPluginManager.m */; };
|
||||
4CD034AC1BFE113B003C002C /* MPPluginHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD034A91BFE113B003C002C /* MPPluginHost.m */; };
|
||||
4CD2B9061849424B0051B395 /* MPAutotypeContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD2B9051849424B0051B395 /* MPAutotypeContext.m */; };
|
||||
4CD5D705177A5F3300100649 /* MPDatabaseSettingsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CD5D704177A5F3300100649 /* MPDatabaseSettingsWindowController.m */; };
|
||||
4CD60C131C104AD4005BE5F8 /* HNHUi.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4CC281881C0F675B00B9174D /* HNHUi.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
@@ -645,10 +644,8 @@
|
||||
4CCFA1331BF0CC7A0078E0A1 /* Test_Password_1234.kdbx */ = {isa = PBXFileReference; lastKnownFileType = file; path = Test_Password_1234.kdbx; sourceTree = "<group>"; };
|
||||
4CD034A41BFE113B003C002C /* MPPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MPPlugin.h; path = MacPass/MPPlugin.h; sourceTree = SOURCE_ROOT; };
|
||||
4CD034A51BFE113B003C002C /* MPPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPPlugin.m; path = MacPass/MPPlugin.m; sourceTree = SOURCE_ROOT; };
|
||||
4CD034A61BFE113B003C002C /* MPPluginHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MPPluginHost.h; path = MacPass/MPPluginHost.h; sourceTree = SOURCE_ROOT; };
|
||||
4CD034A71BFE113B003C002C /* MPPluginHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPPluginHost.m; path = MacPass/MPPluginHost.m; sourceTree = SOURCE_ROOT; };
|
||||
4CD034A81BFE113B003C002C /* MPPluginManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MPPluginManager.h; path = MacPass/MPPluginManager.h; sourceTree = SOURCE_ROOT; };
|
||||
4CD034A91BFE113B003C002C /* MPPluginManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPPluginManager.m; path = MacPass/MPPluginManager.m; sourceTree = SOURCE_ROOT; };
|
||||
4CD034A81BFE113B003C002C /* MPPluginHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MPPluginHost.h; path = MacPass/MPPluginHost.h; sourceTree = SOURCE_ROOT; };
|
||||
4CD034A91BFE113B003C002C /* MPPluginHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPPluginHost.m; path = MacPass/MPPluginHost.m; sourceTree = SOURCE_ROOT; };
|
||||
4CD2B9041849424B0051B395 /* MPAutotypeContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAutotypeContext.h; sourceTree = "<group>"; };
|
||||
4CD2B9051849424B0051B395 /* MPAutotypeContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAutotypeContext.m; sourceTree = "<group>"; };
|
||||
4CD5D703177A5F3300100649 /* MPDatabaseSettingsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPDatabaseSettingsWindowController.h; sourceTree = "<group>"; };
|
||||
@@ -1440,10 +1437,8 @@
|
||||
children = (
|
||||
4CD034A41BFE113B003C002C /* MPPlugin.h */,
|
||||
4CD034A51BFE113B003C002C /* MPPlugin.m */,
|
||||
4CD034A61BFE113B003C002C /* MPPluginHost.h */,
|
||||
4CD034A71BFE113B003C002C /* MPPluginHost.m */,
|
||||
4CD034A81BFE113B003C002C /* MPPluginManager.h */,
|
||||
4CD034A91BFE113B003C002C /* MPPluginManager.m */,
|
||||
4CD034A81BFE113B003C002C /* MPPluginHost.h */,
|
||||
4CD034A91BFE113B003C002C /* MPPluginHost.m */,
|
||||
);
|
||||
name = Plugin;
|
||||
path = MacPassPlugin;
|
||||
@@ -1700,7 +1695,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4CD034AC1BFE113B003C002C /* MPPluginManager.m in Sources */,
|
||||
4CD034AC1BFE113B003C002C /* MPPluginHost.m in Sources */,
|
||||
4C77E37315B84A240093A587 /* main.m in Sources */,
|
||||
4CBA2ABA17074C07006D8139 /* MPSettingsHelper.m in Sources */,
|
||||
4C77E37A15B84A240093A587 /* MPAppDelegate.m in Sources */,
|
||||
@@ -1710,7 +1705,6 @@
|
||||
4CA0B2FC15BCAF8600654E32 /* MPSettingsWindowController.m in Sources */,
|
||||
4C4F72D118DF704400E8D378 /* DDHotKeyTextField.m in Sources */,
|
||||
4C83814215BF4677001AE468 /* MPDocumentWindowController.m in Sources */,
|
||||
4CD034AB1BFE113B003C002C /* MPPluginHost.m in Sources */,
|
||||
4C2E382316D1421B00037A9D /* MPIconHelper.m in Sources */,
|
||||
4C2E382616D1470200037A9D /* MPViewController.m in Sources */,
|
||||
4C65FAE916D16DDB006E0577 /* MPPasswordInputController.m in Sources */,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#import "MPDocumentWindowController.h"
|
||||
#import "MPLockDaemon.h"
|
||||
#import "MPPasswordCreatorViewController.h"
|
||||
#import "MPPluginManager.h"
|
||||
#import "MPPluginHost.h"
|
||||
#import "MPSettingsHelper.h"
|
||||
#import "MPSettingsWindowController.h"
|
||||
#import "MPStringLengthValueTransformer.h"
|
||||
@@ -161,7 +161,7 @@ NSString *const MPDidChangeStoredKeyFilesSettings = @"com.hicknhack.macpass.MPDi
|
||||
[MPLockDaemon defaultDaemon];
|
||||
[MPAutotypeDaemon defaultDaemon];
|
||||
/* Create Plugin Manager */
|
||||
[MPPluginManager sharedManager];
|
||||
[MPPluginHost sharedHost];
|
||||
#ifndef DEBUG
|
||||
/* Only enable updater in Release */
|
||||
[SUUpdater sharedUpdater];
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class MPPluginManager;
|
||||
@class MPPluginHost;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kMPPluginFileExtension;
|
||||
|
||||
@@ -20,11 +20,10 @@ FOUNDATION_EXPORT NSString *const kMPPluginFileExtension;
|
||||
@property (copy, readonly) NSString *name;
|
||||
@property (copy, readonly) NSString *version;
|
||||
|
||||
- (instancetype)initWithPluginManager:(MPPluginManager *)manager NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithPluginHost:(MPPluginHost *)host NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (void)didLoadPlugin;
|
||||
- (void)willUnloadPlugin;
|
||||
|
||||
@end
|
||||
|
||||
@@ -51,5 +50,10 @@ FOUNDATION_EXPORT NSString *const kMPPluginFileExtension;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPPlugin (Deprecated)
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
- (instancetype)initWithPluginManager:(id)manager;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
//
|
||||
|
||||
#import "MPPlugin.h"
|
||||
#import "MPPluginManager.h"
|
||||
#import "MPPluginHost.h"
|
||||
#import "MPSettingsHelper.h"
|
||||
|
||||
NSString *const kMPPluginFileExtension = @"mpplugin";
|
||||
|
||||
@implementation MPPlugin
|
||||
|
||||
- (instancetype)initWithPluginManager:(MPPluginManager *)manager {
|
||||
- (instancetype)initWithPluginHost:(MPPluginHost *)host {
|
||||
self = [super init];
|
||||
return self;
|
||||
}
|
||||
@@ -49,8 +49,14 @@ NSString *const kMPPluginFileExtension = @"mpplugin";
|
||||
|
||||
}
|
||||
|
||||
- (void)willUnloadPlugin {
|
||||
@end
|
||||
|
||||
@implementation MPPlugin (Deprecated)
|
||||
|
||||
- (instancetype)initWithPluginManager:(id)manager {
|
||||
NSLog(@"Deprecated initalizer. Use initWithPluginHost: instead!");
|
||||
self = [self initWithPluginManager:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -2,30 +2,26 @@
|
||||
// MPPluginHost.h
|
||||
// MacPass
|
||||
//
|
||||
// Created by Michael Starke on 13/11/15.
|
||||
// Copyright © 2015 HicknHack Software GmbH. All rights reserved.
|
||||
// Created by Michael Starke on 16/07/15.
|
||||
// Copyright (c) 2015 HicknHack Software GmbH. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
FOUNDATION_EXPORT NSString *const MPPluginHostWillLoadPlugin;
|
||||
FOUNDATION_EXPORT NSString *const MPPluginHostDidLoadPlugin;
|
||||
|
||||
@class KPKNode;
|
||||
@class KPKEntry;
|
||||
@class KPKGroup;
|
||||
FOUNDATION_EXPORT NSString *const MPPluginHostPluginBundleIdentifiyerKey;
|
||||
|
||||
typedef BOOL (^NodeMatchBlock)(KPKNode *aNode);
|
||||
@class MPPlugin;
|
||||
|
||||
@interface MPPluginHost : NSObject
|
||||
|
||||
@property (readonly, copy) NSArray <MPPlugin __kindof*> *plugins;
|
||||
@property (nonatomic, readonly) BOOL loadUnsecurePlugins;
|
||||
|
||||
+ (instancetype)sharedHost;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (NSArray<KPKEntry *> *)filteredEntriesUsingBlock:(NodeMatchBlock)matchBlock;
|
||||
- (NSArray<KPKGroup *> *)filteredGroupsUsingBlock:(NodeMatchBlock)matchBlock;
|
||||
|
||||
- (void)presentError:(NSError *)error completionHandler:(void (^)(NSModalResponse response))completionHandler;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -2,54 +2,214 @@
|
||||
// MPPluginHost.m
|
||||
// MacPass
|
||||
//
|
||||
// Created by Michael Starke on 13/11/15.
|
||||
// Copyright © 2015 HicknHack Software GmbH. All rights reserved.
|
||||
// Created by Michael Starke on 16/07/15.
|
||||
// Copyright (c) 2015 HicknHack Software GmbH. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPPluginHost.h"
|
||||
#import "MPDocument.h"
|
||||
|
||||
#import "MPPlugin.h"
|
||||
#import "NSApplication+MPAdditions.h"
|
||||
#import "MPSettingsHelper.h"
|
||||
|
||||
#import "KeePassKit/KeePassKit.h"
|
||||
|
||||
|
||||
NSString *const MPPluginHostWillLoadPlugin = @"com.hicknhack.macpass.MPPluginHostWillLoadPlugin";
|
||||
NSString *const MPPluginHostDidLoadPlugin = @"comt.hicknhack.macpass.MPPluginHostDidLoadPlugin";
|
||||
|
||||
NSString *const MPPluginHostPluginBundleIdentifiyerKey = @"MPPluginHostPluginBundleIdentifiyerKey";
|
||||
|
||||
|
||||
@interface MPPluginHost ()
|
||||
|
||||
@property (strong) NSMutableArray<MPPlugin __kindof *> *mutablePlugins;
|
||||
@property (nonatomic) BOOL loadUnsecurePlugins;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPluginHost
|
||||
|
||||
static MPPluginHost *_instance;
|
||||
+ (NSSet *)keyPathsForValuesAffectingPlugins {
|
||||
return [NSSet setWithObject:NSStringFromSelector(@selector(mutablePlugins))];
|
||||
}
|
||||
|
||||
+ (instancetype)sharedHost {
|
||||
static MPPluginHost *instance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_instance = [[MPPluginHost alloc] _init];
|
||||
instance = [[MPPluginHost alloc] _init];
|
||||
});
|
||||
return _instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return _instance;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)_init {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
_mutablePlugins = [[NSMutableArray alloc] init];
|
||||
_loadUnsecurePlugins = [[NSUserDefaults standardUserDefaults] boolForKey:kMPSettingsKeyLoadUnsecurePlugins];
|
||||
[self _loadPlugins];
|
||||
|
||||
[self bind:NSStringFromSelector(@selector(loadUnsecurePlugins))
|
||||
toObject:[NSUserDefaultsController sharedUserDefaultsController]
|
||||
withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyLoadUnsecurePlugins]
|
||||
options:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray<KPKEntry *> *)filteredEntriesUsingBlock:(NodeMatchBlock)matchBlock {
|
||||
NSArray *currentDocuments = [NSDocumentController sharedDocumentController].documents;
|
||||
NSMutableArray *entries = [[NSMutableArray alloc] initWithCapacity:200];
|
||||
for(MPDocument *document in currentDocuments) {
|
||||
if(document.tree) {
|
||||
[entries addObjectsFromArray:document.tree.allEntries];
|
||||
- (NSArray<MPPlugin *> *)plugins {
|
||||
return [self.mutablePlugins copy];
|
||||
}
|
||||
|
||||
- (void)_loadPlugins {
|
||||
NSURL *appSupportDir = [NSApp applicationSupportDirectoryURL:YES];
|
||||
NSError *error;
|
||||
NSArray *externalPluginsURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:appSupportDir
|
||||
includingPropertiesForKeys:@[]
|
||||
options:NSDirectoryEnumerationSkipsHiddenFiles
|
||||
error:&error];
|
||||
|
||||
NSArray *internalPluginsURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[NSBundle mainBundle].builtInPlugInsURL
|
||||
includingPropertiesForKeys:@[]
|
||||
options:NSDirectoryEnumerationSkipsHiddenFiles
|
||||
error:&error];
|
||||
|
||||
|
||||
if(!externalPluginsURLs) {
|
||||
// No external plugins
|
||||
NSLog(@"No external plugins found!");
|
||||
}
|
||||
if(!internalPluginsURLs) {
|
||||
// No internal plugins
|
||||
NSLog(@"No internal plugins found!");
|
||||
}
|
||||
NSArray *pluginURLs = [externalPluginsURLs arrayByAddingObjectsFromArray:internalPluginsURLs];
|
||||
|
||||
for(NSURL *pluginURL in pluginURLs) {
|
||||
|
||||
if(![self _validURL:pluginURL]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(![self _validSignature:pluginURL]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSBundle *pluginBundle = [NSBundle bundleWithURL:pluginURL];
|
||||
if(!pluginBundle) {
|
||||
NSLog(@"Could not create bundle %@", pluginURL.path);
|
||||
continue;
|
||||
}
|
||||
NSError *error;
|
||||
if(![pluginBundle preflightAndReturnError:&error]) {
|
||||
NSLog(@"Preflight Error %@ %@", error.localizedDescription, error.localizedFailureReason );
|
||||
continue;
|
||||
};
|
||||
|
||||
if([self _validateUniqueBundle:pluginBundle]) {
|
||||
NSLog(@"Plugin %@ already loaded!", pluginBundle.bundleIdentifier);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(![pluginBundle loadAndReturnError:&error]) {
|
||||
NSLog(@"Bunlde Loading Error %@ %@", error.localizedDescription, error.localizedFailureReason);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(![self _validateClass:pluginBundle.principalClass]) {
|
||||
NSLog(@"Wrong principal Class.");
|
||||
continue;
|
||||
}
|
||||
if([pluginBundle.principalClass instancesRespondToSelector:NSSelectorFromString(@"initWithPluginManager:")]) {
|
||||
NSLog(@"Plugin uses old interface. Update plugin to use initWithPluginHost: instead of initWithPluginManager:!");
|
||||
}
|
||||
|
||||
MPPlugin *plugin = [[pluginBundle.principalClass alloc] initWithPluginHost:self];
|
||||
if(plugin) {
|
||||
NSLog(@"Loaded plugin instance %@", pluginBundle.principalClass);
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPPluginHostWillLoadPlugin
|
||||
object:self
|
||||
userInfo:@{ MPPluginHostPluginBundleIdentifiyerKey : plugin.identifier }];
|
||||
[self.mutablePlugins addObject:plugin];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPPluginHostDidLoadPlugin
|
||||
object:self
|
||||
userInfo:@{ MPPluginHostPluginBundleIdentifiyerKey : plugin.identifier }];
|
||||
}
|
||||
else {
|
||||
NSLog(@"Unable to create instance of plugin class %@", pluginBundle.principalClass);
|
||||
}
|
||||
}
|
||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { return matchBlock(evaluatedObject); }];
|
||||
return [[NSArray alloc] initWithArray:[entries filteredArrayUsingPredicate:predicate] copyItems:YES];
|
||||
}
|
||||
|
||||
- (NSArray<KPKGroup *> *)filteredGroupsUsingBlock:(NodeMatchBlock)matchBlock {
|
||||
NSAssert(NO, @"Not implemented");
|
||||
return nil;
|
||||
- (BOOL)_validateUniqueBundle:(NSBundle *)bundle {
|
||||
for(MPPlugin *plugin in self.mutablePlugins) {
|
||||
NSBundle *pluginBundle = [NSBundle bundleForClass:plugin.class];
|
||||
if([pluginBundle.bundleIdentifier isEqualToString:bundle.bundleIdentifier]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)presentError:(NSError *)error completionHandler:(void (^)(NSModalResponse))completionHandler {
|
||||
- (BOOL)_validURL:(NSURL *)url {
|
||||
return (NSOrderedSame == [url.pathExtension compare:kMPPluginFileExtension options:NSCaseInsensitiveSearch]);
|
||||
}
|
||||
|
||||
- (BOOL)_validateClass:(Class)class {
|
||||
return [class isSubclassOfClass:[MPPlugin class]];
|
||||
}
|
||||
|
||||
/* Code by Jedda Wignall<jedda@jedda.me> http://jedda.me/2012/03/verifying-plugin-bundles-using-code-signing/ */
|
||||
- (BOOL)_validSignature:(NSURL *)url {
|
||||
if(!url.path) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if(self.loadUnsecurePlugins) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSTask * task = [[NSTask alloc] init];
|
||||
NSPipe * pipe = [NSPipe pipe];
|
||||
NSArray* args = @[ @"--verify",
|
||||
/*[NSString stringWithFormat:@"-R=anchor = \"%@\"", [[NSBundle mainBundle] pathForResource:@"BlargsoftCodeCA" ofType:@"cer"]],*/
|
||||
url.path ];
|
||||
task.launchPath = @"/usr/bin/codesign";
|
||||
task.standardOutput = pipe;
|
||||
task.standardError = pipe;
|
||||
task.arguments = args;
|
||||
[task launch];
|
||||
[task waitUntilExit];
|
||||
|
||||
if(task.terminationStatus == 0) {
|
||||
return YES;
|
||||
}
|
||||
NSString *pluginPath = url.path ? url.path : @"<emptyPath>";
|
||||
NSString * taskString = [[NSString alloc] initWithData:pipe.fileHandleForReading.readDataToEndOfFile encoding:NSASCIIStringEncoding];
|
||||
if ([taskString rangeOfString:@"modified"].length > 0 || [taskString rangeOfString:@"a sealed resource is missing or invalid"].length > 0) {
|
||||
// The plugin has been modified or resources removed since being signed. You probably don't want to load this.
|
||||
NSLog(@"Plugin %@ modified - not loaded", pluginPath); // log a real error here
|
||||
}
|
||||
else if ([taskString rangeOfString:@"failed to satisfy"].length > 0) {
|
||||
// The plugin is missing resources since being signed. Don't load.
|
||||
// throw an error
|
||||
NSLog(@"Plugin %@ not signed by correct CA - not loaded", pluginPath); // log a real error here
|
||||
}
|
||||
else if ([taskString rangeOfString:@"not signed at all"].length > 0) {
|
||||
// The plugin was not code signed at all. Don't load.
|
||||
NSLog(@"Plugin %@ not signed at all - don't load.", pluginPath); // log a real error here
|
||||
}
|
||||
else {
|
||||
NSLog(@"Unkown CodeSign Error!");
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
//
|
||||
// MPPluginManager.h
|
||||
// MacPass
|
||||
//
|
||||
// Created by Michael Starke on 16/07/15.
|
||||
// Copyright (c) 2015 HicknHack Software GmbH. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXPORT NSString *const MPPluginManagerWillLoadPlugin;
|
||||
FOUNDATION_EXPORT NSString *const MPPluginManagerDidLoadPlugin;
|
||||
FOUNDATION_EXPORT NSString *const MPPluginManagerWillUnloadPlugin;
|
||||
FOUNDATION_EXPORT NSString *const MPPluginManagerDidUnloadPlugin;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const MPPluginManagerPluginBundleIdentifiyerKey;
|
||||
|
||||
@class MPPlugin;
|
||||
|
||||
@interface MPPluginManager : NSObject
|
||||
|
||||
@property (readonly, copy) NSArray <MPPlugin __kindof*> *plugins;
|
||||
@property (nonatomic, readonly) BOOL loadUnsecurePlugins;
|
||||
|
||||
+ (instancetype)sharedManager;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
@@ -1,211 +0,0 @@
|
||||
//
|
||||
// MPPluginManager.m
|
||||
// MacPass
|
||||
//
|
||||
// Created by Michael Starke on 16/07/15.
|
||||
// Copyright (c) 2015 HicknHack Software GmbH. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPPluginManager.h"
|
||||
|
||||
#import "MPPlugin.h"
|
||||
#import "NSApplication+MPAdditions.h"
|
||||
#import "MPSettingsHelper.h"
|
||||
|
||||
#import "KeePassKit/KeePassKit.h"
|
||||
|
||||
|
||||
NSString *const MPPluginManagerWillLoadPlugin = @"com.hicknhack.macpass.MPPluginManagerWillLoadPlugin";
|
||||
NSString *const MPPluginManagerDidLoadPlugin = @"comt.hicknhack.macpass.MPPluginManagerDidLoadPlugin";
|
||||
|
||||
NSString *const MPPluginManagerPluginBundleIdentifiyerKey = @"MPPluginManagerPluginBundleIdentifiyerKey";
|
||||
|
||||
|
||||
@interface MPPluginManager ()
|
||||
|
||||
@property (strong) NSMutableArray<MPPlugin __kindof *> *mutablePlugins;
|
||||
@property (nonatomic) BOOL loadUnsecurePlugins;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPluginManager
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingPlugins {
|
||||
return [NSSet setWithObject:NSStringFromSelector(@selector(mutablePlugins))];
|
||||
}
|
||||
|
||||
+ (instancetype)sharedManager {
|
||||
static MPPluginManager *instance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[MPPluginManager alloc] _init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)_init {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
_mutablePlugins = [[NSMutableArray alloc] init];
|
||||
_loadUnsecurePlugins = [[NSUserDefaults standardUserDefaults] boolForKey:kMPSettingsKeyLoadUnsecurePlugins];
|
||||
[self _loadPlugins];
|
||||
|
||||
[self bind:NSStringFromSelector(@selector(loadUnsecurePlugins))
|
||||
toObject:[NSUserDefaultsController sharedUserDefaultsController]
|
||||
withKeyPath:[MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyLoadUnsecurePlugins]
|
||||
options:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray<MPPlugin *> *)plugins {
|
||||
return [self.mutablePlugins copy];
|
||||
}
|
||||
|
||||
- (void)_loadPlugins {
|
||||
NSURL *appSupportDir = [NSApp applicationSupportDirectoryURL:YES];
|
||||
NSError *error;
|
||||
NSArray *externalPluginsURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:appSupportDir
|
||||
includingPropertiesForKeys:@[]
|
||||
options:NSDirectoryEnumerationSkipsHiddenFiles
|
||||
error:&error];
|
||||
|
||||
NSArray *internalPluginsURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[NSBundle mainBundle].builtInPlugInsURL
|
||||
includingPropertiesForKeys:@[]
|
||||
options:NSDirectoryEnumerationSkipsHiddenFiles
|
||||
error:&error];
|
||||
|
||||
|
||||
if(!externalPluginsURLs) {
|
||||
// No external plugins
|
||||
NSLog(@"No external plugins found!");
|
||||
}
|
||||
if(!internalPluginsURLs) {
|
||||
// No internal plugins
|
||||
NSLog(@"No internal plugins found!");
|
||||
}
|
||||
NSArray *pluginURLs = [externalPluginsURLs arrayByAddingObjectsFromArray:internalPluginsURLs];
|
||||
|
||||
for(NSURL *pluginURL in pluginURLs) {
|
||||
|
||||
if(![self _validURL:pluginURL]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(![self _validSignature:pluginURL]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSBundle *pluginBundle = [NSBundle bundleWithURL:pluginURL];
|
||||
if(!pluginBundle) {
|
||||
NSLog(@"Could not create bundle %@", pluginURL.path);
|
||||
continue;
|
||||
}
|
||||
NSError *error;
|
||||
if(![pluginBundle preflightAndReturnError:&error]) {
|
||||
NSLog(@"Preflight Error %@ %@", error.localizedDescription, error.localizedFailureReason );
|
||||
continue;
|
||||
};
|
||||
|
||||
if([self _validateUniqueBundle:pluginBundle]) {
|
||||
NSLog(@"Plugin %@ already loaded!", pluginBundle.bundleIdentifier);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(![pluginBundle loadAndReturnError:&error]) {
|
||||
NSLog(@"Bunlde Loading Error %@ %@", error.localizedDescription, error.localizedFailureReason);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(![self _validateClass:pluginBundle.principalClass]) {
|
||||
NSLog(@"Wrong principal Class.");
|
||||
continue;
|
||||
}
|
||||
MPPlugin *plugin = [[pluginBundle.principalClass alloc] initWithPluginManager:self];
|
||||
if(plugin) {
|
||||
NSLog(@"Loaded plugin instance %@", pluginBundle.principalClass);
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPPluginManagerWillLoadPlugin
|
||||
object:self
|
||||
userInfo:@{ MPPluginManagerPluginBundleIdentifiyerKey : plugin.identifier }];
|
||||
[self.mutablePlugins addObject:plugin];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPPluginManagerDidLoadPlugin
|
||||
object:self
|
||||
userInfo:@{ MPPluginManagerPluginBundleIdentifiyerKey : plugin.identifier }];
|
||||
}
|
||||
else {
|
||||
NSLog(@"Unable to create instance of plugin class %@", pluginBundle.principalClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)_validateUniqueBundle:(NSBundle *)bundle {
|
||||
for(MPPlugin *plugin in self.mutablePlugins) {
|
||||
NSBundle *pluginBundle = [NSBundle bundleForClass:plugin.class];
|
||||
if([pluginBundle.bundleIdentifier isEqualToString:bundle.bundleIdentifier]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)_validURL:(NSURL *)url {
|
||||
return (NSOrderedSame == [url.pathExtension compare:kMPPluginFileExtension options:NSCaseInsensitiveSearch]);
|
||||
}
|
||||
|
||||
- (BOOL)_validateClass:(Class)class {
|
||||
return [class isSubclassOfClass:[MPPlugin class]];
|
||||
}
|
||||
|
||||
/* Code by Jedda Wignall<jedda@jedda.me> http://jedda.me/2012/03/verifying-plugin-bundles-using-code-signing/ */
|
||||
- (BOOL)_validSignature:(NSURL *)url {
|
||||
if(!url.path) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if(self.loadUnsecurePlugins) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSTask * task = [[NSTask alloc] init];
|
||||
NSPipe * pipe = [NSPipe pipe];
|
||||
NSArray* args = @[ @"--verify",
|
||||
/*[NSString stringWithFormat:@"-R=anchor = \"%@\"", [[NSBundle mainBundle] pathForResource:@"BlargsoftCodeCA" ofType:@"cer"]],*/
|
||||
url.path ];
|
||||
task.launchPath = @"/usr/bin/codesign";
|
||||
task.standardOutput = pipe;
|
||||
task.standardError = pipe;
|
||||
task.arguments = args;
|
||||
[task launch];
|
||||
[task waitUntilExit];
|
||||
|
||||
if(task.terminationStatus == 0) {
|
||||
return YES;
|
||||
}
|
||||
NSString *pluginPath = url.path ? url.path : @"<emptyPath>";
|
||||
NSString * taskString = [[NSString alloc] initWithData:pipe.fileHandleForReading.readDataToEndOfFile encoding:NSASCIIStringEncoding];
|
||||
if ([taskString rangeOfString:@"modified"].length > 0 || [taskString rangeOfString:@"a sealed resource is missing or invalid"].length > 0) {
|
||||
// The plugin has been modified or resources removed since being signed. You probably don't want to load this.
|
||||
NSLog(@"Plugin %@ modified - not loaded", pluginPath); // log a real error here
|
||||
}
|
||||
else if ([taskString rangeOfString:@"failed to satisfy"].length > 0) {
|
||||
// The plugin is missing resources since being signed. Don't load.
|
||||
// throw an error
|
||||
NSLog(@"Plugin %@ not signed by correct CA - not loaded", pluginPath); // log a real error here
|
||||
}
|
||||
else if ([taskString rangeOfString:@"not signed at all"].length > 0) {
|
||||
// The plugin was not code signed at all. Don't load.
|
||||
NSLog(@"Plugin %@ not signed at all - don't load.", pluginPath); // log a real error here
|
||||
}
|
||||
else {
|
||||
NSLog(@"Unkown CodeSign Error!");
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
#import "MPPluginSettingsController.h"
|
||||
#import "MPPluginManager.h"
|
||||
#import "MPPluginHost.h"
|
||||
#import "MPPlugin.h"
|
||||
|
||||
#import "MPSettingsHelper.h"
|
||||
@@ -50,7 +50,7 @@
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
|
||||
return [MPPluginManager sharedManager].plugins.count;
|
||||
return [MPPluginHost sharedHost].plugins.count;
|
||||
}
|
||||
|
||||
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
|
||||
@@ -75,7 +75,7 @@
|
||||
}
|
||||
|
||||
- (MPPlugin *)pluginForRow:(NSInteger)row {
|
||||
NSArray<MPPlugin __kindof *> *plugins = [MPPluginManager sharedManager].plugins;
|
||||
NSArray<MPPlugin __kindof *> *plugins = [MPPluginHost sharedHost].plugins;
|
||||
if(0 > row || row >= plugins.count) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user