This repository was archived by the owner on Mar 1, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 714
Expand file tree
/
Copy pathfido_client_plugin.cc
More file actions
224 lines (199 loc) · 7.36 KB
/
Copy pathfido_client_plugin.cc
File metadata and controls
224 lines (199 loc) · 7.36 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
/* Copyright (c) 2021, 2022, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program 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 General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <mysql.h>
#include <mysql/client_plugin.h>
#include "my_dbug.h"
#include "fido_assertion.h"
#include "fido_common.h"
#include "fido_registration.h"
#ifndef NDEBUG
static bool is_fido_testing = false;
#endif
static unsigned char registration_challenge[128] = {0};
static unsigned char *registration_challenge_response = nullptr;
static bool do_registration();
/*
Handler to callback function which will pass informative messages generated by
this plugin caller. This callback function is registered via
mysql_plugin_option("fido_messages_callback")
If callback is not registered, all messaged are redirected to stderr/stdout.
*/
plugin_messages_callback mc = nullptr;
/*
Helper method which redirect the messages based on what value is set in
redirect_message
*/
void get_plugin_messages(const std::string &msg, message_type type) {
/* if callback is registered, pass msg to callback function */
if (mc) {
mc(msg.c_str());
} else {
if (type == message_type::ERROR) {
std::cerr << msg.c_str() << std::endl;
std::cerr.flush();
} else if (type == message_type::INFO) {
std::cout << msg.c_str() << std::endl;
}
}
}
/**
authentication_fido_client plugin API to initialize
*/
static int fido_auth_client_plugin_init(char *, size_t, int, va_list) {
return 0;
}
/**
Deinitialize authentication_fido_client plugin
*/
static int fido_auth_client_plugin_deinit() { return 0; }
/**
authentication_fido_client plugin API to allow server to pass optional data
for plugin to process
*/
static int fido_auth_client_plugin_option(const char *option, const void *val) {
#ifndef NDEBUG
if (strcmp(option, "is_fido_testing") == 0) {
is_fido_testing = *static_cast<const bool *>(val);
return false;
}
#endif
if (strcmp(option, "fido_messages_callback") == 0) {
mc = (plugin_messages_callback)(const_cast<void *>(val));
return false;
}
if (strcmp(option, "registration_challenge") == 0) {
unsigned char *p =
reinterpret_cast<unsigned char *>(const_cast<void *>(val));
memcpy(registration_challenge, p, strlen(reinterpret_cast<char *>(p)));
/* finish registration */
if (do_registration()) return true;
return false;
}
return true;
}
/**
authentication_fido_client plugin API to allow server to get optional data
from plugin
*/
static int fido_auth_client_get_plugin_option(const char *option, void *val) {
if (strcmp(option, "registration_response") == 0) {
*(static_cast<unsigned char **>(val)) = registration_challenge_response;
}
return 0;
}
/**
FIDO client side authentication method. This method does following:
1. Receive challenge from server side FIDO plugin. This challenge
comprises of salt, relying party name.
2. Send this challenge to FIDO device and get the signed challenge.
Signed challenge includes signature and authenticator data, which
is to be verified by server side plugin with public key.
@param [in] vio Virtual I/O interface
@return authentication status
@retval CR_OK Successful authentication
@retval true Authentication failure
*/
static int fido_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *) {
unsigned char *server_challenge = nullptr;
int server_challenge_len = 0;
/** Get the challenge from the MySQL server. */
server_challenge_len = vio->read_packet(vio, &server_challenge);
if (server_challenge_len == 0) {
/*
an empty packet means registration step is pending, thus for now allow
connection with limited operations for user so that user can perform
registration step.
*/
return CR_OK_AUTH_IN_SANDBOX_MODE;
}
unsigned char *buff = nullptr;
size_t length = 0;
#ifndef NDEBUG
if (is_fido_testing) {
length = 33;
buff = new (std::nothrow) unsigned char[length];
memcpy(buff, "\nsakila \nsakila \nsakila ", length);
vio->write_packet(vio, buff, length);
delete[] buff;
return CR_OK;
} else
#endif
{
fido_assertion *fa = new fido_assertion();
if (fa->prepare_assert(server_challenge) || fa->sign_challenge()) {
delete fa;
return true;
}
/* copy signed challenge into buff */
fa->get_signed_challenge(&buff, length);
/* send signed challenge to fido server plugin */
vio->write_packet(vio, buff, length);
delete fa;
delete[] buff;
}
return CR_OK;
}
/**
FIDO client side registration method. This method does following:
1. Receive challenge from server side FIDO plugin. This challenge
comprises of username, salt and relying party name.
2. Send this challenge to FIDO device and get the signature, authenticator
data and x509 certificate generated by device. This is sent to server
as challenge response.
@return registration status
@retval false Successful registration
@retval true Registration failure
*/
static bool do_registration() {
#ifndef NDEBUG
if (is_fido_testing) {
const char *dummy = "\nSIGNATURE \nAUHENDATA \nCERT ";
size_t sz = strlen(dummy);
memcpy(registration_challenge, dummy, sz);
/* dummy challenge response for testing */
registration_challenge_response = new unsigned char[sz + 1];
memcpy(registration_challenge_response, dummy, sz);
registration_challenge_response[sz] = 0;
return false;
} else
#endif
{
fido_registration *fr = new fido_registration();
if (fr->make_credentials(const_cast<const char *>(
reinterpret_cast<char *>(registration_challenge)))) {
delete fr;
return true;
}
if (fr->make_challenge_response(registration_challenge_response)) {
delete fr;
return true;
}
delete fr;
}
return false;
}
mysql_declare_client_plugin(AUTHENTICATION) "authentication_fido_client",
MYSQL_CLIENT_PLUGIN_AUTHOR_ORACLE, "Fido Client Authentication Plugin",
{0, 1, 0}, "GPL", nullptr, fido_auth_client_plugin_init,
fido_auth_client_plugin_deinit, fido_auth_client_plugin_option,
fido_auth_client_get_plugin_option, fido_auth_client,
nullptr, mysql_end_client_plugin;