Merge lp:~sergei.glushchenko/percona-pam-for-mysql/BT32086-bug1160348 into lp:percona-pam-for-mysql

Proposed by Sergei Glushchenko
Status: Merged
Approved by: Laurynas Biveinis
Approved revision: 29
Merged at revision: 29
Proposed branch: lp:~sergei.glushchenko/percona-pam-for-mysql/BT32086-bug1160348
Merge into: lp:percona-pam-for-mysql
Diff against target: 515 lines (+226/-88)
10 files modified
CMakeLists.txt (+2/-2)
configure.ac (+2/-5)
src/Makefile.am (+6/-3)
src/auth_mapping.c (+53/-61)
src/auth_mapping.h (+2/-7)
src/auth_pam.c (+2/-1)
src/auth_pam_common.c (+6/-8)
src/auth_pam_compat.c (+2/-1)
src/groups.c (+98/-0)
src/groups.h (+53/-0)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-pam-for-mysql/BT32086-bug1160348
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) Approve
Review via email: mp+167766@code.launchpad.net

Description of the change

This adds support for supplementary groups to PAM plugin user mapping. getgrouplist is used to obtain full list of groups user belongs to. If user is a member of several mapped groups, the group which is listed in authentication string first will be taken.

Also minor issues were addressed, such as copyright changed in files, broken link was removed, libmysqlclient is taken from mysql_config output.

Tested on CentOS 5 with PAM Unix and Mac OS X 10.7 with Open Directory.

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

Looks good.

Please convert bug 1160348 to a blueprint for Percona Server, and please consider expanding the PAM Jenkins test setup to handle this.

review: Approve
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

Laurynas,

I don't like current concept of test cases for PAM. They are very intrusive. They add users and groups, create files in /etc and so on. Is there any chance that we revisit it in future?

I believe that tests should be easy to run by every user who downloaded and built software. Current approach makes it very hard to do. One should either run test suite as root or manually setup users and groups. Also there is no cleanup at all. It means that created user accounts remain after test is finished which is not good at all.

Can't we use some kind of mocks or stubs for NSS lookups and PAM functions?
We also could easily cover large of the code by unit tests.

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

The current test cases are meant to be run in VMs, and as such they get their job done. I don't see a problem with extending the current testcases first, I don't think the current MP is the right place/time to pursue the alternatives.

For alternatives in general we can do what MariaDB did, i.e. a mock PAM module.

Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :

So, for every new test case I have to create issue in "Percona Dev Services" board to change Jenkins configuration. In this case I need a user to be created and added to several groups. I find that it is not convenient.

Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

Sergei -

Please create an issue "consider migrating away from VM-based PAM testing" instead.

To work with the current tests, please have a look at test/dbqp/percona_tests/percona_pam. I don't think there is any need to change Jenkins configuration itself.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2012-06-13 04:13:02 +0000
3+++ CMakeLists.txt 2013-06-06 14:09:25 +0000
4@@ -1,4 +1,4 @@
5-# (C) 2011 Percona Inc.
6+# (C) 2011-2013 Percona Ireland Ltd.
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10@@ -23,7 +23,7 @@
11 IF(HAVE_PAM AND HAVE_MYSQLCLIENT AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R)
12 SET(AUTH_PAM_COMMON_SOURCES
13 src/auth_pam_common.c src/lib_auth_pam_client.c src/lib_auth_pam_client.h
14- src/auth_mapping.h src/auth_mapping.c)
15+ src/auth_mapping.h src/auth_mapping.c src/groups.c src/groups.h)
16 SET(AUTH_PAM_SOURCES ${AUTH_PAM_COMMON_SOURCES} src/auth_pam.c)
17 SET(AUTH_PAM_COMPAT_SOURCES ${AUTH_PAM_COMMON_SOURCES} src/auth_pam_compat.c)
18 MYSQL_ADD_PLUGIN(auth_pam ${AUTH_PAM_SOURCES} LINK_LIBRARIES pam)
19
20=== modified file 'configure.ac'
21--- configure.ac 2012-02-10 04:21:32 +0000
22+++ configure.ac 2013-06-06 14:09:25 +0000
23@@ -1,5 +1,5 @@
24 # -*- Autoconf -*-
25-# (C) 2011 Percona Inc.
26+# (C) 2011-2013 Percona Ireland Ltd.
27 #
28 # This program is free software; you can redistribute it and/or modify
29 # it under the terms of the GNU General Public License as published by
30@@ -46,10 +46,7 @@
31 AC_MSG_ERROR(
32 [Unable to find PAM. Please install the PAM development libraries])
33 )
34-AC_CHECK_LIB([mysqlclient], [mysql_init], [DIALOG_LIBS="$DIALOG_LIBS -lmysqlclient"],
35- AC_MSG_ERROR(
36- [Unable to find libmysqlclient. Please install the mysql development libraries])
37-)
38+DIALOG_LIBS="$DIALOG_LIBS "`${MYSQL_CONFIG} --libs_r`
39
40 # Replace -I with -isystem for the MySQL header include GCC option to silence
41 # warnings originating from them
42
43=== modified file 'src/Makefile.am'
44--- src/Makefile.am 2012-02-13 06:19:11 +0000
45+++ src/Makefile.am 2013-06-06 14:09:25 +0000
46@@ -1,4 +1,4 @@
47-# (C) 2011 Percona Inc.
48+# (C) 2011-2013 Percona Ireland Ltd.
49 #
50 # This program is free software; you can redistribute it and/or modify
51 # it under the terms of the GNU General Public License as published by
52@@ -20,11 +20,14 @@
53 plugin_CPPFLAGS = -DMYSQL_DYNAMIC_PLUGIN
54 plugin_LDFLAGS = -module -avoid-version -shared
55
56-auth_pam_la_SOURCES = auth_pam_common.c auth_pam.c lib_auth_pam_client.h lib_auth_pam_client.c auth_mapping.c
57+auth_pam_la_SOURCES = auth_pam_common.c auth_pam.c lib_auth_pam_client.h \
58+ lib_auth_pam_client.c auth_mapping.c groups.c
59 auth_pam_la_CPPFLAGS = $(plugin_CPPFLAGS)
60 auth_pam_la_LDFLAGS = $(plugin_LDFLAGS) $(AUTH_PAM_LIBS)
61
62-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
63+auth_pam_compat_la_SOURCES = auth_pam_common.c auth_pam_compat.c \
64+ lib_auth_pam_client.h lib_auth_pam_client.c \
65+ auth_mapping.c groups.c
66 auth_pam_compat_la_CPPFLAGS = $(plugin_CPPFLAGS)
67 auth_pam_compat_la_LDFLAGS = $(plugin_LDFLAGS) $(AUTH_PAM_LIBS)
68
69
70=== modified file 'src/auth_mapping.c'
71--- src/auth_mapping.c 2012-02-09 16:25:22 +0000
72+++ src/auth_mapping.c 2013-06-06 14:09:25 +0000
73@@ -1,5 +1,5 @@
74 /*
75-(C) 2012 Percona Inc.
76+(C) 2012, 2013 Percona Ireland Ltd.
77
78 This program is free software; you can redistribute it and/or modify
79 it under the terms of the GNU General Public License as published by
80@@ -16,19 +16,13 @@
81 */
82
83 #include "auth_mapping.h"
84-#include <pwd.h>
85-#include <grp.h>
86+#include "groups.h"
87 #include <string.h>
88 #include <stdlib.h>
89 #include <ctype.h>
90
91 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
92
93-/** The maximum length of buffer for storing NSS record. NSS will store in
94- buffer the whole result of lookup request including user name,
95- gecos, etc. */
96-enum { max_nss_name_len = 10240 };
97-
98 /** Token representation:
99 token type, string repr, length of token */
100 struct token
101@@ -50,43 +44,6 @@
102 const char *ptr;
103 };
104
105-
106-/** Lookup NSS database for group name by specified user name.
107- On sucess user_group returned, otherwise NULL */
108-char *lookup_user_group (const char *user_name,
109- char *user_group, int user_group_len)
110-{
111- struct passwd pwd, *pwd_result;
112- struct group grp, *grp_result;
113- char *buf;
114- int error;
115-
116- buf= malloc(max_nss_name_len);
117- if (buf == NULL)
118- return NULL;
119-
120- error= getpwnam_r(user_name, &pwd, buf, max_nss_name_len, &pwd_result);
121- if (error != 0 || pwd_result == NULL)
122- {
123- free(buf);
124- return NULL;
125- }
126-
127- error= getgrgid_r(pwd_result->pw_gid,
128- &grp, buf, max_nss_name_len, &grp_result);
129- if (error != 0 || grp_result == NULL)
130- {
131- free(buf);
132- return NULL;
133- }
134-
135- strncpy(user_group, grp_result->gr_name, user_group_len);
136- user_group[user_group_len]= '\0';
137- free(buf);
138-
139- return user_group;
140-}
141-
142 /** Get next token from buf. Returns new buf position. */
143 static const char *get_token(struct token *token,
144 const char *buf)
145@@ -147,7 +104,7 @@
146 otherwise NULL */
147 const char *mapping_iter_next(struct mapping_iter *it)
148 {
149- struct token token[4];
150+ struct token token[4]= {{0}};
151
152 /* read next 4 tokens */
153 it->ptr= get_token(token + 3,
154@@ -182,28 +139,63 @@
155 free(it);
156 }
157
158-/** Get value by given key. On success value_buf returned,
159- otherwise NULL */
160-char *mapping_get_value(const char *key, char *value_buf, int value_buf_len,
161- const char *mapping_string)
162+/** Get mapped value for given user name.
163+ Value is looked up by using all user groups as a key.
164+ Auth string is iterated only once, while groups are iterated
165+ for every key-value pair. This is mean than auth string order
166+ is dominant.
167+
168+ Example:
169+
170+ given:
171+ user "foo" is the member of "wheel", "staff" and "bar".
172+ auth string is "mysql, root=user1, bar=user2, staff=user3"
173+
174+ result is "user2".
175+
176+ On success value_buf returned, otherwise NULL */
177+char *mapping_lookup_user(const char *user_name,
178+ char *value_buf, int value_buf_len,
179+ const char *mapping_string)
180 {
181- struct mapping_iter *it= mapping_iter_new(mapping_string);
182- int key_len= strlen(key);
183+ /* Iterate through the key-value list stored in auth_string and
184+ find key (which is interpreted as group name) in the list of groups
185+ for specified user. If match is found, store appropriate value in
186+ the authenticated_as field. */
187+ struct groups_iter *group_it;
188+ struct mapping_iter *keyval_it;
189+ const char *key;
190+ const char *group;
191
192- if (it == NULL)
193+ keyval_it= mapping_iter_new(mapping_string);
194+ if (keyval_it == NULL)
195 return NULL;
196
197- while (mapping_iter_next(it))
198+ group_it= groups_iter_new(user_name);
199+ if (group_it == NULL)
200 {
201- if (key_len == it->key_len && strncmp(key, it->key, key_len) == 0)
202- {
203- memcpy(value_buf, it->value, MIN(value_buf_len, it->value_len));
204- value_buf[MIN(value_buf_len, it->value_len)]= '\0';
205- mapping_iter_free(it);
206- return value_buf;
207+ mapping_iter_free(keyval_it);
208+ return NULL;
209+ }
210+
211+ while ((key= mapping_iter_next(keyval_it)) != NULL) {
212+ while ((group= groups_iter_next(group_it)) != NULL) {
213+ if (keyval_it->key_len == strlen(group) &&
214+ strncmp(key, group, keyval_it->key_len) == 0) {
215+ /* match is found */
216+ memcpy(value_buf, keyval_it->value,
217+ MIN(value_buf_len, keyval_it->value_len));
218+ value_buf[MIN(value_buf_len, keyval_it->value_len)]= '\0';
219+ groups_iter_free(group_it);
220+ mapping_iter_free(keyval_it);
221+ return value_buf;
222+ }
223 }
224+ groups_iter_reset(group_it);
225 }
226- mapping_iter_free(it);
227+
228+ groups_iter_free(group_it);
229+ mapping_iter_free(keyval_it);
230
231 return NULL;
232 }
233
234=== modified file 'src/auth_mapping.h'
235--- src/auth_mapping.h 2012-02-13 06:19:11 +0000
236+++ src/auth_mapping.h 2013-06-06 14:09:25 +0000
237@@ -1,7 +1,7 @@
238 #ifndef AUTH_MAPPING_INCLUDED
239 #define AUTH_MAPPING_INCLUDED
240 /*
241- (C) 2012 Percona Inc.
242+ (C) 2012, 2013 Percona Ireland Ltd.
243
244 This program is free software; you can redistribute it and/or modify
245 it under the terms of the GNU General Public License as published by
246@@ -31,11 +31,6 @@
247 /** Mapping iterator. It's not exposed outsude */
248 struct mapping_iter;
249
250-/** Lookup NSS database for group name by specified user name.
251- On sucess user_group returned, otherwise NULL */
252-char *lookup_user_group (const char *user_name,
253- char *user_group, int user_group_len);
254-
255 /** Create iterator through mapping string.
256 Initially iterator set to position before first
257 key-value pair. On success non-NULL pointer returned, otherwise NULL */
258@@ -59,7 +54,7 @@
259
260 /** Get value by given key. On success value_buf returned,
261 otherwise NULL */
262-char *mapping_get_value(const char *key, char *value_buf, int value_buf_len,
263+char *mapping_lookup_user(const char *key, char *value_buf, int value_buf_len,
264 const char *mapping_string);
265
266 /** Get service name for auth_string. On success buf returned,
267
268=== modified file 'src/auth_pam.c'
269--- src/auth_pam.c 2012-02-13 06:19:11 +0000
270+++ src/auth_pam.c 2013-06-06 14:09:25 +0000
271@@ -1,5 +1,5 @@
272 /*
273-(C) 2012 Percona Inc.
274+(C) 2012, 2013 Percona Ireland Ltd.
275
276 This program is free software; you can redistribute it and/or modify
277 it under the terms of the GNU General Public License as published by
278@@ -54,6 +54,7 @@
279 #include "config.h"
280 #endif
281
282+#include <string.h>
283 #include "auth_pam_common.h"
284
285 /** The maximum length of buffered PAM messages, i.e. any messages up to the
286
287=== modified file 'src/auth_pam_common.c'
288--- src/auth_pam_common.c 2012-02-13 06:19:11 +0000
289+++ src/auth_pam_common.c 2013-06-06 14:09:25 +0000
290@@ -1,5 +1,5 @@
291 /*
292-(C) 2011 Percona Inc.
293+(C) 2011-2013 Percona Ireland Ltd.
294
295 This program is free software; you can redistribute it and/or modify
296 it under the terms of the GNU General Public License as published by
297@@ -19,8 +19,10 @@
298 #include "config.h"
299 #endif
300
301+#include <string.h>
302 #include "auth_pam_common.h"
303 #include "auth_mapping.h"
304+#include "groups.h"
305
306 /* The server plugin */
307
308@@ -110,7 +112,6 @@
309 struct pam_conv conv_func_info= { &vio_server_conv, &data };
310 int error;
311 char *pam_mapped_user_name;
312- char user_group[MYSQL_USERNAME_LENGTH];
313 char service_name[max_pam_service_name_len];
314
315 /* Set service name as specified in auth_string. If no auth_string
316@@ -173,13 +174,10 @@
317 info->authenticated_as[MYSQL_USERNAME_LENGTH]= '\0';
318 }
319
320- /* If auth_string specified, then lookup user group,
321- get mapped MySQL user name and use it as CURRENT_USER() value */
322- if (info->auth_string &&
323- lookup_user_group(pam_mapped_user_name, user_group, sizeof(user_group)))
324+ if (info->auth_string)
325 {
326- mapping_get_value(user_group, info->authenticated_as,
327- MYSQL_USERNAME_LENGTH, info->auth_string);
328+ mapping_lookup_user(pam_mapped_user_name, info->authenticated_as,
329+ MYSQL_USERNAME_LENGTH, info->auth_string);
330 }
331
332 error= pam_end(pam_handle, error);
333
334=== modified file 'src/auth_pam_compat.c'
335--- src/auth_pam_compat.c 2012-02-13 06:19:11 +0000
336+++ src/auth_pam_compat.c 2013-06-06 14:09:25 +0000
337@@ -1,5 +1,5 @@
338 /*
339-(C) 2012 Percona Inc.
340+(C) 2012, 2013 Percona Ireland Ltd.
341
342 This program is free software; you can redistribute it and/or modify
343 it under the terms of the GNU General Public License as published by
344@@ -52,6 +52,7 @@
345 #include "config.h"
346 #endif
347
348+#include <string.h>
349 #include "auth_pam_common.h"
350
351 int auth_pam_client_talk_init(void **talk_data)
352
353=== added file 'src/groups.c'
354--- src/groups.c 1970-01-01 00:00:00 +0000
355+++ src/groups.c 2013-06-06 14:09:25 +0000
356@@ -0,0 +1,98 @@
357+/*
358+(C) 2013 Percona Ireland Ltd.
359+
360+This program is free software; you can redistribute it and/or modify
361+it under the terms of the GNU General Public License as published by
362+the Free Software Foundation; version 2 of the License.
363+
364+This program is distributed in the hope that it will be useful,
365+but WITHOUT ANY WARRANTY; without even the implied warranty of
366+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
367+GNU General Public License for more details.
368+
369+You should have received a copy of the GNU General Public License
370+along with this program; if not, write to the Free Software
371+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
372+*/
373+
374+#include <pwd.h>
375+#include <grp.h>
376+#include <string.h>
377+#include <stdlib.h>
378+#include <unistd.h>
379+
380+enum { max_nss_name_len = 10240 };
381+enum { max_number_of_groups = 1024 };
382+
383+/** Groups iterator. It's not exposed outsude */
384+struct groups_iter {
385+ char buf[max_nss_name_len];
386+ gid_t groups[max_number_of_groups];
387+ int current_group;
388+ int ngroups;
389+};
390+
391+/** Create iterator through user groups.
392+ Initially iterator set to position before first
393+ group. On success non-NULL pointer returned, otherwise NULL */
394+struct groups_iter *groups_iter_new(const char *user_name)
395+{
396+ struct passwd pwd, *pwd_result;
397+ int error;
398+ struct groups_iter *it;
399+
400+ it= calloc(1, sizeof(struct groups_iter));
401+ if (it == NULL)
402+ return NULL;
403+
404+ error= getpwnam_r(user_name, &pwd, it->buf, max_nss_name_len, &pwd_result);
405+ if (error != 0 || pwd_result == NULL)
406+ {
407+ free(it);
408+ return NULL;
409+ }
410+
411+ it->ngroups= max_number_of_groups;
412+ error= getgrouplist(user_name, pwd_result->pw_gid, it->groups, &it->ngroups);
413+ if (error == -1)
414+ {
415+ free(it);
416+ return NULL;
417+ }
418+
419+ return it;
420+}
421+
422+/** Move iterator to next group.
423+ On success group name is returned,
424+ otherwise NULL */
425+const char *groups_iter_next(struct groups_iter *it)
426+{
427+ struct group grp, *grp_result;
428+ int error;
429+
430+ if (it->current_group >= it->ngroups)
431+ return NULL;
432+
433+ error= getgrgid_r(it->groups[it->current_group++],
434+ &grp, it->buf, max_nss_name_len, &grp_result);
435+ if (error != 0 || grp_result == NULL)
436+ {
437+ return NULL;
438+ }
439+
440+ return grp_result->gr_name;
441+}
442+
443+/** Make iterator to point to beginning again */
444+void groups_iter_reset(struct groups_iter *it)
445+{
446+ it->current_group= 0;
447+}
448+
449+/** Finish iteration and release iterator */
450+void groups_iter_free(struct groups_iter *it)
451+{
452+ free(it);
453+}
454+
455
456=== added file 'src/groups.h'
457--- src/groups.h 1970-01-01 00:00:00 +0000
458+++ src/groups.h 2013-06-06 14:09:25 +0000
459@@ -0,0 +1,53 @@
460+#ifndef AUTH_PAM_GROUPS_INCLUDED
461+#define AUTH_PAM_GROUPS_INCLUDED
462+/*
463+ (C) 2013 Percona Ireland Ltd.
464+
465+ This program is free software; you can redistribute it and/or modify
466+ it under the terms of the GNU General Public License as published by
467+ the Free Software Foundation; version 2 of the License.
468+
469+ This program is distributed in the hope that it will be useful,
470+ but WITHOUT ANY WARRANTY; without even the implied warranty of
471+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
472+ GNU General Public License for more details.
473+
474+ You should have received a copy of the GNU General Public License
475+ along with this program; if not, write to the Free Software
476+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
477+*/
478+
479+/**
480+ @file
481+
482+ PAM authentication for MySQL, interface for groups enumeration.
483+
484+*/
485+
486+#ifdef __cplusplus
487+extern "C" {
488+#endif
489+
490+struct groups_iter;
491+
492+/** Create iterator through user groups.
493+ Initially iterator set to position before first
494+ group. On success non-NULL pointer returned, otherwise NULL */
495+struct groups_iter *groups_iter_new(const char *user_name);
496+
497+/** Move iterator to next group.
498+ On success group name is returned,
499+ otherwise NULL */
500+const char *groups_iter_next(struct groups_iter *it);
501+
502+/** Make iterator to point to beginning again */
503+void groups_iter_reset(struct groups_iter *it);
504+
505+/** Finish iteration and release iterator */
506+void groups_iter_free(struct groups_iter *it);
507+
508+#ifdef __cplusplus
509+}
510+#endif
511+
512+#endif
513
514=== removed symlink 'test/dbqp/workdir'
515=== target was u'/dev/shm/qp_workdir_root_e5f64ede-8707-4589-9c7a-fc598c0e2f6f'

Subscribers

People subscribed via source and target branches