MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
dialog.c
Go to the documentation of this file.
1 /* Copyright (c) 2010, 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 
37 #if defined (WIN32) && !defined (RTLD_DEFAULT)
38 # define RTLD_DEFAULT GetModuleHandle(NULL)
39 #endif
40 
41 #include <my_global.h>
42 #include <mysql.h>
43 #include <mysql/plugin_auth.h>
44 #include <mysql/client_plugin.h>
45 #include <string.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 
49 #if !defined (_GNU_SOURCE)
50 # define _GNU_SOURCE /* for RTLD_DEFAULT */
51 #endif
52 
58 #define ORDINARY_QUESTION "\2"
59 #define LAST_QUESTION "\3"
60 #define PASSWORD_QUESTION "\4"
61 #define LAST_PASSWORD "\5"
62 
63 /********************* SERVER SIDE ****************************************/
64 
68 static int two_questions(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
69 {
70  unsigned char *pkt;
71  int pkt_len;
72 
73  /* send a password question */
74  if (vio->write_packet(vio, (const unsigned char *) PASSWORD_QUESTION "Password, please:", 18))
75  return CR_ERROR;
76 
77  /* read the answer */
78  if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
79  return CR_ERROR;
80 
81  info->password_used= PASSWORD_USED_YES;
82 
83  /* fail if the password is wrong */
84  if (strcmp((const char *) pkt, info->auth_string))
85  return CR_ERROR;
86 
87  /* send the last, ordinary, question */
88  if (vio->write_packet(vio, (const unsigned char *) LAST_QUESTION "Are you sure ?", 15))
89  return CR_ERROR;
90 
91  /* read the answer */
92  if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
93  return CR_ERROR;
94 
95  /* check the reply */
96  return strcmp((const char *) pkt, "yes, of course") ? CR_ERROR : CR_OK;
97 }
98 
99 static struct st_mysql_auth two_handler=
100 {
101  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
102  "dialog", /* requires dialog client plugin */
103  two_questions
104 };
105 
106 /* dialog demo where the number of questions is not known in advance */
107 static int three_attempts(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
108 {
109  unsigned char *pkt;
110  int pkt_len, i;
111 
112  for (i= 0; i < 3; i++)
113  {
114  /* send the prompt */
115  if (vio->write_packet(vio,
116  (const unsigned char *) PASSWORD_QUESTION "Password, please:", 18))
117  return CR_ERROR;
118 
119  /* read the password */
120  if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
121  return CR_ERROR;
122 
123  info->password_used= PASSWORD_USED_YES;
124 
125  /*
126  finish, if the password is correct.
127  note, that we did not mark the prompt packet as "last"
128  */
129  if (strcmp((const char *) pkt, info->auth_string) == 0)
130  return CR_OK;
131  }
132 
133  return CR_ERROR;
134 }
135 
136 static struct st_mysql_auth three_handler=
137 {
138  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
139  "dialog", /* requires dialog client plugin */
140  three_attempts
141 };
142 
143 mysql_declare_plugin(dialog)
144 {
145  MYSQL_AUTHENTICATION_PLUGIN,
146  &two_handler,
147  "two_questions",
148  "Sergei Golubchik",
149  "Dialog plugin demo 1",
150  PLUGIN_LICENSE_GPL,
151  NULL,
152  NULL,
153  0x0100,
154  NULL,
155  NULL,
156  NULL,
157  0,
158 },
159 {
160  MYSQL_AUTHENTICATION_PLUGIN,
161  &three_handler,
162  "three_attempts",
163  "Sergei Golubchik",
164  "Dialog plugin demo 2",
165  PLUGIN_LICENSE_GPL,
166  NULL,
167  NULL,
168  0x0100,
169  NULL,
170  NULL,
171  NULL,
172  0,
173 }
174 mysql_declare_plugin_end;
175 
176 /********************* CLIENT SIDE ***************************************/
177 /*
178  This plugin performs a dialog with the user, asking questions and
179  reading answers. Depending on the client it may be desirable to do it
180  using GUI, or console, with or without curses, or read answers
181  from a smartcard, for example.
182 
183  To support all this variety, the dialog plugin has a callback function
184  "authentication_dialog_ask". If the client has a function of this name
185  dialog plugin will use it for communication with the user. Otherwise
186  a default fgets() based implementation will be used.
187 */
188 
205 typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql,
206  int type, const char *prompt, char *buf, int buf_len);
207 
209 
210 static char *builtin_ask(MYSQL *mysql __attribute__((unused)),
211  int type __attribute__((unused)),
212  const char *prompt,
213  char *buf, int buf_len)
214 {
215  char *ptr;
216  fputs(prompt, stdout);
217  fputc(' ', stdout);
218  if (fgets(buf, buf_len, stdin) == NULL)
219  return NULL;
220  if ((ptr= strchr(buf, '\n')))
221  *ptr= 0;
222 
223  return buf;
224 }
225 
242 static int perform_dialog(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
243 {
244  unsigned char *pkt, cmd= 0;
245  int pkt_len, res;
246  char reply_buf[1024], *reply;
247 
248  do
249  {
250  /* read the prompt */
251  pkt_len= vio->read_packet(vio, &pkt);
252  if (pkt_len < 0)
253  return CR_ERROR;
254 
255  if (pkt == 0)
256  {
257  /*
258  in mysql_change_user() the client sends the first packet, so
259  the first vio->read_packet() does nothing (pkt == 0).
260 
261  We send the "password", assuming the client knows what it's doing.
262  (in other words, the dialog plugin should be only set as a default
263  authentication plugin on the client if the first question
264  asks for a password - which will be sent in clear text, by the way)
265  */
266  reply= mysql->passwd;
267  }
268  else
269  {
270  cmd= *pkt++;
271 
272  /* is it MySQL protocol packet ? */
273  if (cmd == 0 || cmd == 254)
274  return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */
275 
276  /*
277  asking for a password with an empty prompt means mysql->password
278  otherwise we ask the user and read the reply
279  */
280  if ((cmd >> 1) == 2 && *pkt == 0)
281  reply= mysql->passwd;
282  else
283  reply= ask(mysql, cmd >> 1, (const char *) pkt,
284  reply_buf, sizeof(reply_buf));
285  if (!reply)
286  return CR_ERROR;
287  }
288  /* send the reply to the server */
289  res= vio->write_packet(vio, (const unsigned char *) reply,
290  strlen(reply)+1);
291 
292  if (reply != mysql->passwd && reply != reply_buf)
293  free(reply);
294 
295  if (res)
296  return CR_ERROR;
297 
298  /* repeat unless it was the last question */
299  } while ((cmd & 1) != 1);
300 
301  /* the job of reading the ok/error packet is left to the server */
302  return CR_OK;
303 }
304 
312 static int init_dialog(char *unused1 __attribute__((unused)),
313  size_t unused2 __attribute__((unused)),
314  int unused3 __attribute__((unused)),
315  va_list unused4 __attribute__((unused)))
316 {
317  void *sym= dlsym(RTLD_DEFAULT, "mysql_authentication_dialog_ask");
318  ask= sym ? (mysql_authentication_dialog_ask_t) sym : builtin_ask;
319  return 0;
320 }
321 
322 mysql_declare_client_plugin(AUTHENTICATION)
323  "dialog",
324  "Sergei Golubchik",
325  "Dialog Client Authentication Plugin",
326  {0,1,0},
327  "GPL",
328  NULL,
329  init_dialog,
330  NULL,
331  NULL,
332  perform_dialog
333 mysql_end_client_plugin;
334