hickory_proto/openssl/
tls_server.rs

1// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! DNS over TLS server implementations for OpenSSL
9
10use std::fs::File;
11use std::io;
12use std::io::Read;
13use std::path::Path;
14
15use crate::error::{ProtoError, ProtoResult};
16use openssl::ssl::{SslAcceptor, SslMethod, SslOptions, SslVerifyMode};
17
18pub use openssl::pkcs12::Pkcs12;
19pub use openssl::pkey::{PKey, Private};
20pub use openssl::stack::Stack;
21pub use openssl::x509::X509;
22
23/// Read the certificate from the specified path.
24///
25/// If the password is specified, then it will be used to decode the Certificate
26#[allow(clippy::type_complexity)]
27pub fn read_cert_pkcs12(
28    path: &Path,
29    password: Option<&str>,
30) -> ProtoResult<((Option<X509>, Option<Stack<X509>>), Option<PKey<Private>>)> {
31    let mut file = File::open(path).map_err(|e| {
32        ProtoError::from(format!(
33            "error opening pkcs12 cert file: {}: {}",
34            path.display(),
35            e
36        ))
37    })?;
38
39    let mut pkcs12_bytes = vec![];
40    file.read_to_end(&mut pkcs12_bytes).map_err(|e| {
41        ProtoError::from(format!(
42            "could not read pkcs12 from: {}: {}",
43            path.display(),
44            e
45        ))
46    })?;
47    let pkcs12 = Pkcs12::from_der(&pkcs12_bytes).map_err(|e| {
48        ProtoError::from(format!(
49            "badly formatted pkcs12 from: {}: {}",
50            path.display(),
51            e
52        ))
53    })?;
54    let parsed = pkcs12.parse2(password.unwrap_or("")).map_err(|e| {
55        ProtoError::from(format!(
56            "failed to open pkcs12 from: {}: {}",
57            path.display(),
58            e
59        ))
60    })?;
61
62    Ok(((parsed.cert, parsed.ca), parsed.pkey))
63}
64
65/// Read the certificate from the specified path.
66///
67/// If the password is specified, then it will be used to decode the Certificate
68pub fn read_cert_pem(path: &Path) -> ProtoResult<(X509, Option<Stack<X509>>)> {
69    let mut file = File::open(path).map_err(|e| {
70        ProtoError::from(format!(
71            "error opening cert file: {}: {}",
72            path.display(),
73            e
74        ))
75    })?;
76
77    let mut key_bytes = vec![];
78    file.read_to_end(&mut key_bytes).map_err(|e| {
79        ProtoError::from(format!(
80            "could not read cert key from: {}: {}",
81            path.display(),
82            e
83        ))
84    })?;
85
86    let cert_chain = X509::stack_from_pem(&key_bytes)?;
87    let cert_count = cert_chain.len();
88    let mut iter = cert_chain.into_iter();
89
90    let cert = match iter.next() {
91        None => {
92            return Err(ProtoError::from(format!(
93                "no certs read from file: {}",
94                path.display()
95            )))
96        }
97        Some(cert) => cert,
98    };
99
100    if cert_count < 1 {
101        Ok((cert, None))
102    } else {
103        let mut stack = Stack::<X509>::new()?;
104        for c in iter {
105            stack.push(c)?;
106        }
107        Ok((cert, Some(stack)))
108    }
109}
110
111/// Reads a private key from a pkcs8 formatted, and possibly encoded file
112pub fn read_key_from_pkcs8(path: &Path, password: Option<&str>) -> ProtoResult<PKey<Private>> {
113    let mut file = File::open(path)?;
114    let mut buf = Vec::new();
115    file.read_to_end(&mut buf)?;
116
117    match password.map(str::as_bytes) {
118        Some(password) => {
119            PKey::private_key_from_pkcs8_passphrase(&buf, password).map_err(Into::into)
120        }
121        None => PKey::private_key_from_pkcs8_passphrase(&buf, &[0_u8; 0]).map_err(Into::into),
122    }
123}
124
125/// Reads a private key from a der formatted file
126pub fn read_key_from_der(path: &Path) -> ProtoResult<PKey<Private>> {
127    let mut file = File::open(path)?;
128    let mut buf = Vec::new();
129    file.read_to_end(&mut buf)?;
130
131    PKey::private_key_from_der(&buf).map_err(Into::into)
132}
133
134/// Construct the new Acceptor with the associated pkcs12 data
135pub fn new_acceptor(
136    cert: X509,
137    chain: Option<Stack<X509>>,
138    key: PKey<Private>,
139) -> io::Result<SslAcceptor> {
140    // TODO: make an internal error type with conversions
141    let mut builder = SslAcceptor::mozilla_modern(SslMethod::tls())?;
142
143    builder.set_private_key(&key)?;
144    builder.set_certificate(&cert)?;
145    builder.set_verify(SslVerifyMode::NONE);
146    builder.set_options(
147        SslOptions::NO_COMPRESSION
148            | SslOptions::NO_SSLV2
149            | SslOptions::NO_SSLV3
150            | SslOptions::NO_TLSV1
151            | SslOptions::NO_TLSV1_1,
152    );
153
154    if let Some(ref chain) = chain {
155        for cert in chain {
156            builder.add_extra_chain_cert(cert.to_owned())?;
157        }
158    }
159
160    // validate our certificate and private key match
161    builder.check_private_key()?;
162
163    Ok(builder.build())
164}