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