OpenVPN 3 Core Library
Loading...
Searching...
No Matches
authcert.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#ifndef OPENVPN_AUTH_AUTHCERT_H
13#define OPENVPN_AUTH_AUTHCERT_H
14
15#include <string>
16#include <vector>
17#include <sstream>
18#include <cstring>
19#include <cstdint>
20#include <memory>
21#include <utility>
22
24#include <openvpn/common/rc.hpp>
31#include <openvpn/common/socktypes.hpp> // for ntohl/htonl
32
33namespace openvpn {
34
35class OpenSSLContext;
36class MbedTLSContext;
37
38class AuthCert : public RC<thread_unsafe_refcount>
39{
40 public:
41 // AuthCert needs to friend SSL implementation classes
42 friend class OpenSSLContext;
43 friend class MbedTLSContext;
44
46
47 class Fail
48 {
49 public:
50 // Ordered by severity. If many errors are present, the
51 // most severe error will be returned by get_code().
52 enum Type
53 {
54 OK = 0, // OK MUST be 0
55 EXPIRED, // less severe...
58 SNI_ERROR, // more severe...
59 N
60 };
61
62 void add_fail(const size_t depth, const Type new_code, std::string reason)
63 {
64 if (new_code > code)
65 code = new_code;
66 while (errors.size() <= depth)
67 errors.emplace_back();
68 std::string &err = errors[depth];
69 if (err.empty())
70 err = std::move(reason);
71 else if (err.find(reason) == std::string::npos)
72 {
73 err += ", ";
74 err += reason;
75 }
76 }
77
78 bool is_fail() const
79 {
80 return code != OK;
81 }
82
83 Type get_code() const
84 {
85 return code;
86 }
87
88 std::string to_string(const bool use_prefix) const
89 {
90 std::string ret;
91 if (use_prefix)
92 {
94 ret += ": ";
95 }
96 bool notfirst = false;
97 for (size_t i = 0; i < errors.size(); ++i)
98 {
99 if (errors[i].empty())
100 continue;
101 if (notfirst)
102 ret += ", ";
103 notfirst = true;
104 ret += errors[i];
105 ret += " [";
107 ret += ']';
108 }
109 return ret;
110 }
111
112 static std::string render_code(const Type code)
113 {
114 switch (code)
115 {
116 case OK:
117 return "OK";
118 case CERT_FAIL:
119 default:
120 return "CERT_FAIL";
121 case BAD_CERT_TYPE:
122 return "BAD_CERT_TYPE";
123 case EXPIRED:
124 return "EXPIRED";
125 case SNI_ERROR:
126 return "SNI_ERROR";
127 }
128 }
129
130 private:
131 Type code{OK}; // highest-valued cert fail code
132 std::vector<std::string> errors; // human-readable cert errors by depth
133 };
134
135 class Serial
136 {
137 public:
138 OPENVPN_EXCEPTION(serial_number_error);
139
141 {
142 std::memset(serial_number, 0xff, sizeof(serial_number));
143 }
144
145 Serial(const std::int64_t sn)
146 {
147 init_from_int64(sn);
148 }
149
150 Serial(const std::string &sn_str)
151 {
152 init_from_string(sn_str);
153 }
154
155#ifdef OPENVPN_JSON_INTERNAL
156 Serial(const Json::Value &jsn)
157 {
158 switch (jsn.type())
159 {
160 case Json::intValue:
161 case Json::uintValue:
162 init_from_int64(jsn.asInt64());
163 break;
164 case Json::stringValue:
165 init_from_string(jsn.asStringRef());
166 break;
167 case Json::nullValue:
168 throw serial_number_error("JSON serial is missing");
169 break;
170 default:
171 throw serial_number_error("JSON serial is of incorrect type (must be integer or string)");
172 }
173 }
174#endif
175
176 bool defined() const
177 {
178 for (size_t i = 0; i < 5; ++i)
179 if (serial_number32[i] != 0xffffffffU)
180 return true;
181 return false;
182 }
183
184 std::int64_t as_int64() const
185 {
186 if (serial_number32[0] != 0
187 || serial_number32[1] != 0
188 || serial_number32[2] != 0)
189 {
190 return -1;
191 }
192 const std::int64_t ret = std::int64_t((std::uint64_t(ntohl(serial_number32[3])) << 32)
193 | std::uint64_t(ntohl(serial_number32[4])));
194 if (ret < 0)
195 return -1;
196 return ret;
197 }
198
199 bool operator==(const Serial &other) const
200 {
201 return !std::memcmp(serial_number, other.serial_number, sizeof(serial_number));
202 }
203
204 bool operator!=(const Serial &other) const
205 {
206 return !operator==(other);
207 }
208
209 std::string to_string() const
210 {
211 return to_string(serial_number);
212 }
213
214 static std::string to_string(const std::uint8_t *serial_number)
215 {
216 std::string ret;
217 bool leading0 = true;
218 for (size_t i = 0; i < size(); ++i)
219 {
220 const std::uint8_t byte = serial_number[i];
221 const bool last = (i == size() - 1);
222 if (!byte && leading0 && !last)
223 continue;
224 RenderHexByte rhb(byte);
225 ret += rhb.char1();
226 ret += rhb.char2();
227 if (!last)
228 ret += ':';
229 leading0 = false;
230 }
231 return ret;
232 }
233
234 const std::uint8_t *number() const
235 {
236 return serial_number;
237 }
238
239 std::uint8_t *number()
240 {
241 return serial_number;
242 }
243
244 static constexpr size_t size()
245 {
246 return sizeof(serial_number);
247 }
248
249 private:
250 std::uint8_t parse_hex(const char c)
251 {
252 const int h = parse_hex_char(c);
253 if (h < 0)
254 throw Exception(std::string("'") + c + "' is not a hex char");
255 return std::uint8_t(h);
256 }
257
258 void init_from_int64(const std::int64_t sn)
259 {
260 if (sn >= 0)
261 {
262 serial_number32[0] = 0;
263 serial_number32[1] = 0;
264 serial_number32[2] = 0;
265 serial_number32[3] = htonl(std::uint32_t(sn >> 32));
266 serial_number32[4] = htonl(std::uint32_t(sn));
267 }
268 else
269 std::memset(serial_number, 0xff, sizeof(serial_number));
270 }
271
272 void init_from_string(const std::string &sn_str)
273 {
274 enum State
275 {
276 C1, // character #1 of hex byte
277 C2, // character #2 of hex byte
278 C2REQ, // like C2 but character is required
279 };
280
281 State state = C2REQ;
282 int i = int(sizeof(serial_number) - 1);
283 std::memset(serial_number, 0, sizeof(serial_number));
284
285 try
286 {
287 for (auto ci = sn_str.crbegin(); ci != sn_str.crend(); ++ci)
288 {
289 const char c = *ci;
290 switch (state)
291 {
292 case C2:
293 if (c == ':')
294 {
295 state = C2REQ;
296 break;
297 }
298 // fallthrough
299 case C2REQ:
300 if (c == ':')
301 throw Exception("spurious colon");
302 if (i < 0)
303 throw Exception("serial number too large (C2)");
304 serial_number[i] = parse_hex(c);
305 state = C1;
306 break;
307 case C1:
308 if (c == ':') // colon delimiter is optional
309 {
310 state = C2REQ;
311 --i;
312 break;
313 }
314 if (i < 0)
315 throw Exception("serial number too large (C1)");
316 serial_number[i--] |= static_cast<uint8_t>(parse_hex(c) << 4);
317 state = C2;
318 break;
319 default:
320 throw Exception("unknown state");
321 }
322 }
323 if (state == C2REQ)
324 throw Exception("expected leading serial number hex digit");
325 }
326 catch (const std::exception &e)
327 {
328 throw serial_number_error(e.what());
329 }
330 }
331
332 // certificate serial number in big-endian format
333 union {
334 std::uint8_t serial_number[20];
335 std::uint32_t serial_number32[5];
336 };
337 };
338
340 : defined_(false)
341 {
342 std::memset(issuer_fp, 0, sizeof(issuer_fp));
343 }
344
345 AuthCert(std::string cn_arg, const std::int64_t sn)
346 : defined_(true),
347 cn(std::move(cn_arg)),
348 serial(sn)
349 {
350 std::memset(issuer_fp, 0, sizeof(issuer_fp));
351 }
352
353#ifdef UNIT_TEST
354 AuthCert(const std::string &cn_arg,
355 const std::string &issuer_fp_arg,
356 const Serial &serial_arg)
357 : defined_(true),
358 cn(cn_arg),
359 serial(serial_arg)
360 {
361 parse_issuer_fp(issuer_fp_arg);
362 }
363#endif
364
365 bool defined() const
366 {
367 return defined_;
368 }
369
370 bool sni_defined() const
371 {
372 return !sni.empty();
373 }
374
375 bool cn_defined() const
376 {
377 return !cn.empty();
378 }
379
380 template <typename T>
382 {
383 return bin_prefix<T>(issuer_fp);
384 }
385
386 bool sn_defined() const
387 {
388 return serial.defined();
389 }
390
391 std::int64_t serial_number_as_int64() const
392 {
393 return serial.as_int64();
394 }
395
396 const Serial &get_serial() const
397 {
398 return serial;
399 }
400
401 bool operator==(const AuthCert &other) const
402 {
403 return sni == other.sni
404 && cn == other.cn
405 && serial == other.serial
406 && !std::memcmp(issuer_fp, other.issuer_fp, sizeof(issuer_fp));
407 }
408
409 bool operator!=(const AuthCert &other) const
410 {
411 return !operator==(other);
412 }
413
414 std::string to_string() const
415 {
416 std::ostringstream os;
417 if (!sni.empty())
418 os << "SNI=" << sni << ' ';
419 if (sni_metadata)
420 os << "SNI_CN=" << sni_metadata->sni_client_name(*this) << ' ';
421 os << "CN=" << cn;
422 if (serial.defined())
423 os << " SN=" << serial.to_string();
424 os << " ISSUER_FP=" << issuer_fp_str(false);
425 return os.str();
426 }
427
428 // example return for SN=65536: 01:00:00:00:00
429 std::string serial_number_str() const
430 {
431 return serial.to_string();
432 }
433
434 std::string issuer_fp_str(const bool openssl_fmt) const
435 {
436 if (openssl_fmt)
437 return render_hex_sep(issuer_fp, sizeof(issuer_fp), ':', true);
438 return render_hex(issuer_fp, sizeof(issuer_fp), false);
439 }
440
441 std::string normalize_cn() const // remove trailing "_AUTOLOGIN" from AS certs
442 {
443 if (cn.ends_with("_AUTOLOGIN"))
444 return cn.substr(0, cn.length() - 10);
445 return cn;
446 }
447
448 // Allow sni_metadata object, if it exists, to generate the client name.
449 // Otherwise fall back to normalize_cn().
450 std::string sni_client_name() const
451 {
452 if (sni_metadata)
453 return sni_metadata->sni_client_name(*this);
454 return normalize_cn();
455 }
456
457 const std::string &get_sni() const
458 {
459 return sni;
460 }
461
462 const std::string &get_cn() const
463 {
464 return cn;
465 }
466
468 {
469 return x509_track.get();
470 }
471
472 std::unique_ptr<X509Track::Set> x509_track_take_ownership()
473 {
474 return std::move(x509_track);
475 }
476
477 void add_fail(const size_t depth, const Fail::Type new_code, std::string reason)
478 {
479 if (!fail)
480 fail.reset(new Fail());
481 fail->add_fail(depth, new_code, std::move(reason));
482 }
483
484 bool is_fail() const
485 {
486 return fail && fail->is_fail();
487 }
488
489 const Fail *get_fail() const
490 {
491 return fail.get();
492 }
493
494 std::string fail_str() const
495 {
496 if (fail)
497 return fail->to_string(true);
498 return "OK";
499 }
500
501#ifndef UNIT_TEST
502 private:
503#endif
504
505#ifdef UNIT_TEST
506 void parse_issuer_fp(const std::string &issuer_fp_hex)
507 {
508 Buffer buf(issuer_fp, sizeof(issuer_fp), false);
509 parse_hex(buf, issuer_fp_hex);
510 if (buf.size() != sizeof(issuer_fp))
511 throw Exception("bad length in issuer_fp hex string");
512 }
513#endif
515
516 std::string sni; // SNI (server name indication)
517 std::string cn; // common name
518 Serial serial; // certificate serial number
519 std::uint8_t issuer_fp[20]; // issuer cert fingerprint
520
521 std::unique_ptr<Fail> fail;
522 std::unique_ptr<X509Track::Set> x509_track;
524};
525} // namespace openvpn
526
527#endif
std::string to_string(const bool use_prefix) const
Definition authcert.hpp:88
Type get_code() const
Definition authcert.hpp:83
static std::string render_code(const Type code)
Definition authcert.hpp:112
void add_fail(const size_t depth, const Type new_code, std::string reason)
Definition authcert.hpp:62
std::vector< std::string > errors
Definition authcert.hpp:132
static std::string to_string(const std::uint8_t *serial_number)
Definition authcert.hpp:214
Serial(const std::string &sn_str)
Definition authcert.hpp:150
bool operator==(const Serial &other) const
Definition authcert.hpp:199
const std::uint8_t * number() const
Definition authcert.hpp:234
bool operator!=(const Serial &other) const
Definition authcert.hpp:204
void init_from_string(const std::string &sn_str)
Definition authcert.hpp:272
OPENVPN_EXCEPTION(serial_number_error)
std::int64_t as_int64() const
Definition authcert.hpp:184
static constexpr size_t size()
Definition authcert.hpp:244
Serial(const std::int64_t sn)
Definition authcert.hpp:145
std::string to_string() const
Definition authcert.hpp:209
std::uint32_t serial_number32[5]
Definition authcert.hpp:335
std::uint8_t parse_hex(const char c)
Definition authcert.hpp:250
std::uint8_t * number()
Definition authcert.hpp:239
void init_from_int64(const std::int64_t sn)
Definition authcert.hpp:258
std::uint8_t serial_number[20]
Definition authcert.hpp:334
bool defined() const
Definition authcert.hpp:365
AuthCert(std::string cn_arg, const std::int64_t sn)
Definition authcert.hpp:345
const Fail * get_fail() const
Definition authcert.hpp:489
SNI::Metadata::UPtr sni_metadata
Definition authcert.hpp:523
bool cn_defined() const
Definition authcert.hpp:375
std::string serial_number_str() const
Definition authcert.hpp:429
std::string to_string() const
Definition authcert.hpp:414
std::string sni
Definition authcert.hpp:516
std::string issuer_fp_str(const bool openssl_fmt) const
Definition authcert.hpp:434
std::unique_ptr< Fail > fail
Definition authcert.hpp:521
bool operator==(const AuthCert &other) const
Definition authcert.hpp:401
std::string fail_str() const
Definition authcert.hpp:494
std::string sni_client_name() const
Definition authcert.hpp:450
std::unique_ptr< X509Track::Set > x509_track_take_ownership()
Definition authcert.hpp:472
std::string cn
Definition authcert.hpp:517
bool operator!=(const AuthCert &other) const
Definition authcert.hpp:409
std::uint8_t issuer_fp[20]
Definition authcert.hpp:519
const Serial & get_serial() const
Definition authcert.hpp:396
const std::string & get_cn() const
Definition authcert.hpp:462
bool sn_defined() const
Definition authcert.hpp:386
std::string normalize_cn() const
Definition authcert.hpp:441
std::int64_t serial_number_as_int64() const
Definition authcert.hpp:391
const X509Track::Set * x509_track_get() const
Definition authcert.hpp:467
const std::string & get_sni() const
Definition authcert.hpp:457
std::unique_ptr< X509Track::Set > x509_track
Definition authcert.hpp:522
bool sni_defined() const
Definition authcert.hpp:370
void add_fail(const size_t depth, const Fail::Type new_code, std::string reason)
Definition authcert.hpp:477
T issuer_fp_prefix() const
Definition authcert.hpp:381
bool is_fail() const
Definition authcert.hpp:484
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:908
char char2() const
Definition hexstr.hpp:99
char char1() const
Definition hexstr.hpp:95
std::unique_ptr< Metadata > UPtr
void parse_hex(V &dest, const std::string &str)
Definition hexstr.hpp:350
std::string to_string(const T &t)
Convert a value to a string.
Definition to_string.hpp:45
int parse_hex_char(const int c)
Definition hexstr.hpp:64
std::string render_hex(const unsigned char *data, size_t size, const bool caps=false)
Definition hexstr.hpp:133
std::string render_hex_sep(const unsigned char *data, size_t size, const char sep, const bool caps=false)
Definition hexstr.hpp:178
std::string ret
std::ostringstream os