Skip to content

Commit b452308

Browse files
WL#15006: WebauthN authentication plugin
Description: New authenticaiton plugin - authentication_webauthn Change-Id: I9e09016922b568fc1b6d8f7559d16408aab8ae42
1 parent 9e6d810 commit b452308

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2796
-615
lines changed

CMakeLists.txt

+29-7
Original file line numberDiff line numberDiff line change
@@ -933,8 +933,10 @@ OPTION(WITH_AUTHENTICATION_KERBEROS
933933

934934
IF(WITH_INTERNAL AND (NOT SOLARIS))
935935
SET(WITH_AUTHENTICATION_FIDO_DEFAULT ON)
936+
SET(WITH_AUTHENTICATION_WEBAUTHN_DEFAULT ON)
936937
ELSE()
937938
SET(WITH_AUTHENTICATION_FIDO_DEFAULT OFF)
939+
SET(WITH_AUTHENTICATION_WEBAUTHN_DEFAULT OFF)
938940
ENDIF()
939941

940942
IF(WITH_INTERNAL)
@@ -945,11 +947,16 @@ OPTION(WITH_AUTHENTICATION_FIDO
945947
"Report error if the FIDO authentication plugin cannot be built."
946948
${WITH_AUTHENTICATION_FIDO_DEFAULT})
947949

950+
OPTION(WITH_AUTHENTICATION_WEBAUTHN
951+
"Report error if the WEBAUTHN authentication plugin cannot be built."
952+
${WITH_AUTHENTICATION_WEBAUTHN_DEFAULT})
953+
948954
# Default ON if we are building server-side plugins.
949955
# Also default ON in pushbuild, for our community builds.
950956
IF(WITH_AUTHENTICATION_KERBEROS OR
951957
WITH_AUTHENTICATION_LDAP OR
952958
WITH_AUTHENTICATION_FIDO OR
959+
WITH_AUTHENTICATION_WEBAUTHN OR
953960
DEFINED ENV{PB2WORKDIR})
954961
SET(WITH_AUTHENTICATION_CLIENT_PLUGINS_DEFAULT ON)
955962
ELSE()
@@ -963,6 +970,8 @@ ENDIF()
963970
# authentication_ldap_sasl_client.so
964971
# The FIDO client authentication plugin
965972
# authentication_fido_client.so
973+
# The WEBAUTHN client authentication plugin
974+
# authentication_webauthn_client.so
966975
OPTION(WITH_AUTHENTICATION_CLIENT_PLUGINS
967976
"Build client-side authentication plugins, even if server-side are disabled"
968977
${WITH_AUTHENTICATION_CLIENT_PLUGINS_DEFAULT})
@@ -2002,21 +2011,34 @@ IF(APPLE)
20022011
ENDFOREACH()
20032012
ENDIF()
20042013

2005-
IF(WITH_AUTHENTICATION_FIDO OR WITH_AUTHENTICATION_CLIENT_PLUGINS)
2014+
IF(WITH_AUTHENTICATION_FIDO OR WITH_AUTHENTICATION_WEBAUTHN OR
2015+
WITH_AUTHENTICATION_CLIENT_PLUGINS)
20062016
IF(WITH_FIDO STREQUAL "system" AND
20072017
NOT WITH_SSL STREQUAL "system")
2008-
MESSAGE(WARNING "-DWITH_AUTHENTICATION_FIDO=ON")
2018+
IF (WITH_AUTHENTICATION_FIDO)
2019+
MESSAGE(WARNING "-DWITH_AUTHENTICATION_FIDO=ON")
2020+
ENDIF()
2021+
IF (WITH_AUTHENTICATION_WEBAUTHN)
2022+
MESSAGE(WARNING "-DWITH_AUTHENTICATION_WEBAUTHN=ON")
2023+
ENDIF()
20092024
MESSAGE(FATAL_ERROR "Inconsistent options for FIDO/SSL")
20102025
ENDIF()
20112026

20122027
# FIDO (or libudev) missing, warn about what is missing, and break the build.
2013-
IF(WITH_AUTHENTICATION_FIDO AND NOT FIDO_FOUND)
2028+
IF((WITH_AUTHENTICATION_FIDO OR WITH_AUTHENTICATION_WEBAUTHN) AND
2029+
NOT FIDO_FOUND)
20142030
SET(UDEV_WARN_MISSING)
20152031
SET(FIDO_WARN_MISSING)
20162032
WARN_MISSING_SYSTEM_UDEV(UDEV_WARN_MISSING)
20172033
WARN_MISSING_SYSTEM_FIDO(FIDO_WARN_MISSING)
2018-
MESSAGE(FATAL_ERROR
2019-
"-DWITH_AUTHENTICATION_FIDO=ON, but missing required libraries")
2034+
IF (WITH_AUTHENTICATION_FIDO)
2035+
MESSAGE(FATAL_ERROR
2036+
"-DWITH_AUTHENTICATION_FIDO=ON, but missing required libraries")
2037+
ENDIF()
2038+
IF (WITH_AUTHENTICATION_WEBAUTHN)
2039+
MESSAGE(FATAL_ERROR
2040+
"-DWITH_AUTHENTICATION_WEBAUTHN=ON, but missing required libraries")
2041+
ENDIF()
20202042
ENDIF()
20212043
ENDIF()
20222044

@@ -2069,8 +2091,8 @@ IF(WITH_PROTOBUF STREQUAL "bundled" OR WITH_FIDO STREQUAL "bundled")
20692091
# Do not break the build here in case of missing libudev on Linux.
20702092
SET(UDEV_WARN_MISSING FALSE)
20712093
WARN_MISSING_SYSTEM_UDEV(UDEV_WARN_MISSING)
2072-
IF((WITH_AUTHENTICATION_FIDO OR WITH_AUTHENTICATION_CLIENT_PLUGINS)
2073-
AND NOT UDEV_WARN_MISSING)
2094+
IF((WITH_AUTHENTICATION_FIDO OR WITH_AUTHENTICATION_WEBAUTHN
2095+
OR WITH_AUTHENTICATION_CLIENT_PLUGINS) AND NOT UDEV_WARN_MISSING)
20742096
# Silence warning about CMP0075
20752097
CMAKE_PUSH_CHECK_STATE()
20762098
SET(CMAKE_REQUIRED_LIBRARIES)

client/client_priv.h

+1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ enum options_client {
186186
OPT_TLS_SNI_SERVERNAME,
187187
OPT_INIT_COMMAND_ADD,
188188
OPT_OUTPUT_AS_VERSION,
189+
OPT_AUTHENTICATION_WEBAUTHN_CLIENT_PRESERVE_PRIVACY,
189190
/* Add new option above this */
190191
OPT_MAX_CLIENT_OPTION
191192
};

client/common/user_registration.cc

+97-61
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/*
32
Copyright (c) 2021, 2023, Oracle and/or its affiliates.
43
@@ -37,20 +36,23 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
3736

3837
#define QUERY_LENGTH 2048
3938
#define MAX_QUERY_LENGTH 4096
39+
#define ENCODING_LENGTH 4
40+
#define CAPABILITY_BIT_LENGTH 1
4041

4142
/**
42-
This helper method parses --fido-register-factor option values, and
43-
inserts the parsed values in list.
43+
This helper method parses --register-factor/--fido-register-factor
44+
option values, and inserts the parsed values in list.
4445
45-
@param[in] what_factor comma separated string containing what all factors
46-
are to be registered
47-
@param[out] list container holding individual factors
46+
@param [in] what_factor Comma separated list of values, which specifies
47+
which factor requires registration.
48+
Valid values are "2", "3", "2,3" or "3,2"
49+
@param [out] factors container holding individual factors
4850
4951
@return true failed
5052
@return false success
5153
*/
52-
static bool parse_register_option(char *what_factor,
53-
std::vector<unsigned int> &list) {
54+
bool parse_register_option(const char *what_factor,
55+
std::vector<unsigned int> &factors) {
5456
std::string token;
5557
std::stringstream str(what_factor);
5658
while (getline(str, token, ',')) {
@@ -64,7 +66,7 @@ static bool parse_register_option(char *what_factor,
6466
}
6567
/* nth_factor can be either 2 or 3 */
6668
if (nth_factor < 2 || nth_factor > 3) return true;
67-
list.push_back(nth_factor);
69+
factors.push_back(nth_factor);
6870
}
6971
return false;
7072
}
@@ -75,15 +77,16 @@ static bool parse_register_option(char *what_factor,
7577
7678
Please refer @ref sect_fido_info for more information.
7779
78-
@param mysql mysql connection handle
79-
@param register_option Comma separated list of values, which specifies
80-
which factor requires registration. Valid values are "2", "3", "2,3" or "3,2"
81-
@param errmsg Buffer tol hold error message in case of error.
80+
@param [in] mysql_handle mysql connection handle
81+
@param [in] register_option Comma separated list of values, which
82+
specifies which factor requires registration. Valid values are "2", "3", "2,3"
83+
or "3,2"
84+
@param [out] errmsg Buffer to hold error message in case of error.
8285
8386
@return true failed
8487
@return false success
8588
*/
86-
bool user_device_registration(MYSQL *mysql, char *register_option,
89+
bool user_device_registration(MYSQL *mysql_handle, char *register_option,
8790
char *errmsg) {
8891
char query[QUERY_LENGTH] = {0};
8992
char *query_ptr = nullptr;
@@ -92,55 +95,68 @@ bool user_device_registration(MYSQL *mysql, char *register_option,
9295
ulong *lengths;
9396
uchar *server_challenge = nullptr;
9497
uchar *server_challenge_response = nullptr;
95-
96-
if (!mysql) {
97-
sprintf(errmsg, "MySQL internal error. ");
98-
return true;
99-
}
100-
101-
std::vector<unsigned int> list;
102-
if (parse_register_option(register_option, list)) {
103-
sprintf(errmsg,
104-
"Incorrect value specified for --fido-register-factor option. "
105-
"Correct values can be '2', '3', '2,3' or '3,2'.");
98+
std::string client_plugin_name{"authentication_fido_client"};
99+
struct st_mysql_client_plugin *plugin_handler = nullptr;
100+
std::stringstream err{};
101+
102+
auto print_error = [&errmsg, &mysql_handle, &err](bool print_mysql_error) {
103+
if (print_mysql_error) {
104+
sprintf(errmsg, "%s: %d (%s): %s\n", err.str().c_str(),
105+
mysql_errno(mysql_handle), mysql_sqlstate(mysql_handle),
106+
mysql_error(mysql_handle));
107+
} else {
108+
sprintf(errmsg, "%s\n", err.str().c_str());
109+
}
110+
};
111+
112+
std::vector<unsigned int> factors;
113+
if (parse_register_option(register_option, factors)) {
114+
err << "Incorrect value specified for "
115+
"--register-factor/--fido-register-factor option. "
116+
"Correct values can be '2', '3', '2,3' or '3,2'.";
117+
print_error(false);
106118
return true;
107119
}
108-
109-
for (auto f : list) {
120+
for (auto f : factors) {
110121
sprintf(query, "ALTER USER USER() %d FACTOR INITIATE REGISTRATION", f);
111-
if (mysql_real_query(mysql, query, (ulong)strlen(query))) {
112-
sprintf(errmsg, "Initiate registration failed with error: %s. ",
113-
mysql_error(mysql));
122+
if (mysql_real_query(mysql_handle, query, (ulong)strlen(query))) {
123+
err << "Initiate registration for " << f << " factor: ALTER USER failed";
124+
print_error(true);
114125
return true;
115126
}
116-
if (!(result = mysql_store_result(mysql))) {
117-
sprintf(errmsg, "Initiate registration failed with error: %s. ",
118-
mysql_error(mysql));
127+
if (!(result = mysql_store_result(mysql_handle))) {
128+
err << "Initiate registration for " << f
129+
<< " factor: Cannot process result";
130+
print_error(true);
119131
return true;
120132
}
121133
if (mysql_num_rows(result) > 1) {
122-
sprintf(errmsg, "Initiate registration failed with error: %s. ",
123-
mysql_error(mysql));
134+
err << "Initiate registration for " << f << " factor: Unexpected result";
135+
print_error(true);
124136
mysql_free_result(result);
125137
return true;
126138
}
139+
127140
row = mysql_fetch_row(result);
128141
lengths = mysql_fetch_lengths(result);
129142
/*
130143
max length of challenge can be 32 (random challenge) +
131144
255 (relying party ID) + 255 (host name) + 32 (user name) + 4 byte for
132-
length encodings
145+
length encodings + 1 byte capability
133146
*/
134-
if (lengths[0] > (CHALLENGE_LENGTH + RELYING_PARTY_ID_LENGTH +
135-
HOSTNAME_LENGTH + USERNAME_LENGTH + 4)) {
136-
sprintf(errmsg, "Received server challenge is corrupt. Please retry.\n");
147+
if (lengths[0] >
148+
(CHALLENGE_LENGTH + RELYING_PARTY_ID_LENGTH + HOSTNAME_LENGTH +
149+
USERNAME_LENGTH + ENCODING_LENGTH + CAPABILITY_BIT_LENGTH)) {
150+
err << "Initiate registration for " << f
151+
<< " factor: Received server challenge is corrupt. "
152+
"Please retry.";
153+
print_error(false);
137154
mysql_free_result(result);
138155
return true;
139156
}
140157
server_challenge = static_cast<uchar *>(my_malloc(
141158
PSI_NOT_INSTRUMENTED, lengths[0] + 1, MYF(MY_WME | MY_ZEROFILL)));
142159
memcpy(server_challenge, row[0], lengths[0]);
143-
mysql_free_result(result);
144160

145161
auto cleanup_guard = create_scope_guard([&] {
146162
if (server_challenge_response) {
@@ -157,28 +173,47 @@ bool user_device_registration(MYSQL *mysql, char *register_option,
157173
}
158174
});
159175

160-
/* load fido client authentication plugin if required */
161-
struct st_mysql_client_plugin *p =
162-
mysql_client_find_plugin(mysql, "authentication_fido_client",
176+
if (mysql_num_fields(result) >= 2) {
177+
if (!lengths[1] || !row[1]) {
178+
err << "Initiate registration for " << f
179+
<< " factor: No client plugin name received. Please retry.";
180+
print_error(false);
181+
mysql_free_result(result);
182+
return true;
183+
}
184+
client_plugin_name.assign(row[1], lengths[1]);
185+
}
186+
mysql_free_result(result);
187+
188+
plugin_handler =
189+
mysql_client_find_plugin(mysql_handle, client_plugin_name.c_str(),
163190
MYSQL_CLIENT_AUTHENTICATION_PLUGIN);
164-
if (!p) {
165-
sprintf(
166-
errmsg,
167-
"Loading authentication_fido_client plugin failed with error: %s. ",
168-
mysql_error(mysql));
191+
/* check if client plugin is loaded */
192+
if (!plugin_handler) {
193+
err << "Initiate registration for " << f
194+
<< " factor: Loading client plugin '" << client_plugin_name
195+
<< "'failed with error";
196+
print_error(true);
169197
return true;
170198
}
171199
/* set server challenge in plugin */
172-
if (mysql_plugin_options(p, "registration_challenge", server_challenge)) {
173-
sprintf(errmsg,
174-
"Failed to set plugin options \"registration_challenge\".\n");
200+
if (mysql_plugin_options(plugin_handler, "registration_challenge",
201+
server_challenge)) {
202+
err << "Finish registration for " << f
203+
<< " factor: Failed to set plugin options \"registration_challenge\" "
204+
"for plugin '"
205+
<< client_plugin_name << "'.";
206+
print_error(false);
175207
return true;
176208
}
177209
/* get challenge response from plugin, and release the memory */
178-
if (mysql_plugin_get_option(p, "registration_response",
210+
if (mysql_plugin_get_option(plugin_handler, "registration_response",
179211
&server_challenge_response)) {
180-
sprintf(errmsg,
181-
"Failed to get plugin options \"registration_response\".\n");
212+
err << "Finish registration for " << f
213+
<< " factor: Failed to get plugin options \"registration_response\". "
214+
"for pugin '"
215+
<< client_plugin_name << "'.";
216+
print_error(false);
182217
return true;
183218
}
184219

@@ -190,10 +225,11 @@ bool user_device_registration(MYSQL *mysql, char *register_option,
190225
size_t tot_query_len =
191226
n + strlen(reinterpret_cast<char *>(server_challenge_response));
192227
if (tot_query_len >= MAX_QUERY_LENGTH) {
193-
sprintf(
194-
errmsg,
195-
"registration_response length exceeds max supported length of %d.\n",
196-
MAX_QUERY_LENGTH);
228+
err << "Finish registration for " << f
229+
<< " factor: registration_response length exceeds max "
230+
"supported length of "
231+
<< MAX_QUERY_LENGTH << "\n";
232+
print_error(false);
197233
return true;
198234
}
199235
if (tot_query_len >= QUERY_LENGTH) {
@@ -206,9 +242,9 @@ bool user_device_registration(MYSQL *mysql, char *register_option,
206242
"ALTER USER USER() %d FACTOR FINISH REGISTRATION SET "
207243
"CHALLENGE_RESPONSE AS '%s'",
208244
f, server_challenge_response);
209-
if (mysql_real_query(mysql, query, (ulong)strlen(query))) {
210-
sprintf(errmsg, "Finish registration failed with error: %s.\n",
211-
mysql_error(mysql));
245+
if (mysql_real_query(mysql_handle, query, (ulong)strlen(query))) {
246+
err << "Finish registration for " << f << " factor failed";
247+
print_error(true);
212248
return true;
213249
}
214250
}

client/include/user_registration.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/*
32
Copyright (c) 2021, 2023, Oracle and/or its affiliates.
43
@@ -28,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2827

2928
#include <mysql.h>
3029

31-
bool user_device_registration(MYSQL *mysql, char *register_option,
30+
bool user_device_registration(MYSQL *mysql_handle, char *register_option,
3231
char *errmsg);
3332

34-
#endif // USER_REGISTRATION_H_
33+
#endif // USER_REGISTRATION_H_

0 commit comments

Comments
 (0)