1 /*
2 Copyright (c) 2017, MariaDB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
16
17 #include <mysql/plugin_auth.h>
18 #include "common.h"
19
20 #if !defined(__attribute__) && !defined(__GNUC__)
21 #define __attribute__(A)
22 #endif
23
24 #define PASSWORD_LEN_BUF 44 /* base64 of 32 bytes */
25 #define PASSWORD_LEN 43 /* we won't store the last byte, padding '=' */
26
27 #define CRYPTO_LONGS (CRYPTO_BYTES/sizeof(long))
28 #define NONCE_LONGS (NONCE_BYTES/sizeof(long))
29
30 /************************** SERVER *************************************/
31
32 static int loaded= 0;
33
auth(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)34 static int auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
35 {
36 int pkt_len;
37 unsigned long nonce[CRYPTO_LONGS + NONCE_LONGS];
38 unsigned char *pkt, *reply= (unsigned char*)nonce;
39 unsigned char pk[PASSWORD_LEN_BUF/4*3];
40 char pw[PASSWORD_LEN_BUF];
41
42 /* prepare the pk */
43 if (info->auth_string_length != PASSWORD_LEN)
44 return CR_ERROR; // bad password in the user table
45 memcpy(pw, info->auth_string, PASSWORD_LEN);
46 pw[PASSWORD_LEN]= '=';
47 if (my_base64_decode(pw, PASSWORD_LEN_BUF, pk, NULL, 0) != CRYPTO_PUBLICKEYBYTES)
48 return CR_ERROR; // bad password in the user table
49
50 info->password_used= PASSWORD_USED_YES;
51
52 /* prepare random nonce */
53 if (my_random_bytes((unsigned char *)nonce, (int)sizeof(nonce)))
54 return CR_ERROR; // eh? OpenSSL error
55
56 /* send it */
57 if (vio->write_packet(vio, reply + CRYPTO_BYTES, NONCE_BYTES))
58 return CR_AUTH_HANDSHAKE;
59
60 /* read the signature */
61 if ((pkt_len= vio->read_packet(vio, &pkt)) != CRYPTO_BYTES)
62 return CR_AUTH_HANDSHAKE;
63 memcpy(reply, pkt, CRYPTO_BYTES);
64
65 if (crypto_sign_open(reply, CRYPTO_BYTES + NONCE_BYTES, pk))
66 return CR_AUTH_USER_CREDENTIALS; // wrong password provided by the user
67
68 return CR_OK;
69 }
70
71 static struct st_mysql_auth info =
72 {
73 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
74 "client_ed25519",
75 auth
76 };
77
init(void * p)78 static int init(void *p __attribute__((unused)))
79 {
80 loaded= 1;
81 return 0;
82 }
83
deinit(void * p)84 static int deinit(void *p __attribute__((unused)))
85 {
86 loaded= 0;
87 return 0;
88 }
89
maria_declare_plugin(ed25519)90 maria_declare_plugin(ed25519)
91 {
92 MYSQL_AUTHENTICATION_PLUGIN,
93 &info,
94 "ed25519",
95 "Sergei Golubchik",
96 "Elliptic curve ED25519 based authentication",
97 PLUGIN_LICENSE_GPL,
98 init,
99 deinit,
100 0x0100,
101 NULL,
102 NULL,
103 "1.0",
104 MariaDB_PLUGIN_MATURITY_STABLE
105 }
106 maria_declare_plugin_end;
107
108 /************************** UDF ****************************************/
109 MYSQL_PLUGIN_EXPORT
ed25519_password(UDF_INIT * initid,UDF_ARGS * args,char * result,unsigned long * length,char * is_null,char * error)110 char *ed25519_password(UDF_INIT *initid __attribute__((unused)),
111 UDF_ARGS *args, char *result, unsigned long *length,
112 char *is_null, char *error __attribute__((unused)))
113 {
114 unsigned char pk[CRYPTO_PUBLICKEYBYTES];
115
116 if ((*is_null= !args->args[0]))
117 return NULL;
118
119 *length= PASSWORD_LEN;
120 crypto_sign_keypair(pk, (unsigned char*)args->args[0], args->lengths[0]);
121 my_base64_encode(pk, CRYPTO_PUBLICKEYBYTES, result);
122 return result;
123 }
124
125 /*
126 At least one of _init/_deinit is needed unless the server is started
127 with --allow_suspicious_udfs.
128 */
129 MYSQL_PLUGIN_EXPORT
ed25519_password_init(UDF_INIT * initid,UDF_ARGS * args,char * message)130 my_bool ed25519_password_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
131 {
132 if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT)
133 {
134 strcpy(message,"Wrong arguments to ed25519_password()");
135 return 1;
136 }
137 if (!loaded)
138 {
139 /* cannot work unless the plugin is loaded, we need services. */
140 strcpy(message,"Authentication plugin ed25519 is not loaded");
141 return 1;
142 }
143 initid->max_length= PASSWORD_LEN_BUF;
144 return 0;
145 }
146