1/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License as
5 published by the Free Software Foundation; version 2 of the
6 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
16
17#include <my_global.h>
18#include <mysql/plugin_auth.h>
19#include <mysql/client_plugin.h>
20#include <string.h>
21#include <stdio.h>
22#include <stdlib.h>
23
24/********************* SERVER SIDE ****************************************/
25
26static int qa_auth_interface (MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
27{
28 unsigned char *pkt;
29 int pkt_len, err= CR_OK;
30
31 /* send a password question */
32 if (vio->write_packet(vio, (const unsigned char *) PASSWORD_QUESTION, 1))
33 return CR_ERROR;
34
35 /* read the answer */
36 if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
37 return CR_ERROR;
38
39 info->password_used= PASSWORD_USED_YES;
40
41 /* fail if the password is wrong */
42 if (strcmp((const char *) pkt, info->auth_string))
43 return CR_ERROR;
44
45/* Check the contens of components of info */
46 if (strcmp(info->user_name, "qa_test_1_user")== 0)
47 {
48 if (info->user_name_length != 14)
49 err= CR_ERROR;
50 if (strcmp(info->auth_string, "qa_test_1_dest"))
51 err= CR_ERROR;
52 if (info->auth_string_length != 14)
53 err= CR_ERROR;
54/* To be set by the plugin */
55/* if (strcmp(info->authenticated_as, "qa_test_1_user")) */
56/* err= CR_ERROR; */
57/* To be set by the plugin */
58/* if (strcmp(info->external_user, "")) */
59/* err= CR_ERROR; */
60 if (info->password_used != PASSWORD_USED_YES)
61 err= CR_ERROR;
62 if (strcmp(info->host_or_ip, "localhost"))
63 err= CR_ERROR;
64 if (info->host_or_ip_length != 9)
65 err= CR_ERROR;
66 }
67/* Assign values to the components of info even if not intended and watch the effect */
68 else if (strcmp(info->user_name, "qa_test_2_user")== 0)
69 {
70 /* Overwriting not intended, but with effect on USER() */
71 strcpy((char*) info->user_name, "user_name");
72 info->user_name_length= 9;
73 /* Overwriting not intended, effect not visible */
74 strcpy((char *)info->auth_string, "auth_string");
75 info->auth_string_length= 11;
76 /* Assign with account for authorization, effect on CURRENT_USER() */
77 strcpy(info->authenticated_as, "authenticated_as");
78 /* Assign with an external account, effect on @@local.EXTERNAL_USER */
79 strcpy(info->external_user, "externaluser");
80 /* Overwriting will cause a core dump */
81/* strcpy(info->host_or_ip, "host_or_ip"); */
82/* info->host_or_ip_length= 10; */
83 }
84/* Invalid, means too high values for length */
85 else if (strcmp(info->user_name, "qa_test_3_user")== 0)
86 {
87/* Original value is 14. Test runs also with higher value. Changes have no effect.*/
88 info->user_name_length= 28;
89 strcpy((char *)info->auth_string, "qa_test_3_dest");
90/* Original value is 14. Test runs also with higher value. Changes have no effect.*/
91 info->auth_string_length= 28;
92 strcpy(info->authenticated_as, info->auth_string);
93 strcpy(info->external_user, info->auth_string);
94 }
95/* Invalid, means too low values for length */
96 else if (strcmp(info->user_name, "qa_test_4_user")== 0)
97 {
98/* Original value is 14. Test runs also with lower value. Changes have no effect.*/
99 info->user_name_length= 8;
100 strcpy((char *)info->auth_string, "qa_test_4_dest");
101/* Original value is 14. Test runs also with lower value. Changes have no effect.*/
102 info->auth_string_length= 8;
103 strcpy(info->authenticated_as, info->auth_string);
104 strcpy(info->external_user, info->auth_string);
105 }
106/* Overwrite with empty values */
107 else if (strcmp(info->user_name, "qa_test_5_user")== 0)
108 {
109/* This assignment has no effect.*/
110 strcpy((char*) info->user_name, "");
111 info->user_name_length= 0;
112/* This assignment has no effect.*/
113 strcpy((char *)info->auth_string, "");
114 info->auth_string_length= 0;
115/* This assignment caused an error or an "empty" user */
116 strcpy(info->authenticated_as, "");
117/* This assignment has no effect.*/
118 strcpy(info->external_user, "");
119 /* Overwriting will cause a core dump */
120/* strcpy(info->host_or_ip, ""); */
121/* info->host_or_ip_length= 0; */
122 }
123/* Set to 'root' */
124 else if (strcmp(info->user_name, "qa_test_6_user")== 0)
125 {
126 strcpy(info->authenticated_as, "root");
127 }
128 else
129 {
130 err= CR_ERROR;
131 }
132 return err;
133}
134
135static struct st_mysql_auth qa_auth_test_handler=
136{
137 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
138 "qa_auth_interface", /* requires test_plugin client's plugin */
139 qa_auth_interface
140};
141
142mysql_declare_plugin(test_plugin)
143{
144 MYSQL_AUTHENTICATION_PLUGIN,
145 &qa_auth_test_handler,
146 "qa_auth_interface",
147 "Horst Hunger",
148 "plugin API test plugin",
149 PLUGIN_LICENSE_GPL,
150 NULL,
151 NULL,
152 0x0100,
153 NULL,
154 NULL,
155 NULL,
156 0,
157}
158mysql_declare_plugin_end;
159
160/********************* CLIENT SIDE ***************************************/
161/*
162 client plugin used for testing the plugin API
163*/
164#include <mysql.h>
165
166/**
167 The main function of the test plugin.
168
169 Reads the prompt, check if the handshake is done and if the prompt is a
170 password request and returns the password. Otherwise return error.
171
172 @note
173 1. this plugin shows how a client authentication plugin
174 may read a MySQL protocol OK packet internally - which is important
175 where a number of packets is not known in advance.
176 2. the first byte of the prompt is special. it is not
177 shown to the user, but signals whether it is the last question
178 (prompt[0] & 1 == 1) or not last (prompt[0] & 1 == 0),
179 and whether the input is a password (not echoed).
180 3. the prompt is expected to be sent zero-terminated
181*/
182static int test_plugin_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
183{
184 unsigned char *pkt, cmd= 0;
185 int pkt_len, res;
186 char *reply;
187
188 do
189 {
190 /* read the prompt */
191 pkt_len= vio->read_packet(vio, &pkt);
192 if (pkt_len < 0)
193 return CR_ERROR;
194
195 if (pkt == 0)
196 {
197 /*
198 in mysql_change_user() the client sends the first packet, so
199 the first vio->read_packet() does nothing (pkt == 0).
200
201 We send the "password", assuming the client knows what its doing.
202 (in other words, the dialog plugin should be only set as a default
203 authentication plugin on the client if the first question
204 asks for a password - which will be sent in cleat text, by the way)
205 */
206 reply= mysql->passwd;
207 }
208 else
209 {
210 cmd= *pkt++;
211
212 /* is it MySQL protocol (0=OK or 254=need old password) packet ? */
213 if (cmd == 0 || cmd == 254)
214 return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */
215
216 /*
217 asking for a password with an empty prompt means mysql->password
218 otherwise return an error
219 */
220 if ((cmd == LAST_PASSWORD[0] || cmd == PASSWORD_QUESTION[0]) && *pkt == 0)
221 reply= mysql->passwd;
222 else
223 return CR_ERROR;
224 }
225 if (!reply)
226 return CR_ERROR;
227 /* send the reply to the server */
228 res= vio->write_packet(vio, (const unsigned char *) reply,
229 (int)strlen(reply) + 1);
230
231 if (res)
232 return CR_ERROR;
233
234 /* repeat unless it was the last question */
235 } while (cmd != LAST_QUESTION[0] && cmd != PASSWORD_QUESTION[0]);
236
237 /* the job of reading the ok/error packet is left to the server */
238 return CR_OK;
239}
240
241
242mysql_declare_client_plugin(AUTHENTICATION)
243 "qa_auth_interface",
244 "Horst Hunger",
245 "Dialog Client Authentication Plugin",
246 {0,1,0},
247 "GPL",
248 NULL,
249 NULL,
250 NULL,
251 NULL,
252 test_plugin_client
253mysql_end_client_plugin;
254