Merge lp:~sergei.glushchenko/percona-pam-for-mysql/two-plugins into lp:percona-pam-for-mysql
- two-plugins
- Merge into percona-pam-for-mysql
Status: | Merged |
---|---|
Approved by: | Laurynas Biveinis |
Approved revision: | 23 |
Merged at revision: | 23 |
Proposed branch: | lp:~sergei.glushchenko/percona-pam-for-mysql/two-plugins |
Merge into: | lp:percona-pam-for-mysql |
Diff against target: |
729 lines (+410/-205) 7 files modified
CMakeLists.txt (+5/-2) src/Makefile.am (+6/-2) src/auth_mapping.h (+1/-2) src/auth_pam.c (+175/-0) src/auth_pam_common.c (+21/-199) src/auth_pam_common.h (+71/-0) src/auth_pam_compat.c (+131/-0) |
To merge this branch: | bzr merge lp:~sergei.glushchenko/percona-pam-for-mysql/two-plugins |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Laurynas Biveinis (community) | Approve | ||
Review via email: mp+92716@code.launchpad.net |
Commit message
Description of the change
Full-featured PAM auth server plugin was split into two parts.
First one is generic PAM conversation function, second one performs
client-specific message sending and receiving.
(auth_pam_common.c and auth_pam.c) modules. Oracle-compatible
server-side plugin was implemented (auth_pam_
Both plugins use auth_pam_common.c. All client-specific behavior
localized in auth_pam.c and auth_pam_compat.c.
auth_pam_compat plugin do not support dialog with client and the only
way to specify password with *mysql* client is using -p option.
Example:
mysql -u test -p111
or
mysql -u test -p
Enter password:
Besides there is no visible difference between case when -p option
used and when it not. So password_used is always set to
PASSWORD_
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2012-02-09 16:25:22 +0000 | |||
3 | +++ CMakeLists.txt 2012-02-13 06:42:18 +0000 | |||
4 | @@ -19,9 +19,12 @@ | |||
5 | 19 | CHECK_SYMBOL_EXISTS(getpwnam_r "pwd.h" HAVE_GETPWNAM_R) | 19 | CHECK_SYMBOL_EXISTS(getpwnam_r "pwd.h" HAVE_GETPWNAM_R) |
6 | 20 | CHECK_SYMBOL_EXISTS(getgrgid_r "grp.h" HAVE_GETGRGID_R) | 20 | CHECK_SYMBOL_EXISTS(getgrgid_r "grp.h" HAVE_GETGRGID_R) |
7 | 21 | IF(HAVE_PAM AND HAVE_MYSQLCLIENT AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R) | 21 | IF(HAVE_PAM AND HAVE_MYSQLCLIENT AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R) |
10 | 22 | SET(AUTH_PAM_SOURCES | 22 | SET(AUTH_PAM_COMMON_SOURCES |
11 | 23 | src/auth_pam.c src/lib_auth_pam_client.c src/lib_auth_pam_client.h | 23 | src/auth_pam_common.c src/lib_auth_pam_client.c src/lib_auth_pam_client.h |
12 | 24 | src/auth_mapping.h src/auth_mapping.c) | 24 | src/auth_mapping.h src/auth_mapping.c) |
13 | 25 | SET(AUTH_PAM_SOURCES ${AUTH_PAM_COMMON_SOURCES} src/auth_pam.c) | ||
14 | 26 | SET(AUTH_PAM_COMPAT_SOURCES ${AUTH_PAM_COMMON_SOURCES} src/auth_pam_compat.c) | ||
15 | 25 | MYSQL_ADD_PLUGIN(auth_pam ${AUTH_PAM_SOURCES} LINK_LIBRARIES pam) | 27 | MYSQL_ADD_PLUGIN(auth_pam ${AUTH_PAM_SOURCES} LINK_LIBRARIES pam) |
16 | 28 | MYSQL_ADD_PLUGIN(auth_pam_compat ${AUTH_PAM_COMPAT_SOURCES} LINK_LIBRARIES pam) | ||
17 | 26 | MYSQL_ADD_PLUGIN(dialog src/dialog.c LINK_LIBRARIES mysqlclient) | 29 | MYSQL_ADD_PLUGIN(dialog src/dialog.c LINK_LIBRARIES mysqlclient) |
18 | 27 | ENDIF(HAVE_PAM AND HAVE_MYSQLCLIENT AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R) | 30 | ENDIF(HAVE_PAM AND HAVE_MYSQLCLIENT AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R) |
19 | 28 | 31 | ||
20 | === modified file 'src/Makefile.am' | |||
21 | --- src/Makefile.am 2012-02-10 04:21:32 +0000 | |||
22 | +++ src/Makefile.am 2012-02-13 06:42:18 +0000 | |||
23 | @@ -16,14 +16,18 @@ | |||
24 | 16 | 16 | ||
25 | 17 | plugindir = @PLUGINDIR@ | 17 | plugindir = @PLUGINDIR@ |
26 | 18 | 18 | ||
28 | 19 | plugin_LTLIBRARIES = auth_pam.la test_auth_pam_client.la dialog.la | 19 | plugin_LTLIBRARIES = auth_pam.la auth_pam_compat.la test_auth_pam_client.la dialog.la |
29 | 20 | plugin_CPPFLAGS = -DMYSQL_DYNAMIC_PLUGIN | 20 | plugin_CPPFLAGS = -DMYSQL_DYNAMIC_PLUGIN |
30 | 21 | plugin_LDFLAGS = -module -avoid-version -shared | 21 | plugin_LDFLAGS = -module -avoid-version -shared |
31 | 22 | 22 | ||
33 | 23 | auth_pam_la_SOURCES = auth_pam.c lib_auth_pam_client.h lib_auth_pam_client.c auth_mapping.c | 23 | auth_pam_la_SOURCES = auth_pam_common.c auth_pam.c lib_auth_pam_client.h lib_auth_pam_client.c auth_mapping.c |
34 | 24 | auth_pam_la_CPPFLAGS = $(plugin_CPPFLAGS) | 24 | auth_pam_la_CPPFLAGS = $(plugin_CPPFLAGS) |
35 | 25 | auth_pam_la_LDFLAGS = $(plugin_LDFLAGS) $(AUTH_PAM_LIBS) | 25 | auth_pam_la_LDFLAGS = $(plugin_LDFLAGS) $(AUTH_PAM_LIBS) |
36 | 26 | 26 | ||
37 | 27 | auth_pam_compat_la_SOURCES = auth_pam_common.c auth_pam_compat.c lib_auth_pam_client.h lib_auth_pam_client.c auth_mapping.c | ||
38 | 28 | auth_pam_compat_la_CPPFLAGS = $(plugin_CPPFLAGS) | ||
39 | 29 | auth_pam_compat_la_LDFLAGS = $(plugin_LDFLAGS) $(AUTH_PAM_LIBS) | ||
40 | 30 | |||
41 | 27 | test_auth_pam_client_la_SOURCES = test_auth_pam_client.c \ | 31 | test_auth_pam_client_la_SOURCES = test_auth_pam_client.c \ |
42 | 28 | lib_auth_pam_client.h lib_auth_pam_client.c | 32 | lib_auth_pam_client.h lib_auth_pam_client.c |
43 | 29 | test_auth_pam_client_la_LDFLAGS = $(plugin_LDFLAGS) | 33 | test_auth_pam_client_la_LDFLAGS = $(plugin_LDFLAGS) |
44 | 30 | 34 | ||
45 | === modified file 'src/auth_mapping.h' | |||
46 | --- src/auth_mapping.h 2012-02-09 16:25:22 +0000 | |||
47 | +++ src/auth_mapping.h 2012-02-13 06:42:18 +0000 | |||
48 | @@ -20,9 +20,8 @@ | |||
49 | 20 | /** | 20 | /** |
50 | 21 | @file | 21 | @file |
51 | 22 | 22 | ||
53 | 23 | PAM authentication for MySQL, common definitions for side plugins. | 23 | PAM authentication for MySQL, interface for user mapping. |
54 | 24 | 24 | ||
55 | 25 | For the general description, see the top comment in auth_pam.c. | ||
56 | 26 | */ | 25 | */ |
57 | 27 | 26 | ||
58 | 28 | #ifdef __cplusplus | 27 | #ifdef __cplusplus |
59 | 29 | 28 | ||
60 | === added file 'src/auth_pam.c' | |||
61 | --- src/auth_pam.c 1970-01-01 00:00:00 +0000 | |||
62 | +++ src/auth_pam.c 2012-02-13 06:42:18 +0000 | |||
63 | @@ -0,0 +1,175 @@ | |||
64 | 1 | /* | ||
65 | 2 | (C) 2012 Percona Inc. | ||
66 | 3 | |||
67 | 4 | This program is free software; you can redistribute it and/or modify | ||
68 | 5 | it under the terms of the GNU General Public License as published by | ||
69 | 6 | the Free Software Foundation; version 2 of the License. | ||
70 | 7 | |||
71 | 8 | This program is distributed in the hope that it will be useful, | ||
72 | 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
73 | 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
74 | 11 | GNU General Public License for more details. | ||
75 | 12 | |||
76 | 13 | You should have received a copy of the GNU General Public License | ||
77 | 14 | along with this program; if not, write to the Free Software | ||
78 | 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
79 | 16 | */ | ||
80 | 17 | |||
81 | 18 | /** | ||
82 | 19 | @file | ||
83 | 20 | |||
84 | 21 | PAM authentication for MySQL, server-side plugin for the | ||
85 | 22 | production use. | ||
86 | 23 | |||
87 | 24 | A general-purpose PAM authentication plugin for MySQL. Acts as a mediator | ||
88 | 25 | between the MySQL server, the MySQL client, and the PAM backend. Dialog plugin | ||
89 | 26 | used as client plugin. | ||
90 | 27 | |||
91 | 28 | The server plugin requests authentication from the PAM backend, forwards any | ||
92 | 29 | requests and messages from the PAM backend over the wire to the client (in | ||
93 | 30 | cleartext) and reads back any replies for the backend. | ||
94 | 31 | |||
95 | 32 | This plugin does not encrypt the communication channel in any way. If this is | ||
96 | 33 | required, a SSL connection should be used. | ||
97 | 34 | |||
98 | 35 | To install this plugin, copy the .so file to the plugin directory and do | ||
99 | 36 | |||
100 | 37 | INSTALL PLUGIN auth_pam SONAME 'auth_pam.so'; | ||
101 | 38 | |||
102 | 39 | To use this plugin for one particular user, specify it at user's creation time | ||
103 | 40 | (TODO: tested with localhost only): | ||
104 | 41 | |||
105 | 42 | CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam; | ||
106 | 43 | |||
107 | 44 | Alternatively UPDATE the mysql.user table to set the plugin value for an | ||
108 | 45 | existing user. | ||
109 | 46 | |||
110 | 47 | Also it is possible to use this plugin to authenticate anonymous users: | ||
111 | 48 | |||
112 | 49 | CREATE USER ''@'hostname' IDENTIFIED WITH auth_pam; | ||
113 | 50 | |||
114 | 51 | */ | ||
115 | 52 | |||
116 | 53 | #ifdef HAVE_CONFIG_H | ||
117 | 54 | #include "config.h" | ||
118 | 55 | #endif | ||
119 | 56 | |||
120 | 57 | #include "auth_pam_common.h" | ||
121 | 58 | |||
122 | 59 | /** The maximum length of buffered PAM messages, i.e. any messages up to the | ||
123 | 60 | next PAM reply-requiring message. 10K should be more than enough by order | ||
124 | 61 | of magnitude. */ | ||
125 | 62 | enum { max_pam_buffered_msg_len = 10240 }; | ||
126 | 63 | |||
127 | 64 | struct pam_msg_buf { | ||
128 | 65 | unsigned char buf[max_pam_buffered_msg_len]; | ||
129 | 66 | unsigned char* ptr; | ||
130 | 67 | }; | ||
131 | 68 | |||
132 | 69 | static char pam_msg_style_to_char (int pam_msg_style) | ||
133 | 70 | { | ||
134 | 71 | /* Magic byte for the dialog plugin, '\2' is defined as ORDINARY_QUESTION | ||
135 | 72 | and '\4' as PASSWORD_QUESTION there. */ | ||
136 | 73 | return (pam_msg_style == PAM_PROMPT_ECHO_ON) ? '\2' : '\4'; | ||
137 | 74 | } | ||
138 | 75 | |||
139 | 76 | int auth_pam_client_talk_init(void **talk_data) | ||
140 | 77 | { | ||
141 | 78 | struct pam_msg_buf *msg_buf= calloc(1, sizeof(struct pam_msg_buf)); | ||
142 | 79 | *talk_data= (void*)msg_buf; | ||
143 | 80 | if (msg_buf != NULL) | ||
144 | 81 | { | ||
145 | 82 | msg_buf->ptr= msg_buf->buf + 1; | ||
146 | 83 | return PAM_SUCCESS; | ||
147 | 84 | } | ||
148 | 85 | return PAM_BUF_ERR; | ||
149 | 86 | } | ||
150 | 87 | |||
151 | 88 | void auth_pam_client_talk_finalize(void *talk_data) | ||
152 | 89 | { | ||
153 | 90 | free(talk_data); | ||
154 | 91 | } | ||
155 | 92 | |||
156 | 93 | int auth_pam_talk_perform(const struct pam_message *msg, | ||
157 | 94 | struct pam_response *resp, | ||
158 | 95 | struct pam_conv_data *data, | ||
159 | 96 | void *talk_data) | ||
160 | 97 | { | ||
161 | 98 | struct pam_msg_buf *msg_buf= (struct pam_msg_buf*)talk_data; | ||
162 | 99 | |||
163 | 100 | /* Append the PAM message or prompt to the unsent message buffer */ | ||
164 | 101 | if (msg->msg) | ||
165 | 102 | { | ||
166 | 103 | unsigned char *last_buf_pos = msg_buf->buf + max_pam_buffered_msg_len - 1; | ||
167 | 104 | if (msg_buf->ptr + strlen(msg->msg) >= last_buf_pos) | ||
168 | 105 | { | ||
169 | 106 | /* Cannot happen: the PAM message buffer too small. */ | ||
170 | 107 | MY_ASSERT_UNREACHABLE(); | ||
171 | 108 | return PAM_CONV_ERR; | ||
172 | 109 | } | ||
173 | 110 | memcpy(msg_buf->ptr, msg->msg, strlen(msg->msg)); | ||
174 | 111 | msg_buf->ptr+= strlen(msg->msg); | ||
175 | 112 | *(msg_buf->ptr)++= '\n'; | ||
176 | 113 | } | ||
177 | 114 | |||
178 | 115 | if (msg->msg_style == PAM_PROMPT_ECHO_OFF | ||
179 | 116 | || msg->msg_style == PAM_PROMPT_ECHO_ON) | ||
180 | 117 | { | ||
181 | 118 | int pkt_len; | ||
182 | 119 | unsigned char *pkt; | ||
183 | 120 | |||
184 | 121 | msg_buf->buf[0]= pam_msg_style_to_char(msg->msg_style); | ||
185 | 122 | |||
186 | 123 | /* Write the message. */ | ||
187 | 124 | if (data->vio->write_packet(data->vio, msg_buf->buf, | ||
188 | 125 | msg_buf->ptr - msg_buf->buf - 1)) | ||
189 | 126 | return PAM_CONV_ERR; | ||
190 | 127 | |||
191 | 128 | /* Read the answer */ | ||
192 | 129 | if ((pkt_len= data->vio->read_packet(data->vio, &pkt)) | ||
193 | 130 | < 0) | ||
194 | 131 | return PAM_CONV_ERR; | ||
195 | 132 | |||
196 | 133 | resp->resp= malloc(pkt_len + 1); | ||
197 | 134 | if (resp->resp == NULL) | ||
198 | 135 | return PAM_BUF_ERR; | ||
199 | 136 | |||
200 | 137 | strncpy(resp->resp, (char *)pkt, pkt_len); | ||
201 | 138 | resp->resp[pkt_len]= '\0'; | ||
202 | 139 | |||
203 | 140 | if (msg->msg_style == PAM_PROMPT_ECHO_OFF) | ||
204 | 141 | data->info->password_used= PASSWORD_USED_YES; | ||
205 | 142 | |||
206 | 143 | msg_buf->ptr= msg_buf->buf + 1; | ||
207 | 144 | } | ||
208 | 145 | |||
209 | 146 | return PAM_SUCCESS; | ||
210 | 147 | } | ||
211 | 148 | |||
212 | 149 | static struct st_mysql_auth pam_auth_handler= | ||
213 | 150 | { | ||
214 | 151 | MYSQL_AUTHENTICATION_INTERFACE_VERSION, | ||
215 | 152 | "dialog", | ||
216 | 153 | &authenticate_user_with_pam_server | ||
217 | 154 | }; | ||
218 | 155 | |||
219 | 156 | mysql_declare_plugin(auth_pam) | ||
220 | 157 | { | ||
221 | 158 | MYSQL_AUTHENTICATION_PLUGIN, | ||
222 | 159 | &pam_auth_handler, | ||
223 | 160 | "auth_pam", | ||
224 | 161 | "Percona, Inc.", | ||
225 | 162 | "PAM authentication plugin", | ||
226 | 163 | PLUGIN_LICENSE_GPL, | ||
227 | 164 | NULL, | ||
228 | 165 | NULL, | ||
229 | 166 | 0x0001, | ||
230 | 167 | NULL, | ||
231 | 168 | NULL, | ||
232 | 169 | NULL | ||
233 | 170 | #if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103 | ||
234 | 171 | , | ||
235 | 172 | 0 | ||
236 | 173 | #endif | ||
237 | 174 | } | ||
238 | 175 | mysql_declare_plugin_end; | ||
239 | 0 | 176 | ||
240 | === renamed file 'src/auth_pam.c' => 'src/auth_pam_common.c' | |||
241 | --- src/auth_pam.c 2012-02-09 16:25:22 +0000 | |||
242 | +++ src/auth_pam_common.c 2012-02-13 06:42:18 +0000 | |||
243 | @@ -15,69 +15,11 @@ | |||
244 | 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
245 | 16 | */ | 16 | */ |
246 | 17 | 17 | ||
247 | 18 | /** | ||
248 | 19 | @file | ||
249 | 20 | |||
250 | 21 | PAM authentication for MySQL, server- and client-side plugins for the | ||
251 | 22 | production use. | ||
252 | 23 | |||
253 | 24 | A general-purpose PAM authentication plugin for MySQL. Acts as a mediator | ||
254 | 25 | between the MySQL server, the MySQL client, and the PAM backend. Consists of | ||
255 | 26 | both the server and the client plugin. | ||
256 | 27 | |||
257 | 28 | The server plugin requests authentication from the PAM backend, forwards any | ||
258 | 29 | requests and messages from the PAM backend over the wire to the client (in | ||
259 | 30 | cleartext) and reads back any replies for the backend. | ||
260 | 31 | |||
261 | 32 | The client plugin inputs from the TTY any requested info/passwords as | ||
262 | 33 | requested and sends them back over the wire to the server. | ||
263 | 34 | |||
264 | 35 | This plugin does not encrypt the communication channel in any way. If this is | ||
265 | 36 | required, a SSL connection should be used. | ||
266 | 37 | |||
267 | 38 | To install this plugin, copy the .so file to the plugin directory and do | ||
268 | 39 | |||
269 | 40 | INSTALL PLUGIN auth_pam SONAME 'auth_pam.so'; | ||
270 | 41 | |||
271 | 42 | To use this plugin for one particular user, specify it at user's creation time | ||
272 | 43 | (TODO: tested with localhost only): | ||
273 | 44 | |||
274 | 45 | CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam; | ||
275 | 46 | |||
276 | 47 | Alternatively UPDATE the mysql.user table to set the plugin value for an | ||
277 | 48 | existing user. | ||
278 | 49 | |||
279 | 50 | Also it is possible to use this plugin to authenticate anonymous users: | ||
280 | 51 | |||
281 | 52 | CREATE USER ''@'hostname' IDENTIFIED WITH auth_pam; | ||
282 | 53 | |||
283 | 54 | */ | ||
284 | 55 | #ifdef HAVE_CONFIG_H | 18 | #ifdef HAVE_CONFIG_H |
285 | 56 | #include "config.h" | 19 | #include "config.h" |
286 | 57 | #endif | 20 | #endif |
287 | 58 | 21 | ||
310 | 59 | #include <assert.h> | 22 | #include "auth_pam_common.h" |
289 | 60 | |||
290 | 61 | #include <security/pam_appl.h> | ||
291 | 62 | #include <security/pam_modules.h> | ||
292 | 63 | #include <security/pam_misc.h> | ||
293 | 64 | |||
294 | 65 | /* Define these macros ourselves, so we don't have to include my_global.h and | ||
295 | 66 | can compile against unconfigured MySQL source tree. */ | ||
296 | 67 | #define STDCALL | ||
297 | 68 | |||
298 | 69 | #define MY_ASSERT_UNREACHABLE() assert(0) | ||
299 | 70 | |||
300 | 71 | #include <mysql/plugin.h> | ||
301 | 72 | #include <mysql/plugin_auth.h> | ||
302 | 73 | #include <mysql/client_plugin.h> | ||
303 | 74 | |||
304 | 75 | #ifdef HAVE_GETPASS | ||
305 | 76 | #include <unistd.h> /* getpass() */ | ||
306 | 77 | #else | ||
307 | 78 | #error "Please add support for echo-less input for your platform" | ||
308 | 79 | #endif | ||
309 | 80 | |||
311 | 81 | #include "auth_mapping.h" | 23 | #include "auth_mapping.h" |
312 | 82 | 24 | ||
313 | 83 | /* The server plugin */ | 25 | /* The server plugin */ |
314 | @@ -99,37 +41,10 @@ | |||
315 | 99 | } | 41 | } |
316 | 100 | } | 42 | } |
317 | 101 | 43 | ||
318 | 102 | static char pam_msg_style_to_char (int pam_msg_style) | ||
319 | 103 | { | ||
320 | 104 | /* Do not use any of MySQL client-server protocol reserved values, i.e. \0 */ | ||
321 | 105 | switch (pam_msg_style) | ||
322 | 106 | { | ||
323 | 107 | case PAM_PROMPT_ECHO_OFF: return '\2'; | ||
324 | 108 | case PAM_PROMPT_ECHO_ON: return '\3'; | ||
325 | 109 | case PAM_ERROR_MSG: return '\4'; | ||
326 | 110 | case PAM_TEXT_INFO: return '\5'; | ||
327 | 111 | default: | ||
328 | 112 | MY_ASSERT_UNREACHABLE(); | ||
329 | 113 | return '\1'; | ||
330 | 114 | } | ||
331 | 115 | } | ||
332 | 116 | |||
333 | 117 | /** The maximum length of buffered PAM messages, i.e. any messages up to the | ||
334 | 118 | next PAM reply-requiring message. 10K should be more than enough by order | ||
335 | 119 | of magnitude. */ | ||
336 | 120 | enum { max_pam_buffered_msg_len = 10240 }; | ||
337 | 121 | |||
338 | 122 | /** The maximum length of service name. It shouldn't be too long as it's | 44 | /** The maximum length of service name. It shouldn't be too long as it's |
339 | 123 | filename in pam.d directory also */ | 45 | filename in pam.d directory also */ |
340 | 124 | enum { max_pam_service_name_len = 64 }; | 46 | enum { max_pam_service_name_len = 64 }; |
341 | 125 | 47 | ||
342 | 126 | struct pam_conv_data { | ||
343 | 127 | MYSQL_PLUGIN_VIO *vio; | ||
344 | 128 | MYSQL_SERVER_AUTH_INFO *info; | ||
345 | 129 | unsigned char buf[max_pam_buffered_msg_len]; | ||
346 | 130 | unsigned char* ptr; | ||
347 | 131 | }; | ||
348 | 132 | |||
349 | 133 | static void free_pam_response (struct pam_response ** resp, int n) | 48 | static void free_pam_response (struct pam_response ** resp, int n) |
350 | 134 | { | 49 | { |
351 | 135 | int i; | 50 | int i; |
352 | @@ -145,6 +60,8 @@ | |||
353 | 145 | struct pam_response ** resp, void *appdata_ptr) | 60 | struct pam_response ** resp, void *appdata_ptr) |
354 | 146 | { | 61 | { |
355 | 147 | int i; | 62 | int i; |
356 | 63 | int error; | ||
357 | 64 | void *talk_data; | ||
358 | 148 | struct pam_conv_data *data= (struct pam_conv_data *)appdata_ptr; | 65 | struct pam_conv_data *data= (struct pam_conv_data *)appdata_ptr; |
359 | 149 | 66 | ||
360 | 150 | if (data == NULL) | 67 | if (data == NULL) |
361 | @@ -157,80 +74,39 @@ | |||
362 | 157 | if (*resp == NULL) | 74 | if (*resp == NULL) |
363 | 158 | return PAM_BUF_ERR; | 75 | return PAM_BUF_ERR; |
364 | 159 | 76 | ||
365 | 77 | error= auth_pam_client_talk_init(&talk_data); | ||
366 | 78 | if (error != PAM_SUCCESS) | ||
367 | 79 | { | ||
368 | 80 | free_pam_response(resp, 0); | ||
369 | 81 | return error; | ||
370 | 82 | } | ||
371 | 83 | |||
372 | 160 | for (i = 0; i < num_msg; i++) | 84 | for (i = 0; i < num_msg; i++) |
373 | 161 | { | 85 | { |
374 | 162 | if (!valid_pam_msg_style(msg[i]->msg_style)) | 86 | if (!valid_pam_msg_style(msg[i]->msg_style)) |
375 | 163 | { | 87 | { |
376 | 88 | auth_pam_client_talk_finalize(talk_data); | ||
377 | 164 | free_pam_response(resp, i); | 89 | free_pam_response(resp, i); |
378 | 165 | return PAM_CONV_ERR; | 90 | return PAM_CONV_ERR; |
379 | 166 | } | 91 | } |
380 | 167 | 92 | ||
437 | 168 | /* Append the PAM message or prompt to the unsent message buffer */ | 93 | error= auth_pam_talk_perform(msg[i], resp[i], data, talk_data); |
438 | 169 | if (msg[i]->msg) | 94 | if (error != PAM_SUCCESS) |
439 | 170 | { | 95 | { |
440 | 171 | unsigned char *last_buf_pos = data->buf + max_pam_buffered_msg_len - 1; | 96 | auth_pam_client_talk_finalize(talk_data); |
441 | 172 | if (data->ptr + strlen(msg[i]->msg) >= last_buf_pos) | 97 | free_pam_response(resp, i); |
442 | 173 | { | 98 | return error; |
387 | 174 | free_pam_response(resp, i); | ||
388 | 175 | /* Cannot happen: the PAM message buffer too small. */ | ||
389 | 176 | MY_ASSERT_UNREACHABLE(); | ||
390 | 177 | return PAM_CONV_ERR; | ||
391 | 178 | } | ||
392 | 179 | memcpy(data->ptr, msg[i]->msg, strlen(msg[i]->msg)); | ||
393 | 180 | data->ptr+= strlen(msg[i]->msg); | ||
394 | 181 | *(data->ptr)++= '\n'; | ||
395 | 182 | } | ||
396 | 183 | |||
397 | 184 | if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF | ||
398 | 185 | || msg[i]->msg_style == PAM_PROMPT_ECHO_ON) | ||
399 | 186 | { | ||
400 | 187 | int pkt_len; | ||
401 | 188 | unsigned char *pkt; | ||
402 | 189 | |||
403 | 190 | /* Magic byte for the dialog plugin, '\2' is defined as ORDINARY_QUESTION | ||
404 | 191 | and '\4' as PASSWORD_QUESTION there. */ | ||
405 | 192 | data->buf[0]= (msg[i]->msg_style == PAM_PROMPT_ECHO_ON ? '\2' : '\4'); | ||
406 | 193 | |||
407 | 194 | /* Write the message. */ | ||
408 | 195 | if (data->vio->write_packet(data->vio, data->buf, | ||
409 | 196 | data->ptr - data->buf - 1)) | ||
410 | 197 | { | ||
411 | 198 | free_pam_response(resp, i); | ||
412 | 199 | return PAM_CONV_ERR; | ||
413 | 200 | } | ||
414 | 201 | |||
415 | 202 | /* Read the answer */ | ||
416 | 203 | if ((pkt_len= data->vio->read_packet(data->vio, &pkt)) | ||
417 | 204 | < 0) | ||
418 | 205 | { | ||
419 | 206 | free_pam_response(resp, i); | ||
420 | 207 | return PAM_CONV_ERR; | ||
421 | 208 | } | ||
422 | 209 | |||
423 | 210 | (*resp)[i].resp= malloc(pkt_len + 1); | ||
424 | 211 | if ((*resp)[i].resp == NULL) | ||
425 | 212 | { | ||
426 | 213 | free_pam_response(resp, i); | ||
427 | 214 | return PAM_BUF_ERR; | ||
428 | 215 | } | ||
429 | 216 | |||
430 | 217 | strncpy((*resp)[i].resp, (char *)pkt, pkt_len); | ||
431 | 218 | (*resp)[i].resp[pkt_len]= '\0'; | ||
432 | 219 | |||
433 | 220 | if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) | ||
434 | 221 | data->info->password_used= PASSWORD_USED_YES; | ||
435 | 222 | |||
436 | 223 | data->ptr= data->buf + 1; | ||
443 | 224 | } | 99 | } |
444 | 225 | } | 100 | } |
445 | 101 | auth_pam_client_talk_finalize(talk_data); | ||
446 | 226 | return PAM_SUCCESS; | 102 | return PAM_SUCCESS; |
447 | 227 | } | 103 | } |
448 | 228 | 104 | ||
451 | 229 | static int authenticate_user_with_pam_server (MYSQL_PLUGIN_VIO *vio, | 105 | int authenticate_user_with_pam_server (MYSQL_PLUGIN_VIO *vio, |
452 | 230 | MYSQL_SERVER_AUTH_INFO *info) | 106 | MYSQL_SERVER_AUTH_INFO *info) |
453 | 231 | { | 107 | { |
454 | 232 | pam_handle_t *pam_handle; | 108 | pam_handle_t *pam_handle; |
456 | 233 | struct pam_conv_data data= { vio, info, "", data.buf+1 }; | 109 | struct pam_conv_data data= { vio, info }; |
457 | 234 | struct pam_conv conv_func_info= { &vio_server_conv, &data }; | 110 | struct pam_conv conv_func_info= { &vio_server_conv, &data }; |
458 | 235 | int error; | 111 | int error; |
459 | 236 | char *pam_mapped_user_name; | 112 | char *pam_mapped_user_name; |
460 | @@ -312,57 +188,3 @@ | |||
461 | 312 | 188 | ||
462 | 313 | return CR_OK; | 189 | return CR_OK; |
463 | 314 | } | 190 | } |
464 | 315 | |||
465 | 316 | static struct st_mysql_auth pam_auth_handler= | ||
466 | 317 | { | ||
467 | 318 | MYSQL_AUTHENTICATION_INTERFACE_VERSION, | ||
468 | 319 | "dialog", | ||
469 | 320 | &authenticate_user_with_pam_server | ||
470 | 321 | }; | ||
471 | 322 | |||
472 | 323 | static struct st_mysql_auth test_pam_auth_handler= | ||
473 | 324 | { | ||
474 | 325 | MYSQL_AUTHENTICATION_INTERFACE_VERSION, | ||
475 | 326 | "auth_pam_test", | ||
476 | 327 | &authenticate_user_with_pam_server | ||
477 | 328 | }; | ||
478 | 329 | |||
479 | 330 | mysql_declare_plugin(auth_pam) | ||
480 | 331 | { | ||
481 | 332 | MYSQL_AUTHENTICATION_PLUGIN, | ||
482 | 333 | &pam_auth_handler, | ||
483 | 334 | "auth_pam", | ||
484 | 335 | "Percona, Inc.", | ||
485 | 336 | "PAM authentication plugin", | ||
486 | 337 | PLUGIN_LICENSE_GPL, | ||
487 | 338 | NULL, | ||
488 | 339 | NULL, | ||
489 | 340 | 0x0001, | ||
490 | 341 | NULL, | ||
491 | 342 | NULL, | ||
492 | 343 | NULL | ||
493 | 344 | #if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103 | ||
494 | 345 | , | ||
495 | 346 | 0 | ||
496 | 347 | #endif | ||
497 | 348 | }, | ||
498 | 349 | { | ||
499 | 350 | MYSQL_AUTHENTICATION_PLUGIN, | ||
500 | 351 | &test_pam_auth_handler, | ||
501 | 352 | "test_auth_pam_server", | ||
502 | 353 | "Percona, Inc.", | ||
503 | 354 | "PAM authentication plugin that requests the test version of the" | ||
504 | 355 | " client-side plugin. DO NOT USE IN PRODUCTION.", | ||
505 | 356 | PLUGIN_LICENSE_GPL, | ||
506 | 357 | NULL, | ||
507 | 358 | NULL, | ||
508 | 359 | 0x0001, | ||
509 | 360 | NULL, | ||
510 | 361 | NULL, | ||
511 | 362 | NULL | ||
512 | 363 | #if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103 | ||
513 | 364 | , | ||
514 | 365 | 0 | ||
515 | 366 | #endif | ||
516 | 367 | } | ||
517 | 368 | mysql_declare_plugin_end; | ||
518 | 369 | 191 | ||
519 | === added file 'src/auth_pam_common.h' | |||
520 | --- src/auth_pam_common.h 1970-01-01 00:00:00 +0000 | |||
521 | +++ src/auth_pam_common.h 2012-02-13 06:42:18 +0000 | |||
522 | @@ -0,0 +1,71 @@ | |||
523 | 1 | #ifndef AUTH_PAM_COMMON_INCLUDED | ||
524 | 2 | #define AUTH_PAM_COMMON_INCLUDED | ||
525 | 3 | /* | ||
526 | 4 | (C) 2012 Percona Inc. | ||
527 | 5 | |||
528 | 6 | This program is free software; you can redistribute it and/or modify | ||
529 | 7 | it under the terms of the GNU General Public License as published by | ||
530 | 8 | the Free Software Foundation; version 2 of the License. | ||
531 | 9 | |||
532 | 10 | This program is distributed in the hope that it will be useful, | ||
533 | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
534 | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
535 | 13 | GNU General Public License for more details. | ||
536 | 14 | |||
537 | 15 | You should have received a copy of the GNU General Public License | ||
538 | 16 | along with this program; if not, write to the Free Software | ||
539 | 17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
540 | 18 | */ | ||
541 | 19 | |||
542 | 20 | /** | ||
543 | 21 | @file | ||
544 | 22 | |||
545 | 23 | PAM authentication for MySQL, common definitions for side plugins. | ||
546 | 24 | |||
547 | 25 | For the general description, see the top comment in auth_pam_common.c. | ||
548 | 26 | */ | ||
549 | 27 | |||
550 | 28 | /* Define these macros ourselves, so we don't have to include my_global.h and | ||
551 | 29 | can compile against unconfigured MySQL source tree. */ | ||
552 | 30 | #define STDCALL | ||
553 | 31 | |||
554 | 32 | #include <security/pam_appl.h> | ||
555 | 33 | #include <security/pam_modules.h> | ||
556 | 34 | #include <security/pam_misc.h> | ||
557 | 35 | |||
558 | 36 | #include <mysql/plugin.h> | ||
559 | 37 | #include <mysql/plugin_auth.h> | ||
560 | 38 | #include <mysql/client_plugin.h> | ||
561 | 39 | |||
562 | 40 | #include <assert.h> | ||
563 | 41 | |||
564 | 42 | #define MY_ASSERT_UNREACHABLE() assert(0) | ||
565 | 43 | |||
566 | 44 | #ifdef __cplusplus | ||
567 | 45 | extern "C" { | ||
568 | 46 | #endif | ||
569 | 47 | |||
570 | 48 | struct pam_conv_data { | ||
571 | 49 | MYSQL_PLUGIN_VIO *vio; | ||
572 | 50 | MYSQL_SERVER_AUTH_INFO *info; | ||
573 | 51 | }; | ||
574 | 52 | |||
575 | 53 | /** Define following three functions for your specific client plugin */ | ||
576 | 54 | |||
577 | 55 | int auth_pam_client_talk_init(void **talk_data); | ||
578 | 56 | |||
579 | 57 | int auth_pam_talk_perform(const struct pam_message *msg, | ||
580 | 58 | struct pam_response *resp, | ||
581 | 59 | struct pam_conv_data *data, | ||
582 | 60 | void *talk_data); | ||
583 | 61 | |||
584 | 62 | void auth_pam_client_talk_finalize(void *talk_data); | ||
585 | 63 | |||
586 | 64 | int authenticate_user_with_pam_server (MYSQL_PLUGIN_VIO *vio, | ||
587 | 65 | MYSQL_SERVER_AUTH_INFO *info); | ||
588 | 66 | |||
589 | 67 | #ifdef __cplusplus | ||
590 | 68 | } | ||
591 | 69 | #endif | ||
592 | 70 | |||
593 | 71 | #endif | ||
594 | 0 | 72 | ||
595 | === added file 'src/auth_pam_compat.c' | |||
596 | --- src/auth_pam_compat.c 1970-01-01 00:00:00 +0000 | |||
597 | +++ src/auth_pam_compat.c 2012-02-13 06:42:18 +0000 | |||
598 | @@ -0,0 +1,131 @@ | |||
599 | 1 | /* | ||
600 | 2 | (C) 2012 Percona Inc. | ||
601 | 3 | |||
602 | 4 | This program is free software; you can redistribute it and/or modify | ||
603 | 5 | it under the terms of the GNU General Public License as published by | ||
604 | 6 | the Free Software Foundation; version 2 of the License. | ||
605 | 7 | |||
606 | 8 | This program is distributed in the hope that it will be useful, | ||
607 | 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
608 | 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
609 | 11 | GNU General Public License for more details. | ||
610 | 12 | |||
611 | 13 | You should have received a copy of the GNU General Public License | ||
612 | 14 | along with this program; if not, write to the Free Software | ||
613 | 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
614 | 16 | */ | ||
615 | 17 | |||
616 | 18 | /** | ||
617 | 19 | @file | ||
618 | 20 | |||
619 | 21 | PAM authentication for MySQL, server-side plugin for the | ||
620 | 22 | production use. | ||
621 | 23 | |||
622 | 24 | Oracle MySQL-compatible plugin. Acts as a mediator | ||
623 | 25 | between the MySQL server, the MySQL client, and the PAM backend. | ||
624 | 26 | |||
625 | 27 | The server plugin requests authentication from the PAM backend, and reads one | ||
626 | 28 | phrase from client plugin. mysql_clear_password plugin used as client plugin. | ||
627 | 29 | |||
628 | 30 | This plugin does not encrypt the communication channel in any way. If this is | ||
629 | 31 | required, a SSL connection should be used. | ||
630 | 32 | |||
631 | 33 | To install this plugin, copy the .so file to the plugin directory and do | ||
632 | 34 | |||
633 | 35 | INSTALL PLUGIN auth_pam SONAME 'auth_pam_compat.so'; | ||
634 | 36 | |||
635 | 37 | To use this plugin for one particular user, specify it at user's creation time | ||
636 | 38 | (TODO: tested with localhost only): | ||
637 | 39 | |||
638 | 40 | CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam_compat; | ||
639 | 41 | |||
640 | 42 | Alternatively UPDATE the mysql.user table to set the plugin value for an | ||
641 | 43 | existing user. | ||
642 | 44 | |||
643 | 45 | Also it is possible to use this plugin to authenticate anonymous users: | ||
644 | 46 | |||
645 | 47 | CREATE USER ''@'hostname' IDENTIFIED WITH auth_pam_compat; | ||
646 | 48 | |||
647 | 49 | */ | ||
648 | 50 | |||
649 | 51 | #ifdef HAVE_CONFIG_H | ||
650 | 52 | #include "config.h" | ||
651 | 53 | #endif | ||
652 | 54 | |||
653 | 55 | #include "auth_pam_common.h" | ||
654 | 56 | |||
655 | 57 | int auth_pam_client_talk_init(void **talk_data) | ||
656 | 58 | { | ||
657 | 59 | int *num_talks= calloc(1, sizeof(int)); | ||
658 | 60 | *talk_data= (void*)num_talks; | ||
659 | 61 | return (num_talks != NULL) ? PAM_SUCCESS : PAM_BUF_ERR; | ||
660 | 62 | } | ||
661 | 63 | |||
662 | 64 | void auth_pam_client_talk_finalize(void *talk_data) | ||
663 | 65 | { | ||
664 | 66 | free(talk_data); | ||
665 | 67 | } | ||
666 | 68 | |||
667 | 69 | int auth_pam_talk_perform(const struct pam_message *msg, | ||
668 | 70 | struct pam_response *resp, | ||
669 | 71 | struct pam_conv_data *data, | ||
670 | 72 | void *talk_data) | ||
671 | 73 | { | ||
672 | 74 | int pkt_len; | ||
673 | 75 | unsigned char *pkt; | ||
674 | 76 | int *num_talks= (int*)talk_data; | ||
675 | 77 | |||
676 | 78 | if (msg->msg_style == PAM_PROMPT_ECHO_OFF | ||
677 | 79 | || msg->msg_style == PAM_PROMPT_ECHO_ON) | ||
678 | 80 | { | ||
679 | 81 | /* mysql_clear_password plugin has support for only single phrase */ | ||
680 | 82 | if (*num_talks > 1) | ||
681 | 83 | return PAM_CONV_ERR; | ||
682 | 84 | |||
683 | 85 | /* Read the answer */ | ||
684 | 86 | if ((pkt_len= data->vio->read_packet(data->vio, &pkt)) | ||
685 | 87 | < 0) | ||
686 | 88 | return PAM_CONV_ERR; | ||
687 | 89 | |||
688 | 90 | resp->resp= malloc(pkt_len + 1); | ||
689 | 91 | if (resp->resp == NULL) | ||
690 | 92 | return PAM_BUF_ERR; | ||
691 | 93 | |||
692 | 94 | strncpy(resp->resp, (char *)pkt, pkt_len); | ||
693 | 95 | resp->resp[pkt_len]= '\0'; | ||
694 | 96 | |||
695 | 97 | /* we could only guess whether password was used or not */ | ||
696 | 98 | data->info->password_used= PASSWORD_USED_NO_MENTION; | ||
697 | 99 | ++(*num_talks); | ||
698 | 100 | } | ||
699 | 101 | |||
700 | 102 | return PAM_SUCCESS; | ||
701 | 103 | } | ||
702 | 104 | |||
703 | 105 | static struct st_mysql_auth pam_auth_handler= | ||
704 | 106 | { | ||
705 | 107 | MYSQL_AUTHENTICATION_INTERFACE_VERSION, | ||
706 | 108 | "mysql_clear_password", | ||
707 | 109 | &authenticate_user_with_pam_server | ||
708 | 110 | }; | ||
709 | 111 | |||
710 | 112 | mysql_declare_plugin(auth_pam) | ||
711 | 113 | { | ||
712 | 114 | MYSQL_AUTHENTICATION_PLUGIN, | ||
713 | 115 | &pam_auth_handler, | ||
714 | 116 | "auth_pam_compat", | ||
715 | 117 | "Percona, Inc.", | ||
716 | 118 | "PAM authentication plugin", | ||
717 | 119 | PLUGIN_LICENSE_GPL, | ||
718 | 120 | NULL, | ||
719 | 121 | NULL, | ||
720 | 122 | 0x0001, | ||
721 | 123 | NULL, | ||
722 | 124 | NULL, | ||
723 | 125 | NULL | ||
724 | 126 | #if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103 | ||
725 | 127 | , | ||
726 | 128 | 0 | ||
727 | 129 | #endif | ||
728 | 130 | } | ||
729 | 131 | mysql_declare_plugin_end; |
LGTM, thank you.