1 /*
2 (C) 2013, 2016 Percona LLC and/or its affiliates
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the 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 Street, Fifth Floor, Boston, MA  02110-1301, USA
16 */
17 
18 #include <pwd.h>
19 #include <grp.h>
20 #include <errno.h>
21 #include <my_sys.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 
26 static int gr_buf_size= 0;
27 
28 /** Groups iterator. It's not exposed outsude */
29 struct groups_iter {
30   char *buf;
31   gid_t *groups;
32   int current_group;
33   int ngroups;
34   int buf_size;
35 };
36 
37 /** Create iterator through user groups.
38     Initially iterator set to position before first
39     group. On success non-NULL pointer returned, otherwise NULL */
groups_iter_new(const char * user_name)40 struct groups_iter *groups_iter_new(const char *user_name)
41 {
42   struct passwd pwd, *pwd_result;
43   int error;
44   struct groups_iter *it;
45 
46   if (gr_buf_size <= 0)
47   {
48     long gr_size_max, pw_size_max;
49     gr_size_max= sysconf(_SC_GETGR_R_SIZE_MAX);
50     pw_size_max= sysconf(_SC_GETPW_R_SIZE_MAX);
51     gr_buf_size= gr_size_max > pw_size_max ? gr_size_max : pw_size_max;
52   }
53 
54   it= (struct groups_iter *) my_malloc(sizeof(struct groups_iter),
55                                        MYF(MY_FAE | MY_ZEROFILL));
56 
57   it->buf_size= gr_buf_size;
58   if (it->buf_size <= 0)
59     it->buf_size= 1024;
60 
61   it->buf= (char *) my_malloc(it->buf_size, MYF(MY_FAE));
62 
63   while ((error= getpwnam_r(user_name, &pwd, it->buf, it->buf_size,
64                             &pwd_result)) == ERANGE)
65   {
66     it->buf_size= it->buf_size * 2;
67     it->buf= (char *) my_realloc(it->buf, it->buf_size, MYF(MY_FAE));
68   }
69   if (error != 0 || pwd_result == NULL)
70   {
71     fprintf(stderr, "auth_pam: Unable to obtain the passwd entry for the user "
72                     "'%s'.", user_name);
73     my_free(it->buf);
74     my_free(it);
75     return NULL;
76   }
77 
78   gr_buf_size= it->buf_size;
79 
80   it->ngroups= 1024;
81   it->groups= (gid_t *) my_malloc(it->ngroups * sizeof(gid_t), MYF(MY_FAE));
82   error= getgrouplist(user_name, pwd_result->pw_gid, it->groups, &it->ngroups);
83   if (error == -1)
84   {
85     it->groups= (gid_t *) my_realloc(it->groups, it->ngroups * sizeof(gid_t),
86                                      MYF(MY_FAE));
87     error= getgrouplist(user_name, pwd_result->pw_gid, it->groups,
88                         &it->ngroups);
89     if (error == -1)
90     {
91       fprintf(stderr, "auth_pam: Unable to obtain the group access list for "
92                       "the user '%s'.", user_name);
93       my_free(it->buf);
94       my_free(it->groups);
95       my_free(it);
96       return NULL;
97     }
98   }
99 
100   return it;
101 }
102 
103 /** Move iterator to next group.
104     On success group name is returned,
105     otherwise NULL */
groups_iter_next(struct groups_iter * it)106 const char *groups_iter_next(struct groups_iter *it)
107 {
108   struct group grp, *grp_result;
109   int error;
110 
111   if (it->current_group >= it->ngroups)
112     return NULL;
113 
114   while ((error= getgrgid_r(it->groups[it->current_group], &grp, it->buf,
115                             it->buf_size, &grp_result)) == ERANGE)
116   {
117     it->buf_size= it->buf_size * 2;
118     it->buf= (char *) my_realloc(it->buf, it->buf_size, MYF(MY_FAE));
119   }
120   if (error != 0 || grp_result == NULL)
121   {
122     fprintf(stderr, "auth_pam: Unable to obtain the group record for the group "
123                     "id %d.", (int) it->groups[it->current_group]);
124     return NULL;
125   }
126   ++it->current_group;
127 
128   return grp_result->gr_name;
129 }
130 
131 /** Make iterator to point to the beginning again */
groups_iter_reset(struct groups_iter * it)132 void groups_iter_reset(struct groups_iter *it)
133 {
134   it->current_group= 0;
135 }
136 
137 /** Finish iteration and release iterator */
groups_iter_free(struct groups_iter * it)138 void groups_iter_free(struct groups_iter *it)
139 {
140   my_free(it->buf);
141   my_free(it->groups);
142   my_free(it);
143 }
144 
145