OpenVPN 3 Core Library
Loading...
Searching...
No Matches
cr.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// Encapsulate the state of a static or dynamic authentication challenge.
13
14#ifndef OPENVPN_AUTH_CR_H
15#define OPENVPN_AUTH_CR_H
16
17#include <string>
18#include <sstream>
19#include <vector>
20
24#include <openvpn/common/rc.hpp>
26
27// Static Challenge response:
28// SCRV1:<BASE64_PASSWORD>:<BASE64_RESPONSE>
29//
30// Dynamic Challenge:
31// CRV1:<FLAGS>:<STATE_ID>:<BASE64_USERNAME>:<CHALLENGE_TEXT>
32// FLAGS is a comma-separated list of options:
33// E -- echo
34// R -- response required
35//
36// Dynamic Challenge response:
37// Username: [username decoded from username_base64]
38// Password: CRV1::<STATE_ID>::<RESPONSE_TEXT>
39
40namespace openvpn {
41class ChallengeResponse : public RC<thread_unsafe_refcount>
42{
43 public:
45
46 OPENVPN_SIMPLE_EXCEPTION(dynamic_challenge_parse_error);
47 OPENVPN_SIMPLE_EXCEPTION(static_challenge_parse_error);
48
50 : echo(false), response_required(false)
51 {
52 }
53
54 explicit ChallengeResponse(const std::string &cookie)
55 : echo(false), response_required(false)
56 {
57 init(cookie);
58 }
59
60 ChallengeResponse(const std::string &cookie, const std::string &user)
61 : echo(false), response_required(false)
62 {
63 if (!is_dynamic(cookie) && cookie.find_first_of(':') == std::string::npos)
64 {
65 state_id = cookie;
66 username = user;
67 }
68 else
69 init(cookie);
70 }
71
72 void init(const std::string &cookie)
73 {
74 typedef std::vector<std::string> StringList;
75 StringList sl;
76 sl.reserve(5);
77 Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, cookie, ':', 0, 4);
78 if (sl.size() != 5)
79 throw dynamic_challenge_parse_error();
80 if (sl[0] != "CRV1")
81 throw dynamic_challenge_parse_error();
82
83 // parse options
84 {
85 StringList opt;
86 opt.reserve(2);
87 Split::by_char_void<StringList, NullLex, Split::NullLimit>(opt, sl[1], ',');
88 for (StringList::const_iterator i = opt.begin(); i != opt.end(); ++i)
89 {
90 if (*i == "E")
91 echo = true;
92 else if (*i == "R")
93 response_required = true;
94 }
95 }
96
97 // save state ID
98 state_id = sl[2];
99
100 // save username
101 try
102 {
103 username = base64->decode(sl[3]);
104 }
105 catch (const Base64::base64_decode_error &)
106 {
107 throw dynamic_challenge_parse_error();
108 }
109
110 // save challenge
111 challenge_text = sl[4];
112 }
113
114 static bool is_dynamic(const std::string &s)
115 {
116 return string::starts_with(s, "CRV1:");
117 }
118
119 static bool is_static(const std::string &s)
120 {
121 return string::starts_with(s, "SCRV1:");
122 }
123
124 static void validate_dynamic(const std::string &cookie)
125 {
126 ChallengeResponse cr(cookie);
127 }
128
129 std::string construct_dynamic_password(const std::string &response) const
130 {
131 std::ostringstream os;
132 os << "CRV1::" << state_id << "::" << response;
133 return os.str();
134 }
135
136 static std::string construct_static_password(const std::string &password,
137 const std::string &response)
138 {
139 std::ostringstream os;
140 os << "SCRV1:" << base64->encode(password) << ':' << base64->encode(response);
141 return os.str();
142 }
143
144 static void parse_static_cookie(const std::string &cookie,
145 std::string &password,
146 std::string &response)
147 {
148 typedef std::vector<std::string> StringList;
149 StringList sl;
150 sl.reserve(3);
151 Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, cookie, ':');
152 if (sl.size() != 3)
153 throw static_challenge_parse_error();
154 if (sl[0] != "SCRV1")
155 throw static_challenge_parse_error();
156
157 // get password
158 try
159 {
160 password = base64->decode(sl[1]);
161 }
162 catch (const Base64::base64_decode_error &)
163 {
164 throw static_challenge_parse_error();
165 }
166
167 // get response
168 try
169 {
170 response = base64->decode(sl[2]);
171 }
172 catch (const Base64::base64_decode_error &)
173 {
174 throw static_challenge_parse_error();
175 }
176 }
177
178 static std::string generate_dynamic_challenge(const std::string &session_token,
179 const std::string &username,
180 const std::string &challenge,
181 const bool echo,
182 const bool response_required)
183 {
184 std::ostringstream os;
185 bool comma = false;
186 os << "CRV1:";
187 if (echo)
188 {
189 if (comma)
190 os << ",";
191 os << "E";
192 comma = true;
193 }
195 {
196 if (comma)
197 os << ",";
198 os << "R";
199 comma = true;
200 }
201 os << ':' << session_token;
202 os << ':' << base64->encode(username);
203 os << ':' << challenge;
204 return os.str();
205 }
206
207 const std::string &get_state_id() const
208 {
209 return state_id;
210 }
211 const std::string &get_username() const
212 {
213 return username;
214 }
215 bool get_echo() const
216 {
217 return echo;
218 }
220 {
221 return response_required;
222 }
223 const std::string &get_challenge_text() const
224 {
225 return challenge_text;
226 }
227
228 private:
229 bool echo;
231 std::string state_id;
232 std::string username;
233 std::string challenge_text;
234};
235} // namespace openvpn
236
237#endif
std::string encode(const V &data) const
Definition base64.hpp:139
size_t decode(void *data, size_t len, const std::string &str) const
Definition base64.hpp:186
static bool is_dynamic(const std::string &s)
Definition cr.hpp:114
bool get_echo() const
Definition cr.hpp:215
std::string construct_dynamic_password(const std::string &response) const
Definition cr.hpp:129
ChallengeResponse(const std::string &cookie, const std::string &user)
Definition cr.hpp:60
const std::string & get_username() const
Definition cr.hpp:211
void init(const std::string &cookie)
Definition cr.hpp:72
static void parse_static_cookie(const std::string &cookie, std::string &password, std::string &response)
Definition cr.hpp:144
std::string state_id
Definition cr.hpp:231
static std::string generate_dynamic_challenge(const std::string &session_token, const std::string &username, const std::string &challenge, const bool echo, const bool response_required)
Definition cr.hpp:178
static bool is_static(const std::string &s)
Definition cr.hpp:119
static std::string construct_static_password(const std::string &password, const std::string &response)
Definition cr.hpp:136
const std::string & get_state_id() const
Definition cr.hpp:207
std::string challenge_text
Definition cr.hpp:233
OPENVPN_SIMPLE_EXCEPTION(static_challenge_parse_error)
bool get_response_required() const
Definition cr.hpp:219
const std::string & get_challenge_text() const
Definition cr.hpp:223
static void validate_dynamic(const std::string &cookie)
Definition cr.hpp:124
RCPtr< ChallengeResponse > Ptr
Definition cr.hpp:44
std::string username
Definition cr.hpp:232
OPENVPN_SIMPLE_EXCEPTION(dynamic_challenge_parse_error)
ChallengeResponse(const std::string &cookie)
Definition cr.hpp:54
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
bool starts_with(const STRING &str, const std::string &prefix)
Definition string.hpp:79
const Base64 * base64
Definition base64.hpp:299
std::ostringstream os