Asylo
enclave_manager.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2018 Asylo authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef ASYLO_PLATFORM_CORE_ENCLAVE_MANAGER_H_
20 #define ASYLO_PLATFORM_CORE_ENCLAVE_MANAGER_H_
21 
22 // Declares the enclave client API, providing types and methods for loading,
23 // accessing, and finalizing enclaves.
24 
25 #include <string>
26 #include <utility>
27 
28 #include "absl/container/flat_hash_map.h"
29 #include "absl/memory/memory.h"
30 #include "absl/strings/string_view.h"
31 #include "absl/synchronization/mutex.h"
32 #include "absl/time/time.h"
33 #include "absl/types/variant.h"
34 #include "asylo/enclave.pb.h" // IWYU pragma: export
35 #include "asylo/platform/arch/fork.pb.h"
36 #include "asylo/platform/core/enclave_client.h"
37 #include "asylo/platform/core/enclave_config_util.h"
38 #include "asylo/platform/core/shared_resource_manager.h"
39 #include "asylo/platform/primitives/enclave_type.h"
40 #include "asylo/platform/primitives/util/message.h"
41 #include "asylo/util/status.h" // IWYU pragma: export
42 #include "asylo/util/statusor.h"
43 
44 namespace asylo {
45 class EnclaveLoader;
46 
47 /// Enclave Manager configuration.
49  public:
50  /// Configuration server connection attributes.
51  ///
52  /// A part of an enclave's configuration is expected to be the same across all
53  /// enclaves running under a single instance of an OS. An Enclave manager can
54  /// obtain such configuration from the Asylo daemon running on the system.
55  /// Alternately, the creator of the enclave manager can directly provide such
56  /// configuration to the enclave manager. To this end, an
57  /// EnclaveManagerOptions instance either holds the information necessary for
58  /// connecting to the config server, or holds a HostConfig proto. If the
59  /// enclave manager is configured with an options object containing the
60  /// server-connection information, the enclave manager obtains the necessary
61  /// information by contacting the Asylo daemon. Else, the enclave manager
62  /// directly uses the HostConfig info stored within the options structure.
63  ///
64  /// The ConfigServerConnectionAttributes struct holds information necessary
65  /// for contacting the config server running inside the Asylo daemon.
67  ConfigServerConnectionAttributes(std::string address,
68  absl::Duration timeout)
71 
72  std::string server_address;
74  };
75 
76  /// Constructs a default EnclaveManagerOptions object.
78 
79  /// Configures a connection to the config server.
80  ///
81  /// Sets the information necessary for contacting the config server within the
82  /// Asylo daemon.
83  ///
84  /// \return A reference to this EnclaveManagerOptions object.
86  std::string address, absl::Duration timeout);
87 
88  /// Sets the HostConfig proto within this object.
89  ///
90  /// \return A reference to this EnclaveManagerOptions object.
91  EnclaveManagerOptions &set_host_config(HostConfig config);
92 
93  /// Returns the address of the configuration server.
94  ///
95  /// \return The address of the server from which the HostConfig information
96  /// can be obtained. Returns an error if
97  /// ConfigServerConnectionAttributes are not set.
99 
100  /// Returns the configuration server connection timeout.
101  ///
102  /// \return The connection timeout for the server from which the HostConfig
103  /// information can be obtained, or an error if
104  /// ConfigServerConnectionAttributes are not set.
106 
107  /// Returns the embedded HostConfig object.
108  ///
109  /// \return The HostConfig information embedded within this object, or an
110  /// error if such information is not embedded within the object.
112 
113  /// Returns true if a HostConfig instance is embedded in this object.
114  bool holds_host_config() const;
115 
116  private:
117  // A variant that either holds information necessary for connecting to the
118  // config server or a HostConfig proto.
119  absl::variant<ConfigServerConnectionAttributes, HostConfig> host_config_info_;
120 };
121 
122 /// A manager object responsible for creating and managing enclave instances.
123 ///
124 /// EnclaveManager is a singleton class that tracks the status of enclaves
125 /// within a process. Users of this class first supply a configuration using the
126 /// static Configure() method, and then get a pointer to the singleton instance
127 /// as specified by this configuration by calling the static Instance() method.
128 /// Note that the EnclaveManager class must be configured before the instance
129 /// pointer can be obtained.
130 ///
131 /// The EnclaveManager::Configure() method takes an instance of the
132 /// EnclaveManagerOptions as its only input. This instance can be configured by
133 /// calling its public setter methods. Note that these setter methods return an
134 /// instance of the EnclaveManagerOptions() by reference so that the various
135 /// setters could be chained together.
136 ///
137 /// Example Usage:
138 /// ```
139 /// EnclaveManager::Configure(
140 /// EnclaveManagerOptions()
141 /// .set_config_server_connection_attributes(
142 /// "[::]:8000",
143 /// absl::Milliseconds(100)));
144 /// auto manager_result = EnclaveManager::Instance();
145 /// if (!manager_result.ok()) {
146 /// LOG(QFATAL) << manager_result.status();
147 /// }
148 /// EnclaveManager *manager = manager_result.ValueOrDie();
149 /// ...
150 /// ```
151 ///
152 /// One of the responsibilities of the EnclaveManager class is to provide sane
153 /// initial configuration to the enclaves it launches. The contents of the
154 /// EnclaveManagerOptions instance control how the default values for the
155 /// configuration are chosen.
156 class EnclaveManager {
157  public:
158  /// Fetches the EnclaveManager singleton instance.
159  ///
160  /// \return A StatusOr containing either the global EnclaveManager instance or
161  /// an error describing why it could not be returned.
162  static StatusOr<EnclaveManager *> Instance();
163 
164  /// Configures the enclave manager.
165  ///
166  /// \param options Configuration options as described in
167  /// EnclaveManagerOptions.
169 
170  /// Loads an enclave.
171  ///
172  /// Loads a new enclave with default enclave config settings and binds it to a
173  /// name. The actual work of opening the enclave is delegated to the passed
174  /// loader object.
175  ///
176  /// It is an error to specify a name which is already bound to an enclave.
177  ///
178  /// Example:
179  /// ```
180  /// LoadEnclave("/EchoEnclave", SgxLoader("echoService.so"));
181  /// ```
182  ///
183  /// \param name Name to bind the loaded enclave under.
184  /// \param loader Configured enclave loader to load from.
185  /// \param base_address Start address to load enclave(optional).
186  /// \param enclave_size The size of the enclave in memory(only needed if
187  /// |base_address| is specified).
189  void *base_address = nullptr,
190  const size_t enclave_size = 0);
191 
192  /// Loads an enclave.
193  ///
194  /// Loads a new enclave with custom enclave config settings and binds it to a
195  /// name. The actual work of opening the enclave is delegated to the passed
196  /// loader object.
197  ///
198  /// It is an error to specify a name which is already bound to an enclave.
199  ///
200  /// Example:
201  ///
202  /// ```
203  /// EnclaveConfig config;
204  /// ... // populate config proto.
205  /// LoadEnclave("/EchoEnclave", SgxLoader("echoService.so"), config);
206  /// ```
207  ///
208  /// \param name Name to bind the loaded enclave under.
209  /// \param loader Configured enclave loader to load from.
210  /// \param config Enclave configuration to launch the enclave with.
211  /// \param base_address Start address to load enclave(optional).
212  /// \param enclave_size The size of the enclave in memory(only needed if
213  /// |base_address| is specified).
215  EnclaveConfig config, void *base_address = nullptr,
216  const size_t enclave_size = 0);
217 
218  /// Fetches a client to a loaded enclave.
219  ///
220  /// \param name The name of an EnclaveClient that may be registered in the
221  /// EnclaveManager.
222  /// \return A mutable pointer to the EnclaveClient if the name is
223  /// registered. Otherwise returns nullptr.
224  EnclaveClient *GetClient(const std::string &name) const
225  LOCKS_EXCLUDED(client_table_lock_);
226 
227  /// Returns the name of an enclave client.
228  ///
229  /// \param client A pointer to a client that may be registered in the
230  /// EnclaveManager.
231  /// \return The name of an enclave client. If no enclave matches `client` the
232  /// empty string will be returned.
233  const std::string GetName(const EnclaveClient *client) const
234  LOCKS_EXCLUDED(client_table_lock_);
235 
236  /// Destroys an enclave.
237  ///
238  /// Destroys an enclave. This method calls `client's` EnterAndFinalize entry
239  /// point with final_input unless `skip_finalize` is true, then calls
240  /// `client's` DestroyEnclave method, and then removes client's name from the
241  /// EnclaveManager client registry. The manager owns the client, so removing
242  /// it calls client's destructor and frees its memory. The client is destroyed
243  /// regardless of whether `client's` EnterAndFinalize method succeeds or
244  /// fails. This method must not be invoked more than once.
245  ///
246  /// \param client A client attached to the enclave to destroy.
247  /// \param final_input Input to pass the enclave's finalizer.
248  /// \param skip_finalize If true, the enclave is destroyed without invoking
249  /// its Finalize method.
250  /// \return The Status returned by the enclave's Finalize method, or an
251  /// OK Status if that was skipped.
253  bool skip_finalize = false)
254  LOCKS_EXCLUDED(client_table_lock_);
255 
256  /// Fetches the shared resource manager object.
257  ///
258  /// \return The SharedResourceManager instance.
260  return &shared_resource_manager_;
261  }
262 
263  /// Fetches the shared resource manager object.
264  ///
265  /// \return The SharedResourceManager instance.
267  return &shared_resource_manager_;
268  }
269 
270  /// Get the loader of an enclave. This should only be used during fork in
271  /// order to load an enclave with the same loader as the parent.
272  EnclaveLoader *GetLoaderFromClient(EnclaveClient *client)
273  LOCKS_EXCLUDED(client_table_lock_);
274 
275  private:
276  EnclaveManager() EXCLUSIVE_LOCKS_REQUIRED(mu_);
277  EnclaveManager(EnclaveManager const &) = delete;
278  EnclaveManager &operator=(EnclaveManager const &) = delete;
279 
280  // Retrieves and returns a HostConfig proto as specified by the
281  // EnclaveManagerOptions which the EnclaveManager was configured when its
282  // sngleton instance was created.
283  HostConfig GetHostConfig() EXCLUSIVE_LOCKS_REQUIRED(mu_);
284 
285  // Loads a new enclave with custom enclave config settings and binds it to a
286  // name. The actual work of opening the enclave is delegated to the passed
287  // loader object.
288  Status LoadEnclaveInternal(const std::string &name,
289  const EnclaveLoader &loader,
290  const EnclaveConfig &config,
291  void *base_address = nullptr,
292  const size_t enclave_size = 0)
293  LOCKS_EXCLUDED(client_table_lock_);
294 
295  // Deletes an enclave client reference that points to an enclave that no
296  // longer exists. This should only happen during fork.
297  void RemoveEnclaveReference(const std::string &name)
298  LOCKS_EXCLUDED(client_table_lock_);
299 
300  // Create a thread to periodically update logic.
301  void SpawnWorkerThread();
302 
303  // Top level loop run by the background worker thread.
304  void WorkerLoop();
305 
306  // Execute a single iteration of the work loop.
307  void Tick();
308 
309  // Manager object for untrusted resources shared with enclaves.
310  SharedResourceManager shared_resource_manager_;
311 
312  // Value synchronized to CLOCK_MONOTONIC by the worker loop.
313  std::atomic<int64_t> clock_monotonic_;
314 
315  // Value synchronized to CLOCK_REALTIME by the worker loop.
316  std::atomic<int64_t> clock_realtime_;
317 
318  // A mutex guarding |client_by_name_|, |name_by_client_|, and
319  // |loader_by_client_| tables.
320  mutable absl::Mutex client_table_lock_;
321 
322  absl::flat_hash_map<std::string, std::unique_ptr<EnclaveClient>>
323  client_by_name_ GUARDED_BY(client_table_lock_);
324  absl::flat_hash_map<const EnclaveClient *, std::string> name_by_client_
325  GUARDED_BY(client_table_lock_);
326 
327  absl::flat_hash_map<const EnclaveClient *, std::unique_ptr<EnclaveLoader>>
328  loader_by_client_ GUARDED_BY(client_table_lock_);
329 
330  // A part of the configuration for enclaves launched by the enclave manager
331  // comes from the Asylo daemon. This member caches such configuration.
332  HostConfig host_config_;
333 
334  // Mutex guarding the static state of this class.
335  static absl::Mutex mu_;
336 
337  // Indication whether the class has been configured so that an instance could
338  // be created.
339  static bool configured_ GUARDED_BY(mu_);
340 
341  // Configuration options for this class.
342  static EnclaveManagerOptions *options_ GUARDED_BY(mu_);
343 
344  // Singleton instance of this class.
345  static EnclaveManager *instance_ GUARDED_BY(mu_);
346 };
347 
348 /// An abstract enclave loader.
349 ///
350 /// Host applications must load an enclave before using it. This is accomplished
351 /// via an architecture specific implementation of the EnclaveLoader interface.
353  public:
354  virtual ~EnclaveLoader() = default;
355 
356  protected:
357  // Only allow the enclave loading via the manager object.
358  friend class EnclaveManager;
359 
360  // Loads an enclave, returning a pointer to a client on success and a non-ok
361  // status on failure.
363  const std::string &name) const {
365  return LoadEnclave(name, /*base_address=*/nullptr, /*enclave_size=*/0,
366  config);
367  }
368 
369  // Loads an enclave at the specified address, returning a pointer to a client
370  // on success and a non-ok status on failure.
372  const std::string &name, void *base_address, const size_t enclave_size,
373  const EnclaveConfig &config) const = 0;
374 
375  // Gets a copy of the loader that loaded a previous enclave. This is only used
376  // by fork to load a child enclave with the same loader as the parent.
377  virtual StatusOr<std::unique_ptr<EnclaveLoader>> Copy() const = 0;
378 };
379 
380 // Stores the mapping between signals and the enclave with a handler installed
381 // for that signal.
382 class EnclaveSignalDispatcher {
383  public:
384  static EnclaveSignalDispatcher *GetInstance();
385 
386  // Associates a signal with an enclave which registers a handler for it.
387  // It's not supported for multiple enclaves to register the same signal. In
388  // that case, the latter will overwrite the former.
389  //
390  // Returns the enclave client that previous registered |signum|, or nullptr if
391  // no enclave has registered |signum| yet.
393  LOCKS_EXCLUDED(signal_enclave_map_lock_);
394 
395  // Gets the enclave that registered a handler for |signum|.
397  LOCKS_EXCLUDED(signal_enclave_map_lock_);
398 
399  // Deregisters all the signals registered by |client|.
401  LOCKS_EXCLUDED(signal_enclave_map_lock_);
402 
403  // Looks for the enclave client that registered |signum|, and calls
404  // EnterAndHandleSignal() with that enclave client. |signum|, |info| and
405  // |ucontext| are passed into the enclave.
407  void *ucontext);
408 
409  private:
410  EnclaveSignalDispatcher() = default; // Private to enforce singleton.
411  EnclaveSignalDispatcher(EnclaveSignalDispatcher const &) = delete;
412  void operator=(EnclaveSignalDispatcher const &) = delete;
413 
414  // Mapping of signal number to the enclave client that registered it.
415  absl::flat_hash_map<int, EnclaveClient *> signal_to_client_map_
416  GUARDED_BY(signal_enclave_map_lock_);
417 
418  // A mutex that guards signal_to_client_map_ and client_to_signal_map_.
419  mutable absl::Mutex signal_enclave_map_lock_;
420 };
421 
422 } // namespace asylo
423 
424 #endif // ASYLO_PLATFORM_CORE_ENCLAVE_MANAGER_H_
Status EnterEnclaveAndHandleSignal(int signum, siginfo_t *info, void *ucontext)
virtual StatusOr< std::unique_ptr< EnclaveLoader > > Copy() const =0
ConfigServerConnectionAttributes(std::string address, absl::Duration timeout)
Definition: enclave_manager.h:67
static StatusOr< EnclaveManager * > Instance()
Fetches the EnclaveManager singleton instance.
Configuration server connection attributes.
Definition: enclave_manager.h:66
EnclaveManagerOptions & set_host_config(HostConfig config)
Sets the HostConfig proto within this object.
bool holds_host_config() const
Returns true if a HostConfig instance is embedded in this object.
EnclaveClient * GetClient(const std::string &name) const LOCKS_EXCLUDED(client_table_lock_)
Fetches a client to a loaded enclave.
StatusOr< absl::Duration > get_config_server_connection_timeout() const
Returns the configuration server connection timeout.
Status LoadEnclave(const std::string &name, const EnclaveLoader &loader, EnclaveConfig config, void *base_address=nullptr, const size_t enclave_size=0)
Loads an enclave.
StatusOr< EnclaveClient * > GetClientForSignal(int signum) const LOCKS_EXCLUDED(signal_enclave_map_lock_)
StatusOr< std::string > get_config_server_address() const
Returns the address of the configuration server.
Status LoadEnclave(const std::string &name, const EnclaveLoader &loader, void *base_address=nullptr, const size_t enclave_size=0)
Loads an enclave.
static Status Configure(const EnclaveManagerOptions &options)
Configures the enclave manager.
virtual ~EnclaveLoader()=default
static EnclaveSignalDispatcher * GetInstance()
SharedResourceManager * shared_resources()
Fetches the shared resource manager object.
Definition: enclave_manager.h:259
EnclaveManagerOptions & set_config_server_connection_attributes(std::string address, absl::Duration timeout)
Configures a connection to the config server.
const EnclaveClient * RegisterSignal(int signum, EnclaveClient *client) LOCKS_EXCLUDED(signal_enclave_map_lock_)
absl::Duration connection_timeout
Definition: enclave_manager.h:73
Enclave Manager configuration.
Definition: enclave_manager.h:48
const SharedResourceManager * shared_resources() const
Fetches the shared resource manager object.
Definition: enclave_manager.h:266
EnclaveManagerOptions()
Constructs a default EnclaveManagerOptions object.
Definition: aead_cryptor.h:32
Status DeregisterAllSignalsForClient(EnclaveClient *client) LOCKS_EXCLUDED(signal_enclave_map_lock_)
Status DestroyEnclave(EnclaveClient *client, const EnclaveFinal &final_input, bool skip_finalize=false) LOCKS_EXCLUDED(client_table_lock_)
Destroys an enclave.
EnclaveLoader * GetLoaderFromClient(EnclaveClient *client) LOCKS_EXCLUDED(client_table_lock_)
Get the loader of an enclave.
const std::string GetName(const EnclaveClient *client) const LOCKS_EXCLUDED(client_table_lock_)
Returns the name of an enclave client.
StatusOr< HostConfig > get_host_config() const
Returns the embedded HostConfig object.
std::string server_address
Definition: enclave_manager.h:72