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