-
-
Notifications
You must be signed in to change notification settings - Fork 191
Expand file tree
/
Copy pathhttp_request_auth.cpp
More file actions
293 lines (259 loc) · 9.34 KB
/
Copy pathhttp_request_auth.cpp
File metadata and controls
293 lines (259 loc) · 9.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/*
This file is part of libhttpserver
Copyright (C) 2011-2019 Sebastiano Merlino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
*/
// http_request public-API forwarders for the auth/credentials surface:
// get_user / get_pass / get_digested_user, check_digest_auth /
// check_digest_auth_digest, and the high-level TLS / client-certificate
// accessors. The declarations live in httpserver/http_request.hpp.
#include "httpserver/http_request.hpp"
#include <stdint.h>
#include <microhttpd.h> // NOLINT(build/include_order)
#include <string>
#include <string_view>
#include "httpserver/detail/http_request_impl.hpp"
#include "httpserver/http_utils.hpp"
namespace httpserver {
std::string_view http_request::get_user() const {
#ifdef HAVE_BAUTH
// Use user_pass_fetched instead of !username.empty() as the cache
// guard: an empty username string after a successful MHD call is a
// valid result (no auth header), but would cause a redundant re-fetch
// on every subsequent get_user() call without the boolean.
if (!impl_->user_pass_fetched) {
impl_->fetch_user_pass();
// fetch_user_pass() sets the flag only on the live path; set it
// here so the test-request (null connection_) path caches too.
impl_->user_pass_fetched = true;
}
return impl_->username;
#else
// BAUTH disabled at build time -> empty view (documented sentinel).
return std::string_view{};
#endif
}
std::string_view http_request::get_pass() const {
#ifdef HAVE_BAUTH
// Mirror the get_user() boolean guard.
if (!impl_->user_pass_fetched) {
impl_->fetch_user_pass();
impl_->user_pass_fetched = true;
}
return impl_->password;
#else
// BAUTH disabled at build time -> empty view (documented sentinel).
return std::string_view{};
#endif
}
std::string_view http_request::get_digested_user() const {
#ifdef HAVE_DAUTH
// Fast-path: cached value (non-empty) or test-request (null connection,
// digested_user holds whatever create_test_request().digested_user() set).
// The two conditions are ORed so the null-connection check is not
// repeated below.
if (!impl_->digested_user.empty() || impl_->connection_ == nullptr) {
return impl_->digested_user;
}
struct MHD_DigestAuthUsernameInfo* info = MHD_digest_auth_get_username3(impl_->connection_);
impl_->digested_user = EMPTY;
if (info != nullptr) {
if (info->username != nullptr) {
impl_->digested_user.assign(info->username, info->username_len);
}
MHD_free(info);
}
return impl_->digested_user;
#else
// DAUTH disabled at build time -> empty view (documented sentinel).
return std::string_view{};
#endif
}
http::http_utils::digest_auth_result http_request::check_digest_auth(
const std::string& realm,
const std::string& password,
unsigned int nonce_timeout,
uint32_t max_nc,
http::http_utils::digest_algorithm algo) const {
#ifdef HAVE_DAUTH
// CWE-476 guard: test-request path sets connection_ to nullptr.
// Passing nullptr to MHD_digest_auth_check3 is undefined behaviour;
// return the same WRONG_HEADER sentinel used on the HAVE_DAUTH-off
// path and by get_digested_user() when connection_ is null.
if (impl_->connection_ == nullptr) {
return http::http_utils::digest_auth_result::WRONG_HEADER;
}
std::string_view digested_user = get_digested_user();
enum MHD_DigestAuthResult result = MHD_digest_auth_check3(
impl_->connection_,
realm.c_str(),
digested_user.data(),
password.c_str(),
nonce_timeout,
max_nc,
MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT,
static_cast<MHD_DigestAuthMultiAlgo3>(algo));
return static_cast<http::http_utils::digest_auth_result>(result);
#else
// DAUTH disabled at build time -> the call is documented
// to "return a sentinel result". WRONG_HEADER
// is the most explicit "this request was never authenticated"
// terminal value of the digest_auth_result enum.
(void)realm;
(void)password;
(void)nonce_timeout;
(void)max_nc;
(void)algo;
return http::http_utils::digest_auth_result::WRONG_HEADER;
#endif
}
http::http_utils::digest_auth_result http_request::check_digest_auth_digest(
const std::string& realm,
const void* userdigest,
size_t userdigest_size,
unsigned int nonce_timeout,
uint32_t max_nc,
http::http_utils::digest_algorithm algo) const {
#ifdef HAVE_DAUTH
// CWE-476 guard: same null-connection guard as check_digest_auth.
if (impl_->connection_ == nullptr) {
return http::http_utils::digest_auth_result::WRONG_HEADER;
}
std::string_view digested_user = get_digested_user();
enum MHD_DigestAuthResult result = MHD_digest_auth_check_digest3(
impl_->connection_,
realm.c_str(),
digested_user.data(),
userdigest,
userdigest_size,
nonce_timeout,
max_nc,
MHD_DIGEST_AUTH_MULT_QOP_ANY_NON_INT,
static_cast<MHD_DigestAuthMultiAlgo3>(algo));
return static_cast<http::http_utils::digest_auth_result>(result);
#else
(void)realm;
(void)userdigest;
(void)userdigest_size;
(void)nonce_timeout;
(void)max_nc;
(void)algo;
return http::http_utils::digest_auth_result::WRONG_HEADER;
#endif
}
// ----------------------------------------------------------------------
// High-level GnuTLS accessors. Public surface is unconditional
// (same symbols visible whether HAVE_GNUTLS is on or off at build time);
// only the body of each method dispatches on HAVE_GNUTLS. Sentinel
// returns when GnuTLS is disabled match the architecture spec §7:
// empty for the four string_view accessors, false for the three
// predicates, -1 for the two time accessors.
//
// Three of the five `noexcept` accessors (is_client_cert_verified and
// the two time accessors) wrap populate_all_cert_fields() in try/catch
// so a hypothetical std::bad_alloc from the cache-fill path doesn't
// violate the noexcept commitment; on throw they return the documented
// sentinel. The other two (has_tls_session / has_client_certificate)
// never call populate_all_cert_fields() -- they query the session
// directly through non-allocating impl methods, so no try/catch is
// needed. The four string_view accessors deliberately omit `noexcept`
// so the allocator failure mode is observable.
// ----------------------------------------------------------------------
bool http_request::has_tls_session() const noexcept {
#ifdef HAVE_GNUTLS
return impl_->has_tls_session();
#else
return false;
#endif
}
bool http_request::has_client_certificate() const noexcept {
#ifdef HAVE_GNUTLS
return impl_->has_client_certificate();
#else
return false;
#endif
}
std::string_view http_request::get_client_cert_dn() const {
#ifdef HAVE_GNUTLS
impl_->populate_all_cert_fields();
return std::string_view(impl_->client_cert_dn.data(),
impl_->client_cert_dn.size());
#else
return std::string_view{};
#endif
}
std::string_view http_request::get_client_cert_issuer_dn() const {
#ifdef HAVE_GNUTLS
impl_->populate_all_cert_fields();
return std::string_view(impl_->client_cert_issuer_dn.data(),
impl_->client_cert_issuer_dn.size());
#else
return std::string_view{};
#endif
}
std::string_view http_request::get_client_cert_cn() const {
#ifdef HAVE_GNUTLS
impl_->populate_all_cert_fields();
return std::string_view(impl_->client_cert_cn.data(),
impl_->client_cert_cn.size());
#else
return std::string_view{};
#endif
}
std::string_view http_request::get_client_cert_fingerprint_sha256() const {
#ifdef HAVE_GNUTLS
impl_->populate_all_cert_fields();
return std::string_view(impl_->client_cert_fingerprint_sha256.data(),
impl_->client_cert_fingerprint_sha256.size());
#else
return std::string_view{};
#endif
}
bool http_request::is_client_cert_verified() const noexcept {
#ifdef HAVE_GNUTLS
try {
impl_->populate_all_cert_fields();
return impl_->client_cert_verified;
} catch (...) {
return false;
}
#else
return false;
#endif
}
std::int64_t http_request::get_client_cert_not_before() const noexcept {
#ifdef HAVE_GNUTLS
try {
impl_->populate_all_cert_fields();
return impl_->client_cert_not_before;
} catch (...) {
return -1;
}
#else
return -1;
#endif
}
std::int64_t http_request::get_client_cert_not_after() const noexcept {
#ifdef HAVE_GNUTLS
try {
impl_->populate_all_cert_fields();
return impl_->client_cert_not_after;
} catch (...) {
return -1;
}
#else
return -1;
#endif
}
} // namespace httpserver