Line data Source code
1 : /*
2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 : #pragma once
18 :
19 : #include "enumclass_utils.h"
20 : #include <dhtnet/certstore.h>
21 :
22 : #include <string>
23 : #include <vector>
24 : #include <memory>
25 :
26 : // OpenDHT
27 : namespace dht {
28 : namespace crypto {
29 : struct Certificate;
30 : }
31 : } // namespace dht
32 :
33 : namespace jami {
34 : namespace tls {
35 :
36 : #if !defined(S_IRWXG)
37 : #define S_IRWXG 00070
38 : #endif /* S_IRWXG */
39 : #if !defined(S_IRGRP)
40 : #define S_IRGRP 00040
41 : #endif /* S_IRGRP */
42 : #if !defined(S_IWGRP)
43 : #define S_IWGRP 00020
44 : #endif /* S_IWGRP */
45 : #if !defined(S_IXGRP)
46 : #define S_IXGRP 00010
47 : #endif /* S_IXGRP */
48 : #if !defined(S_IRWXO)
49 : #define S_IRWXO 00007
50 : #endif /* S_IRWXO */
51 : #if !defined(S_IROTH)
52 : #define S_IROTH 00004
53 : #endif /* S_IROTH */
54 : #if !defined(S_IWOTH)
55 : #define S_IWOTH 00002
56 : #endif /* S_IWOTH */
57 : #if !defined(S_IXOTH)
58 : #define S_IXOTH 00001
59 : #endif /* S_IXOTH */
60 :
61 : class TlsValidatorException : public std::runtime_error
62 : {
63 : public:
64 0 : TlsValidatorException(const std::string& str)
65 0 : : std::runtime_error(str) {};
66 : };
67 :
68 : class TlsValidator
69 : {
70 : public:
71 : /**
72 : * @enum CertificateCheck All validation fields
73 : *
74 : */
75 : enum class CertificateCheck {
76 : HAS_PRIVATE_KEY, /** This certificate has a build in private key */
77 : EXPIRED, /** This certificate is past its expiration date */
78 : STRONG_SIGNING, /** This certificate has been signed with a brute-force-able method */
79 : NOT_SELF_SIGNED, /** This certificate has been self signed */
80 : KEY_MATCH, /** The public and private keys provided don't match */
81 : PRIVATE_KEY_STORAGE_PERMISSION, /** The file hosting the private key isn't correctly secured */
82 : PUBLIC_KEY_STORAGE_PERMISSION, /** The file hosting the public key isn't correctly secured */
83 : PRIVATE_KEY_DIRECTORY_PERMISSIONS, /** The folder storing the private key isn't correctly
84 : secured */
85 : PUBLIC_KEY_DIRECTORY_PERMISSIONS, /** The folder storing the public key isn't correctly
86 : secured */
87 : PRIVATE_KEY_STORAGE_LOCATION, /** Some operating systems have extra policies for certificate
88 : storage */
89 : PUBLIC_KEY_STORAGE_LOCATION, /** Some operating systems have extra policies for certificate
90 : storage */
91 : PRIVATE_KEY_SELINUX_ATTRIBUTES, /** Some operating systems require keys to have extra
92 : attributes */
93 : PUBLIC_KEY_SELINUX_ATTRIBUTES, /** Some operating systems require keys to have extra
94 : attributes */
95 : EXIST, /** The certificate file doesn't exist or is not accessible */
96 : VALID, /** The file is not a certificate */
97 : VALID_AUTHORITY, /** The claimed authority did not sign the certificate */
98 : KNOWN_AUTHORITY, /** Some operating systems provide a list of trusted authorities, use it */
99 : NOT_REVOKED, /** The certificate has been revoked by the authority */
100 : AUTHORITY_MISMATCH, /** The certificate and authority mismatch */
101 : UNEXPECTED_OWNER, /** The certificate has an expected owner */
102 : NOT_ACTIVATED, /** The certificate has not been activated yet */
103 : COUNT__,
104 : };
105 :
106 : /**
107 : * @enum CertificateDetails Informative fields about a certificate
108 : */
109 : enum class CertificateDetails {
110 : EXPIRATION_DATE, /** The certificate expiration date */
111 : ACTIVATION_DATE, /** The certificate activation date */
112 : REQUIRE_PRIVATE_KEY_PASSWORD, /** Does the private key require a password */
113 : PUBLIC_SIGNATURE,
114 : VERSION_NUMBER,
115 : SERIAL_NUMBER,
116 : ISSUER,
117 : SUBJECT_KEY_ALGORITHM,
118 : SUBJECT_KEY,
119 : CN,
120 : UID,
121 : N,
122 : O,
123 : SIGNATURE_ALGORITHM,
124 : MD5_FINGERPRINT,
125 : SHA1_FINGERPRINT,
126 : PUBLIC_KEY_ID,
127 : ISSUER_DN,
128 : ISSUER_CN,
129 : ISSUER_UID,
130 : ISSUER_N,
131 : ISSUER_O,
132 : NEXT_EXPECTED_UPDATE_DATE,
133 : OUTGOING_SERVER, /** The hostname/outgoing server used for this certificate */
134 : IS_CA,
135 : COUNT__
136 : };
137 :
138 : /**
139 : * @enum CheckValuesType Categories of possible values for each CertificateCheck
140 : */
141 : enum class CheckValuesType {
142 : BOOLEAN,
143 : ISO_DATE,
144 : CUSTOM,
145 : NUMBER,
146 : COUNT__,
147 : };
148 :
149 : /**
150 : * @enum CheckValue possible values for check
151 : *
152 : * All boolean check use PASSED when the test result is positive and
153 : * FAILED when it is negative. All new check need to keep this convention
154 : * or ::isValid() result will become unrepresentative of the real state.
155 : *
156 : * CUSTOM should be avoided when possible. This enum can be extended when
157 : * new validated types are required.
158 : */
159 : enum class CheckValues {
160 : PASSED, /** Equivalent of a boolean "true" */
161 : FAILED, /** Equivalent of a boolean "false" */
162 : UNSUPPORTED, /** The operating system doesn't support or require the check */
163 : ISO_DATE, /** The check value is an ISO 8601 date YYYY-MM-DD[TH24:MM:SS+00:00] */
164 : CUSTOM, /** The check value cannot be represented with a finite set of values */
165 : NUMBER,
166 : COUNT__,
167 : };
168 :
169 : /**
170 : * @typedef CheckResult A validated and unvalidated result pair
171 : *
172 : * The CheckValue is the most important value of the pair. The string
173 : * can either be the value of a CheckValues::CUSTOM result or an
174 : * error code (where applicable).
175 : */
176 : using CheckResult = std::pair<CheckValues, std::string>;
177 :
178 : /**
179 : * Create a TlsValidator for a given certificate
180 : * @param certificate The certificate path
181 : * @param privatekey An optional private key file path
182 : * @param privatekeyPasswd An optional private key password
183 : * @param caList An optional CA list to use for certificate validation
184 : */
185 : TlsValidator(const dhtnet::tls::CertificateStore& certStore,
186 : const std::string& certificate,
187 : const std::string& privatekey = "",
188 : const std::string& privatekeyPasswd = "",
189 : const std::string& caList = "");
190 :
191 : TlsValidator(const dhtnet::tls::CertificateStore& certStore,
192 : const std::vector<std::vector<uint8_t>>& certificate_chain_raw);
193 :
194 : TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::vector<uint8_t>& certificate_raw);
195 :
196 : TlsValidator(const dhtnet::tls::CertificateStore& certStore, const std::shared_ptr<dht::crypto::Certificate>&);
197 :
198 : ~TlsValidator();
199 :
200 : bool hasCa() const;
201 :
202 : bool isValid(bool verbose = false);
203 :
204 : // Security checks
205 : CheckResult hasPrivateKey();
206 : CheckResult notExpired();
207 : CheckResult strongSigning();
208 : CheckResult notSelfSigned();
209 : CheckResult keyMatch();
210 : CheckResult privateKeyStoragePermissions();
211 : CheckResult publicKeyStoragePermissions();
212 : CheckResult privateKeyDirectoryPermissions();
213 : CheckResult publicKeyDirectoryPermissions();
214 : CheckResult privateKeyStorageLocation();
215 : CheckResult publicKeyStorageLocation();
216 : CheckResult privateKeySelinuxAttributes();
217 : CheckResult publicKeySelinuxAttributes();
218 : CheckResult exist();
219 : CheckResult valid();
220 : CheckResult validAuthority();
221 : CheckResult knownAuthority();
222 : CheckResult notRevoked();
223 : CheckResult authorityMatch();
224 : CheckResult expectedOwner();
225 : CheckResult activated();
226 :
227 : // Certificate details
228 : CheckResult getExpirationDate();
229 : CheckResult getActivationDate();
230 : CheckResult requirePrivateKeyPassword();
231 : CheckResult getPublicSignature();
232 : CheckResult getVersionNumber();
233 : CheckResult getSerialNumber();
234 : CheckResult getIssuer();
235 : CheckResult getSubjectKeyAlgorithm();
236 : CheckResult getSubjectKey();
237 : CheckResult getCN();
238 : CheckResult getUID();
239 : CheckResult getN();
240 : CheckResult getO();
241 : CheckResult getSignatureAlgorithm();
242 : CheckResult getMd5Fingerprint();
243 : CheckResult getSha1Fingerprint();
244 : CheckResult getPublicKeyId();
245 : CheckResult getIssuerDN();
246 : CheckResult getIssuerCN();
247 : CheckResult getIssuerUID();
248 : CheckResult getIssuerN();
249 : CheckResult getIssuerO();
250 : CheckResult outgoingServer();
251 : CheckResult isCA();
252 :
253 : void setCaTlsValidator(const TlsValidator& validator);
254 :
255 : std::map<std::string, std::string> getSerializedChecks();
256 :
257 : std::map<std::string, std::string> getSerializedDetails();
258 :
259 0 : std::shared_ptr<dht::crypto::Certificate> getCertificate() const { return x509crt_; }
260 :
261 : private:
262 : // Enum class names
263 : static const EnumClassNames<CertificateCheck> CertificateCheckNames;
264 :
265 : static const EnumClassNames<CertificateDetails> CertificateDetailsNames;
266 :
267 : static const EnumClassNames<const CheckValuesType> CheckValuesTypeNames;
268 :
269 : static const EnumClassNames<CheckValues> CheckValuesNames;
270 :
271 : /**
272 : * Map check to their check method
273 : */
274 : static const CallbackMatrix1D<CertificateCheck, TlsValidator, CheckResult> checkCallback;
275 :
276 : /**
277 : * Map check to their getter method
278 : */
279 : static const CallbackMatrix1D<CertificateDetails, TlsValidator, CheckResult> getterCallback;
280 :
281 : /**
282 : * Valid values for each categories
283 : */
284 : static const Matrix2D<CheckValuesType, CheckValues, bool> acceptedCheckValuesResult;
285 :
286 : static const Matrix1D<CertificateCheck, CheckValuesType> enforcedCheckType;
287 :
288 : const dhtnet::tls::CertificateStore& certStore_;
289 : std::string certificatePath_;
290 : std::string privateKeyPath_;
291 : std::string caListPath_ {};
292 :
293 : std::vector<uint8_t> certificateContent_;
294 :
295 : std::shared_ptr<dht::crypto::Certificate> x509crt_;
296 :
297 : bool certificateFileFound_ {false};
298 : bool certificateFound_ {false};
299 : bool privateKeyFound_ {false};
300 : bool privateKeyPassword_ {false};
301 : bool privateKeyMatch_ {false};
302 :
303 : bool caChecked_ {false};
304 : unsigned int caValidationOutput_ {
305 : 0}; // 0 means "no flags set", where flags are ones from gnutls_certificate_status_t
306 :
307 : mutable char copy_buffer[4096];
308 :
309 : /**
310 : * Helper method to convert a CheckResult into a std::string
311 : */
312 : std::string getStringValue(const CertificateCheck check, const CheckResult result);
313 :
314 : // Helper
315 : unsigned int compareToCa();
316 :
317 : public:
318 : #if 0 // TODO reimplement this method. do not use it as it
319 : /**
320 : * Verify that the local hostname points to a valid SSL server by
321 : * establishing a connection to it and by validating its certificate.
322 : *
323 : * @param host the DNS domain address that the certificate should feature
324 : * @return 0 if success, -1 otherwise
325 : */
326 : static int verifyHostnameCertificate(const std::string& host,
327 : const uint16_t port);
328 : #endif
329 :
330 : }; // TlsValidator
331 :
332 : } // namespace tls
333 : } // namespace jami
|