parsec_service/utils/
service_builder.rs

1// Copyright 2019 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3//! Assemble the service from a user-defined config
4//!
5//! The service builder is required to bootstrap all the components based on a
6//! provided configuration.
7use super::global_config::GlobalConfigBuilder;
8use crate::authenticators::Authenticate;
9use crate::back::{
10    backend_handler::{BackEndHandler, BackEndHandlerBuilder},
11    dispatcher::DispatcherBuilder,
12};
13use crate::front::{
14    domain_socket::DomainSocketListenerBuilder, front_end::FrontEndHandler,
15    front_end::FrontEndHandlerBuilder, listener::Listen,
16};
17use crate::key_info_managers::KeyInfoManagerFactory;
18use crate::providers::{core::ProviderBuilder as CoreProviderBuilder, Provide};
19use crate::utils::config::{
20    AuthenticatorConfig, KeyInfoManagerConfig, ListenerConfig, ListenerType, ProviderConfig,
21    ServiceConfig,
22};
23use anyhow::Result;
24use log::{error, warn};
25use parsec_interface::operations_protobuf::ProtobufConverter;
26use parsec_interface::requests::AuthType;
27use parsec_interface::requests::{BodyType, ProviderId};
28use std::collections::HashMap;
29use std::collections::HashSet;
30use std::io::{Error, ErrorKind};
31use std::sync::Arc;
32use std::time::Duration;
33use threadpool::{Builder as ThreadPoolBuilder, ThreadPool};
34
35#[cfg(feature = "direct-authenticator")]
36use crate::authenticators::direct_authenticator::DirectAuthenticator;
37#[cfg(feature = "jwt-svid-authenticator")]
38use crate::authenticators::jwt_svid_authenticator::JwtSvidAuthenticator;
39#[cfg(feature = "unix-peer-credentials-authenticator")]
40use crate::authenticators::unix_peer_credentials_authenticator::UnixPeerCredentialsAuthenticator;
41
42#[cfg(feature = "cryptoauthlib-provider")]
43use crate::providers::cryptoauthlib::ProviderBuilder as CryptoAuthLibProviderBuilder;
44#[cfg(feature = "mbed-crypto-provider")]
45use crate::providers::mbed_crypto::ProviderBuilder as MbedCryptoProviderBuilder;
46#[cfg(feature = "pkcs11-provider")]
47use crate::providers::pkcs11::ProviderBuilder as Pkcs11ProviderBuilder;
48#[cfg(feature = "tpm-provider")]
49use crate::providers::tpm::ProviderBuilder as TpmProviderBuilder;
50#[cfg(feature = "trusted-service-provider")]
51use crate::providers::trusted_service::ProviderBuilder as TrustedServiceProviderBuilder;
52
53#[cfg(feature = "cryptoauthlib-provider")]
54use crate::providers::cryptoauthlib::Provider as CryptoAuthLibProvider;
55#[cfg(feature = "mbed-crypto-provider")]
56use crate::providers::mbed_crypto::Provider as MbedCryptoProvider;
57#[cfg(feature = "pkcs11-provider")]
58use crate::providers::pkcs11::Provider as Pkcs11Provider;
59#[cfg(feature = "tpm-provider")]
60use crate::providers::tpm::Provider as TpmProvider;
61#[cfg(feature = "trusted-service-provider")]
62use crate::providers::trusted_service::Provider as TrustedServiceProvider;
63
64#[cfg(any(
65    feature = "mbed-crypto-provider",
66    feature = "pkcs11-provider",
67    feature = "tpm-provider",
68    feature = "cryptoauthlib-provider",
69    feature = "trusted-service-provider"
70))]
71use crate::providers::ProviderIdentity;
72
73#[cfg(any(
74    feature = "mbed-crypto-provider",
75    feature = "pkcs11-provider",
76    feature = "tpm-provider",
77    feature = "cryptoauthlib-provider",
78    feature = "trusted-service-provider"
79))]
80use log::info;
81
82const WIRE_PROTOCOL_VERSION_MINOR: u8 = 0;
83const WIRE_PROTOCOL_VERSION_MAJOR: u8 = 1;
84
85/// Default value for the limit on the request body size (in bytes) - equal to 1MB
86const DEFAULT_BODY_LEN_LIMIT: usize = 1 << 20;
87
88/// Default value for the limit on the buffer size for response (in bytes) - equal to 1MB
89pub const DEFAULT_BUFFER_SIZE_LIMIT: usize = 1 << 20;
90
91type Provider = Arc<dyn Provide + Send + Sync>;
92type Authenticator = Box<dyn Authenticate + Send + Sync>;
93
94/// Service component builder and assembler
95///
96/// Entity responsible for converting a Parsec service configuration into a fully formed service.
97/// Each component is independently created after which its ownership can be passed to the previous
98/// component in the ownership chain. The service's ownership is then passed in the form of
99/// ownership of a `FrontEndHandler` instance.
100#[derive(Copy, Clone, Debug)]
101pub struct ServiceBuilder;
102
103impl ServiceBuilder {
104    /// Evaluate the provided configuration and assemble a service based on it. If the configuration contains
105    /// any errors or inconsistencies, an `Err` is returned.
106    ///
107    /// # Errors
108    /// * if any of the fields specified in the configuration are inconsistent (e.g. key info manager with name 'X'
109    /// requested for a certain provider does not exist) or if required fields are missing, an error of kind
110    /// `InvalidData` is returned with a string describing the cause more accurately.
111    pub fn build_service(config: &ServiceConfig) -> Result<FrontEndHandler> {
112        GlobalConfigBuilder::new()
113            .with_log_error_details(config.core_settings.log_error_details.unwrap_or(false))
114            .with_buffer_size_limit(
115                config
116                    .core_settings
117                    .buffer_size_limit
118                    .unwrap_or(DEFAULT_BUFFER_SIZE_LIMIT),
119            )
120            .with_allow_deprecated(config.core_settings.allow_deprecated.unwrap_or(false))
121            .build();
122
123        let authenticators = build_authenticators(&config.authenticator)?;
124
125        if authenticators[0].0 == AuthType::Direct {
126            warn!("Direct authenticator has been set as the default one. It is only secure under specific requirements. Please make sure to read the Recommendations on a Secure Parsec Deployment at https://parallaxsecond.github.io/parsec-book/parsec_security/secure_deployment.html");
127        }
128
129        let key_info_manager_builders = get_key_info_manager_builders(
130            config.key_manager.as_ref().unwrap_or(&Vec::new()),
131            authenticators[0].0,
132        )?;
133
134        let providers = build_providers(
135            config.provider.as_ref().unwrap_or(&Vec::new()),
136            key_info_manager_builders,
137        )?;
138
139        if providers.is_empty() {
140            error!("Parsec needs at least one provider to start. No valid provider could be created from the configuration.");
141            return Err(Error::new(ErrorKind::InvalidData, "need one provider").into());
142        }
143
144        let backend_handlers = build_backend_handlers(providers, &authenticators)?;
145
146        let dispatcher = DispatcherBuilder::new()
147            .with_backends(backend_handlers)
148            .build()?;
149
150        let mut front_end_handler_builder = FrontEndHandlerBuilder::new();
151        for (auth_type, authenticator) in authenticators {
152            front_end_handler_builder =
153                front_end_handler_builder.with_authenticator(auth_type, authenticator);
154        }
155        front_end_handler_builder = front_end_handler_builder
156            .with_dispatcher(dispatcher)
157            .with_body_len_limit(
158                config
159                    .core_settings
160                    .body_len_limit
161                    .unwrap_or(DEFAULT_BODY_LEN_LIMIT),
162            );
163
164        Ok(front_end_handler_builder.build()?)
165    }
166
167    /// Construct the service IPC front component and return ownership to it.
168    pub fn start_listener(config: ListenerConfig) -> Result<Box<dyn Listen>> {
169        let listener = match config.listener_type {
170            ListenerType::DomainSocket => DomainSocketListenerBuilder::new()
171                .with_timeout(Duration::from_millis(config.timeout))
172                .with_socket_path(config.socket_path.map(|s| s.into()))
173                .build(),
174        }?;
175
176        Ok(Box::new(listener))
177    }
178
179    /// Construct the thread pool that will be used to process all service requests.
180    pub fn build_threadpool(num_threads: Option<usize>) -> ThreadPool {
181        let mut threadpool_builder = ThreadPoolBuilder::new();
182        if let Some(num_threads) = num_threads {
183            threadpool_builder = threadpool_builder.num_threads(num_threads);
184        }
185        threadpool_builder.build()
186    }
187}
188
189fn build_backend_handlers(
190    mut providers: Vec<(ProviderId, Provider)>,
191    authenticators: &[(AuthType, Authenticator)],
192) -> Result<HashMap<ProviderId, BackEndHandler>> {
193    let mut map = HashMap::new();
194
195    let mut core_provider_builder = CoreProviderBuilder::new()
196        .with_wire_protocol_version(WIRE_PROTOCOL_VERSION_MINOR, WIRE_PROTOCOL_VERSION_MAJOR);
197
198    for (_auth_type, authenticator) in authenticators {
199        let authenticator_info = authenticator
200            .describe()
201            .map_err(|_| Error::new(ErrorKind::Other, "Failed to describe authenticator"))?;
202        core_provider_builder = core_provider_builder.with_authenticator_info(authenticator_info);
203    }
204
205    for (provider_id, provider) in providers.drain(..) {
206        core_provider_builder = core_provider_builder.with_provider(provider.clone());
207
208        let backend_handler = BackEndHandlerBuilder::new()
209            .with_provider(provider)
210            .with_converter(Box::from(ProtobufConverter {}))
211            .with_provider_id(provider_id)
212            .with_content_type(BodyType::Protobuf)
213            .with_accept_type(BodyType::Protobuf)
214            .build()?;
215        let _ = map.insert(provider_id, backend_handler);
216    }
217
218    let core_provider_backend = BackEndHandlerBuilder::new()
219        .with_provider(Arc::new(core_provider_builder.build()?))
220        .with_converter(Box::from(ProtobufConverter {}))
221        .with_provider_id(ProviderId::Core)
222        .with_content_type(BodyType::Protobuf)
223        .with_accept_type(BodyType::Protobuf)
224        .build()?;
225
226    let _ = map.insert(ProviderId::Core, core_provider_backend);
227
228    Ok(map)
229}
230
231fn build_providers(
232    configs: &[ProviderConfig],
233    kim_factorys: HashMap<String, KeyInfoManagerFactory>,
234) -> Result<Vec<(ProviderId, Provider)>> {
235    let mut providers = Vec::new();
236    let mut provider_names = HashSet::new();
237    for config in configs {
238        // Check for duplicate providers.
239        let provider_id = config.provider_id();
240
241        // Check for duplicate provider names.
242        let provider_name = config.provider_name()?;
243        if provider_names.contains(&provider_name) {
244            error!("Duplicate provider names found.\n{} was found twice.\nThe \'[[provider]] name config option can be used to differentiate between providers of the same type.\nPlease check your config.toml file.", provider_name);
245            return Err(
246                Error::new(ErrorKind::InvalidData, "duplicate provider names found").into(),
247            );
248        }
249        let _ = provider_names.insert(provider_name.clone());
250
251        let kim_factory = match kim_factorys.get(config.key_info_manager()) {
252            Some(kim_factory) => kim_factory,
253            None => {
254                format_error!(
255                    "Key info manager builder with specified name was not found",
256                    config.key_info_manager()
257                );
258                return Err(Error::new(
259                    ErrorKind::InvalidData,
260                    "key info manager builder not found",
261                )
262                .into());
263            }
264        };
265        // The safety is checked by the fact that only one instance per provider type is enforced.
266        let provider = match unsafe { get_provider(config, kim_factory) } {
267            Ok(None) => {
268                warn!("Provider {} is skipped.", provider_id);
269                continue;
270            }
271            Ok(Some(provider)) => provider,
272            Err(e) => {
273                format_error!(
274                    &format!("Provider with ID {} cannot be created", provider_id),
275                    e
276                );
277                return Err(Error::new(ErrorKind::Other, "failed to create provider").into());
278            }
279        };
280        providers.push((provider_id, provider));
281    }
282
283    Ok(providers)
284}
285
286// This cfg_attr is used to allow the fact that key_info_manager is not used when there is no
287// providers.
288#[cfg_attr(
289    not(all(
290        feature = "mbed-crypto-provider",
291        feature = "pkcs11-provider",
292        feature = "tpm-provider",
293        feature = "cryptoauthlib-provider",
294        feature = "trusted-service-provider"
295    )),
296    allow(unused_variables),
297    allow(clippy::match_single_binding)
298)]
299// Ok(None) is returned when the provider is skipped by configuration.
300unsafe fn get_provider(
301    config: &ProviderConfig,
302    kim_factory: &KeyInfoManagerFactory,
303) -> Result<Option<Provider>> {
304    match config {
305        #[cfg(feature = "mbed-crypto-provider")]
306        ProviderConfig::MbedCrypto { .. } => {
307            info!("Creating a Mbed Crypto Provider.");
308            let provider_identity = ProviderIdentity::new(
309                MbedCryptoProvider::PROVIDER_UUID.to_string(),
310                config.provider_name()?,
311            );
312            Ok(Some(Arc::new(
313                MbedCryptoProviderBuilder::new()
314                    .with_key_info_store(kim_factory.build_client(provider_identity))
315                    .with_provider_name(config.provider_name()?)
316                    .build()?,
317            )))
318        }
319        #[cfg(feature = "pkcs11-provider")]
320        ProviderConfig::Pkcs11 {
321            library_path,
322            slot_number,
323            serial_number,
324            user_pin,
325            software_public_operations,
326            allow_export,
327            ..
328        } => {
329            info!("Creating a PKCS 11 Provider.");
330            let provider_identity = ProviderIdentity::new(
331                Pkcs11Provider::PROVIDER_UUID.to_string(),
332                config.provider_name()?,
333            );
334            Ok(Some(Arc::new(
335                Pkcs11ProviderBuilder::new()
336                    .with_key_info_store(kim_factory.build_client(provider_identity))
337                    .with_provider_name(config.provider_name()?)
338                    .with_pkcs11_library_path(library_path.clone())
339                    .with_slot_number(*slot_number)
340                    .with_serial_number(serial_number.clone())
341                    .with_user_pin(user_pin.clone())
342                    .with_software_public_operations(*software_public_operations)
343                    .with_allow_export(*allow_export)
344                    .build()?,
345            )))
346        }
347        #[cfg(feature = "tpm-provider")]
348        ProviderConfig::Tpm {
349            tcti,
350            owner_hierarchy_auth,
351            endorsement_hierarchy_auth,
352            skip_if_no_tpm,
353            ..
354        } => {
355            use std::str::FromStr;
356            use tss_esapi::tcti_ldr::{TctiContext, TctiNameConf};
357            info!("Creating a TPM Provider.");
358
359            let provider_identity = ProviderIdentity::new(
360                TpmProvider::PROVIDER_UUID.to_string(),
361                config.provider_name()?,
362            );
363
364            let tcti_name_conf = TctiNameConf::from_str(tcti).map_err(|_| {
365                Error::new(ErrorKind::InvalidData, "Invalid TCTI configuration string")
366            })?;
367            if *skip_if_no_tpm == Some(true) {
368                // TODO: When the TPM Provider uses the new TctiContext, pass it directly to the
369                // builder.
370                let _tcti_context = match TctiContext::initialize(tcti_name_conf) {
371                    Ok(tcti_context) => tcti_context,
372                    Err(e) => {
373                        format_error!("Error creating a TCTI context", e);
374                        // We make the assumption that the TCTI Name Configuration is correct
375                        // and that if we failed creating a TCTI Contecxt it means that there
376                        // is no TPM support on the platform.
377                        return Ok(None);
378                    }
379                };
380            }
381
382            let mut builder = TpmProviderBuilder::new()
383                .with_key_info_store(kim_factory.build_client(provider_identity))
384                .with_tcti(tcti)
385                .with_provider_name(config.provider_name()?)
386                .with_owner_hierarchy_auth(owner_hierarchy_auth.clone());
387            if endorsement_hierarchy_auth.is_some() {
388                builder = builder.with_endorsement_hierarchy_auth(
389                    endorsement_hierarchy_auth.as_ref().unwrap().clone(),
390                );
391            }
392            Ok(Some(Arc::new(builder.build()?)))
393        }
394        #[cfg(feature = "cryptoauthlib-provider")]
395        ProviderConfig::CryptoAuthLib {
396            device_type,
397            iface_type,
398            wake_delay,
399            rx_retries,
400            slave_address,
401            bus,
402            baud,
403            access_key_file_name,
404            ..
405        } => {
406            info!("Creating a CryptoAuthentication Library Provider.");
407            let provider_identity = ProviderIdentity::new(
408                CryptoAuthLibProvider::PROVIDER_UUID.to_string(),
409                config.provider_name()?,
410            );
411            Ok(Some(Arc::new(
412                CryptoAuthLibProviderBuilder::new()
413                    .with_key_info_store(kim_factory.build_client(provider_identity))
414                    .with_provider_name(config.provider_name()?)
415                    .with_device_type(device_type.to_string())
416                    .with_iface_type(iface_type.to_string())
417                    .with_wake_delay(*wake_delay)
418                    .with_rx_retries(*rx_retries)
419                    .with_slave_address(*slave_address)
420                    .with_bus(*bus)
421                    .with_baud(*baud)
422                    .with_access_key_file(access_key_file_name.clone())
423                    .build()?,
424            )))
425        }
426        #[cfg(feature = "trusted-service-provider")]
427        ProviderConfig::TrustedService { .. } => {
428            info!("Creating a Trusted Service Provider.");
429            let provider_identity = ProviderIdentity::new(
430                TrustedServiceProvider::PROVIDER_UUID.to_string(),
431                config.provider_name()?,
432            );
433            Ok(Some(Arc::new(
434                TrustedServiceProviderBuilder::new()
435                    .with_key_info_store(kim_factory.build_client(provider_identity))
436                    .with_provider_name(config.provider_name()?)
437                    .build()?,
438            )))
439        }
440        #[cfg(not(all(
441            feature = "mbed-crypto-provider",
442            feature = "pkcs11-provider",
443            feature = "tpm-provider",
444            feature = "cryptoauthlib-provider",
445            feature = "trusted-service-provider"
446        )))]
447        _ => {
448            error!(
449                "Provider \"{:?}\" chosen in the configuration was not compiled in Parsec binary.",
450                config
451            );
452            Err(Error::new(ErrorKind::InvalidData, "provider not compiled").into())
453        }
454    }
455}
456
457fn get_key_info_manager_builders(
458    configs: &[KeyInfoManagerConfig],
459    default_auth_type: AuthType,
460) -> Result<HashMap<String, KeyInfoManagerFactory>> {
461    let mut map = HashMap::new();
462    for config in configs {
463        let _ = map.insert(
464            config.name.clone(),
465            KeyInfoManagerFactory::new(config, default_auth_type)?,
466        );
467    }
468
469    Ok(map)
470}
471
472// Allowed to simplify the cfg blocks
473#[allow(clippy::unnecessary_wraps)]
474fn build_authenticators(config: &AuthenticatorConfig) -> Result<Vec<(AuthType, Authenticator)>> {
475    // The authenticators supported by the Parsec service.
476    // NOTE: order here is important. The order in which the elements are added here is the
477    // order in which they will be returned to any client requesting them!
478    // Currently only one authenticator is allowed by the Parsec service
479    // See parallaxsecond/parsec#271
480    let mut authenticators: Vec<(AuthType, Authenticator)> = Vec::new();
481
482    match config {
483        #[cfg(feature = "direct-authenticator")]
484        AuthenticatorConfig::Direct { admins } => authenticators.push((
485            AuthType::Direct,
486            Box::from(DirectAuthenticator::new(
487                admins.as_ref().cloned().unwrap_or_default(),
488            )),
489        )),
490        #[cfg(feature = "unix-peer-credentials-authenticator")]
491        AuthenticatorConfig::UnixPeerCredentials { admins } => authenticators.push((
492            AuthType::UnixPeerCredentials,
493            Box::from(UnixPeerCredentialsAuthenticator::new(
494                admins.as_ref().cloned().unwrap_or_default(),
495            )),
496        )),
497        #[cfg(feature = "jwt-svid-authenticator")]
498        AuthenticatorConfig::JwtSvid {
499            workload_endpoint,
500            admins,
501        } => {
502            let jwt_svid_authenticator = match JwtSvidAuthenticator::new(
503                workload_endpoint.to_string(),
504                admins.as_ref().cloned().unwrap_or_default(),
505            ) {
506                Some(authenticator) => authenticator,
507                None => {
508                    return Err(Error::new(
509                        ErrorKind::Other,
510                        "can not create a SPIFFE Workload API client",
511                    )
512                    .into())
513                }
514            };
515            authenticators.push((AuthType::JwtSvid, Box::from(jwt_svid_authenticator)))
516        }
517        #[cfg(not(all(
518            feature = "direct-authenticator",
519            feature = "unix-peer-credentials-authenticator",
520            feature = "jwt-svid-authenticator",
521        )))]
522        _ => {
523            error!(
524                "Authenticator \"{:?}\" chosen in the configuration was not compiled in Parsec binary.",
525                config
526            );
527            return Err(Error::new(ErrorKind::InvalidData, "authenticator not compiled").into());
528        }
529    };
530
531    Ok(authenticators)
532}