1 | /*
|
---|
2 | * Copyright (C) 2021 Apple Inc. All rights reserved.
|
---|
3 | *
|
---|
4 | * Redistribution and use in source and binary forms, with or without
|
---|
5 | * modification, are permitted provided that the following conditions
|
---|
6 | * are met:
|
---|
7 | * 1. Redistributions of source code must retain the above copyright
|
---|
8 | * notice, this list of conditions and the following disclaimer.
|
---|
9 | * 2. Redistributions in binary form must reproduce the above copyright
|
---|
10 | * notice, this list of conditions and the following disclaimer in the
|
---|
11 | * documentation and/or other materials provided with the distribution.
|
---|
12 | *
|
---|
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
---|
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
---|
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
---|
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
---|
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
---|
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
---|
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
---|
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
---|
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
---|
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
---|
23 | * THE POSSIBILITY OF SUCH DAMAGE.
|
---|
24 | */
|
---|
25 |
|
---|
26 | #import "config.h"
|
---|
27 | #import "PushClientConnection.h"
|
---|
28 |
|
---|
29 | #import "AppBundleRequest.h"
|
---|
30 | #import "CodeSigning.h"
|
---|
31 | #import "WebPushDaemon.h"
|
---|
32 | #import "WebPushDaemonConnectionConfiguration.h"
|
---|
33 | #import <JavaScriptCore/ConsoleTypes.h>
|
---|
34 | #import <wtf/HexNumber.h>
|
---|
35 | #import <wtf/Vector.h>
|
---|
36 | #import <wtf/cocoa/Entitlements.h>
|
---|
37 |
|
---|
38 | namespace WebPushD {
|
---|
39 |
|
---|
40 | Ref<ClientConnection> ClientConnection::create(xpc_connection_t connection)
|
---|
41 | {
|
---|
42 | return adoptRef(*new ClientConnection(connection));
|
---|
43 | }
|
---|
44 |
|
---|
45 | ClientConnection::ClientConnection(xpc_connection_t connection)
|
---|
46 | : m_xpcConnection(connection)
|
---|
47 | {
|
---|
48 | }
|
---|
49 |
|
---|
50 | void ClientConnection::updateConnectionConfiguration(const WebPushDaemonConnectionConfiguration& configuration)
|
---|
51 | {
|
---|
52 | if (configuration.hostAppAuditTokenData)
|
---|
53 | setHostAppAuditTokenData(*configuration.hostAppAuditTokenData);
|
---|
54 |
|
---|
55 | m_useMockBundlesForTesting = configuration.useMockBundlesForTesting;
|
---|
56 | }
|
---|
57 |
|
---|
58 | void ClientConnection::setHostAppAuditTokenData(const Vector<uint8_t>& tokenData)
|
---|
59 | {
|
---|
60 | audit_token_t token;
|
---|
61 | if (tokenData.size() != sizeof(token)) {
|
---|
62 | ASSERT_WITH_MESSAGE(false, "Attempt to set an audit token from incorrect number of bytes");
|
---|
63 | return;
|
---|
64 | }
|
---|
65 |
|
---|
66 | memcpy(&token, tokenData.data(), tokenData.size());
|
---|
67 |
|
---|
68 | if (hasHostAppAuditToken()) {
|
---|
69 | // Verify the token being set is equivalent to the last one set
|
---|
70 | audit_token_t& existingAuditToken = *m_hostAppAuditToken;
|
---|
71 | RELEASE_ASSERT(!memcmp(&existingAuditToken, &token, sizeof(token)));
|
---|
72 | return;
|
---|
73 | }
|
---|
74 |
|
---|
75 | m_hostAppAuditToken = WTFMove(token);
|
---|
76 | }
|
---|
77 |
|
---|
78 | const String& ClientConnection::hostAppCodeSigningIdentifier()
|
---|
79 | {
|
---|
80 | if (!m_hostAppCodeSigningIdentifier) {
|
---|
81 | if (!m_hostAppAuditToken)
|
---|
82 | m_hostAppCodeSigningIdentifier = String();
|
---|
83 | else
|
---|
84 | m_hostAppCodeSigningIdentifier = WebKit::codeSigningIdentifier(*m_hostAppAuditToken);
|
---|
85 | }
|
---|
86 |
|
---|
87 | return *m_hostAppCodeSigningIdentifier;
|
---|
88 | }
|
---|
89 |
|
---|
90 | bool ClientConnection::hostAppHasPushEntitlement()
|
---|
91 | {
|
---|
92 | if (!m_hostAppHasPushEntitlement) {
|
---|
93 | if (!m_hostAppAuditToken)
|
---|
94 | return false;
|
---|
95 | m_hostAppHasPushEntitlement = WTF::hasEntitlement(*m_hostAppAuditToken, "com.apple.private.webkit.webpush");
|
---|
96 | }
|
---|
97 |
|
---|
98 | return *m_hostAppHasPushEntitlement;
|
---|
99 | }
|
---|
100 |
|
---|
101 | void ClientConnection::setDebugModeIsEnabled(bool enabled)
|
---|
102 | {
|
---|
103 | if (enabled == m_debugModeEnabled)
|
---|
104 | return;
|
---|
105 |
|
---|
106 | m_debugModeEnabled = enabled;
|
---|
107 | broadcastDebugMessage(makeString("Turned Debug Mode ", m_debugModeEnabled ? "on" : "off"));
|
---|
108 | }
|
---|
109 |
|
---|
110 | void ClientConnection::broadcastDebugMessage(const String& message)
|
---|
111 | {
|
---|
112 | String messageIdentifier;
|
---|
113 | auto signingIdentifer = hostAppCodeSigningIdentifier();
|
---|
114 | if (signingIdentifer.isEmpty())
|
---|
115 | messageIdentifier = makeString ("[(0x", hex(reinterpret_cast<uint64_t>(m_xpcConnection.get()), WTF::HexConversionMode::Lowercase), ")] ");
|
---|
116 | else
|
---|
117 | messageIdentifier = makeString ("[", signingIdentifer, " (0x", hex(reinterpret_cast<uint64_t>(m_xpcConnection.get()), WTF::HexConversionMode::Lowercase), ")] ");
|
---|
118 |
|
---|
119 | Daemon::singleton().broadcastDebugMessage(JSC::MessageLevel::Info, makeString(messageIdentifier, message));
|
---|
120 | }
|
---|
121 |
|
---|
122 | void ClientConnection::enqueueAppBundleRequest(std::unique_ptr<AppBundleRequest>&& request)
|
---|
123 | {
|
---|
124 | RELEASE_ASSERT(m_xpcConnection);
|
---|
125 | m_pendingBundleRequests.append(WTFMove(request));
|
---|
126 | maybeStartNextAppBundleRequest();
|
---|
127 | }
|
---|
128 |
|
---|
129 | void ClientConnection::maybeStartNextAppBundleRequest()
|
---|
130 | {
|
---|
131 | RELEASE_ASSERT(m_xpcConnection);
|
---|
132 |
|
---|
133 | if (m_currentBundleRequest || m_pendingBundleRequests.isEmpty())
|
---|
134 | return;
|
---|
135 |
|
---|
136 | m_currentBundleRequest = m_pendingBundleRequests.takeFirst();
|
---|
137 | m_currentBundleRequest->start();
|
---|
138 | }
|
---|
139 |
|
---|
140 | void ClientConnection::didCompleteAppBundleRequest(AppBundleRequest& request)
|
---|
141 | {
|
---|
142 | // If our connection was closed there should be no in-progress bundle requests.
|
---|
143 | RELEASE_ASSERT(m_xpcConnection);
|
---|
144 |
|
---|
145 | ASSERT(m_currentBundleRequest.get() == &request);
|
---|
146 | m_currentBundleRequest = nullptr;
|
---|
147 |
|
---|
148 | maybeStartNextAppBundleRequest();
|
---|
149 | }
|
---|
150 |
|
---|
151 | void ClientConnection::connectionClosed()
|
---|
152 | {
|
---|
153 | broadcastDebugMessage("Connection closed");
|
---|
154 |
|
---|
155 | RELEASE_ASSERT(m_xpcConnection);
|
---|
156 | m_xpcConnection = nullptr;
|
---|
157 |
|
---|
158 | if (m_currentBundleRequest) {
|
---|
159 | m_currentBundleRequest->cancel();
|
---|
160 | m_currentBundleRequest = nullptr;
|
---|
161 | }
|
---|
162 |
|
---|
163 | Deque<std::unique_ptr<AppBundleRequest>> pendingBundleRequests;
|
---|
164 | pendingBundleRequests.swap(m_pendingBundleRequests);
|
---|
165 | for (auto& requst : pendingBundleRequests)
|
---|
166 | requst->cancel();
|
---|
167 | }
|
---|
168 |
|
---|
169 | } // namespace WebPushD
|
---|