├── .gitignore
├── .swift-format
├── CSAuthSample Example App
├── App
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── CSAuthSample_Example_App.entitlements
│ ├── ContentView.swift
│ ├── Info.plist
│ └── MessageSender.swift
├── CSAuthSample Example App.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Example App.xcscheme
├── Common
│ ├── Build Scripts
│ │ └── GlobalSetupScript.swift
│ ├── Config
│ │ └── CSAuthSample-Example.xcconfig
│ ├── ExampleCommandSet.swift
│ ├── HelperToolProtocol.swift
│ ├── Identifiers.swift
│ └── en.lproj
│ │ └── Prompts.strings
├── Helper Tool
│ ├── Build Scripts
│ │ └── SetupHelperPlists.swift
│ ├── HelperConnection.swift
│ ├── Info.plist
│ ├── Launchd.plist
│ └── main.swift
└── XPC Service
│ ├── Info.plist
│ ├── ServiceDelegate.swift
│ ├── XPCService.swift
│ ├── XPCServiceProtocol.swift
│ └── main.swift
├── CSAuthSample.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ ├── CSAuthSample.xccheckout
│ └── IDEWorkspaceChecks.plist
├── LICENSE.md
├── Package.swift
├── README.md
└── Sources
├── CSAuthSampleApp
├── HelperClient.swift
└── SandboxWorkaround.swift
├── CSAuthSampleCommon
├── CSASCommon.m
└── include
│ ├── CSASCommon.h
│ └── module.modulemap
└── CSAuthSampleHelper
├── CSASHelperConnection.m
├── CSASHelperConnectionInternal.h
├── CSASHelperConnectionWrapper.h
├── CSASHelperConnectionWrapper.m
├── CSASHelperTool.m
├── CSASHelperToolInternal.h
└── include
├── CSASHelperConnection.h
├── CSASHelperTool.h
└── module.modulemap
/.gitignore:
--------------------------------------------------------------------------------
1 | xcuserdata
2 | .DS_Store
3 | .build
4 | swiftpm
5 |
6 |
--------------------------------------------------------------------------------
/.swift-format:
--------------------------------------------------------------------------------
1 | {
2 | "fileScopedDeclarationPrivacy" : {
3 | "accessLevel" : "private"
4 | },
5 | "indentation" : {
6 | "spaces" : 4
7 | },
8 | "indentConditionalCompilationBlocks" : true,
9 | "indentSwitchCaseLabels" : false,
10 | "lineBreakAroundMultilineExpressionChainComponents" : false,
11 | "lineBreakBeforeControlFlowKeywords" : false,
12 | "lineBreakBeforeEachArgument" : false,
13 | "lineBreakBeforeEachGenericRequirement" : false,
14 | "lineLength" : 125,
15 | "maximumBlankLines" : 1,
16 | "prioritizeKeepingFunctionOutputTogether" : false,
17 | "respectsExistingLineBreaks" : true,
18 | "rules" : {
19 | "AllPublicDeclarationsHaveDocumentation" : true,
20 | "AlwaysUseLowerCamelCase" : true,
21 | "AmbiguousTrailingClosureOverload" : true,
22 | "BeginDocumentationCommentWithOneLineSummary" : true,
23 | "DoNotUseSemicolons" : true,
24 | "DontRepeatTypeInStaticProperties" : true,
25 | "FileScopedDeclarationPrivacy" : true,
26 | "FullyIndirectEnum" : true,
27 | "GroupNumericLiterals" : true,
28 | "IdentifiersMustBeASCII" : true,
29 | "NeverForceUnwrap" : false,
30 | "NeverUseForceTry" : false,
31 | "NeverUseImplicitlyUnwrappedOptionals" : false,
32 | "NoAccessLevelOnExtensionDeclaration" : true,
33 | "NoBlockComments" : true,
34 | "NoCasesWithOnlyFallthrough" : true,
35 | "NoEmptyTrailingClosureParentheses" : true,
36 | "NoLabelsInCasePatterns" : true,
37 | "NoLeadingUnderscores" : false,
38 | "NoParensAroundConditions" : true,
39 | "NoVoidReturnOnFunctionSignature" : true,
40 | "OneCasePerLine" : true,
41 | "OneVariableDeclarationPerLine" : true,
42 | "OnlyOneTrailingClosureArgument" : true,
43 | "OrderedImports" : true,
44 | "ReturnVoidInsteadOfEmptyTuple" : false,
45 | "UseLetInEveryBoundCaseVariable" : true,
46 | "UseShorthandTypeNames" : true,
47 | "UseSingleLinePropertyGetter" : true,
48 | "UseSynthesizedInitializer" : true,
49 | "UseTripleSlashForDocumentationComments" : true,
50 | "ValidateDocumentationComments" : true
51 | },
52 | "tabWidth" : 8,
53 | "version" : 1
54 | }
55 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CSAuthSample Example App
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | //
7 |
8 | import Cocoa
9 | import SwiftUI
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 | var window: NSWindow!
14 |
15 | func applicationDidFinishLaunching(_ aNotification: Notification) {
16 | let contentView = ContentView()
17 |
18 | window = NSWindow(
19 | contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
20 | styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
21 | backing: .buffered, defer: false)
22 | window.center()
23 | window.title = "CSAuthSample Example App"
24 | window.setFrameAutosaveName("Main Window")
25 | window.contentView = NSHostingView(rootView: contentView)
26 | window.makeKeyAndOrderFront(nil)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/App/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/App/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/App/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/App/CSAuthSample_Example_App.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/App/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // CSAuthSample Example App
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 | @State private var response = ""
12 | @State private var messageSendInProgress = false
13 |
14 | var body: some View {
15 | VStack {
16 | Button {
17 | self.messageSendInProgress = true
18 |
19 | MessageSender.shared.sayHello {
20 | self.messageSendInProgress = false
21 |
22 | switch $0 {
23 | case .success(let reply):
24 | self.response = "Received reply from helper:\n\n\(reply)"
25 | case .failure(let error):
26 | self.response = "Received error from helper:\n\n\(error.localizedDescription)"
27 | }
28 | }
29 | } label: {
30 | Text("Say Hello")
31 | }.padding().disabled(self.messageSendInProgress)
32 | Text("Response:")
33 | Text($response.wrappedValue)
34 | .frame(maxWidth: .infinity, maxHeight: .infinity)
35 | }.padding().frame(minWidth: 300)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/App/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | LSApplicationCategoryType
24 | public.app-category.developer-tools
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Free for public use
29 | NSMainStoryboardFile
30 | Main
31 | NSPrincipalClass
32 | NSApplication
33 | NSSupportsAutomaticTermination
34 |
35 | NSSupportsSuddenTermination
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/App/MessageSender.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MessageSender.swift
3 | // Example App
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | //
7 |
8 | import CSAuthSampleApp
9 | import Foundation
10 |
11 | class MessageSender {
12 | static let shared = MessageSender()
13 |
14 | private var _xpcConnection: NSXPCConnection?
15 | private var sema = DispatchSemaphore(value: 1)
16 |
17 | private var xpcConnection: NSXPCConnection {
18 | self.sema.wait()
19 | defer { self.sema.signal() }
20 |
21 | if let connection = self._xpcConnection {
22 | return connection
23 | }
24 |
25 | let connection = NSXPCConnection(serviceName: Identifiers.xpcServiceID)
26 |
27 | connection.remoteObjectInterface = NSXPCInterface(with: XPCServiceProtocol.self)
28 |
29 | connection.invalidationHandler = { [weak connection] in
30 | self.sema.wait()
31 | defer { self.sema.signal() }
32 |
33 | connection?.invalidationHandler = nil
34 | self._xpcConnection = nil
35 | }
36 |
37 | connection.resume()
38 |
39 | self._xpcConnection = connection
40 | return connection
41 | }
42 |
43 | func sayHello(reply: @escaping (Result) -> Void) {
44 | let proxy = self.getProxy { reply(.failure($0)) }
45 | let sandboxWorkaround = SandboxWorkaround()
46 |
47 | proxy.sayHello(message: "Hello there, helper tool!") {
48 | sandboxWorkaround.stop()
49 |
50 | if let replyMessage = $0 {
51 | reply(.success(replyMessage))
52 | } else {
53 | reply(.failure($1 ?? CocoaError(.fileReadUnknown)))
54 | }
55 | }
56 | }
57 |
58 | func getProxy(errorHandler: @escaping (Error) -> Void) -> XPCServiceProtocol {
59 | return self.xpcConnection.remoteObjectProxyWithErrorHandler(errorHandler) as! XPCServiceProtocol
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/CSAuthSample Example App.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 2805EA37243A55F4008BD2DC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EA36243A55F4008BD2DC /* AppDelegate.swift */; };
11 | 2805EA39243A55F4008BD2DC /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EA38243A55F4008BD2DC /* ContentView.swift */; };
12 | 2805EA41243A55F5008BD2DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2805EA3F243A55F5008BD2DC /* Main.storyboard */; };
13 | 2805EA50243A563B008BD2DC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EA4F243A563B008BD2DC /* main.swift */; };
14 | 2805EA95243A73FB008BD2DC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EA94243A73FB008BD2DC /* main.swift */; };
15 | 2805EA99243A73FB008BD2DC /* Example XPC Service.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 2805EA8E243A73FB008BD2DC /* Example XPC Service.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
16 | 2805EAC1243A7577008BD2DC /* XPCServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EA90243A73FB008BD2DC /* XPCServiceProtocol.swift */; };
17 | 2805EAC2243A757B008BD2DC /* XPCServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EA90243A73FB008BD2DC /* XPCServiceProtocol.swift */; };
18 | 2805EAD5243A7B9A008BD2DC /* HelperToolProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EAD4243A7B9A008BD2DC /* HelperToolProtocol.swift */; };
19 | 2805EAD6243A7B9A008BD2DC /* HelperToolProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EAD4243A7B9A008BD2DC /* HelperToolProtocol.swift */; };
20 | 2805EADF243A81BF008BD2DC /* Prompts.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2805EAE1243A81BF008BD2DC /* Prompts.strings */; };
21 | 2805EAE2243A862A008BD2DC /* XPCService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EAD9243A7EAD008BD2DC /* XPCService.swift */; };
22 | 2805EAE3243A8635008BD2DC /* ExampleCommandSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EADB243A7FC2008BD2DC /* ExampleCommandSet.swift */; };
23 | 2805EAE4243A8636008BD2DC /* ExampleCommandSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EADB243A7FC2008BD2DC /* ExampleCommandSet.swift */; };
24 | 2805EAF6243ADC5B008BD2DC /* ServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EAF5243ADC5B008BD2DC /* ServiceDelegate.swift */; };
25 | 2805EAF8243AEB00008BD2DC /* MessageSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EAF7243AEB00008BD2DC /* MessageSender.swift */; };
26 | 2805EAFA243AEBD4008BD2DC /* Identifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EAF9243AEBD4008BD2DC /* Identifiers.swift */; };
27 | 2805EAFB243AEBD4008BD2DC /* Identifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EAF9243AEBD4008BD2DC /* Identifiers.swift */; };
28 | 2805EAFC243AEBD4008BD2DC /* Identifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2805EAF9243AEBD4008BD2DC /* Identifiers.swift */; };
29 | 2821463F2443EC4D008BD2DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2821463E2443EC4D008BD2DC /* Assets.xcassets */; };
30 | 28F83F082447FEB0008BD2DC /* Example Helper Tool in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2805EA4D243A563B008BD2DC /* Example Helper Tool */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
31 | 28F83F2A244808B1008BD2DC /* HelperConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28F83F29244808B1008BD2DC /* HelperConnection.swift */; };
32 | /* End PBXBuildFile section */
33 |
34 | /* Begin PBXContainerItemProxy section */
35 | 28D533FC25FD2C750029362F /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = 2805EA2B243A55F4008BD2DC /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = 2805EA4C243A563B008BD2DC;
40 | remoteInfo = "Example Helper Tool";
41 | };
42 | 28D533FE25FD2C7B0029362F /* PBXContainerItemProxy */ = {
43 | isa = PBXContainerItemProxy;
44 | containerPortal = 2805EA2B243A55F4008BD2DC /* Project object */;
45 | proxyType = 1;
46 | remoteGlobalIDString = 2805EA4C243A563B008BD2DC;
47 | remoteInfo = "Example Helper Tool";
48 | };
49 | 28D5340625FD2CA90029362F /* PBXContainerItemProxy */ = {
50 | isa = PBXContainerItemProxy;
51 | containerPortal = 2805EA2B243A55F4008BD2DC /* Project object */;
52 | proxyType = 1;
53 | remoteGlobalIDString = 2805EA8D243A73FB008BD2DC;
54 | remoteInfo = "Example XPC Service";
55 | };
56 | /* End PBXContainerItemProxy section */
57 |
58 | /* Begin PBXCopyFilesBuildPhase section */
59 | 2805EA4B243A563B008BD2DC /* CopyFiles */ = {
60 | isa = PBXCopyFilesBuildPhase;
61 | buildActionMask = 2147483647;
62 | dstPath = /usr/share/man/man1/;
63 | dstSubfolderSpec = 0;
64 | files = (
65 | );
66 | runOnlyForDeploymentPostprocessing = 1;
67 | };
68 | 2805EA9D243A73FB008BD2DC /* Embed XPC Services */ = {
69 | isa = PBXCopyFilesBuildPhase;
70 | buildActionMask = 2147483647;
71 | dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
72 | dstSubfolderSpec = 16;
73 | files = (
74 | 2805EA99243A73FB008BD2DC /* Example XPC Service.xpc in Embed XPC Services */,
75 | );
76 | name = "Embed XPC Services";
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | 28F83F072447FEA5008BD2DC /* CopyFiles */ = {
80 | isa = PBXCopyFilesBuildPhase;
81 | buildActionMask = 2147483647;
82 | dstPath = Contents/Library/LaunchServices;
83 | dstSubfolderSpec = 1;
84 | files = (
85 | 28F83F082447FEB0008BD2DC /* Example Helper Tool in CopyFiles */,
86 | );
87 | runOnlyForDeploymentPostprocessing = 0;
88 | };
89 | /* End PBXCopyFilesBuildPhase section */
90 |
91 | /* Begin PBXFileReference section */
92 | 2805EA33243A55F4008BD2DC /* Example App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Example App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
93 | 2805EA36243A55F4008BD2DC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
94 | 2805EA38243A55F4008BD2DC /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
95 | 2805EA40243A55F5008BD2DC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
96 | 2805EA42243A55F5008BD2DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
97 | 2805EA43243A55F5008BD2DC /* CSAuthSample_Example_App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CSAuthSample_Example_App.entitlements; sourceTree = ""; };
98 | 2805EA4D243A563B008BD2DC /* Example Helper Tool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Example Helper Tool"; sourceTree = BUILT_PRODUCTS_DIR; };
99 | 2805EA4F243A563B008BD2DC /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
100 | 2805EA8E243A73FB008BD2DC /* Example XPC Service.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = "Example XPC Service.xpc"; sourceTree = BUILT_PRODUCTS_DIR; };
101 | 2805EA90243A73FB008BD2DC /* XPCServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCServiceProtocol.swift; sourceTree = ""; };
102 | 2805EA94243A73FB008BD2DC /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
103 | 2805EA96243A73FB008BD2DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
104 | 2805EAD4243A7B9A008BD2DC /* HelperToolProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperToolProtocol.swift; sourceTree = ""; };
105 | 2805EAD9243A7EAD008BD2DC /* XPCService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCService.swift; sourceTree = ""; };
106 | 2805EADB243A7FC2008BD2DC /* ExampleCommandSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleCommandSet.swift; sourceTree = ""; };
107 | 2805EAE0243A81BF008BD2DC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Prompts.strings; sourceTree = ""; };
108 | 2805EAF5243ADC5B008BD2DC /* ServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceDelegate.swift; sourceTree = ""; };
109 | 2805EAF7243AEB00008BD2DC /* MessageSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSender.swift; sourceTree = ""; };
110 | 2805EAF9243AEBD4008BD2DC /* Identifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identifiers.swift; sourceTree = ""; };
111 | 2821463D2443E4CB008BD2DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
112 | 2821463E2443EC4D008BD2DC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
113 | 282146402443ED57008BD2DC /* CSAuthSample-Example.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "CSAuthSample-Example.xcconfig"; sourceTree = ""; };
114 | 282146452444060B008BD2DC /* Launchd.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Launchd.plist; sourceTree = ""; };
115 | 28F83F29244808B1008BD2DC /* HelperConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperConnection.swift; sourceTree = ""; };
116 | 28F83F3824496868008BD2DC /* GlobalSetupScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSetupScript.swift; sourceTree = ""; };
117 | 28F83F3924496D1A008BD2DC /* SetupHelperPlists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupHelperPlists.swift; sourceTree = ""; };
118 | /* End PBXFileReference section */
119 |
120 | /* Begin PBXFrameworksBuildPhase section */
121 | 2805EA30243A55F4008BD2DC /* Frameworks */ = {
122 | isa = PBXFrameworksBuildPhase;
123 | buildActionMask = 2147483647;
124 | files = (
125 | );
126 | runOnlyForDeploymentPostprocessing = 0;
127 | };
128 | 2805EA4A243A563B008BD2DC /* Frameworks */ = {
129 | isa = PBXFrameworksBuildPhase;
130 | buildActionMask = 2147483647;
131 | files = (
132 | );
133 | runOnlyForDeploymentPostprocessing = 0;
134 | };
135 | 2805EA8B243A73FB008BD2DC /* Frameworks */ = {
136 | isa = PBXFrameworksBuildPhase;
137 | buildActionMask = 2147483647;
138 | files = (
139 | );
140 | runOnlyForDeploymentPostprocessing = 0;
141 | };
142 | /* End PBXFrameworksBuildPhase section */
143 |
144 | /* Begin PBXGroup section */
145 | 2805EA2A243A55F4008BD2DC = {
146 | isa = PBXGroup;
147 | children = (
148 | 2805EAD3243A7B79008BD2DC /* Common */,
149 | 2805EA35243A55F4008BD2DC /* App */,
150 | 2805EA8F243A73FB008BD2DC /* XPC Service */,
151 | 2805EA4E243A563B008BD2DC /* Helper Tool */,
152 | 2805EA34243A55F4008BD2DC /* Products */,
153 | 2805EA9E243A740E008BD2DC /* Frameworks */,
154 | );
155 | sourceTree = "";
156 | };
157 | 2805EA34243A55F4008BD2DC /* Products */ = {
158 | isa = PBXGroup;
159 | children = (
160 | 2805EA33243A55F4008BD2DC /* Example App.app */,
161 | 2805EA4D243A563B008BD2DC /* Example Helper Tool */,
162 | 2805EA8E243A73FB008BD2DC /* Example XPC Service.xpc */,
163 | );
164 | name = Products;
165 | sourceTree = "";
166 | };
167 | 2805EA35243A55F4008BD2DC /* App */ = {
168 | isa = PBXGroup;
169 | children = (
170 | 2805EA36243A55F4008BD2DC /* AppDelegate.swift */,
171 | 2805EA38243A55F4008BD2DC /* ContentView.swift */,
172 | 2805EAF7243AEB00008BD2DC /* MessageSender.swift */,
173 | 2805EA3F243A55F5008BD2DC /* Main.storyboard */,
174 | 2805EA42243A55F5008BD2DC /* Info.plist */,
175 | 2821463E2443EC4D008BD2DC /* Assets.xcassets */,
176 | 2805EA43243A55F5008BD2DC /* CSAuthSample_Example_App.entitlements */,
177 | );
178 | path = App;
179 | sourceTree = "";
180 | };
181 | 2805EA4E243A563B008BD2DC /* Helper Tool */ = {
182 | isa = PBXGroup;
183 | children = (
184 | 28F83F3B24496D5D008BD2DC /* Build Scripts */,
185 | 2805EA4F243A563B008BD2DC /* main.swift */,
186 | 28F83F29244808B1008BD2DC /* HelperConnection.swift */,
187 | 2821463D2443E4CB008BD2DC /* Info.plist */,
188 | 282146452444060B008BD2DC /* Launchd.plist */,
189 | );
190 | path = "Helper Tool";
191 | sourceTree = "";
192 | };
193 | 2805EA8F243A73FB008BD2DC /* XPC Service */ = {
194 | isa = PBXGroup;
195 | children = (
196 | 2805EA90243A73FB008BD2DC /* XPCServiceProtocol.swift */,
197 | 2805EAD9243A7EAD008BD2DC /* XPCService.swift */,
198 | 2805EAF5243ADC5B008BD2DC /* ServiceDelegate.swift */,
199 | 2805EA94243A73FB008BD2DC /* main.swift */,
200 | 2805EA96243A73FB008BD2DC /* Info.plist */,
201 | );
202 | path = "XPC Service";
203 | sourceTree = "";
204 | };
205 | 2805EA9E243A740E008BD2DC /* Frameworks */ = {
206 | isa = PBXGroup;
207 | children = (
208 | );
209 | name = Frameworks;
210 | sourceTree = "";
211 | };
212 | 2805EAD3243A7B79008BD2DC /* Common */ = {
213 | isa = PBXGroup;
214 | children = (
215 | 2805EAF9243AEBD4008BD2DC /* Identifiers.swift */,
216 | 2805EAD4243A7B9A008BD2DC /* HelperToolProtocol.swift */,
217 | 2805EADB243A7FC2008BD2DC /* ExampleCommandSet.swift */,
218 | 2805EAE1243A81BF008BD2DC /* Prompts.strings */,
219 | 28F83F37244967AC008BD2DC /* Build Scripts */,
220 | 282146442443FA83008BD2DC /* Config */,
221 | );
222 | path = Common;
223 | sourceTree = "";
224 | };
225 | 282146442443FA83008BD2DC /* Config */ = {
226 | isa = PBXGroup;
227 | children = (
228 | 282146402443ED57008BD2DC /* CSAuthSample-Example.xcconfig */,
229 | );
230 | path = Config;
231 | sourceTree = "";
232 | };
233 | 28F83F37244967AC008BD2DC /* Build Scripts */ = {
234 | isa = PBXGroup;
235 | children = (
236 | 28F83F3824496868008BD2DC /* GlobalSetupScript.swift */,
237 | );
238 | path = "Build Scripts";
239 | sourceTree = "";
240 | };
241 | 28F83F3B24496D5D008BD2DC /* Build Scripts */ = {
242 | isa = PBXGroup;
243 | children = (
244 | 28F83F3924496D1A008BD2DC /* SetupHelperPlists.swift */,
245 | );
246 | path = "Build Scripts";
247 | sourceTree = "";
248 | };
249 | /* End PBXGroup section */
250 |
251 | /* Begin PBXNativeTarget section */
252 | 2805EA32243A55F4008BD2DC /* Example App */ = {
253 | isa = PBXNativeTarget;
254 | buildConfigurationList = 2805EA46243A55F5008BD2DC /* Build configuration list for PBXNativeTarget "Example App" */;
255 | buildPhases = (
256 | 2805EA2F243A55F4008BD2DC /* Sources */,
257 | 2805EA30243A55F4008BD2DC /* Frameworks */,
258 | 2805EA31243A55F4008BD2DC /* Resources */,
259 | 2805EA9D243A73FB008BD2DC /* Embed XPC Services */,
260 | );
261 | buildRules = (
262 | );
263 | dependencies = (
264 | 28D533FF25FD2C7B0029362F /* PBXTargetDependency */,
265 | 28D5340725FD2CA90029362F /* PBXTargetDependency */,
266 | );
267 | name = "Example App";
268 | packageProductDependencies = (
269 | );
270 | productName = "CSAuthSample Example App";
271 | productReference = 2805EA33243A55F4008BD2DC /* Example App.app */;
272 | productType = "com.apple.product-type.application";
273 | };
274 | 2805EA4C243A563B008BD2DC /* Example Helper Tool */ = {
275 | isa = PBXNativeTarget;
276 | buildConfigurationList = 2805EA51243A563B008BD2DC /* Build configuration list for PBXNativeTarget "Example Helper Tool" */;
277 | buildPhases = (
278 | 282146772447DB93008BD2DC /* ShellScript */,
279 | 2805EA49243A563B008BD2DC /* Sources */,
280 | 2805EA4A243A563B008BD2DC /* Frameworks */,
281 | 2805EA4B243A563B008BD2DC /* CopyFiles */,
282 | );
283 | buildRules = (
284 | );
285 | dependencies = (
286 | );
287 | name = "Example Helper Tool";
288 | packageProductDependencies = (
289 | );
290 | productName = "CSAuthSample Example Helper Tool";
291 | productReference = 2805EA4D243A563B008BD2DC /* Example Helper Tool */;
292 | productType = "com.apple.product-type.tool";
293 | };
294 | 2805EA8D243A73FB008BD2DC /* Example XPC Service */ = {
295 | isa = PBXNativeTarget;
296 | buildConfigurationList = 2805EA9A243A73FB008BD2DC /* Build configuration list for PBXNativeTarget "Example XPC Service" */;
297 | buildPhases = (
298 | 2805EA8A243A73FB008BD2DC /* Sources */,
299 | 2805EA8B243A73FB008BD2DC /* Frameworks */,
300 | 2805EA8C243A73FB008BD2DC /* Resources */,
301 | 28F83F072447FEA5008BD2DC /* CopyFiles */,
302 | );
303 | buildRules = (
304 | );
305 | dependencies = (
306 | 28D533FD25FD2C750029362F /* PBXTargetDependency */,
307 | );
308 | name = "Example XPC Service";
309 | packageProductDependencies = (
310 | );
311 | productName = "Example App XPC Service";
312 | productReference = 2805EA8E243A73FB008BD2DC /* Example XPC Service.xpc */;
313 | productType = "com.apple.product-type.xpc-service";
314 | };
315 | /* End PBXNativeTarget section */
316 |
317 | /* Begin PBXProject section */
318 | 2805EA2B243A55F4008BD2DC /* Project object */ = {
319 | isa = PBXProject;
320 | attributes = {
321 | LastSwiftUpdateCheck = 1140;
322 | LastUpgradeCheck = 1320;
323 | ORGANIZATIONNAME = "Charles Srstka";
324 | TargetAttributes = {
325 | 2805EA32243A55F4008BD2DC = {
326 | CreatedOnToolsVersion = 11.4;
327 | };
328 | 2805EA4C243A563B008BD2DC = {
329 | CreatedOnToolsVersion = 11.4;
330 | };
331 | 2805EA8D243A73FB008BD2DC = {
332 | CreatedOnToolsVersion = 11.4;
333 | };
334 | };
335 | };
336 | buildConfigurationList = 2805EA2E243A55F4008BD2DC /* Build configuration list for PBXProject "CSAuthSample Example App" */;
337 | compatibilityVersion = "Xcode 9.3";
338 | developmentRegion = en;
339 | hasScannedForEncodings = 0;
340 | knownRegions = (
341 | en,
342 | Base,
343 | );
344 | mainGroup = 2805EA2A243A55F4008BD2DC;
345 | packageReferences = (
346 | );
347 | productRefGroup = 2805EA34243A55F4008BD2DC /* Products */;
348 | projectDirPath = "";
349 | projectRoot = "";
350 | targets = (
351 | 2805EA32243A55F4008BD2DC /* Example App */,
352 | 2805EA8D243A73FB008BD2DC /* Example XPC Service */,
353 | 2805EA4C243A563B008BD2DC /* Example Helper Tool */,
354 | );
355 | };
356 | /* End PBXProject section */
357 |
358 | /* Begin PBXResourcesBuildPhase section */
359 | 2805EA31243A55F4008BD2DC /* Resources */ = {
360 | isa = PBXResourcesBuildPhase;
361 | buildActionMask = 2147483647;
362 | files = (
363 | 2821463F2443EC4D008BD2DC /* Assets.xcassets in Resources */,
364 | 2805EA41243A55F5008BD2DC /* Main.storyboard in Resources */,
365 | );
366 | runOnlyForDeploymentPostprocessing = 0;
367 | };
368 | 2805EA8C243A73FB008BD2DC /* Resources */ = {
369 | isa = PBXResourcesBuildPhase;
370 | buildActionMask = 2147483647;
371 | files = (
372 | 2805EADF243A81BF008BD2DC /* Prompts.strings in Resources */,
373 | );
374 | runOnlyForDeploymentPostprocessing = 0;
375 | };
376 | /* End PBXResourcesBuildPhase section */
377 |
378 | /* Begin PBXShellScriptBuildPhase section */
379 | 282146772447DB93008BD2DC /* ShellScript */ = {
380 | isa = PBXShellScriptBuildPhase;
381 | buildActionMask = 2147483647;
382 | files = (
383 | );
384 | inputFileListPaths = (
385 | );
386 | inputPaths = (
387 | );
388 | outputFileListPaths = (
389 | );
390 | outputPaths = (
391 | "$(DERIVED_FILE_DIR)/Info.plist",
392 | "$(DERIVED_FILE_DIR)/Launchd.plist",
393 | );
394 | runOnlyForDeploymentPostprocessing = 0;
395 | shellPath = /bin/sh;
396 | shellScript = "swift \"$SRCROOT/Helper Tool/Build Scripts/SetupHelperPlists.swift\"\n";
397 | };
398 | /* End PBXShellScriptBuildPhase section */
399 |
400 | /* Begin PBXSourcesBuildPhase section */
401 | 2805EA2F243A55F4008BD2DC /* Sources */ = {
402 | isa = PBXSourcesBuildPhase;
403 | buildActionMask = 2147483647;
404 | files = (
405 | 2805EAF8243AEB00008BD2DC /* MessageSender.swift in Sources */,
406 | 2805EA39243A55F4008BD2DC /* ContentView.swift in Sources */,
407 | 2805EAC1243A7577008BD2DC /* XPCServiceProtocol.swift in Sources */,
408 | 2805EAFA243AEBD4008BD2DC /* Identifiers.swift in Sources */,
409 | 2805EA37243A55F4008BD2DC /* AppDelegate.swift in Sources */,
410 | );
411 | runOnlyForDeploymentPostprocessing = 0;
412 | };
413 | 2805EA49243A563B008BD2DC /* Sources */ = {
414 | isa = PBXSourcesBuildPhase;
415 | buildActionMask = 2147483647;
416 | files = (
417 | 28F83F2A244808B1008BD2DC /* HelperConnection.swift in Sources */,
418 | 2805EAE4243A8636008BD2DC /* ExampleCommandSet.swift in Sources */,
419 | 2805EA50243A563B008BD2DC /* main.swift in Sources */,
420 | 2805EAFC243AEBD4008BD2DC /* Identifiers.swift in Sources */,
421 | 2805EAD6243A7B9A008BD2DC /* HelperToolProtocol.swift in Sources */,
422 | );
423 | runOnlyForDeploymentPostprocessing = 0;
424 | };
425 | 2805EA8A243A73FB008BD2DC /* Sources */ = {
426 | isa = PBXSourcesBuildPhase;
427 | buildActionMask = 2147483647;
428 | files = (
429 | 2805EAE3243A8635008BD2DC /* ExampleCommandSet.swift in Sources */,
430 | 2805EA95243A73FB008BD2DC /* main.swift in Sources */,
431 | 2805EAC2243A757B008BD2DC /* XPCServiceProtocol.swift in Sources */,
432 | 2805EAD5243A7B9A008BD2DC /* HelperToolProtocol.swift in Sources */,
433 | 2805EAFB243AEBD4008BD2DC /* Identifiers.swift in Sources */,
434 | 2805EAF6243ADC5B008BD2DC /* ServiceDelegate.swift in Sources */,
435 | 2805EAE2243A862A008BD2DC /* XPCService.swift in Sources */,
436 | );
437 | runOnlyForDeploymentPostprocessing = 0;
438 | };
439 | /* End PBXSourcesBuildPhase section */
440 |
441 | /* Begin PBXTargetDependency section */
442 | 28D533FD25FD2C750029362F /* PBXTargetDependency */ = {
443 | isa = PBXTargetDependency;
444 | target = 2805EA4C243A563B008BD2DC /* Example Helper Tool */;
445 | targetProxy = 28D533FC25FD2C750029362F /* PBXContainerItemProxy */;
446 | };
447 | 28D533FF25FD2C7B0029362F /* PBXTargetDependency */ = {
448 | isa = PBXTargetDependency;
449 | target = 2805EA4C243A563B008BD2DC /* Example Helper Tool */;
450 | targetProxy = 28D533FE25FD2C7B0029362F /* PBXContainerItemProxy */;
451 | };
452 | 28D5340725FD2CA90029362F /* PBXTargetDependency */ = {
453 | isa = PBXTargetDependency;
454 | target = 2805EA8D243A73FB008BD2DC /* Example XPC Service */;
455 | targetProxy = 28D5340625FD2CA90029362F /* PBXContainerItemProxy */;
456 | };
457 | /* End PBXTargetDependency section */
458 |
459 | /* Begin PBXVariantGroup section */
460 | 2805EA3F243A55F5008BD2DC /* Main.storyboard */ = {
461 | isa = PBXVariantGroup;
462 | children = (
463 | 2805EA40243A55F5008BD2DC /* Base */,
464 | );
465 | name = Main.storyboard;
466 | sourceTree = "";
467 | };
468 | 2805EAE1243A81BF008BD2DC /* Prompts.strings */ = {
469 | isa = PBXVariantGroup;
470 | children = (
471 | 2805EAE0243A81BF008BD2DC /* en */,
472 | );
473 | name = Prompts.strings;
474 | sourceTree = "";
475 | };
476 | /* End PBXVariantGroup section */
477 |
478 | /* Begin XCBuildConfiguration section */
479 | 2805EA44243A55F5008BD2DC /* Debug */ = {
480 | isa = XCBuildConfiguration;
481 | baseConfigurationReference = 282146402443ED57008BD2DC /* CSAuthSample-Example.xcconfig */;
482 | buildSettings = {
483 | ALWAYS_SEARCH_USER_PATHS = NO;
484 | CLANG_ANALYZER_NONNULL = YES;
485 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
486 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
487 | CLANG_CXX_LIBRARY = "libc++";
488 | CLANG_ENABLE_MODULES = YES;
489 | CLANG_ENABLE_OBJC_ARC = YES;
490 | CLANG_ENABLE_OBJC_WEAK = YES;
491 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
492 | CLANG_WARN_BOOL_CONVERSION = YES;
493 | CLANG_WARN_COMMA = YES;
494 | CLANG_WARN_CONSTANT_CONVERSION = YES;
495 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
496 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
497 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
498 | CLANG_WARN_EMPTY_BODY = YES;
499 | CLANG_WARN_ENUM_CONVERSION = YES;
500 | CLANG_WARN_INFINITE_RECURSION = YES;
501 | CLANG_WARN_INT_CONVERSION = YES;
502 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
503 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
504 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
505 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
506 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
507 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
508 | CLANG_WARN_STRICT_PROTOTYPES = YES;
509 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
510 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
511 | CLANG_WARN_UNREACHABLE_CODE = YES;
512 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
513 | CODE_SIGN_IDENTITY = "Apple Development";
514 | CODE_SIGN_STYLE = Manual;
515 | COPY_PHASE_STRIP = NO;
516 | DEBUG_INFORMATION_FORMAT = dwarf;
517 | DEVELOPMENT_TEAM = HRLUCP7QP4;
518 | ENABLE_STRICT_OBJC_MSGSEND = YES;
519 | ENABLE_TESTABILITY = YES;
520 | GCC_C_LANGUAGE_STANDARD = gnu11;
521 | GCC_DYNAMIC_NO_PIC = NO;
522 | GCC_NO_COMMON_BLOCKS = YES;
523 | GCC_OPTIMIZATION_LEVEL = 0;
524 | GCC_PREPROCESSOR_DEFINITIONS = (
525 | "DEBUG=1",
526 | "$(inherited)",
527 | );
528 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
529 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
530 | GCC_WARN_UNDECLARED_SELECTOR = YES;
531 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
532 | GCC_WARN_UNUSED_FUNCTION = YES;
533 | GCC_WARN_UNUSED_VARIABLE = YES;
534 | MACOSX_DEPLOYMENT_TARGET = 10.15;
535 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
536 | MTL_FAST_MATH = YES;
537 | ONLY_ACTIVE_ARCH = YES;
538 | SDKROOT = macosx;
539 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
540 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
541 | SWIFT_VERSION = 5.0;
542 | };
543 | name = Debug;
544 | };
545 | 2805EA45243A55F5008BD2DC /* Release */ = {
546 | isa = XCBuildConfiguration;
547 | baseConfigurationReference = 282146402443ED57008BD2DC /* CSAuthSample-Example.xcconfig */;
548 | buildSettings = {
549 | ALWAYS_SEARCH_USER_PATHS = NO;
550 | CLANG_ANALYZER_NONNULL = YES;
551 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
552 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
553 | CLANG_CXX_LIBRARY = "libc++";
554 | CLANG_ENABLE_MODULES = YES;
555 | CLANG_ENABLE_OBJC_ARC = YES;
556 | CLANG_ENABLE_OBJC_WEAK = YES;
557 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
558 | CLANG_WARN_BOOL_CONVERSION = YES;
559 | CLANG_WARN_COMMA = YES;
560 | CLANG_WARN_CONSTANT_CONVERSION = YES;
561 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
562 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
563 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
564 | CLANG_WARN_EMPTY_BODY = YES;
565 | CLANG_WARN_ENUM_CONVERSION = YES;
566 | CLANG_WARN_INFINITE_RECURSION = YES;
567 | CLANG_WARN_INT_CONVERSION = YES;
568 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
569 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
570 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
571 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
572 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
573 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
574 | CLANG_WARN_STRICT_PROTOTYPES = YES;
575 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
576 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
577 | CLANG_WARN_UNREACHABLE_CODE = YES;
578 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
579 | CODE_SIGN_IDENTITY = "Apple Development";
580 | CODE_SIGN_STYLE = Manual;
581 | COPY_PHASE_STRIP = NO;
582 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
583 | DEVELOPMENT_TEAM = HRLUCP7QP4;
584 | ENABLE_NS_ASSERTIONS = NO;
585 | ENABLE_STRICT_OBJC_MSGSEND = YES;
586 | GCC_C_LANGUAGE_STANDARD = gnu11;
587 | GCC_NO_COMMON_BLOCKS = YES;
588 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
589 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
590 | GCC_WARN_UNDECLARED_SELECTOR = YES;
591 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
592 | GCC_WARN_UNUSED_FUNCTION = YES;
593 | GCC_WARN_UNUSED_VARIABLE = YES;
594 | MACOSX_DEPLOYMENT_TARGET = 10.15;
595 | MTL_ENABLE_DEBUG_INFO = NO;
596 | MTL_FAST_MATH = YES;
597 | SDKROOT = macosx;
598 | SWIFT_COMPILATION_MODE = wholemodule;
599 | SWIFT_OPTIMIZATION_LEVEL = "-O";
600 | SWIFT_VERSION = 5.0;
601 | };
602 | name = Release;
603 | };
604 | 2805EA47243A55F5008BD2DC /* Debug */ = {
605 | isa = XCBuildConfiguration;
606 | buildSettings = {
607 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
608 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
609 | CODE_SIGN_ENTITLEMENTS = App/CSAuthSample_Example_App.entitlements;
610 | CODE_SIGN_IDENTITY = "-";
611 | COMBINE_HIDPI_IMAGES = YES;
612 | ENABLE_PREVIEWS = YES;
613 | INFOPLIST_FILE = App/Info.plist;
614 | LD_RUNPATH_SEARCH_PATHS = (
615 | "$(inherited)",
616 | "@executable_path/../Frameworks",
617 | );
618 | MACOSX_DEPLOYMENT_TARGET = 10.15;
619 | PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_ID)";
620 | PRODUCT_NAME = "$(TARGET_NAME)";
621 | };
622 | name = Debug;
623 | };
624 | 2805EA48243A55F5008BD2DC /* Release */ = {
625 | isa = XCBuildConfiguration;
626 | buildSettings = {
627 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
628 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
629 | CODE_SIGN_ENTITLEMENTS = App/CSAuthSample_Example_App.entitlements;
630 | CODE_SIGN_IDENTITY = "-";
631 | COMBINE_HIDPI_IMAGES = YES;
632 | ENABLE_PREVIEWS = YES;
633 | INFOPLIST_FILE = App/Info.plist;
634 | LD_RUNPATH_SEARCH_PATHS = (
635 | "$(inherited)",
636 | "@executable_path/../Frameworks",
637 | );
638 | MACOSX_DEPLOYMENT_TARGET = 10.15;
639 | PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_ID)";
640 | PRODUCT_NAME = "$(TARGET_NAME)";
641 | };
642 | name = Release;
643 | };
644 | 2805EA52243A563B008BD2DC /* Debug */ = {
645 | isa = XCBuildConfiguration;
646 | buildSettings = {
647 | CODE_SIGN_IDENTITY = "-";
648 | OTHER_LDFLAGS = (
649 | "-sectcreate",
650 | __TEXT,
651 | __info_plist,
652 | "\"$(DERIVED_FILE_DIR)/Info.plist\"",
653 | "-sectcreate",
654 | __TEXT,
655 | __launchd_plist,
656 | "\"$(DERIVED_FILE_DIR)/Launchd.plist\"",
657 | );
658 | PRODUCT_NAME = "$(HELPER_ID)";
659 | };
660 | name = Debug;
661 | };
662 | 2805EA53243A563B008BD2DC /* Release */ = {
663 | isa = XCBuildConfiguration;
664 | buildSettings = {
665 | CODE_SIGN_IDENTITY = "-";
666 | OTHER_LDFLAGS = (
667 | "-sectcreate",
668 | __TEXT,
669 | __info_plist,
670 | "\"$(DERIVED_FILE_DIR)/Info.plist\"",
671 | "-sectcreate",
672 | __TEXT,
673 | __launchd_plist,
674 | "\"$(DERIVED_FILE_DIR)/Launchd.plist\"",
675 | );
676 | PRODUCT_NAME = "$(HELPER_ID)";
677 | };
678 | name = Release;
679 | };
680 | 2805EA9B243A73FB008BD2DC /* Debug */ = {
681 | isa = XCBuildConfiguration;
682 | buildSettings = {
683 | CODE_SIGN_IDENTITY = "-";
684 | COMBINE_HIDPI_IMAGES = YES;
685 | INFOPLIST_FILE = "XPC Service/Info.plist";
686 | PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_ID).XPCService";
687 | PRODUCT_NAME = "$(TARGET_NAME)";
688 | SKIP_INSTALL = YES;
689 | };
690 | name = Debug;
691 | };
692 | 2805EA9C243A73FB008BD2DC /* Release */ = {
693 | isa = XCBuildConfiguration;
694 | buildSettings = {
695 | CODE_SIGN_IDENTITY = "-";
696 | COMBINE_HIDPI_IMAGES = YES;
697 | INFOPLIST_FILE = "XPC Service/Info.plist";
698 | PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_ID).XPCService";
699 | PRODUCT_NAME = "$(TARGET_NAME)";
700 | SKIP_INSTALL = YES;
701 | };
702 | name = Release;
703 | };
704 | /* End XCBuildConfiguration section */
705 |
706 | /* Begin XCConfigurationList section */
707 | 2805EA2E243A55F4008BD2DC /* Build configuration list for PBXProject "CSAuthSample Example App" */ = {
708 | isa = XCConfigurationList;
709 | buildConfigurations = (
710 | 2805EA44243A55F5008BD2DC /* Debug */,
711 | 2805EA45243A55F5008BD2DC /* Release */,
712 | );
713 | defaultConfigurationIsVisible = 0;
714 | defaultConfigurationName = Release;
715 | };
716 | 2805EA46243A55F5008BD2DC /* Build configuration list for PBXNativeTarget "Example App" */ = {
717 | isa = XCConfigurationList;
718 | buildConfigurations = (
719 | 2805EA47243A55F5008BD2DC /* Debug */,
720 | 2805EA48243A55F5008BD2DC /* Release */,
721 | );
722 | defaultConfigurationIsVisible = 0;
723 | defaultConfigurationName = Release;
724 | };
725 | 2805EA51243A563B008BD2DC /* Build configuration list for PBXNativeTarget "Example Helper Tool" */ = {
726 | isa = XCConfigurationList;
727 | buildConfigurations = (
728 | 2805EA52243A563B008BD2DC /* Debug */,
729 | 2805EA53243A563B008BD2DC /* Release */,
730 | );
731 | defaultConfigurationIsVisible = 0;
732 | defaultConfigurationName = Release;
733 | };
734 | 2805EA9A243A73FB008BD2DC /* Build configuration list for PBXNativeTarget "Example XPC Service" */ = {
735 | isa = XCConfigurationList;
736 | buildConfigurations = (
737 | 2805EA9B243A73FB008BD2DC /* Debug */,
738 | 2805EA9C243A73FB008BD2DC /* Release */,
739 | );
740 | defaultConfigurationIsVisible = 0;
741 | defaultConfigurationName = Release;
742 | };
743 | /* End XCConfigurationList section */
744 | };
745 | rootObject = 2805EA2B243A55F4008BD2DC /* Project object */;
746 | }
747 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/CSAuthSample Example App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/CSAuthSample Example App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/CSAuthSample Example App.xcodeproj/xcshareddata/xcschemes/Example App.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
11 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
33 |
39 |
40 |
41 |
42 |
43 |
48 |
49 |
50 |
51 |
61 |
63 |
69 |
70 |
71 |
72 |
78 |
80 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Common/Build Scripts/GlobalSetupScript.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GlobalSetupScript.swift
3 | // CSAuthSample Example App
4 | //
5 | // Created by Charles Srstka on 4/12/20.
6 | //
7 |
8 | import Foundation
9 |
10 | let env = ProcessInfo.processInfo.environment
11 | let versionVarName = "CURRENT_PROJECT_VERSION"
12 |
13 | let srcURL = URL(fileURLWithPath: env["SRCROOT"]!)
14 | let derivedFileDir = URL(fileURLWithPath: env["DERIVED_FILE_DIR"]!)
15 | let configURL = srcURL.appendingPathComponent("Common/Config/CSAuthSample-Example.xcconfig")
16 |
17 | let version = Int(env[versionVarName]!)!
18 |
19 | let newConfig = """
20 | \(versionVarName) = \(version + 1)
21 | APP_BUNDLE_ID = \(Identifiers.appID)
22 | XPC_SERVICE_ID = \(Identifiers.xpcServiceID)
23 | HELPER_ID = \(Identifiers.helperID)
24 | CS_REQUIREMENT=\(getRequirement())
25 | """
26 |
27 | try! newConfig.write(to: configURL, atomically: true, encoding: .utf8)
28 |
29 | func getRequirement() -> String {
30 | // Is there any way to generate the designated requirement string without invoking the codesign tool?
31 | // This is a bit of a kludge, but it's the only way I've been find to do this so far other than hard-coding the
32 | // format that `codesign` uses
33 |
34 | let tempFileName = UUID().uuidString
35 | let tempFileURL = `derivedFileDir`.appendingPathComponent(tempFileName)
36 |
37 | try! Data().write(to: tempFileURL)
38 | defer { try! FileManager.default.removeItem(at: tempFileURL) }
39 |
40 | let codesign = Process()
41 | codesign.executableURL = URL(fileURLWithPath: "/usr/bin/codesign")
42 | codesign.arguments = ["-s", env["CODE_SIGN_IDENTITY"]!, "-i", "", tempFileURL.path]
43 | try! codesign.run()
44 | codesign.waitUntilExit()
45 |
46 | var code: SecStaticCode?
47 | assert(SecStaticCodeCreateWithPath(tempFileURL as CFURL, [], &code) == errSecSuccess)
48 |
49 | var requirement: SecRequirement?
50 | assert(SecCodeCopyDesignatedRequirement(code!, [], &requirement) == errSecSuccess)
51 |
52 | var cfRequirementString: CFString?
53 | assert(SecRequirementCopyString(requirement!, [], &cfRequirementString) == errSecSuccess)
54 |
55 | var requirementString = cfRequirementString! as String
56 |
57 | let identifierRange = requirementString.range(of: "identifier \"\(tempFileName)\" and ")!
58 | requirementString.replaceSubrange(identifierRange, with: "")
59 |
60 | return requirementString
61 | }
62 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Common/Config/CSAuthSample-Example.xcconfig:
--------------------------------------------------------------------------------
1 | CURRENT_PROJECT_VERSION = 1
2 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Common/ExampleCommandSet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExampleCommandSet.swift
3 | // Example App
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | // Copyright © 2020-2021 Charles Srstka. All rights reserved.
7 | //
8 |
9 | import CSAuthSampleCommon
10 | import Foundation
11 |
12 | let exampleCommandSet: CommandSet = {
13 | let bundle = Bundle.main
14 |
15 | let sayHelloRightName = "com.charlessoft.CSAuthSample-Example.Say-Hello"
16 | let sayHelloPrompt = bundle.localizedString(forKey: "SayHello", value: nil, table: "Prompts")
17 | let sayHelloSelector = #selector(HelperToolProtocol.sayHello(authorizationData:message:reply:))
18 |
19 | let rights = [
20 | AuthorizationRight(
21 | selector: sayHelloSelector,
22 | name: sayHelloRightName,
23 | rule: kAuthorizationRuleAuthenticateAsAdmin,
24 | prompt: sayHelloPrompt
25 | )
26 | ]
27 |
28 | return CommandSet(authorizationRights: rights)
29 | }()
30 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Common/HelperToolProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HelperToolProtocol.swift
3 | // CSAuthSample Example App
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | //
7 |
8 | import CSAuthSampleCommon
9 | import Foundation
10 |
11 | @objc protocol HelperToolProtocol: BuiltInCommands {
12 | func sayHello(authorizationData: Data, message: String, reply: @escaping (String?, Error?) -> Void)
13 | }
14 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Common/Identifiers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Identifiers.swift
3 | // CSAuthSample Example App
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | // Copyright © 2020-2021 Charles Srstka. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Identifiers {
12 | static let appID = "com.charlessoft.CSAuthSample-Example"
13 | static let helperID = "\(Identifiers.appID).helper"
14 | static let xpcServiceID = "\(Identifiers.appID).XPCService"
15 | }
16 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Common/en.lproj/Prompts.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Prompts.strings
3 | CSAuthSample Example App
4 |
5 | Created by Charles Srstka on 4/5/20.
6 | Copyright © 2020-2021 Charles Srstka. All rights reserved.
7 | */
8 |
9 | "SayHello" = "CSAuthSample Example App would like to say hello.";
10 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Helper Tool/Build Scripts/SetupHelperPlists.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetupHelperPlists.swift
3 | // CSAuthSample Example Helper Tool
4 | //
5 | // Created by Charles Srstka on 4/16/20.
6 | //
7 |
8 | import Foundation
9 |
10 | let env = ProcessInfo.processInfo.environment
11 |
12 | let helperID = env["HELPER_ID"]!
13 | let xpcServiceID = env["XPC_SERVICE_ID"]!
14 |
15 | let srcRoot = URL(fileURLWithPath: env["SRCROOT"]!)
16 | let derivedFileDir = URL(fileURLWithPath: env["DERIVED_FILE_DIR"]!)
17 |
18 | let infoSrcURL = srcRoot.appendingPathComponent("Helper Tool/Info.plist")
19 | let launchdSrcURL = srcRoot.appendingPathComponent("Helper Tool/Launchd.plist")
20 |
21 | let infoURL = derivedFileDir.appendingPathComponent("Info.plist")
22 | let launchdURL = derivedFileDir.appendingPathComponent("Launchd.plist")
23 |
24 | var info = NSDictionary(contentsOf: infoSrcURL) as! [String: Any]
25 | var launchd = NSDictionary(contentsOf: launchdSrcURL) as! [String: Any]
26 |
27 | info[kCFBundleVersionKey as String] = env["CURRENT_PROJECT_VERSION"]!
28 | info[kCFBundleIdentifierKey as String] = "\(helperID)"
29 | info["SMAuthorizedClients"] = ["identifier \"\(xpcServiceID)\" and \(env["CS_REQUIREMENT"]!)"]
30 |
31 | launchd["Label"] = helperID
32 | launchd["MachServices"] = [helperID: true]
33 |
34 | (info as NSDictionary).write(to: infoURL, atomically: true)
35 | (launchd as NSDictionary).write(to: launchdURL, atomically: true)
36 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Helper Tool/HelperConnection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HelperConnection.swift
3 | // Example Helper Tool
4 | //
5 | // Created by Charles Srstka on 4/15/20.
6 | // Copyright © 2020-2021 Charles Srstka. All rights reserved.
7 | //
8 |
9 | import CSAuthSampleHelper
10 | import Foundation
11 |
12 | class HelperConnection: CSAuthSampleHelper.HelperConnection, HelperToolProtocol {
13 | func sayHello(authorizationData: Data, message: String, reply: @escaping (String?, Error?) -> ()) {
14 | if let error = self.checkAuthorization(authorizationData) {
15 | reply(nil, error)
16 | return
17 | }
18 |
19 | let replyMessage = """
20 | Received message from app: “\(message)”
21 | Sending reply to app: “Hello app! My UID is \(getuid()) and my GID is \(getgid())!
22 | """
23 |
24 | reply(replyMessage, nil)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Helper Tool/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleName
6 | CSAuthSample Example App Helper
7 | CFBundleShortVersionString
8 | 1.0
9 | CFBundleVersion
10 | (set by build script)
11 | CFBundleIdentifier
12 | (set by build script)
13 | SMAuthorizedClients
14 |
15 | (set by build script)
16 |
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 |
20 |
21 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Helper Tool/Launchd.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Label
6 | (set by build script)
7 | MachServices
8 | (set by build script)
9 |
10 |
11 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/Helper Tool/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // CSAuthSample Example Helper Tool
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | //
7 |
8 | import CSAuthSampleHelper
9 | import Foundation
10 |
11 | let helperTool = HelperTool(
12 | helperID: Identifiers.helperID,
13 | commandSet: exampleCommandSet,
14 | senderRequirements: nil,
15 | connectionClass: HelperConnection.self,
16 | interface: NSXPCInterface(with: HelperToolProtocol.self)
17 | )
18 |
19 | helperTool.run()
20 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/XPC Service/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Example App XPC Service
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSHumanReadableCopyright
24 | Free for public use
25 | SMPrivilegedExecutables
26 |
27 | com.charlessoft.CSAuthSample-Example.helper
28 | identifier "$(HELPER_ID)" and $(CS_REQUIREMENT)
29 |
30 | XPCService
31 |
32 | JoinExistingSession
33 |
34 | ServiceType
35 | Application
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/XPC Service/ServiceDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ServiceDelegate.swift
3 | // Example XPC Service
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | // Copyright © 2020-2021 Charles Srstka. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ServiceDelegate: NSObject, NSXPCListenerDelegate {
12 | func listener(_ listener: NSXPCListener, shouldAcceptNewConnection connection: NSXPCConnection) -> Bool {
13 | // This method is where the NSXPCListener configures, accepts, and resumes a new incoming NSXPCConnection.
14 |
15 | // Configure the connection.
16 | // First, set the interface that the exported object implements.
17 | connection.exportedInterface = NSXPCInterface(with: XPCServiceProtocol.self)
18 |
19 | // Next, set the object that the connection exports. All messages sent on the connection to
20 | // this service will be sent to the exported object to handle. The connection retains the
21 | // exported object.
22 | let exportedObject = XPCService()
23 | connection.exportedObject = exportedObject
24 |
25 | // Resuming the connection allows the system to deliver more incoming messages.
26 | connection.resume()
27 |
28 | // Returning true from this method tells the system that you have accepted this connection.
29 | // If you want to reject the connection for some reason, call invalidate() on the connection
30 | // and return false.
31 | return true
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/XPC Service/XPCService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XPCService.swift
3 | // Example App XPC Service
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | //
7 |
8 | import CSAuthSampleApp
9 | import Foundation
10 |
11 | // This object implements the protocol which we have defined. It provides the actual behavior for the service.
12 | // It is 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection.
13 | class XPCService: NSObject, XPCServiceProtocol {
14 | private static let bundle = Bundle(for: XPCService.self)
15 | private static let bundleVersion = XPCService.bundle.infoDictionary![kCFBundleVersionKey as String] as! String
16 |
17 | func sayHello(message: String, reply: @escaping (String?, Error?) -> Void) {
18 | self.connectToHelperTool {
19 | switch $0 {
20 | case .success(let (proxy, authData)):
21 | proxy.sayHello(authorizationData: authData, message: message, reply: reply)
22 | case .failure(let error):
23 | reply(nil, error)
24 | }
25 | }
26 | }
27 |
28 | private func connectToHelperTool(closure: @escaping (Result<(HelperToolProtocol, Data), Error>) -> ()) {
29 | let client: HelperClient
30 | let authData: Data
31 |
32 | do {
33 | client = try self.getClient()
34 | authData = try client.authorizationData()
35 | } catch {
36 | closure(.failure(error))
37 | return
38 | }
39 |
40 | client.connectToHelperTool(
41 | helperID: Identifiers.helperID,
42 | protocol: HelperToolProtocol.self,
43 | expectedVersion: XPCService.bundleVersion,
44 | errorHandler: { closure(.failure($0)) },
45 | connectionHandler: { closure(.success(($0, authData))) }
46 | )
47 | }
48 |
49 | private func getClient() throws -> HelperClient {
50 | try HelperClient(commandSet: exampleCommandSet, bundle: .main, tableName: "Prompts")
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/XPC Service/XPCServiceProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Example_App_XPC_ServiceProtocol.swift
3 | // Example App XPC Service
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | //
7 |
8 | import Foundation
9 |
10 | @objc protocol XPCServiceProtocol {
11 | func sayHello(message: String, reply: @escaping (String?, Error?) -> Void)
12 | }
13 |
--------------------------------------------------------------------------------
/CSAuthSample Example App/XPC Service/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // Example App XPC Service
4 | //
5 | // Created by Charles Srstka on 4/5/20.
6 | //
7 |
8 | import Foundation
9 |
10 | // Create the delegate for the service.
11 | let delegate = ServiceDelegate()
12 |
13 | // Set up the one NSXPCListener for this service. It will handle all incoming connections.
14 | let listener = NSXPCListener.service()
15 | listener.delegate = delegate
16 |
17 | // Resuming the serviceListener starts this service. This method does not return.
18 | listener.resume()
19 |
--------------------------------------------------------------------------------
/CSAuthSample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 281ACABD20E962740006B59B /* CSASHelperConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 281ACABA20E960A50006B59B /* CSASHelperConnection.m */; };
11 | 281ACABE20E9672B0006B59B /* CSASHelperConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 281ACAB920E960A50006B59B /* CSASHelperConnection.h */; };
12 | 281ACACF20E967D70006B59B /* CSASHelperConnection.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 281ACAB920E960A50006B59B /* CSASHelperConnection.h */; };
13 | 2835DB8F20E13DDA0006B59B /* HelperClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2835DB5220E13CFB0006B59B /* HelperClient.swift */; };
14 | 2835DB9120E13DF60006B59B /* CSASHelperTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 2835DB9020E13DF60006B59B /* CSASHelperTool.m */; };
15 | 2835DB9520E13E350006B59B /* CSASCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 2835DB9320E13E350006B59B /* CSASCommon.h */; };
16 | 2835DB9620E13E350006B59B /* CSASCommon.m in Sources */ = {isa = PBXBuildFile; fileRef = 2835DB9420E13E350006B59B /* CSASCommon.m */; };
17 | 2835DB9720E13E380006B59B /* CSASCommon.m in Sources */ = {isa = PBXBuildFile; fileRef = 2835DB9420E13E350006B59B /* CSASCommon.m */; };
18 | 2835DBC920E169C00006B59B /* CSASHelperTool.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2835DB9220E13E250006B59B /* CSASHelperTool.h */; };
19 | 2835DBCA20E169C00006B59B /* CSASCommon.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2835DB9320E13E350006B59B /* CSASCommon.h */; };
20 | 287DBE80214AF5370006B59B /* SandboxWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287DBE7E214AF0CE0006B59B /* SandboxWorkaround.swift */; };
21 | 28E085F6244CC842008BD2DC /* CSASHelperConnectionWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 28E085F4244CC842008BD2DC /* CSASHelperConnectionWrapper.m */; };
22 | 28E085F7244CC842008BD2DC /* CSASHelperConnectionWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 28E085F5244CC842008BD2DC /* CSASHelperConnectionWrapper.h */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXContainerItemProxy section */
26 | 28C77306214DAEFB0006B59B /* PBXContainerItemProxy */ = {
27 | isa = PBXContainerItemProxy;
28 | containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
29 | proxyType = 1;
30 | remoteGlobalIDString = 2835DB8520E13DD30006B59B;
31 | remoteInfo = "Helper Library";
32 | };
33 | /* End PBXContainerItemProxy section */
34 |
35 | /* Begin PBXCopyFilesBuildPhase section */
36 | 2835DBC820E169AC0006B59B /* CopyFiles */ = {
37 | isa = PBXCopyFilesBuildPhase;
38 | buildActionMask = 2147483647;
39 | dstPath = "include/$(PRODUCT_NAME)";
40 | dstSubfolderSpec = 16;
41 | files = (
42 | 281ACACF20E967D70006B59B /* CSASHelperConnection.h in CopyFiles */,
43 | 2835DBC920E169C00006B59B /* CSASHelperTool.h in CopyFiles */,
44 | 2835DBCA20E169C00006B59B /* CSASCommon.h in CopyFiles */,
45 | );
46 | runOnlyForDeploymentPostprocessing = 0;
47 | };
48 | /* End PBXCopyFilesBuildPhase section */
49 |
50 | /* Begin PBXFileReference section */
51 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; };
52 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; };
53 | 2805E9EF243A48D8008BD2DC /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; };
54 | 2805E9F1243A4F11008BD2DC /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; };
55 | 281ACAB920E960A50006B59B /* CSASHelperConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSASHelperConnection.h; sourceTree = ""; };
56 | 281ACABA20E960A50006B59B /* CSASHelperConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSASHelperConnection.m; sourceTree = ""; };
57 | 2835DB5220E13CFB0006B59B /* HelperClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperClient.swift; sourceTree = ""; };
58 | 2835DB5820E13D720006B59B /* libCSAuthSampleApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCSAuthSampleApp.a; sourceTree = BUILT_PRODUCTS_DIR; };
59 | 2835DB8620E13DD30006B59B /* libCSAuthSampleHelper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCSAuthSampleHelper.a; sourceTree = BUILT_PRODUCTS_DIR; };
60 | 2835DB9020E13DF60006B59B /* CSASHelperTool.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSASHelperTool.m; sourceTree = ""; };
61 | 2835DB9220E13E250006B59B /* CSASHelperTool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSASHelperTool.h; sourceTree = ""; };
62 | 2835DB9320E13E350006B59B /* CSASCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSASCommon.h; sourceTree = ""; };
63 | 2835DB9420E13E350006B59B /* CSASCommon.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSASCommon.m; sourceTree = ""; };
64 | 2835DBC420E165A70006B59B /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; };
65 | 287DBE7E214AF0CE0006B59B /* SandboxWorkaround.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SandboxWorkaround.swift; sourceTree = ""; };
66 | 289E508420F50B6F0006B59B /* CSASHelperToolInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSASHelperToolInternal.h; sourceTree = ""; };
67 | 28A6F66B16918ECD006F0A93 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = ../../../../System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; };
68 | 28C7733F214DB4CE0006B59B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
69 | 28E085F4244CC842008BD2DC /* CSASHelperConnectionWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSASHelperConnectionWrapper.m; sourceTree = ""; };
70 | 28E085F5244CC842008BD2DC /* CSASHelperConnectionWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSASHelperConnectionWrapper.h; sourceTree = ""; };
71 | 28E085F8244CD5EB008BD2DC /* CSASHelperConnectionInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSASHelperConnectionInternal.h; sourceTree = ""; };
72 | 28F16E4C25FEEB1C00370D24 /* .swift-format */ = {isa = PBXFileReference; lastKnownFileType = text; path = ".swift-format"; sourceTree = ""; };
73 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; };
74 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; };
75 | 4BE4906310445F2F006BE471 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
76 | 4BE4906710445F36006BE471 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
77 | /* End PBXFileReference section */
78 |
79 | /* Begin PBXFrameworksBuildPhase section */
80 | 2835DB5620E13D720006B59B /* Frameworks */ = {
81 | isa = PBXFrameworksBuildPhase;
82 | buildActionMask = 2147483647;
83 | files = (
84 | );
85 | runOnlyForDeploymentPostprocessing = 0;
86 | };
87 | 2835DB8420E13DD30006B59B /* Frameworks */ = {
88 | isa = PBXFrameworksBuildPhase;
89 | buildActionMask = 2147483647;
90 | files = (
91 | );
92 | runOnlyForDeploymentPostprocessing = 0;
93 | };
94 | /* End PBXFrameworksBuildPhase section */
95 |
96 | /* Begin PBXGroup section */
97 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
98 | isa = PBXGroup;
99 | children = (
100 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
101 | 28A6F66B16918ECD006F0A93 /* CoreFoundation.framework */,
102 | 4BE4906310445F2F006BE471 /* Security.framework */,
103 | 4BE4906710445F36006BE471 /* ServiceManagement.framework */,
104 | );
105 | name = "Linked Frameworks";
106 | sourceTree = "";
107 | };
108 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
112 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
113 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
114 | );
115 | name = "Other Frameworks";
116 | sourceTree = "";
117 | };
118 | 19C28FACFE9D520D11CA2CBB /* Products */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 2835DB5820E13D720006B59B /* libCSAuthSampleApp.a */,
122 | 2835DB8620E13DD30006B59B /* libCSAuthSampleHelper.a */,
123 | );
124 | name = Products;
125 | sourceTree = "";
126 | };
127 | 2805E9EB243A4829008BD2DC /* Sources */ = {
128 | isa = PBXGroup;
129 | children = (
130 | 2805E9EC243A4831008BD2DC /* CSAuthSampleApp */,
131 | 2805E9ED243A483D008BD2DC /* CSAuthSampleHelper */,
132 | 2805E9EE243A4852008BD2DC /* CSAuthSampleCommon */,
133 | );
134 | path = Sources;
135 | sourceTree = "";
136 | };
137 | 2805E9EC243A4831008BD2DC /* CSAuthSampleApp */ = {
138 | isa = PBXGroup;
139 | children = (
140 | 2835DB5220E13CFB0006B59B /* HelperClient.swift */,
141 | 287DBE7E214AF0CE0006B59B /* SandboxWorkaround.swift */,
142 | );
143 | path = CSAuthSampleApp;
144 | sourceTree = "";
145 | };
146 | 2805E9ED243A483D008BD2DC /* CSAuthSampleHelper */ = {
147 | isa = PBXGroup;
148 | children = (
149 | 2805E9F2243A5061008BD2DC /* include */,
150 | 289E508420F50B6F0006B59B /* CSASHelperToolInternal.h */,
151 | 2835DB9020E13DF60006B59B /* CSASHelperTool.m */,
152 | 28E085F8244CD5EB008BD2DC /* CSASHelperConnectionInternal.h */,
153 | 281ACABA20E960A50006B59B /* CSASHelperConnection.m */,
154 | 28E085F5244CC842008BD2DC /* CSASHelperConnectionWrapper.h */,
155 | 28E085F4244CC842008BD2DC /* CSASHelperConnectionWrapper.m */,
156 | );
157 | path = CSAuthSampleHelper;
158 | sourceTree = "";
159 | };
160 | 2805E9EE243A4852008BD2DC /* CSAuthSampleCommon */ = {
161 | isa = PBXGroup;
162 | children = (
163 | 2805E9F3243A5085008BD2DC /* include */,
164 | 2835DB9420E13E350006B59B /* CSASCommon.m */,
165 | );
166 | path = CSAuthSampleCommon;
167 | sourceTree = "";
168 | };
169 | 2805E9F2243A5061008BD2DC /* include */ = {
170 | isa = PBXGroup;
171 | children = (
172 | 2835DBC420E165A70006B59B /* module.modulemap */,
173 | 2835DB9220E13E250006B59B /* CSASHelperTool.h */,
174 | 281ACAB920E960A50006B59B /* CSASHelperConnection.h */,
175 | );
176 | path = include;
177 | sourceTree = "";
178 | };
179 | 2805E9F3243A5085008BD2DC /* include */ = {
180 | isa = PBXGroup;
181 | children = (
182 | 2805E9EF243A48D8008BD2DC /* module.modulemap */,
183 | 2835DB9320E13E350006B59B /* CSASCommon.h */,
184 | );
185 | path = include;
186 | sourceTree = "";
187 | };
188 | 29B97314FDCFA39411CA2CEA /* SMJobBless */ = {
189 | isa = PBXGroup;
190 | children = (
191 | 28C7733F214DB4CE0006B59B /* README.md */,
192 | 2805E9F1243A4F11008BD2DC /* Package.swift */,
193 | 28F16E4C25FEEB1C00370D24 /* .swift-format */,
194 | 2805E9EB243A4829008BD2DC /* Sources */,
195 | 29B97323FDCFA39411CA2CEA /* Frameworks */,
196 | 19C28FACFE9D520D11CA2CBB /* Products */,
197 | );
198 | name = SMJobBless;
199 | sourceTree = "";
200 | };
201 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
202 | isa = PBXGroup;
203 | children = (
204 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
205 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
206 | );
207 | name = Frameworks;
208 | sourceTree = "";
209 | };
210 | /* End PBXGroup section */
211 |
212 | /* Begin PBXHeadersBuildPhase section */
213 | 2835DB5420E13D720006B59B /* Headers */ = {
214 | isa = PBXHeadersBuildPhase;
215 | buildActionMask = 2147483647;
216 | files = (
217 | );
218 | runOnlyForDeploymentPostprocessing = 0;
219 | };
220 | 2835DB8220E13DD30006B59B /* Headers */ = {
221 | isa = PBXHeadersBuildPhase;
222 | buildActionMask = 2147483647;
223 | files = (
224 | 281ACABE20E9672B0006B59B /* CSASHelperConnection.h in Headers */,
225 | 28E085F7244CC842008BD2DC /* CSASHelperConnectionWrapper.h in Headers */,
226 | 2835DB9520E13E350006B59B /* CSASCommon.h in Headers */,
227 | );
228 | runOnlyForDeploymentPostprocessing = 0;
229 | };
230 | /* End PBXHeadersBuildPhase section */
231 |
232 | /* Begin PBXNativeTarget section */
233 | 2835DB5720E13D720006B59B /* App Library */ = {
234 | isa = PBXNativeTarget;
235 | buildConfigurationList = 2835DB5E20E13D720006B59B /* Build configuration list for PBXNativeTarget "App Library" */;
236 | buildPhases = (
237 | 2835DB5420E13D720006B59B /* Headers */,
238 | 2835DB5520E13D720006B59B /* Sources */,
239 | 2835DB5620E13D720006B59B /* Frameworks */,
240 | );
241 | buildRules = (
242 | );
243 | dependencies = (
244 | 28C77307214DAEFB0006B59B /* PBXTargetDependency */,
245 | );
246 | name = "App Library";
247 | productName = "App Library (ObjC Helper)";
248 | productReference = 2835DB5820E13D720006B59B /* libCSAuthSampleApp.a */;
249 | productType = "com.apple.product-type.library.static";
250 | };
251 | 2835DB8520E13DD30006B59B /* Helper Library */ = {
252 | isa = PBXNativeTarget;
253 | buildConfigurationList = 2835DB8C20E13DD30006B59B /* Build configuration list for PBXNativeTarget "Helper Library" */;
254 | buildPhases = (
255 | 2835DB8220E13DD30006B59B /* Headers */,
256 | 2835DBC820E169AC0006B59B /* CopyFiles */,
257 | 2835DB8320E13DD30006B59B /* Sources */,
258 | 2835DB8420E13DD30006B59B /* Frameworks */,
259 | );
260 | buildRules = (
261 | );
262 | dependencies = (
263 | );
264 | name = "Helper Library";
265 | productName = "ObjC Helper Library";
266 | productReference = 2835DB8620E13DD30006B59B /* libCSAuthSampleHelper.a */;
267 | productType = "com.apple.product-type.library.static";
268 | };
269 | /* End PBXNativeTarget section */
270 |
271 | /* Begin PBXProject section */
272 | 29B97313FDCFA39411CA2CEA /* Project object */ = {
273 | isa = PBXProject;
274 | attributes = {
275 | LastUpgradeCheck = 1400;
276 | TargetAttributes = {
277 | 2835DB5720E13D720006B59B = {
278 | CreatedOnToolsVersion = 10.0;
279 | LastSwiftMigration = 1020;
280 | };
281 | 2835DB8520E13DD30006B59B = {
282 | CreatedOnToolsVersion = 10.0;
283 | };
284 | };
285 | };
286 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CSAuthSample" */;
287 | compatibilityVersion = "Xcode 9.3";
288 | developmentRegion = en;
289 | hasScannedForEncodings = 1;
290 | knownRegions = (
291 | en,
292 | Base,
293 | );
294 | mainGroup = 29B97314FDCFA39411CA2CEA /* SMJobBless */;
295 | packageReferences = (
296 | );
297 | projectDirPath = "";
298 | projectRoot = "";
299 | targets = (
300 | 2835DB5720E13D720006B59B /* App Library */,
301 | 2835DB8520E13DD30006B59B /* Helper Library */,
302 | );
303 | };
304 | /* End PBXProject section */
305 |
306 | /* Begin PBXSourcesBuildPhase section */
307 | 2835DB5520E13D720006B59B /* Sources */ = {
308 | isa = PBXSourcesBuildPhase;
309 | buildActionMask = 2147483647;
310 | files = (
311 | 2835DB9720E13E380006B59B /* CSASCommon.m in Sources */,
312 | 287DBE80214AF5370006B59B /* SandboxWorkaround.swift in Sources */,
313 | 2835DB8F20E13DDA0006B59B /* HelperClient.swift in Sources */,
314 | );
315 | runOnlyForDeploymentPostprocessing = 0;
316 | };
317 | 2835DB8320E13DD30006B59B /* Sources */ = {
318 | isa = PBXSourcesBuildPhase;
319 | buildActionMask = 2147483647;
320 | files = (
321 | 281ACABD20E962740006B59B /* CSASHelperConnection.m in Sources */,
322 | 2835DB9620E13E350006B59B /* CSASCommon.m in Sources */,
323 | 2835DB9120E13DF60006B59B /* CSASHelperTool.m in Sources */,
324 | 28E085F6244CC842008BD2DC /* CSASHelperConnectionWrapper.m in Sources */,
325 | );
326 | runOnlyForDeploymentPostprocessing = 0;
327 | };
328 | /* End PBXSourcesBuildPhase section */
329 |
330 | /* Begin PBXTargetDependency section */
331 | 28C77307214DAEFB0006B59B /* PBXTargetDependency */ = {
332 | isa = PBXTargetDependency;
333 | target = 2835DB8520E13DD30006B59B /* Helper Library */;
334 | targetProxy = 28C77306214DAEFB0006B59B /* PBXContainerItemProxy */;
335 | };
336 | /* End PBXTargetDependency section */
337 |
338 | /* Begin XCBuildConfiguration section */
339 | 2835DB5F20E13D720006B59B /* Debug */ = {
340 | isa = XCBuildConfiguration;
341 | buildSettings = {
342 | ALWAYS_SEARCH_USER_PATHS = NO;
343 | CLANG_ANALYZER_NONNULL = YES;
344 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
345 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
346 | CLANG_CXX_LIBRARY = "libc++";
347 | CLANG_ENABLE_MODULES = YES;
348 | CLANG_ENABLE_OBJC_ARC = YES;
349 | CLANG_ENABLE_OBJC_WEAK = YES;
350 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
351 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
352 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
353 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
354 | CODE_SIGN_IDENTITY = "Apple Development";
355 | CODE_SIGN_STYLE = Automatic;
356 | COPY_PHASE_STRIP = NO;
357 | DEAD_CODE_STRIPPING = YES;
358 | DEBUG_INFORMATION_FORMAT = dwarf;
359 | DEFINES_MODULE = YES;
360 | EXECUTABLE_PREFIX = lib;
361 | GCC_C_LANGUAGE_STANDARD = gnu11;
362 | GCC_DYNAMIC_NO_PIC = NO;
363 | GCC_PREPROCESSOR_DEFINITIONS = (
364 | "DEBUG=1",
365 | "$(inherited)",
366 | );
367 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
368 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
369 | HEADER_SEARCH_PATHS = $SRCROOT/Sources/CSAuthSampleCommon/include;
370 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
371 | PRODUCT_MODULE_NAME = CSAuthSampleApp;
372 | PRODUCT_NAME = CSAuthSampleApp;
373 | SDKROOT = macosx;
374 | SKIP_INSTALL = YES;
375 | SWIFT_INCLUDE_PATHS = $SRCROOT/Sources/Common;
376 | SWIFT_VERSION = 5.0;
377 | USER_HEADER_SEARCH_PATHS = (
378 | $TARGET_BUILD_DIR/include/CSAuthSampleHelper,
379 | $SRCROOT/ObjCHelper/Internal,
380 | );
381 | };
382 | name = Debug;
383 | };
384 | 2835DB6020E13D720006B59B /* Release */ = {
385 | isa = XCBuildConfiguration;
386 | buildSettings = {
387 | ALWAYS_SEARCH_USER_PATHS = NO;
388 | CLANG_ANALYZER_NONNULL = YES;
389 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
390 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
391 | CLANG_CXX_LIBRARY = "libc++";
392 | CLANG_ENABLE_MODULES = YES;
393 | CLANG_ENABLE_OBJC_ARC = YES;
394 | CLANG_ENABLE_OBJC_WEAK = YES;
395 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
396 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
397 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
398 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
399 | CODE_SIGN_IDENTITY = "Apple Development";
400 | CODE_SIGN_STYLE = Automatic;
401 | COPY_PHASE_STRIP = NO;
402 | DEAD_CODE_STRIPPING = YES;
403 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
404 | DEFINES_MODULE = YES;
405 | ENABLE_NS_ASSERTIONS = NO;
406 | EXECUTABLE_PREFIX = lib;
407 | GCC_C_LANGUAGE_STANDARD = gnu11;
408 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
409 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
410 | HEADER_SEARCH_PATHS = $SRCROOT/Sources/CSAuthSampleCommon/include;
411 | MTL_ENABLE_DEBUG_INFO = NO;
412 | PRODUCT_MODULE_NAME = CSAuthSampleApp;
413 | PRODUCT_NAME = CSAuthSampleApp;
414 | SDKROOT = macosx;
415 | SKIP_INSTALL = YES;
416 | SWIFT_INCLUDE_PATHS = $SRCROOT/Sources/Common;
417 | SWIFT_VERSION = 5.0;
418 | USER_HEADER_SEARCH_PATHS = (
419 | $TARGET_BUILD_DIR/include/CSAuthSampleHelper,
420 | $SRCROOT/ObjCHelper/Internal,
421 | );
422 | };
423 | name = Release;
424 | };
425 | 2835DB8D20E13DD30006B59B /* Debug */ = {
426 | isa = XCBuildConfiguration;
427 | buildSettings = {
428 | ALWAYS_SEARCH_USER_PATHS = NO;
429 | CLANG_ANALYZER_NONNULL = YES;
430 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
431 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
432 | CLANG_CXX_LIBRARY = "libc++";
433 | CLANG_ENABLE_MODULES = YES;
434 | CLANG_ENABLE_OBJC_ARC = YES;
435 | CLANG_ENABLE_OBJC_WEAK = YES;
436 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
437 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
438 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
439 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
440 | CODE_SIGN_IDENTITY = "-";
441 | CODE_SIGN_STYLE = Automatic;
442 | COPY_PHASE_STRIP = NO;
443 | DEAD_CODE_STRIPPING = YES;
444 | DEBUG_INFORMATION_FORMAT = dwarf;
445 | DEFINES_MODULE = YES;
446 | EXECUTABLE_PREFIX = lib;
447 | GCC_C_LANGUAGE_STANDARD = gnu11;
448 | GCC_DYNAMIC_NO_PIC = NO;
449 | GCC_PREPROCESSOR_DEFINITIONS = (
450 | "DEBUG=1",
451 | "$(inherited)",
452 | );
453 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
454 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
455 | HEADER_SEARCH_PATHS = (
456 | $SRCROOT/Sources/CSAuthSampleCommon/include,
457 | $SRCROOT/Sources/CSAuthSampleHelper/include,
458 | );
459 | MODULEMAP_FILE = $SRCROOT/Sources/CSAuthSampleHelper/include/module.modulemap;
460 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
461 | PRODUCT_MODULE_NAME = CSAuthSampleHelper;
462 | PRODUCT_NAME = CSAuthSampleHelper;
463 | SDKROOT = macosx;
464 | SKIP_INSTALL = YES;
465 | };
466 | name = Debug;
467 | };
468 | 2835DB8E20E13DD30006B59B /* Release */ = {
469 | isa = XCBuildConfiguration;
470 | buildSettings = {
471 | ALWAYS_SEARCH_USER_PATHS = NO;
472 | CLANG_ANALYZER_NONNULL = YES;
473 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
474 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
475 | CLANG_CXX_LIBRARY = "libc++";
476 | CLANG_ENABLE_MODULES = YES;
477 | CLANG_ENABLE_OBJC_ARC = YES;
478 | CLANG_ENABLE_OBJC_WEAK = YES;
479 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
480 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
481 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
482 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
483 | CODE_SIGN_IDENTITY = "-";
484 | CODE_SIGN_STYLE = Automatic;
485 | COPY_PHASE_STRIP = NO;
486 | DEAD_CODE_STRIPPING = YES;
487 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
488 | DEFINES_MODULE = YES;
489 | ENABLE_NS_ASSERTIONS = NO;
490 | EXECUTABLE_PREFIX = lib;
491 | GCC_C_LANGUAGE_STANDARD = gnu11;
492 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
493 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
494 | HEADER_SEARCH_PATHS = (
495 | $SRCROOT/Sources/CSAuthSampleCommon/include,
496 | $SRCROOT/Sources/CSAuthSampleHelper/include,
497 | );
498 | MODULEMAP_FILE = $SRCROOT/Sources/CSAuthSampleHelper/include/module.modulemap;
499 | MTL_ENABLE_DEBUG_INFO = NO;
500 | PRODUCT_MODULE_NAME = CSAuthSampleHelper;
501 | PRODUCT_NAME = CSAuthSampleHelper;
502 | SDKROOT = macosx;
503 | SKIP_INSTALL = YES;
504 | };
505 | name = Release;
506 | };
507 | C01FCF4F08A954540054247B /* Debug */ = {
508 | isa = XCBuildConfiguration;
509 | buildSettings = {
510 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
511 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
512 | CLANG_WARN_BOOL_CONVERSION = YES;
513 | CLANG_WARN_COMMA = YES;
514 | CLANG_WARN_CONSTANT_CONVERSION = YES;
515 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
516 | CLANG_WARN_EMPTY_BODY = YES;
517 | CLANG_WARN_ENUM_CONVERSION = YES;
518 | CLANG_WARN_INFINITE_RECURSION = YES;
519 | CLANG_WARN_INT_CONVERSION = YES;
520 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
521 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
522 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
523 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
524 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
525 | CLANG_WARN_STRICT_PROTOTYPES = YES;
526 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
527 | CLANG_WARN_UNREACHABLE_CODE = YES;
528 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
529 | CODE_SIGN_IDENTITY = "Mac Developer";
530 | DEAD_CODE_STRIPPING = YES;
531 | ENABLE_STRICT_OBJC_MSGSEND = YES;
532 | ENABLE_TESTABILITY = YES;
533 | GCC_C_LANGUAGE_STANDARD = gnu99;
534 | GCC_NO_COMMON_BLOCKS = YES;
535 | GCC_OPTIMIZATION_LEVEL = 0;
536 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
537 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
538 | GCC_WARN_UNDECLARED_SELECTOR = YES;
539 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
540 | GCC_WARN_UNUSED_FUNCTION = YES;
541 | GCC_WARN_UNUSED_VARIABLE = YES;
542 | MACOSX_DEPLOYMENT_TARGET = 10.13;
543 | ONLY_ACTIVE_ARCH = YES;
544 | PROVISIONING_PROFILE = "";
545 | SKIP_INSTALL = YES;
546 | SWIFT_COMPILATION_MODE = singlefile;
547 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
548 | SWIFT_VERSION = 4.2;
549 | WARNING_CFLAGS = (
550 | "-Weverything",
551 | "-Werror",
552 | "-Wno-objc-missing-property-synthesis",
553 | "-Wno-pedantic",
554 | "-Wno-documentation",
555 | "-Wno-direct-ivar-access",
556 | "-Wno-assign-enum",
557 | "-Wno-cast-qual",
558 | );
559 | };
560 | name = Debug;
561 | };
562 | C01FCF5008A954540054247B /* Release */ = {
563 | isa = XCBuildConfiguration;
564 | buildSettings = {
565 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
566 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
567 | CLANG_WARN_BOOL_CONVERSION = YES;
568 | CLANG_WARN_COMMA = YES;
569 | CLANG_WARN_CONSTANT_CONVERSION = YES;
570 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
571 | CLANG_WARN_EMPTY_BODY = YES;
572 | CLANG_WARN_ENUM_CONVERSION = YES;
573 | CLANG_WARN_INFINITE_RECURSION = YES;
574 | CLANG_WARN_INT_CONVERSION = YES;
575 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
576 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
577 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
578 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
579 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
580 | CLANG_WARN_STRICT_PROTOTYPES = YES;
581 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
582 | CLANG_WARN_UNREACHABLE_CODE = YES;
583 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
584 | CODE_SIGN_IDENTITY = "Mac Developer";
585 | DEAD_CODE_STRIPPING = YES;
586 | ENABLE_STRICT_OBJC_MSGSEND = YES;
587 | GCC_C_LANGUAGE_STANDARD = gnu99;
588 | GCC_NO_COMMON_BLOCKS = YES;
589 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
590 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
591 | GCC_WARN_UNDECLARED_SELECTOR = YES;
592 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
593 | GCC_WARN_UNUSED_FUNCTION = YES;
594 | GCC_WARN_UNUSED_VARIABLE = YES;
595 | MACOSX_DEPLOYMENT_TARGET = 10.13;
596 | PROVISIONING_PROFILE = "";
597 | SKIP_INSTALL = YES;
598 | SWIFT_COMPILATION_MODE = wholemodule;
599 | SWIFT_OPTIMIZATION_LEVEL = "-O";
600 | SWIFT_VERSION = 4.2;
601 | WARNING_CFLAGS = (
602 | "-Weverything",
603 | "-Werror",
604 | "-Wno-objc-missing-property-synthesis",
605 | "-Wno-pedantic",
606 | "-Wno-documentation",
607 | "-Wno-direct-ivar-access",
608 | "-Wno-assign-enum",
609 | "-Wno-cast-qual",
610 | );
611 | };
612 | name = Release;
613 | };
614 | /* End XCBuildConfiguration section */
615 |
616 | /* Begin XCConfigurationList section */
617 | 2835DB5E20E13D720006B59B /* Build configuration list for PBXNativeTarget "App Library" */ = {
618 | isa = XCConfigurationList;
619 | buildConfigurations = (
620 | 2835DB5F20E13D720006B59B /* Debug */,
621 | 2835DB6020E13D720006B59B /* Release */,
622 | );
623 | defaultConfigurationIsVisible = 0;
624 | defaultConfigurationName = Release;
625 | };
626 | 2835DB8C20E13DD30006B59B /* Build configuration list for PBXNativeTarget "Helper Library" */ = {
627 | isa = XCConfigurationList;
628 | buildConfigurations = (
629 | 2835DB8D20E13DD30006B59B /* Debug */,
630 | 2835DB8E20E13DD30006B59B /* Release */,
631 | );
632 | defaultConfigurationIsVisible = 0;
633 | defaultConfigurationName = Release;
634 | };
635 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CSAuthSample" */ = {
636 | isa = XCConfigurationList;
637 | buildConfigurations = (
638 | C01FCF4F08A954540054247B /* Debug */,
639 | C01FCF5008A954540054247B /* Release */,
640 | );
641 | defaultConfigurationIsVisible = 0;
642 | defaultConfigurationName = Release;
643 | };
644 | /* End XCConfigurationList section */
645 | };
646 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
647 | }
648 |
--------------------------------------------------------------------------------
/CSAuthSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CSAuthSample.xcodeproj/project.xcworkspace/xcshareddata/CSAuthSample.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | 234C1A16-0DB1-439A-9BA3-B1C9DCBC43ED
9 | IDESourceControlProjectName
10 | CSAuthSample
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | E933AC01F0A8FC2C42BCF1CAB754F165E11CE206
14 | https://github.com/atnan/SMJobBlessXPC.git
15 |
16 | IDESourceControlProjectPath
17 | CSAuthSample.xcodeproj
18 | IDESourceControlProjectRelativeInstallPathDictionary
19 |
20 | E933AC01F0A8FC2C42BCF1CAB754F165E11CE206
21 | ../..
22 |
23 | IDESourceControlProjectURL
24 | https://github.com/atnan/SMJobBlessXPC.git
25 | IDESourceControlProjectVersion
26 | 111
27 | IDESourceControlProjectWCCIdentifier
28 | E933AC01F0A8FC2C42BCF1CAB754F165E11CE206
29 | IDESourceControlProjectWCConfigurations
30 |
31 |
32 | IDESourceControlRepositoryExtensionIdentifierKey
33 | public.vcs.git
34 | IDESourceControlWCCIdentifierKey
35 | E933AC01F0A8FC2C42BCF1CAB754F165E11CE206
36 | IDESourceControlWCCName
37 | CSAuthSample
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/CSAuthSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Copyright © 2013-2021 Charles J. Srstka
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.2
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "CSAuthSample",
8 | platforms: [
9 | .macOS(.v10_13)
10 | ],
11 | products: [
12 | .library(
13 | name: "CSAuthSampleApp",
14 | targets: ["CSAuthSampleApp"]
15 | ),
16 | .library(
17 | name: "CSAuthSampleHelper",
18 | targets: ["CSAuthSampleHelper"]
19 | ),
20 | .library(
21 | name: "CSAuthSampleCommon",
22 | targets: ["CSAuthSampleCommon"]
23 | ),
24 | ],
25 | dependencies: [],
26 | targets: [
27 | .target(
28 | name: "CSAuthSampleApp",
29 | dependencies: ["CSAuthSampleCommon"]
30 | ),
31 | .target(
32 | name: "CSAuthSampleHelper",
33 | dependencies: ["CSAuthSampleCommon"]
34 | ),
35 | .target(
36 | name: "CSAuthSampleCommon",
37 | dependencies: []
38 | ),
39 | ]
40 | )
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CSAuthSample
2 |
3 | This is a set of libraries that will assist in writing privileged helper tools for macOS applications.
4 | It is intended to be a little more up to date and easier to use than Apple's aging [EvenBetterAuthorizationSample](https://developer.apple.com/library/archive/samplecode/EvenBetterAuthorizationSample/Introduction/Intro.html).
5 | [Pacifist](https://www.charlessoft.com) has been using it for some time.
6 |
7 | CSAuthSample began as a port of Nathan de Vries’ [SMJobBlessXPC](https://github.com/atnan/SMJobBlessXPC), although it has been rewritten so many times that I doubt any of the original code remains.
8 | At some point it was rewritten around Apple’s [BetterAuthorizationSample](https://developer.apple.com/library/archive/samplecode/BetterAuthorizationSample/Introduction/Intro.html).
9 | More recently it has been rewritten around Apple's [EvenBetterAuthorizationSample](https://developer.apple.com/library/archive/samplecode/EvenBetterAuthorizationSample/Introduction/Intro.html).
10 | This latest rewrite uses NSXPCConnection, and thus requires the helper to be written in either Objective-C or Swift.
11 | For users that require the helper to be in straight C, the older code using libxpc is available in the 'c-helper' branch.
12 |
13 | The current code assumes the front-end application will be written in Swift.
14 | Either Objective-C or Swift can be used to write the helper tool.
15 | If you wish to use Swift for the full stack, a Swift package is available.
16 | For your convenience, there is an example app project included that will show you how to write a Swift-based helper app and corresponding client app.
17 |
18 | For those who like to be on the cutting edge, a Swift-only rewrite which uses Swift Concurrency is available under the `swift-concurrency` branch.
19 |
20 | CSAuthSample is free to use under the terms of the MIT license.
21 |
22 | Enjoy!
23 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleApp/HelperClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HelperClient.swift
3 | // App Library
4 | //
5 | // Created by Charles Srstka on 6/25/18.
6 | //
7 |
8 | import CSAuthSampleCommon
9 | import Foundation
10 | import ServiceManagement
11 |
12 | /// The primary class used by your application to communicate with your helper tool.
13 | ///
14 | /// To use, create an instance and use `connectToHelperTool` to send messages to the helper tool.
15 | public struct HelperClient {
16 | private let authRef: AuthorizationRef
17 |
18 | /// Create a `HelperClient` object.
19 | ///
20 | /// - Parameters:
21 | /// - authData: Authorization data, in the format of an `AuthorizationExternalForm`. If not provided, a new `AuthorizationRef` will be created.
22 | /// - commandSet: A `CommandSet` object describing the messages the helper accepts, and their required authorization levels.
23 | /// - bundle: A bundle containing a strings table containing localized messages to present to the user. Optional.
24 | /// - tableName: The name of a strings table containing localized messages to present to the user. Optional.
25 | /// - Throws: Any errors that occur in the process of creating the `HelperClient`'s internal `AuthorizationRef`.
26 | public init(
27 | authData: Data? = nil, commandSet: CommandSet, bundle: Bundle? = nil, tableName: String? = nil
28 | ) throws {
29 | if let data = authData {
30 | self.authRef = try data.withUnsafeBytes {
31 | guard let extForm = $0.bindMemory(to: AuthorizationExternalForm.self).baseAddress else {
32 | throw CocoaError(.fileReadUnknown)
33 | }
34 |
35 | var authRef: AuthorizationRef?
36 |
37 | let err = AuthorizationCreateFromExternalForm(extForm, &authRef)
38 | if err != errSecSuccess { throw ConvertOSStatus(err) }
39 |
40 | return try authRef ?? { throw CocoaError(.fileReadUnknown) }()
41 | }
42 | } else {
43 | var authRef: AuthorizationRef?
44 | let err = AuthorizationCreate(nil, nil, [], &authRef)
45 | if err != errSecSuccess { throw ConvertOSStatus(err) }
46 | self.authRef = try authRef ?? { throw CocoaError(.fileReadUnknown) }()
47 | }
48 |
49 | commandSet.setupAuthorizationRights(self.authRef, bundle: bundle, tableName: tableName)
50 | }
51 |
52 | /// Generate authorization data.
53 | ///
54 | /// It is generally recommended to send this to the helper tool, in order to establish the identity of the sender and prevent message spoofing.
55 | ///
56 | /// - Throws: Any error that occurs while generating the authorization data.
57 | /// - Returns: A `Data` object containing the authorization data, in the format used by `AuthorizationExternalForm`.
58 | public func authorizationData() throws -> Data {
59 | var authData = Data(count: MemoryLayout.size)
60 |
61 | try authData.withUnsafeMutableBytes {
62 | guard let ptr = $0.bindMemory(to: AuthorizationExternalForm.self).baseAddress else {
63 | throw CocoaError(.fileReadUnknown)
64 | }
65 |
66 | let err = AuthorizationMakeExternalForm(self.authRef, ptr)
67 |
68 | if err != errAuthorizationSuccess {
69 | throw ConvertOSStatus(err)
70 | }
71 | }
72 |
73 | return authData
74 | }
75 |
76 | /// Install the helper tool.
77 | ///
78 | /// - Parameters:
79 | /// - helperID: The bundle identifier of your helper tool, which should generally be distinct from your main application's bundle identifier.
80 | /// - completionHandler: Reports on the success or failure of the installation.
81 | public func installHelperTool(helperID: String, completionHandler: @escaping (Error?) -> Void) {
82 | do {
83 | try self.requestPrivileges([kSMRightBlessPrivilegedHelper], allowUserInteraction: true)
84 | } catch {
85 | completionHandler(error)
86 | return
87 | }
88 |
89 | self.uninstallHelperTool(helperID: helperID) { _ in
90 | self.blessHelperTool(helperID: helperID, completionHandler: completionHandler)
91 | }
92 | }
93 |
94 | /// Uninstall the helper tool.
95 | ///
96 | /// - Parameters:
97 | /// - helperID: The bundle identifier of your helper tool, which should generally be distinct from your main application's bundle identifier.
98 | /// - completionHandler: Reports on the success or failure of the uninstallation.
99 | public func uninstallHelperTool(helperID: String, completionHandler: @escaping (Error?) -> Void) {
100 | do {
101 | let authData = try self.authorizationData()
102 |
103 | let errorHandler: (Error) -> Void = { completionHandler($0) }
104 |
105 | let connectionHandler: (BuiltInCommands) -> Void = { proxy in
106 | proxy.uninstallHelperTool(authorizationData: authData) { uninstallResult in
107 | self.unblessHelperTool(helperID: helperID) {
108 | completionHandler(uninstallResult ?? $0)
109 | }
110 | }
111 | }
112 |
113 | self.connectToHelperTool(
114 | helperID: helperID,
115 | protocol: BuiltInCommands.self,
116 | installIfNecessary: false,
117 | errorHandler: errorHandler,
118 | connectionHandler: connectionHandler
119 | )
120 | } catch {
121 | completionHandler(error)
122 | }
123 | }
124 |
125 | /// Get the version of the helper tool.
126 | ///
127 | /// This is helpful for making sure that the application and helper tool are in sync with each other.
128 | /// If the helper's version does not match the app's version, it is generally a sign that the helper needs to be upgraded.
129 | ///
130 | /// - Parameters:
131 | /// - helperID: The bundle identifier of your helper tool, which should generally be distinct from your main application's bundle identifier.
132 | /// - completionHandler: If successful, returns the version of the helper tool.
133 | public func requestHelperVersion(
134 | helperID: String, completionHandler: @escaping (Result) -> Void
135 | ) {
136 | let conn: NSXPCConnection
137 |
138 | do {
139 | conn = try self._openConnection(
140 | helperID: helperID, interface: nil, protocol: BuiltInCommands.self)
141 | } catch {
142 | completionHandler(.failure(error))
143 | return
144 | }
145 |
146 | self.checkHelperVersion(connection: conn, completionHandler: completionHandler)
147 | }
148 |
149 | /// Send a message to the helper tool, and receive a notification on getting its reply.
150 | ///
151 | /// - Parameters:
152 | /// - helperID: The bundle identifier of your helper tool, which should generally be distinct from your main application's bundle identifier.
153 | /// - proto: A protocol describing the messages the helper tool accepts. Must conform to `BuiltInCommands`.
154 | /// - interface: An optional `NSXPCInterface` describing the helper's interface. If not provided, this will be generated from the `protocol`.
155 | /// - expectedVersion: The expected version of the helper. Optional.
156 | /// - installIfNecessary: Ignored unless `expectedVersion` is provided. If true, the helper tool will be installed if it is not present, or if its version does not match the expected version.
157 | /// - errorHandler: A closure which will be invoked in the event of an error occurring while communicating wth the helper tool.
158 | /// - connectionHandler: A closure which will be invoked upon establishing a successful connection to the helper tool.
159 | public func connectToHelperTool(
160 | helperID: String,
161 | protocol proto: P.Type,
162 | interface: NSXPCInterface? = nil,
163 | expectedVersion: String? = nil,
164 | installIfNecessary: Bool = true,
165 | errorHandler: @escaping (Error) -> Void,
166 | connectionHandler: @escaping (P) -> Void
167 | ) {
168 | let conn: NSXPCConnection
169 |
170 | do {
171 | conn = try self._openConnection(helperID: helperID, interface: interface, protocol: proto)
172 | } catch {
173 | errorHandler(error)
174 | return
175 | }
176 |
177 | if let expectedVersion = expectedVersion {
178 | self.checkHelperVersion(connection: conn) {
179 | switch $0 {
180 | case .success(let version) where version == expectedVersion:
181 | self._connectToHelperTool(
182 | connection: conn,
183 | protocol: proto,
184 | interface: interface,
185 | errorHandler: errorHandler,
186 | connectionHandler: connectionHandler)
187 | case .failure where installIfNecessary, .success where installIfNecessary:
188 | self._installAndConnect(
189 | helperID: helperID,
190 | protocol: proto,
191 | interface: interface,
192 | errorHandler: errorHandler,
193 | connectionHandler: connectionHandler)
194 | case .failure(let error) where !installIfNecessary:
195 | errorHandler(error)
196 | default:
197 | errorHandler(CocoaError(.fileReadUnknown))
198 | }
199 | }
200 | } else {
201 | self._connectToHelperTool(
202 | connection: conn,
203 | protocol: proto,
204 | interface: interface,
205 | errorHandler: errorHandler,
206 | connectionHandler: connectionHandler
207 | )
208 | }
209 | }
210 |
211 | /// Establish a connection via an `NSXPCListenerEndpoint` passed by the helper tool.
212 | ///
213 | /// This can sometimes be useful if more advanced communication between the app and the helper tool is needed.
214 | /// - Parameters:
215 | /// - endpoint: The `NSXPCListenerEndpoint` from which to establish a connection.
216 | /// - proto: A protocol describing the messages the helper tool accepts. Must conform to `BuiltInCommands`.
217 | /// - interface: An optional `NSXPCInterface` describing the helper's interface. If not provided, this will be generated from the `protocol`.
218 | /// - errorHandler: A closure which will be invoked in the event of an error occurring while communicating wth the helper tool.
219 | /// - Throws: Any errors that occur in the process of establishing communication with the helper tool.
220 | /// - Returns: An proxy object, conforming to `proto`, which can be used to send messages to the helper tool.
221 | public func connectViaEndpoint(
222 | _ endpoint: NSXPCListenerEndpoint,
223 | protocol proto: P.Type,
224 | interface: NSXPCInterface? = nil,
225 | errorHandler: @escaping (Error) -> Void
226 | ) throws -> P {
227 | let conn = NSXPCConnection(listenerEndpoint: endpoint)
228 |
229 | return try self.getProxy(conn, protocol: proto, interface: interface, errorHandler: errorHandler)
230 | }
231 |
232 | private func getProxy(
233 | _ conn: NSXPCConnection,
234 | protocol proto: P.Type,
235 | interface: NSXPCInterface?,
236 | errorHandler: @escaping (Error) -> Void
237 | ) throws -> P {
238 | let proxy = conn.remoteObjectProxyWithErrorHandler(errorHandler)
239 |
240 | return try proxy as? P ?? { throw CocoaError(.fileReadUnknown) }()
241 | }
242 |
243 | private func requestPrivileges(_ privs: [String], allowUserInteraction: Bool = true) throws {
244 | if privs.isEmpty {
245 | return
246 | }
247 |
248 | let items = UnsafeMutablePointer.allocate(capacity: privs.count)
249 |
250 | for (index, eachPriv) in privs.enumerated() {
251 | let name: UnsafePointer = eachPriv.withCString {
252 | let len = strlen($0) + 1
253 | let copy = UnsafeMutablePointer.allocate(capacity: len)
254 | copy.initialize(from: $0, count: len)
255 |
256 | return UnsafePointer(copy)
257 | }
258 |
259 | items[index] = AuthorizationItem(name: name, valueLength: 0, value: nil, flags: 0)
260 | }
261 |
262 | defer {
263 | for index in 0.. Void) {
286 | var smError: Unmanaged?
287 | if !SMJobBless(kSMDomainSystemLaunchd, helperID as CFString, self.authRef, &smError) {
288 | completionHandler(smError.map { ConvertCFError($0.takeRetainedValue()) } ?? CocoaError(.fileWriteUnknown))
289 | } else {
290 | completionHandler(nil)
291 | }
292 | }
293 |
294 | private func unblessHelperTool(helperID: String, completionHandler: @escaping (Error?) -> Void) {
295 | var smError: Unmanaged? = nil
296 | // deprecated, but there is still not a decent replacement, so 🤷
297 | // use some rather unfortunate hackery with dlsym to get around deprecation warning
298 |
299 | guard let smJobRemoveSym = dlsym(UnsafeMutableRawPointer(bitPattern: -2), "SMJobRemove") else {
300 | completionHandler(CocoaError(.fileWriteUnknown))
301 | return
302 | }
303 |
304 | let smJobRemove = unsafeBitCast(smJobRemoveSym, to: (@convention(c) (CFString?, CFString, AuthorizationRef?, Bool, UnsafeMutablePointer?>?) -> Bool).self)
305 |
306 | guard smJobRemove(kSMDomainSystemLaunchd, helperID as CFString, self.authRef, true, &smError) else {
307 | completionHandler(smError.map { ConvertCFError($0.takeRetainedValue()) } ?? CocoaError(.fileWriteUnknown))
308 | return
309 | }
310 |
311 | completionHandler(nil)
312 | }
313 |
314 | private func _installAndConnect(
315 | helperID: String,
316 | protocol proto: P.Type,
317 | interface: NSXPCInterface?,
318 | errorHandler: @escaping (Error) -> Void,
319 | connectionHandler: @escaping (P) -> Void
320 | ) {
321 | self.installHelperTool(helperID: helperID) {
322 | if let error = $0 {
323 | errorHandler(error)
324 | return
325 | }
326 |
327 | self.connectToHelperTool(
328 | helperID: helperID,
329 | protocol: proto,
330 | interface: interface,
331 | expectedVersion: nil,
332 | installIfNecessary: false,
333 | errorHandler: errorHandler,
334 | connectionHandler: connectionHandler
335 | )
336 | }
337 | }
338 |
339 | private func _openConnection(
340 | helperID: String,
341 | interface: NSXPCInterface?,
342 | protocol proto: P.Type
343 | ) throws -> NSXPCConnection {
344 | guard let objcProto = proto as Any as AnyObject as? Protocol else {
345 | throw CocoaError(.fileReadUnknown)
346 | }
347 |
348 | let conn = NSXPCConnection(machServiceName: helperID, options: .privileged)
349 |
350 | conn.remoteObjectInterface = interface ?? NSXPCInterface(with: objcProto)
351 | conn.resume()
352 |
353 | return conn
354 | }
355 |
356 | private func _connectToHelperTool(
357 | connection conn: NSXPCConnection,
358 | protocol proto: P.Type,
359 | interface: NSXPCInterface?,
360 | errorHandler: @escaping (Error) -> Void,
361 | connectionHandler: @escaping (P) -> Void
362 | ) {
363 | do {
364 | let proxy = try self.getProxy(conn, protocol: proto, interface: interface, errorHandler: errorHandler)
365 | connectionHandler(proxy)
366 | } catch {
367 | errorHandler(error)
368 | }
369 | }
370 |
371 | private func checkHelperVersion(
372 | connection: NSXPCConnection,
373 | completionHandler: @escaping (Result) -> Void
374 | ) {
375 | let errorHandler: (Error) -> Void = { completionHandler(.failure($0)) }
376 |
377 | guard let proxy = connection.remoteObjectProxyWithErrorHandler(errorHandler) as? BuiltInCommands else {
378 | completionHandler(.failure(CocoaError(.fileReadUnknown)))
379 | return
380 | }
381 |
382 | proxy.getVersion {
383 | if let error = $1 {
384 | completionHandler(.failure(error))
385 | } else if let version = $0 {
386 | completionHandler(.success(version))
387 | } else {
388 | completionHandler(.failure(CocoaError(.fileReadUnknown)))
389 | }
390 | }
391 | }
392 | }
393 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleApp/SandboxWorkaround.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SandboxWorkaround.swift
3 | // App Library
4 | //
5 | // Created by Charles Srstka on 9/13/18.
6 | //
7 |
8 | import Cocoa
9 |
10 | /// Workaround for a macOS UI bug.
11 | ///
12 | /// As of macOS 10.13, there is a UI issue that causes authentication boxes
13 | /// not to automatically gain focus when the app is sandboxed, which significantly
14 | /// impairs the user experience. This class provides an ugly workaround for this
15 | /// issue. Simply create an instance before beginning an operation that may possibly
16 | /// result in an authentication prompt, and stop it in the reply block.
17 | ///
18 | /// Usage example:
19 | /// ```
20 | /// let workaround = SandboxWorkaround()
21 | /// proxy.doSomePrivilegedOperation { reply in
22 | /// workaround.stop()
23 | ///
24 | /// ...
25 | /// }
26 | /// ```
27 | public class SandboxWorkaround {
28 | /// Create an instance of `SandboxWorkaround`.
29 | ///
30 | /// Be sure to call `stop()` once you receive a reply from the helper tool.
31 | public init() {
32 | self.queue.async(execute: self.activateIfReady)
33 | }
34 |
35 | /// Call this within the reply block after sending a message to your helper tool.
36 | public func stop() {
37 | self.doneSemaphore.wait()
38 | defer { self.doneSemaphore.signal() }
39 |
40 | self.done = true
41 | }
42 |
43 | private let queue = DispatchQueue(label: "com.charlessoft.CSAuthSample.SandboxWorkaround.queue")
44 | private let doneSemaphore = DispatchSemaphore(value: 1)
45 | private var done: Bool = false
46 |
47 | private func activateIfReady() {
48 | let agentID = "com.apple.SecurityAgent"
49 |
50 | self.doneSemaphore.wait()
51 | defer { self.doneSemaphore.signal() }
52 |
53 | if self.done {
54 | return
55 | }
56 |
57 | if let securityAgent = NSRunningApplication.runningApplications(
58 | withBundleIdentifier: agentID
59 | )
60 | .last {
61 | securityAgent.activate(options: [])
62 | } else {
63 | self.queue.asyncAfter(
64 | deadline: .now() + .microseconds(10),
65 | execute: self.activateIfReady)
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleCommon/CSASCommon.m:
--------------------------------------------------------------------------------
1 | //
2 | // CSASCommon.m
3 | // Helper Library
4 | //
5 | // Based on Common.m from EvenBetterAuthorizationSample,
6 | // Copyright © 2013 Apple Computer.
7 | //
8 | // Created by Charles Srstka on 6/25/18.
9 | //
10 |
11 | @import Foundation;
12 | @import Darwin.POSIX.syslog;
13 | #import "CSASCommon.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface CSASCommandSet ()
18 |
19 | @property (nonatomic, copy) NSDictionary *rights;
20 |
21 | @end
22 |
23 | static NSString * const rightsKey = @"com.charlessoft.CSAuthSample.CSASCommandSet.Rights";
24 | static NSString * const countKey = @"com.charlessoft.CSAuthSample.CSASCommandSet.Count";
25 | static NSString * const rightNameKey = @"com.charlessoft.CSAuthSample.CSASCommandSet.RightName";
26 | static NSString * const rightValueKey = @"com.charlessoft.CSAuthSample.CSASCommandSet.RightValue";
27 |
28 | @implementation CSASCommandSet
29 |
30 | - (instancetype)initWithAuthorizationRights:(NSArray *)rights {
31 | self = [super init];
32 | if (self == nil) {
33 | return nil;
34 | }
35 |
36 | NSMutableDictionary *rightsDict = [NSMutableDictionary new];
37 |
38 | for (CSASAuthorizationRight *eachRight in rights) {
39 | rightsDict[NSStringFromSelector(eachRight.selector)] = eachRight;
40 | }
41 |
42 | [self addBuiltInAuthorizationRights:rightsDict];
43 |
44 | self->_rights = [rightsDict copy];
45 |
46 | return self;
47 | }
48 |
49 | - (NSArray *)authorizationRights {
50 | return self.rights.allValues;
51 | }
52 |
53 | - (void)addBuiltInAuthorizationRights:(NSMutableDictionary *)rightsDict {
54 | // Add built-in commands
55 |
56 | SEL connectSel = @selector(getEndpointWithAuthorizationData:endpoint:);
57 | NSString *connectName = NSStringFromSelector(connectSel);
58 | if (rightsDict[connectName] == nil) {
59 | NSString *rightName = @"com.charlessoft.CSAuthSample.ConnectWithEndpoint";
60 | const char *rule = kAuthorizationRuleClassAllow;
61 |
62 | rightsDict[connectName] = [[CSASAuthorizationRight alloc] initWithSelector:connectSel
63 | name:rightName
64 | rule:rule
65 | prompt:nil];
66 | }
67 |
68 | SEL versionSel = @selector(getVersionWithReply:);
69 | NSString *versionName = NSStringFromSelector(versionSel);
70 | if (rightsDict[versionName] == nil) {
71 | NSString *rightName = @"com.charlessoft.CSAuthSample.GetVersion";
72 | const char *rule = kAuthorizationRuleClassAllow;
73 |
74 | rightsDict[versionName] = [[CSASAuthorizationRight alloc] initWithSelector:versionSel
75 | name:rightName
76 | rule:rule
77 | prompt:nil];
78 | }
79 |
80 | SEL uninstallSel = @selector(uninstallHelperToolWithAuthorizationData:reply:);
81 | NSString *uninstallName = NSStringFromSelector(uninstallSel);
82 | if (rightsDict[uninstallName] == nil) {
83 | NSString *rightName = @"com.charlessoft.CSAuthSample.UninstallHelper";
84 | const char *rule = kAuthorizationRuleClassAllow;
85 |
86 | rightsDict[uninstallName] = [[CSASAuthorizationRight alloc] initWithSelector:uninstallSel
87 | name:rightName
88 | rule:rule
89 | prompt:nil];
90 | }
91 | }
92 |
93 | - (nullable CSASAuthorizationRight *)authorizationRightForCommand:(SEL)command {
94 | return self.rights[NSStringFromSelector(command)];
95 | }
96 |
97 | - (void)setupAuthorizationRights:(AuthorizationRef)authRef bundle:(nullable NSBundle *)bundle tableName:(nullable NSString *)tableName {
98 | assert(authRef != NULL);
99 |
100 | for (CSASAuthorizationRight *eachRight in self.rights.allValues) {
101 | // First get the right. If we get back errAuthorizationDenied that means there's
102 | // no current definition, so we add our default one.
103 |
104 | const char *rightName = (const char * _Nonnull)eachRight.name.UTF8String;
105 | assert(rightName != NULL);
106 |
107 | CFBundleRef cfBundle = NULL;
108 | if (bundle != nil) {
109 | if (bundle == [NSBundle mainBundle]) {
110 | cfBundle = CFBundleGetMainBundle();
111 | CFRetain(cfBundle);
112 | } else {
113 | cfBundle = CFBundleCreate(kCFAllocatorDefault, (__bridge CFURLRef)bundle.bundleURL);
114 | }
115 | }
116 |
117 | OSStatus err = AuthorizationRightGet(rightName, NULL);
118 | if (err == errAuthorizationDenied) {
119 | err = AuthorizationRightSet(
120 | authRef,
121 | rightName,
122 | (__bridge CFTypeRef)eachRight.rule,
123 | (__bridge CFStringRef)eachRight.prompt,
124 | cfBundle,
125 | (__bridge CFStringRef)tableName
126 | );
127 | assert(err == errAuthorizationSuccess);
128 | } else {
129 | // A right already exists (err == noErr) or any other error occurs, we
130 | // assume that it has been set up in advance by the system administrator or
131 | // this is the second time we've run. Either way, there's nothing more for
132 | // us to do.
133 | }
134 |
135 | if (cfBundle != NULL) {
136 | CFRelease(cfBundle);
137 | }
138 | }
139 | }
140 |
141 | + (BOOL)supportsSecureCoding { return YES; }
142 |
143 | - (nullable instancetype)initWithCoder:(NSCoder *)coder {
144 | self = [super init];
145 | if (self == nil) {
146 | return nil;
147 | }
148 |
149 | NSInteger count = [coder decodeIntegerForKey:countKey];
150 | NSData *rightsData = [coder decodeObjectOfClass:[NSData class] forKey:rightsKey];
151 |
152 | NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:rightsData error:NULL];
153 |
154 | if (unarchiver == nil) {
155 | return nil;
156 | }
157 |
158 | NSMutableDictionary *rightsDict = [NSMutableDictionary new];
159 |
160 | for (NSInteger i = 0; i < count; i++) {
161 | NSString *nameKey = [rightNameKey stringByAppendingFormat:@"%ld", (long)i];
162 | NSString *valueKey = [rightValueKey stringByAppendingFormat:@"%ld", (long)i];
163 |
164 | NSString *name = [unarchiver decodeObjectOfClass:[NSString class] forKey:nameKey];
165 | CSASAuthorizationRight *value = [unarchiver decodeObjectOfClass:[CSASAuthorizationRight class] forKey:valueKey];
166 |
167 | if (name == nil || value == nil) {
168 | return nil;
169 | }
170 |
171 | rightsDict[name] = value;
172 | }
173 |
174 | self->_rights = [rightsDict copy];
175 |
176 | return self;
177 | }
178 |
179 | - (void)encodeWithCoder:(NSCoder *)coder {
180 | NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
181 |
182 | __block NSInteger count = 0;
183 |
184 | [self.rights enumerateKeysAndObjectsUsingBlock:^(NSString *name, CSASAuthorizationRight *right, __unused BOOL *stop) {
185 | [archiver encodeObject:name forKey:[rightNameKey stringByAppendingFormat:@"%ld", (long)count]];
186 | [archiver encodeObject:right forKey:[rightValueKey stringByAppendingFormat:@"%ld", (long)count]];
187 |
188 | count++;
189 | }];
190 |
191 | NSData *data = archiver.encodedData;
192 |
193 | [coder encodeInteger:count forKey:countKey];
194 | [coder encodeObject:data forKey:rightsKey];
195 | }
196 |
197 | @end
198 |
199 | static NSString * const selectorKey = @"com.charlessoft.CSAuthSample.CSASAuthorizationRight.Selector";
200 | static NSString * const nameKey = @"com.charlessoft.CSAuthSample.CSASAuthorizationRight.Name";
201 | static NSString * const ruleKey = @"com.charlessoft.CSAuthSample.CSASAuthorizationRight.Rule";
202 | static NSString * const promptKey = @"com.charlessoft.CSAuthSample.CSASAuthorizationRight.Prompt";
203 |
204 | @implementation CSASAuthorizationRight
205 |
206 | + (BOOL)supportsSecureCoding { return YES; }
207 |
208 | - (instancetype)initWithSelector:(SEL)selector name:(NSString *)name rule:(const char *)rule prompt:(nullable NSString *)prompt {
209 | self = [super init];
210 | if (self == nil) {
211 | return nil;
212 | }
213 |
214 | self->_selector = selector;
215 | self->_name = [name copy];
216 |
217 | self->_rule = (NSString * _Nonnull)[[NSString alloc] initWithUTF8String:rule];
218 | assert(self->_rule != nil);
219 |
220 | self->_prompt = [prompt copy];
221 |
222 | return self;
223 | }
224 |
225 | - (nullable instancetype)initWithCoder:(NSCoder *)coder {
226 | self = [super init];
227 | if (self == nil || !coder.allowsKeyedCoding) {
228 | return nil;
229 | }
230 |
231 | NSString *selName = [coder decodeObjectOfClass:[NSString class] forKey:selectorKey];
232 |
233 | if (selName == nil) {
234 | return nil;
235 | }
236 |
237 | self->_selector = NSSelectorFromString(selName);
238 | self->_name = (NSString * _Nonnull)[coder decodeObjectOfClass:[NSString class] forKey:nameKey];
239 | self->_rule = (NSString * _Nonnull)[coder decodeObjectOfClass:[NSString class] forKey:ruleKey];
240 | self->_prompt = (NSString * _Nonnull)[coder decodeObjectOfClass:[NSString class] forKey:promptKey];
241 |
242 | if (self->_selector == NULL || self->_name == nil || self->_rule == nil) {
243 | return nil;
244 | }
245 |
246 | return self;
247 | }
248 |
249 | - (void)encodeWithCoder:(NSCoder *)coder {
250 | [coder encodeObject:NSStringFromSelector(self->_selector) forKey:selectorKey];
251 | [coder encodeObject:self->_name forKey:nameKey];
252 | [coder encodeObject:self->_rule forKey:ruleKey];
253 |
254 | if (self->_prompt != nil) {
255 | [coder encodeObject:self->_prompt forKey:promptKey];
256 | }
257 | }
258 |
259 | @end
260 |
261 | static NSInteger CSASConvertPOSIXErrorCode(NSInteger code) {
262 | switch (code) {
263 | case ECANCELED:
264 | return NSUserCancelledError;
265 | case ENOENT:
266 | return NSFileNoSuchFileError;
267 | case EFBIG:
268 | return NSFileReadTooLargeError;
269 | case EEXIST:
270 | return NSFileWriteFileExistsError;
271 | case ENOSPC:
272 | return NSFileWriteOutOfSpaceError;
273 | case EROFS:
274 | return NSFileWriteVolumeReadOnlyError;
275 | default:
276 | return 0;
277 | }
278 | }
279 |
280 | static NSInteger CSASConvertOSStatusErrorCode(NSInteger code) {
281 | if (code >= errSecErrnoBase && code <= errSecErrnoLimit) {
282 | NSInteger newCode = CSASConvertPOSIXErrorCode(code - errSecErrnoBase);
283 |
284 | if (newCode != 0) {
285 | return newCode;
286 | }
287 | }
288 |
289 | switch (code) {
290 | case userCanceledErr:
291 | case errAuthorizationCanceled:
292 | case errSecCSCancelled:
293 | case errAEWaitCanceled:
294 | case kernelCanceledErr:
295 | case kOTCanceledErr:
296 | case kECANCELErr:
297 | case errIACanceled:
298 | case kRAConnectionCanceled:
299 | case kTXNUserCanceledOperationErr:
300 | case kFBCindexingCanceled:
301 | case kFBCaccessCanceled:
302 | case kFBCsummarizationCanceled:
303 | return NSUserCancelledError;
304 | case fnfErr:
305 | return NSFileNoSuchFileError;
306 | case fileBoundsErr:
307 | case fsDataTooBigErr:
308 | return NSFileReadTooLargeError;
309 | case dupFNErr:
310 | return NSFileWriteFileExistsError;
311 | case dskFulErr:
312 | case errFSNotEnoughSpaceForOperation:
313 | return NSFileWriteOutOfSpaceError;
314 | case vLckdErr:
315 | return NSFileWriteVolumeReadOnlyError;
316 | default:
317 | return 0;
318 | }
319 | }
320 |
321 | FOUNDATION_EXPORT NSError *CSASConvertNSError(NSError *error) {
322 | // If we can find a NSCocoaError that corresponds to the same error condition as this error, use it.
323 | // NSCocoaError tends to present nicer error messages to the user.
324 | NSInteger newCode = 0;
325 |
326 | if ([error.domain isEqualToString:NSPOSIXErrorDomain]) {
327 | newCode = CSASConvertPOSIXErrorCode(error.code);
328 | } else if ([error.domain isEqualToString:NSOSStatusErrorDomain]) {
329 | newCode = CSASConvertOSStatusErrorCode(error.code);
330 | } else {
331 | newCode = 0;
332 | }
333 |
334 | if (newCode != 0) {
335 | NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
336 |
337 | userInfo[NSUnderlyingErrorKey] = error;
338 |
339 | // Use the built-in error messages instead
340 | userInfo[NSLocalizedFailureReasonErrorKey] = nil;
341 |
342 | return [[NSError alloc] initWithDomain:NSCocoaErrorDomain code:newCode userInfo:userInfo];
343 | } else if ([error.domain isEqualToString:NSOSStatusErrorDomain]) {
344 | // At least try to find a nicer error string to display to the user.
345 |
346 | CFStringRef errString = SecCopyErrorMessageString((OSStatus)error.code, NULL);
347 |
348 | if (errString != NULL) {
349 | NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
350 |
351 | userInfo[NSLocalizedFailureReasonErrorKey] = CFBridgingRelease(errString);
352 |
353 | return [[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:error.code userInfo:userInfo];
354 | }
355 | }
356 |
357 | // We weren't able to improve this error message; just return it as is
358 | return error;
359 | }
360 |
361 | FOUNDATION_EXPORT NSError *CSASConvertCFError(CFErrorRef error) {
362 | return CSASConvertNSError((__bridge NSError *)error);
363 | }
364 |
365 | FOUNDATION_EXPORT NSError *CSASConvertPOSIXError(int err) {
366 | return CSASConvertNSError([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]);
367 | }
368 |
369 | FOUNDATION_EXPORT NSError *CSASConvertOSStatus(OSStatus err) {
370 | return CSASConvertNSError([[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]);
371 | }
372 |
373 | NS_ASSUME_NONNULL_END
374 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleCommon/include/CSASCommon.h:
--------------------------------------------------------------------------------
1 | //
2 | // CSASCommon.h
3 | // Helper Library
4 | //
5 | // Created by Charles Srstka on 6/25/18.
6 | //
7 |
8 | @import Foundation;
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | // Some built-in commands handled by the library which are provided for free.
13 | // The protocol you pass to CSASHelperTool should conform to this protocol.
14 | //
15 | // IMPORTANT: NSXPCConnection can call these methods on any thread. It turns out that our
16 | // implementation of these methods is thread safe but if that's not the case for your code
17 | // you have to implement your own protection (for example, having your own serial queue and
18 | // dispatching over to it).
19 |
20 | NS_SWIFT_NAME(BuiltInCommands) @protocol CSASBuiltInCommands
21 |
22 | // Part of CSASBuiltInCommands. Not used by the standard app (it's part of the sandboxed
23 | // XPC service support). Called by the XPC service to get an endpoint for our listener. It then
24 | // passes this endpoint to the app so that the sandboxed app can talk us directly.
25 | - (void)getEndpointWithAuthorizationData:(NSData *)authData
26 | endpoint:(void (^)(NSXPCListenerEndpoint * _Nullable, NSError * _Nullable))reply
27 | NS_SWIFT_NAME(connect(authorizationData:endpoint:));
28 |
29 | // Part of CSASBuiltInCommands. Returns the version number of the tool.
30 | - (void)getVersionWithReply:(void(^)(NSString * _Nullable version, NSError * _Nullable))reply
31 | NS_SWIFT_NAME(getVersion(reply:));
32 |
33 | // Part of CSASBuiltInCommands. Uninstalls the helper tool.
34 | - (void)uninstallHelperToolWithAuthorizationData:(NSData *)authData
35 | reply:(void (^)(NSError * _Nullable))reply
36 | NS_SWIFT_NAME(uninstallHelperTool(authorizationData:reply:));
37 |
38 | @end
39 |
40 | NS_SWIFT_NAME(AuthorizationRight) @interface CSASAuthorizationRight: NSObject
41 |
42 | - (instancetype)initWithSelector:(SEL)selector
43 | name:(NSString *)name
44 | rule:(const char *)rule
45 | prompt:(nullable NSString *)prompt;
46 |
47 | @property (nonatomic) SEL selector;
48 | @property (nonatomic, copy) NSString *name;
49 | @property (nonatomic, copy) NSString *rule;
50 | @property (nonatomic, copy, nullable) NSString *prompt;
51 |
52 | @end
53 |
54 | NS_SWIFT_NAME(CommandSet) @interface CSASCommandSet: NSObject
55 |
56 | - (instancetype)initWithAuthorizationRights:(NSArray *)rights;
57 |
58 | @property (nonatomic, readonly) NSArray *authorizationRights;
59 |
60 | // For a given command selector, return the associated authorization right name.
61 | - (nullable CSASAuthorizationRight *)authorizationRightForCommand:(SEL)command;
62 |
63 | // Set up the default authorization rights in the authorization database.
64 | - (void)setupAuthorizationRights:(AuthorizationRef)authRef
65 | bundle:(nullable NSBundle *)bundle
66 | tableName:(nullable NSString *)tableName;
67 |
68 | @end
69 |
70 | // Cocoa tends to do a nicer job presenting Cocoa errors than POSIX or OSStatus ones,
71 | // particularly with NSUserCancelledError, in which case -presentError: will skip
72 | // showing the error altogether. For certain other error types, using the Cocoa domain
73 | // will provide a little more information, including, sometimes, the filename for which
74 | // the operation failed. Therefore, convert errors to NSCocoaErrorDomain when possible.
75 |
76 | FOUNDATION_EXPORT NSError *CSASConvertNSError(NSError *error) NS_SWIFT_NAME(ConvertError(_:));
77 | FOUNDATION_EXPORT NSError *CSASConvertCFError(CFErrorRef error) NS_SWIFT_NAME(ConvertCFError(_:));
78 | FOUNDATION_EXPORT NSError *CSASConvertPOSIXError(int err) NS_SWIFT_NAME(ConvertPOSIXError(_:));
79 | FOUNDATION_EXPORT NSError *CSASConvertOSStatus(OSStatus status) NS_SWIFT_NAME(ConvertOSStatus(_:));
80 |
81 | NS_ASSUME_NONNULL_END
82 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleCommon/include/module.modulemap:
--------------------------------------------------------------------------------
1 | module CSAuthSampleCommon {
2 | header "CSASCommon.h"
3 | export *
4 | }
5 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/CSASHelperConnection.m:
--------------------------------------------------------------------------------
1 | //
2 | // CSASHelperConnection.m
3 | // Helper Tool
4 | //
5 | // Created by Charles Srstka on 7/1/18.
6 | //
7 |
8 | @import Foundation;
9 | #import "CSASHelperConnection.h"
10 | #import "CSASHelperConnectionInternal.h"
11 | #import "CSASHelperToolInternal.h"
12 |
13 | @interface CSASHelperConnection ()
14 |
15 | @property (nonatomic, readonly, weak) CSASHelperTool *helperTool;
16 |
17 | @end
18 |
19 | static NSString * const currentCommandKey = @"com.charlessoft.CSAuthSample.currentCommand";
20 |
21 | @implementation CSASHelperConnection
22 |
23 | - (instancetype)initWithConnection:(NSXPCConnection *)connection
24 | helperTool:(CSASHelperTool *)helperTool
25 | commandSet:(CSASCommandSet *)commandSet {
26 | self = [super init];
27 | if (self == nil) {
28 | return nil;
29 | }
30 |
31 | self->_connection = connection;
32 | self->_helperTool = helperTool;
33 | self->_commandSet = commandSet;
34 |
35 | return self;
36 | }
37 |
38 | - (SEL)currentCommand {
39 | NSString *cmdName = [NSThread currentThread].threadDictionary[currentCommandKey];
40 |
41 | return (cmdName == nil) ? nil : NSSelectorFromString(cmdName);
42 | }
43 |
44 | - (void)setCurrentCommand:(SEL)currentCommand {
45 | [NSThread currentThread].threadDictionary[currentCommandKey] = NSStringFromSelector(currentCommand);
46 | }
47 |
48 | - (nullable NSError *)checkAuthorization:(NSData *)authData {
49 | // First check that authData looks reasonable.
50 | if ((authData == nil) || (authData.length != sizeof(AuthorizationExternalForm))) {
51 | return [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:EINVAL userInfo:nil];
52 | }
53 |
54 | // Create an authorization ref from that the external form data contained within.
55 | AuthorizationRef authRef = NULL;
56 | OSStatus err = AuthorizationCreateFromExternalForm(authData.bytes, &authRef);
57 |
58 | if (err != errAuthorizationSuccess) {
59 | return CSASConvertOSStatus(err);
60 | }
61 |
62 | @try {
63 | // Call our authorization method.
64 | return [self _checkAuthorization:authRef forCommand:self.currentCommand];
65 | }
66 | @finally {
67 | OSStatus junk = AuthorizationFree(authRef, 0);
68 | assert(junk == errAuthorizationSuccess);
69 | }
70 | }
71 |
72 | - (nullable NSError *)_checkAuthorization:(AuthorizationRef)auth forCommand:(SEL)command {
73 | CSASAuthorizationRight *authRight = [self.commandSet authorizationRightForCommand:command];
74 |
75 | if (authRight == nil) {
76 | return [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:EINVAL userInfo:nil];
77 | }
78 |
79 | // Authorize the right associated with the command.
80 |
81 | AuthorizationItem oneRight = { NULL, 0, NULL, 0 };
82 | AuthorizationRights rights = { 1, &oneRight };
83 |
84 | oneRight.name = (const char * _Nonnull)authRight.name.UTF8String;
85 | assert(oneRight.name != NULL);
86 |
87 | AuthorizationFlags flags = kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed;
88 | OSStatus err = AuthorizationCopyRights(auth, &rights, NULL, flags, NULL);
89 |
90 | if (err != errAuthorizationSuccess) {
91 | return CSASConvertOSStatus(err);
92 | }
93 |
94 | return nil;
95 | }
96 |
97 | #pragma mark * CSASBuiltInCommands
98 |
99 | - (void)getEndpointWithAuthorizationData:(NSData *)authData endpoint:(void (^)(NSXPCListenerEndpoint * _Nullable, NSError * _Nullable))reply {
100 | NSError *error = [self checkAuthorization:authData];
101 | if (error != nil) {
102 | reply(nil, error);
103 | return;
104 | }
105 |
106 | reply(self.helperTool.listener.endpoint, nil);
107 | }
108 |
109 | - (void)getVersionWithReply:(void (^)(NSString * _Nullable, NSError * _Nullable))reply {
110 | NSString *vers = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
111 |
112 | if (vers != nil) {
113 | reply([[NSString alloc] initWithFormat:@"%@", vers], nil);
114 | } else {
115 | reply(nil, [[NSError alloc] initWithDomain:NSCocoaErrorDomain code:NSFileReadUnknownError userInfo:nil]);
116 | }
117 | }
118 |
119 | - (void)uninstallHelperToolWithAuthorizationData:(NSData *)authData reply:(void (^)(NSError * _Nullable))reply {
120 | NSFileManager *fm = [NSFileManager defaultManager];
121 |
122 | NSURL *helperURL = [NSURL fileURLWithPath:[NSProcessInfo processInfo].arguments[0]];
123 |
124 | if (helperURL == nil) {
125 | reply([[NSError alloc] initWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:nil]);
126 | return;
127 | }
128 |
129 | NSURL *libraryURL = [fm URLForDirectory:NSLibraryDirectory inDomain:NSLocalDomainMask appropriateForURL:nil create:NO error:NULL];
130 |
131 | NSURL *daemonsURL = [libraryURL URLByAppendingPathComponent:@"LaunchDaemons"];
132 |
133 | NSURL *serviceURL = [[daemonsURL URLByAppendingPathComponent:self.helperTool.helperID] URLByAppendingPathExtension:@"plist"];
134 |
135 | NSError *error = [self checkAuthorization:authData];
136 |
137 | if (error != nil) {
138 | reply(error);
139 | return;
140 | }
141 |
142 | if ([helperURL checkResourceIsReachableAndReturnError:NULL] && ![fm removeItemAtURL:helperURL error:&error]) {
143 | reply(error);
144 | return;
145 | }
146 |
147 | if ([serviceURL checkResourceIsReachableAndReturnError:NULL] && ![fm removeItemAtURL:serviceURL error:&error]) {
148 | reply(error);
149 | return;
150 | }
151 |
152 | reply(nil);
153 | }
154 |
155 | @end
156 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/CSASHelperConnectionInternal.h:
--------------------------------------------------------------------------------
1 | //
2 | // CSASHelperConnectionInternal.h
3 | // CSAuthSample
4 | //
5 | // Created by Charles Srstka on 4/19/20.
6 | //
7 |
8 | @import Foundation;
9 | #import "CSASHelperConnection.h"
10 |
11 | @interface CSASHelperConnection ()
12 |
13 | @property (nonatomic) SEL currentCommand;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/CSASHelperConnectionWrapper.h:
--------------------------------------------------------------------------------
1 | //
2 | // CSASHelperConnectionWrapper.h
3 | // Helper Library
4 | //
5 | // Created by Charles Srstka on 4/19/20.
6 | //
7 |
8 | @import Foundation;
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @class CSASHelperTool;
13 | @class CSASCommandSet;
14 |
15 | @interface CSASHelperConnectionWrapper : NSProxy
16 |
17 | - (instancetype)initWithConnectionClass:(Class)connectionClass
18 | xpcConnection:(NSXPCConnection *)xpcConnection
19 | helperTool:(CSASHelperTool *)helperTool
20 | commandSet:(CSASCommandSet *)commandSet;
21 |
22 | @end
23 |
24 | NS_ASSUME_NONNULL_END
25 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/CSASHelperConnectionWrapper.m:
--------------------------------------------------------------------------------
1 | //
2 | // CSASHelperConnectionWrapper.m
3 | // Helper Library
4 | //
5 | // Created by Charles Srstka on 4/19/20.
6 | //
7 |
8 | @import ObjectiveC.runtime;
9 | #import "CSASHelperConnectionWrapper.h"
10 | #import "CSASHelperConnection.h"
11 | #import "CSASHelperTool.h"
12 | #import "CSASHelperToolInternal.h"
13 | #import "CSASHelperConnectionInternal.h"
14 |
15 | @interface CSASHelperConnectionWrapper ()
16 |
17 | @property (nonatomic, readonly) CSASHelperConnection *connection;
18 |
19 | @end
20 |
21 | @implementation CSASHelperConnectionWrapper
22 |
23 | - (instancetype)initWithConnectionClass:(Class)connectionClass
24 | xpcConnection:(NSXPCConnection *)xpcConnection
25 | helperTool:(CSASHelperTool *)helperTool
26 | commandSet:(CSASCommandSet *)commandSet {
27 | self->_connection = [(CSASHelperConnection *)[connectionClass alloc] initWithConnection:xpcConnection
28 | helperTool:helperTool
29 | commandSet:commandSet];
30 |
31 | return self;
32 | }
33 |
34 | - (BOOL)respondsToSelector:(SEL)selector { return [self.connection respondsToSelector:selector]; }
35 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.connection methodSignatureForSelector:sel]; }
36 | - (BOOL)conformsToProtocol:(Protocol *)protocol { return [self.connection conformsToProtocol:protocol]; }
37 |
38 | - (void)forwardInvocation:(NSInvocation *)invocation {
39 | self.connection.currentCommand = invocation.selector;
40 | [invocation invokeWithTarget:self.connection];
41 | }
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/CSASHelperTool.m:
--------------------------------------------------------------------------------
1 | //
2 | // CSASHelperTool.m
3 | // Helper Library
4 | //
5 | // Based on HelperTool.m from EvenBetterAuthorizationSample,
6 | // Copyright © 2013 Apple Computer.
7 | //
8 | // Created by Charles Srstka on 6/25/18.
9 | //
10 |
11 | @import Foundation;
12 | @import Darwin.POSIX.syslog;
13 | @import ObjectiveC.runtime;
14 | #import "CSASHelperTool.h"
15 | #import "CSASHelperToolInternal.h"
16 | #import "CSASHelperConnection.h"
17 | #import "CSASHelperConnectionWrapper.h"
18 |
19 | NS_ASSUME_NONNULL_BEGIN
20 |
21 | @interface CSASHelperTool()
22 |
23 | @property (nonatomic, readonly) CSASCommandSet *commandSet;
24 | @property (nonatomic, readonly) NSArray *requirements;
25 | @property (nonatomic, readonly) Class connectionClass;
26 | @property (nonatomic, readonly) NSXPCInterface *interface;
27 | @property (nonatomic, strong) CSASHelperConnectionWrapper *connectionWrapper;
28 |
29 | @property (nonatomic) NSUInteger connectionCount;
30 |
31 | @end
32 |
33 | @implementation CSASHelperTool
34 |
35 | - (instancetype)initWithHelperID:(NSString *)helperID
36 | commandSet:(CSASCommandSet *)commandSet
37 | senderRequirements:(nullable NSArray *)senderRequirements
38 | connectionClass:(Class)connectionClass
39 | protocol:(Protocol *)protocol {
40 | NSXPCInterface *interface = [NSXPCInterface interfaceWithProtocol:protocol];
41 |
42 | return [self initWithHelperID:helperID
43 | commandSet:commandSet
44 | senderRequirements:senderRequirements
45 | connectionClass:connectionClass
46 | interface:interface];
47 | }
48 |
49 | - (instancetype)initWithHelperID:(NSString *)helperID
50 | commandSet:(CSASCommandSet *)commandSet
51 | senderRequirements:(nullable NSArray *)_senderRequirements
52 | connectionClass:(Class)connectionClass // must be CSASHelperConnection subclass
53 | interface:(NSXPCInterface *)interface {
54 | self = [super init];
55 | if (self == nil) {
56 | return nil;
57 | }
58 |
59 | NSArray *senderRequirements;
60 | if (_senderRequirements != nil) {
61 | senderRequirements = _senderRequirements;
62 | } else {
63 | senderRequirements = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SMAuthorizedClients"];
64 | }
65 |
66 | [CSASHelperTool configureDefaultsForInterface:interface commandSet:commandSet];
67 |
68 | // Set up our XPC listener to handle requests on our Mach service.
69 | self->_listener = [[NSXPCListener alloc] initWithMachServiceName:helperID];
70 | self->_listener.delegate = self;
71 | self->_commandSet = commandSet;
72 | self->_requirements = [senderRequirements copy];
73 | self->_connectionClass = connectionClass;
74 | self->_interface = interface;
75 | self->_connectionCount = 0;
76 | self->_helperID = [helperID copy];
77 |
78 | return self;
79 | }
80 |
81 | + (void)configureDefaultsForInterface:(NSXPCInterface *)interface commandSet:(CSASCommandSet *)commandSet {
82 | NSSet *data = [NSSet setWithObject:[NSData class]];
83 | NSSet *string = [NSSet setWithObject:[NSString class]];
84 | NSSet *endpoint = [NSSet setWithObject:[NSXPCListenerEndpoint class]];
85 | NSSet *error = [NSSet setWithObject:[NSError class]];
86 |
87 | SEL getEndpoint = @selector(getEndpointWithAuthorizationData:endpoint:);
88 |
89 | [interface setClasses:data forSelector:getEndpoint argumentIndex:0 ofReply:NO];
90 | [interface setClasses:endpoint forSelector:getEndpoint argumentIndex:0 ofReply:YES];
91 | [interface setClasses:error forSelector:getEndpoint argumentIndex:1 ofReply:YES];
92 |
93 | SEL getVersion = @selector(getVersionWithReply:);
94 |
95 | [interface setClasses:string forSelector:getVersion argumentIndex:0 ofReply:YES];
96 | [interface setClasses:error forSelector:getVersion argumentIndex:1 ofReply:YES];
97 |
98 | SEL uninstall = @selector(uninstallHelperToolWithAuthorizationData:reply:);
99 |
100 | [interface setClasses:data forSelector:uninstall argumentIndex:0 ofReply:NO];
101 | [interface setClasses:error forSelector:uninstall argumentIndex:0 ofReply:YES];
102 |
103 | for (CSASAuthorizationRight *eachRight in commandSet.authorizationRights) {
104 | // getVersion is allowed not to have an auth parameter
105 | if (sel_isEqual(eachRight.selector, getVersion)) {
106 | continue;
107 | }
108 |
109 | if (![[interface classesForSelector:eachRight.selector argumentIndex:0 ofReply:NO] isEqualToSet:data]) {
110 | NSString *name = @"CSAuthSampleMissingAuthorizationData";
111 | NSString *reason = @"All privileged operations must include an authorizationData parameter.";
112 |
113 | NSException *exception = [[NSException alloc] initWithName:name reason:reason userInfo:nil];
114 |
115 | [exception raise];
116 | }
117 | }
118 | }
119 |
120 | - (void)run {
121 | [self.listener resume];
122 |
123 | [[NSRunLoop currentRunLoop] run];
124 |
125 | // Should never get here. Crash if we do.
126 | exit(EXIT_FAILURE);
127 | }
128 |
129 | - (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)conn {
130 | assert(listener == self.listener);
131 | assert(conn != nil);
132 |
133 | if (![self shouldApproveConnection:conn]) {
134 | return NO;
135 | }
136 |
137 | self->_connectionWrapper = [[CSASHelperConnectionWrapper alloc] initWithConnectionClass:self.connectionClass
138 | xpcConnection:conn
139 | helperTool:self
140 | commandSet:self.commandSet];
141 |
142 | conn.exportedInterface = self.interface;
143 | conn.exportedObject = self->_connectionWrapper;
144 |
145 | // Keep track of how many connections we have open. If the number reaches zero, exit the process.
146 | // This will prevent the helper tool from sticking around long after we're done with it.
147 | self.connectionCount++;
148 | conn.invalidationHandler = ^{
149 | self.connectionCount--;
150 |
151 | if (self.connectionCount == 0) {
152 | exit(0);
153 | }
154 | };
155 |
156 | [conn resume];
157 |
158 | return YES;
159 | }
160 |
161 | - (BOOL)shouldApproveConnection:(NSXPCConnection *)connection {
162 | for (NSString *eachRequirement in self.requirements) {
163 | if ([self checkCodeSigningForConnection:connection requirement:eachRequirement error:NULL]) {
164 | return YES;
165 | }
166 | }
167 |
168 | return NO;
169 | }
170 |
171 | - (BOOL)checkCodeSigningForConnection:(NSXPCConnection *)connection
172 | requirement:(NSString *)req
173 | error:(__autoreleasing NSError * _Nullable * _Nullable)error {
174 | // Check the code signing requirement for the command.
175 |
176 | SecCodeRef secCode = NULL;
177 | SecRequirementRef secRequirement = NULL;
178 |
179 | @try {
180 | pid_t pid = connection.processIdentifier;
181 | NSDictionary *codeAttrs = @{ (__bridge NSString *)kSecGuestAttributePid: @(pid) };
182 |
183 | OSStatus err = SecCodeCopyGuestWithAttributes(NULL, (__bridge CFDictionaryRef)codeAttrs, kSecCSDefaultFlags, &secCode);
184 |
185 | if (err != errSecSuccess) {
186 | if (error) *error = CSASConvertOSStatus(err);
187 | return NO;
188 | }
189 |
190 | err = SecRequirementCreateWithString((__bridge CFStringRef)req, kSecCSDefaultFlags, &secRequirement);
191 |
192 | if (err != errSecSuccess) {
193 | if (error) *error = CSASConvertOSStatus(err);
194 | return NO;
195 | }
196 |
197 | err = SecCodeCheckValidity(secCode, kSecCSDefaultFlags, secRequirement);
198 |
199 | if (err == errSecSuccess) {
200 | return YES;
201 | } else {
202 | if (error) *error = CSASConvertOSStatus(err);
203 | return NO;
204 | }
205 | }
206 | @finally {
207 | if (secCode != NULL) {
208 | CFRelease(secCode);
209 | }
210 |
211 | if (secRequirement != NULL) {
212 | CFRelease(secRequirement);
213 | }
214 | }
215 | }
216 |
217 | - (void)log:(NSString *)format, ... {
218 | va_list list;
219 | va_start(list, format);
220 |
221 | [self logWithPriority:LOG_NOTICE format:format arguments:list];
222 |
223 | va_end(list);
224 | }
225 |
226 | - (void)logWithPriority:(int)priority format:(NSString *)format, ... {
227 | va_list list;
228 | va_start(list, format);
229 |
230 | [self logWithPriority:priority format:format arguments:list];
231 |
232 | va_end(list);
233 | }
234 |
235 | - (void)logWithPriority:(int)priority format:(NSString *)format arguments:(va_list)args {
236 | NSString *string = [[NSString alloc] initWithFormat:format arguments:args];
237 |
238 | syslog(LOG_NOTICE, "%s", string.UTF8String);
239 | }
240 |
241 | @end
242 |
243 | NS_ASSUME_NONNULL_END
244 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/CSASHelperToolInternal.h:
--------------------------------------------------------------------------------
1 | //
2 | // CSASHelperToolInternal.h
3 | // CSAuthSample
4 | //
5 | // Created by Charles Srstka on 7/10/18.
6 | //
7 |
8 | #import "CSASHelperTool.h"
9 |
10 | @interface CSASHelperTool ()
11 |
12 | @property (nonatomic, readonly) NSXPCListener *listener;
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/include/CSASHelperConnection.h:
--------------------------------------------------------------------------------
1 | //
2 | // CSASHelperConnection.h
3 | // Helper Tool
4 | //
5 | // Created by Charles Srstka on 7/1/18.
6 | //
7 |
8 | @import Foundation;
9 | @import CSAuthSampleCommon;
10 | #import "CSASHelperTool.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | NS_SWIFT_NAME(HelperConnection) @interface CSASHelperConnection : NSObject
15 |
16 | @property (nonatomic, readonly) NSXPCConnection *connection;
17 | @property (nonatomic, readonly) CSASCommandSet *commandSet;
18 |
19 | - (instancetype)initWithConnection:(NSXPCConnection *)connection
20 | helperTool:(CSASHelperTool *)helperTool
21 | commandSet:(CSASCommandSet *)commandSet;
22 |
23 | // This method must be called at the beginning of every command, before executing any other code.
24 | // Returns nil on successful authorization, and an `NSError` otherwise.
25 |
26 | - (nullable NSError *)checkAuthorization:(NSData *)authData;
27 |
28 | @end
29 |
30 | NS_ASSUME_NONNULL_END
31 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/include/CSASHelperTool.h:
--------------------------------------------------------------------------------
1 | //
2 | // CSASHelperTool.h
3 | // CSAuthSample
4 | //
5 | // Created by Charles Srstka on 6/25/18.
6 | //
7 |
8 | @import Foundation;
9 | #import "CSASCommon.h"
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | NS_SWIFT_NAME(HelperTool) @interface CSASHelperTool: NSObject
14 |
15 | @property (nonatomic, readonly) NSString *helperID;
16 |
17 | // If nil is passed for senderRequirements, the value for the
18 | // "SMAuthorizedClients" key will be read from the helper tool's
19 | // built-in Info.plist.
20 |
21 | - (instancetype)initWithHelperID:(NSString *)helperID
22 | commandSet:(CSASCommandSet *)commandSet
23 | senderRequirements:(nullable NSArray *)senderRequirements
24 | connectionClass:(Class)connectionClass // must be CSASHelperConnection subclass
25 | protocol:(Protocol *)protocol;
26 |
27 | - (instancetype)initWithHelperID:(NSString *)helperID
28 | commandSet:(CSASCommandSet *)commandSet
29 | senderRequirements:(nullable NSArray *)senderRequirements
30 | connectionClass:(Class)connectionClass // must be CSASHelperConnection subclass
31 | interface:(NSXPCInterface *)interface;
32 |
33 | - (void)run __attribute__((noreturn));
34 |
35 | // Do any security checks prior to allowing a connection.
36 | // The default implementation checks the calling process's code signature
37 | // to make sure it matches one of the requirements passed in the
38 | // senderRequirements parameter when initializing the object.
39 | - (BOOL)shouldApproveConnection:(NSXPCConnection *)connection;
40 |
41 | // Check the code signature of an arbitrary NSXPCConnection and requirement.
42 | // This can be useful if you are establishing additional connections from the helper.
43 | - (BOOL)checkCodeSigningForConnection:(NSXPCConnection *)connection
44 | requirement:(NSString *)req
45 | error:(__autoreleasing NSError * _Nullable * _Nullable)error;
46 |
47 | // Logging methods that wrap the syslog(3) command.
48 | // Priority constants are defined in . Default is LOG_NOTICE.
49 | - (void)log:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2);
50 | - (void)logWithPriority:(int)priority format:(NSString *)format, ... NS_FORMAT_FUNCTION(2, 3);
51 | - (void)logWithPriority:(int)priority format:(NSString *)format arguments:(va_list)args NS_FORMAT_FUNCTION(2,0);
52 |
53 | @end
54 |
55 | NS_ASSUME_NONNULL_END
56 |
--------------------------------------------------------------------------------
/Sources/CSAuthSampleHelper/include/module.modulemap:
--------------------------------------------------------------------------------
1 | module CSAuthSampleHelper {
2 | header "CSASHelperTool.h"
3 | header "CSASHelperConnection.h"
4 | export *
5 | }
6 |
--------------------------------------------------------------------------------