Merge lp:~sergei.glushchenko/percona-pam-for-mysql/use-dialog into lp:percona-pam-for-mysql

Proposed by Sergei Glushchenko
Status: Merged
Approved by: Laurynas Biveinis
Approved revision: no longer in the source branch.
Merged at revision: 19
Proposed branch: lp:~sergei.glushchenko/percona-pam-for-mysql/use-dialog
Merge into: lp:percona-pam-for-mysql
Diff against target: 358 lines (+102/-132)
2 files modified
src/auth_pam.c (+77/-124)
src/dialog.c (+25/-8)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-pam-for-mysql/use-dialog
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) Approve
Review via email: mp+90835@code.launchpad.net

Description of the change

1. Use dialog plugin for client side
2. Cherry-pick MariaDB dialog plugin improvements

To post a comment you must log in.
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

LGTM, please merge.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/auth_pam.c'
2--- src/auth_pam.c 2011-11-02 15:38:11 +0000
3+++ src/auth_pam.c 2012-01-31 08:02:23 +0000
4@@ -37,12 +37,12 @@
5
6 To install this plugin, copy the .so file to the plugin directory and do
7
8- INSTALL PLUGIN auth_pam_server SONAME 'auth_pam.so';
9+ INSTALL PLUGIN auth_pam SONAME 'auth_pam.so';
10
11 To use this plugin for one particular user, specify it at user's creation time
12 (TODO: tested with localhost only):
13
14- CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam_server;
15+ CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam;
16
17 Alternatively UPDATE the mysql.user table to set the plugin value for an
18 existing user.
19@@ -114,6 +114,18 @@
20 }
21 }
22
23+/** The maximum length of buffered PAM messages, i.e. any messages up to the
24+ next PAM reply-requiring message. 10K should be more than enough by order
25+ of magnitude. */
26+enum { max_pam_buffered_msg_len = 10240 };
27+
28+struct pam_conv_data {
29+ MYSQL_PLUGIN_VIO *vio;
30+ MYSQL_SERVER_AUTH_INFO *info;
31+ unsigned char buf[max_pam_buffered_msg_len];
32+ unsigned char* ptr;
33+};
34+
35 static void free_pam_response (struct pam_response ** resp, int n)
36 {
37 int i;
38@@ -129,15 +141,13 @@
39 struct pam_response ** resp, void *appdata_ptr)
40 {
41 int i;
42- int pkt_len;
43- MYSQL_PLUGIN_VIO *vio= NULL;
44+ struct pam_conv_data *data= (struct pam_conv_data *)appdata_ptr;
45
46- if (appdata_ptr == NULL)
47+ if (data == NULL)
48 {
49 MY_ASSERT_UNREACHABLE();
50 return PAM_CONV_ERR;
51 }
52- vio= (MYSQL_PLUGIN_VIO *)appdata_ptr;
53
54 *resp = calloc (sizeof (struct pam_response), num_msg);
55 if (*resp == NULL)
56@@ -145,54 +155,69 @@
57
58 for (i = 0; i < num_msg; i++)
59 {
60- char *buf;
61-
62 if (!valid_pam_msg_style(msg[i]->msg_style))
63 {
64 free_pam_response(resp, i);
65 return PAM_CONV_ERR;
66 }
67
68- /* Format the message. The first byte is the message type, followed by the
69- NUL-terminated message string itself. */
70- buf= malloc(strlen(msg[i]->msg) + 2);
71- if (!buf)
72- {
73- free_pam_response(resp, i);
74- return PAM_BUF_ERR;
75- }
76- buf[0]= pam_msg_style_to_char(msg[i]->msg_style);
77- strcpy(buf + 1, msg[i]->msg);
78-
79- /* Write the message. */
80- if (vio->write_packet(vio, (unsigned char *)buf, strlen(buf) + 1))
81- {
82- free(buf);
83- free_pam_response(resp, i);
84- return PAM_CONV_ERR;
85- }
86- free(buf);
87-
88- /* Is the answer expected? */
89- if ((msg[i]->msg_style != PAM_PROMPT_ECHO_ON)
90- && (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF))
91- continue;
92-
93- /* Read the answer */
94- if ((pkt_len= vio->read_packet(vio, (unsigned char **)&buf)) < 0)
95- {
96- free_pam_response(resp, i);
97- return PAM_CONV_ERR;
98- }
99-
100- (*resp)[i].resp= malloc(pkt_len + 1);
101- if ((*resp)[i].resp == NULL)
102- {
103- free_pam_response(resp, i);
104- return PAM_BUF_ERR;
105- }
106- strncpy((*resp)[i].resp, buf, pkt_len);
107- (*resp)[i].resp[pkt_len]= '\0';
108+ /* Append the PAM message or prompt to the unsent message buffer */
109+ if (msg[i]->msg)
110+ {
111+ unsigned char *last_buf_pos = data->buf + max_pam_buffered_msg_len - 1;
112+ if (data->ptr + strlen(msg[i]->msg) >= last_buf_pos)
113+ {
114+ free_pam_response(resp, i);
115+ /* Cannot happen: the PAM message buffer too small. */
116+ MY_ASSERT_UNREACHABLE();
117+ return PAM_CONV_ERR;
118+ }
119+ memcpy(data->ptr, msg[i]->msg, strlen(msg[i]->msg));
120+ data->ptr+= strlen(msg[i]->msg);
121+ *(data->ptr)++= '\n';
122+ }
123+
124+ if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF
125+ || msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
126+ {
127+ int pkt_len;
128+ unsigned char *pkt;
129+
130+ /* Magic byte for the dialog plugin, '\2' is defined as ORDINARY_QUESTION
131+ and '\4' as PASSWORD_QUESTION there. */
132+ data->buf[0]= (msg[i]->msg_style == PAM_PROMPT_ECHO_ON ? '\2' : '\4');
133+
134+ /* Write the message. */
135+ if (data->vio->write_packet(data->vio, data->buf,
136+ data->ptr - data->buf - 1))
137+ {
138+ free_pam_response(resp, i);
139+ return PAM_CONV_ERR;
140+ }
141+
142+ /* Read the answer */
143+ if ((pkt_len= data->vio->read_packet(data->vio, &pkt))
144+ < 0)
145+ {
146+ free_pam_response(resp, i);
147+ return PAM_CONV_ERR;
148+ }
149+
150+ (*resp)[i].resp= malloc(pkt_len + 1);
151+ if ((*resp)[i].resp == NULL)
152+ {
153+ free_pam_response(resp, i);
154+ return PAM_BUF_ERR;
155+ }
156+
157+ strncpy((*resp)[i].resp, (char *)pkt, pkt_len);
158+ (*resp)[i].resp[pkt_len]= '\0';
159+
160+ if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
161+ data->info->password_used= PASSWORD_USED_YES;
162+
163+ data->ptr= data->buf + 1;
164+ }
165 }
166 return PAM_SUCCESS;
167 }
168@@ -201,11 +226,11 @@
169 MYSQL_SERVER_AUTH_INFO *info)
170 {
171 pam_handle_t *pam_handle;
172- struct pam_conv conv_func_info= { &vio_server_conv, vio };
173+ struct pam_conv_data data= { vio, info, "", data.buf+1 };
174+ struct pam_conv conv_func_info= { &vio_server_conv, &data };
175 int error;
176 char *pam_mapped_user_name;
177
178- /* Impossible to tell if PAM will use passwords or something else */
179 info->password_used= PASSWORD_USED_NO_MENTION;
180
181 error= pam_start(service_name,
182@@ -242,13 +267,6 @@
183 return CR_ERROR;
184 }
185
186- /* Send end-of-auth message */
187- if (vio->write_packet(vio, (const unsigned char *)"\0", 1))
188- {
189- pam_end(pam_handle, error);
190- return CR_ERROR;
191- }
192-
193 /* Get the authenticated user name from PAM */
194 error= pam_get_item(pam_handle, PAM_USER, (void *)&pam_mapped_user_name);
195 if (error != PAM_SUCCESS)
196@@ -277,7 +295,7 @@
197 static struct st_mysql_auth pam_auth_handler=
198 {
199 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
200- "auth_pam",
201+ "dialog",
202 &authenticate_user_with_pam_server
203 };
204
205@@ -292,7 +310,7 @@
206 {
207 MYSQL_AUTHENTICATION_PLUGIN,
208 &pam_auth_handler,
209- "auth_pam_server",
210+ "auth_pam",
211 "Percona, Inc.",
212 "PAM authentication plugin",
213 PLUGIN_LICENSE_GPL,
214@@ -327,68 +345,3 @@
215 #endif
216 }
217 mysql_declare_plugin_end;
218-
219-/* The client plugin */
220-
221-/* Returns malloc-allocated string, NULL in case of memory error. */
222-static char * prompt_echo_off (const char * prompt)
223-{
224- /* TODO: getpass not thread safe. Probably not a big deal in the mysql
225- client program, but may be missing on non-glibc systems. */
226- char* getpass_input= getpass(prompt);
227- return strdup(getpass_input);
228-}
229-
230-/* Returns malloc-allocated string, NULL in case of memory or (unlikely)
231-I/O error. */
232-static char * prompt_echo_on (const char * prompt)
233-{
234- char* c;
235- char fgets_buf[1024];
236- fputs(prompt, stdout);
237- fputc(' ', stdout);
238- c= fgets(fgets_buf, sizeof(fgets_buf), stdin);
239- if (c == NULL)
240- {
241- if (ferror(stdin))
242- return NULL;
243- fgets_buf[0]= '\0';
244- }
245- if ((c= strchr(fgets_buf, '\n')))
246- *c= '\0';
247- else
248- fgets_buf[sizeof(fgets_buf) - 1]= '\0';
249- return strdup(fgets_buf);
250-}
251-
252-static void show_error(const char * message)
253-{
254- fprintf(stderr, "ERROR %s\n", message);
255-}
256-
257-static void show_info(const char * message)
258-{
259- printf("%s\n", message);
260-}
261-
262-static int authenticate_user_with_pam_client (MYSQL_PLUGIN_VIO *vio,
263- struct st_mysql *mysql)
264-{
265- return authenticate_user_with_pam_client_common (vio, mysql,
266- &prompt_echo_off,
267- &prompt_echo_on,
268- &show_error, &show_info);
269-}
270-
271-mysql_declare_client_plugin(AUTHENTICATION)
272- "auth_pam",
273- "Percona, Inc.",
274- "PAM authentication plugin",
275- {0,1,0},
276- "GPL",
277- NULL,
278- NULL, /* init */
279- NULL, /* deinit */
280- NULL, /* options */
281- &authenticate_user_with_pam_client
282-mysql_end_client_plugin;
283
284=== modified file 'src/dialog.c'
285--- src/dialog.c 2011-12-06 13:37:43 +0000
286+++ src/dialog.c 2012-01-31 08:02:23 +0000
287@@ -212,13 +212,28 @@
288 const char *prompt,
289 char *buf, int buf_len)
290 {
291- char *ptr;
292 fputs(prompt, stdout);
293 fputc(' ', stdout);
294- if (fgets(buf, buf_len, stdin) == NULL)
295- return NULL;
296- if ((ptr= strchr(buf, '\n')))
297- *ptr= 0;
298+
299+ if (type == 2) /* password */
300+ {
301+ char *password;
302+ password= get_tty_password("");
303+ strncpy(buf, password, buf_len-1);
304+ buf[buf_len-1]= 0;
305+ free(password);
306+ }
307+ else
308+ {
309+ if (!fgets(buf, buf_len-1, stdin))
310+ buf[0]= 0;
311+ else
312+ {
313+ int len= strlen(buf);
314+ if (len && buf[len-1] == '\n')
315+ buf[len-1]= 0;
316+ }
317+ }
318
319 return buf;
320 }
321@@ -244,6 +259,7 @@
322 unsigned char *pkt, cmd= 0;
323 int pkt_len, res;
324 char reply_buf[1024], *reply;
325+ int first = 1;
326
327 do
328 {
329@@ -252,7 +268,7 @@
330 if (pkt_len < 0)
331 return CR_ERROR;
332
333- if (pkt == 0)
334+ if (pkt == 0 && first)
335 {
336 /*
337 in mysql_change_user() the client sends the first packet, so
338@@ -274,10 +290,10 @@
339 return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */
340
341 /*
342- asking for a password with an empty prompt means mysql->password
343+ asking for a password in the first packet mean mysql->password, if it's set
344 otherwise we ask the user and read the reply
345 */
346- if ((cmd >> 1) == 2 && *pkt == 0)
347+ if ((cmd >> 1) == 2 && first && mysql->passwd[0])
348 reply= mysql->passwd;
349 else
350 reply= ask(mysql, cmd >> 1, (const char *) pkt,
351@@ -296,6 +312,7 @@
352 return CR_ERROR;
353
354 /* repeat unless it was the last question */
355+ first= 0;
356 } while ((cmd & 1) != 1);
357
358 /* the job of reading the ok/error packet is left to the server */

Subscribers

People subscribed via source and target branches