1 /*
2   Pam module to change user names arbitrarily in the pam stack.
3 
4   Compile as
5 
6      gcc pam_user_map.c -shared -lpam -fPIC -o pam_user_map.so
7 
8   Install as appropriate (for example, in /lib/security/).
9   Add to your /etc/pam.d/mysql (preferably, at the end) this line:
10 =========================================================
11 auth            required        pam_user_map.so
12 =========================================================
13 
14   And create /etc/security/user_map.conf with the desired mapping
15   in the format:  orig_user_name: mapped_user_name
16                   @user's_group_name: mapped_user_name
17 =========================================================
18 #comments and empty lines are ignored
19 john: jack
20 bob:  admin
21 top:  accounting
22 @group_ro: readonly
23 =========================================================
24 
25 If something doesn't work as expected you can get verbose
26 comments with the 'debug' option like this
27 =========================================================
28 auth            required        pam_user_map.so debug
29 =========================================================
30 These comments are written to the syslog as 'authpriv.debug'
31 and usually end up in /var/log/secure file.
32 */
33 
34 #include <config_auth_pam.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <syslog.h>
40 #include <grp.h>
41 #include <pwd.h>
42 
43 #ifdef HAVE_PAM_EXT_H
44 #include <security/pam_ext.h>
45 #endif
46 
47 #ifdef HAVE_PAM_APPL_H
48 #include <unistd.h>
49 #include <security/pam_appl.h>
50 #endif
51 
52 #include <security/pam_modules.h>
53 
54 #ifndef HAVE_PAM_SYSLOG
55 #include <stdarg.h>
56 static void
pam_syslog(const pam_handle_t * pamh,int priority,const char * fmt,...)57 pam_syslog (const pam_handle_t *pamh, int priority,
58       const char *fmt, ...)
59 {
60   va_list args;
61   va_start (args, fmt);
62   vsyslog (priority, fmt, args);
63   va_end (args);
64 }
65 #endif
66 
67 #define FILENAME "/etc/security/user_map.conf"
68 #define skip(what) while (*s && (what)) s++
69 #define SYSLOG_DEBUG if (mode_debug) pam_syslog
70 
71 #define GROUP_BUFFER_SIZE 100
72 static const char debug_keyword[]= "debug";
73 
74 #ifdef HAVE_POSIX_GETGROUPLIST
75 typedef gid_t my_gid_t;
76 #else
77 typedef int my_gid_t;
78 #endif
79 
populate_user_groups(const char * user,my_gid_t ** groups)80 static int populate_user_groups(const char *user, my_gid_t **groups)
81 {
82   my_gid_t user_group_id;
83   my_gid_t *loc_groups= *groups;
84   int ng;
85 
86   {
87     struct passwd *pw= getpwnam(user);
88     if (!pw)
89       return 0;
90     user_group_id= pw->pw_gid;
91   }
92 
93   ng= GROUP_BUFFER_SIZE;
94   if (getgrouplist(user, user_group_id, loc_groups, &ng) < 0)
95   {
96     /* The rare case when the user is present in more than */
97     /* GROUP_BUFFER_SIZE groups.                           */
98     loc_groups= (my_gid_t *) malloc(ng * sizeof (my_gid_t));
99 
100     if (!loc_groups)
101       return 0;
102 
103     (void) getgrouplist(user, user_group_id, loc_groups, &ng);
104     *groups= (my_gid_t*)loc_groups;
105   }
106 
107   return ng;
108 }
109 
110 
user_in_group(const my_gid_t * user_groups,int ng,const char * group)111 static int user_in_group(const my_gid_t *user_groups, int ng,const char *group)
112 {
113   my_gid_t group_id;
114   const my_gid_t *groups_end = user_groups + ng;
115 
116   {
117     struct group *g= getgrnam(group);
118     if (!g)
119       return 0;
120     group_id= g->gr_gid;
121   }
122 
123   for (; user_groups < groups_end; user_groups++)
124   {
125     if (*user_groups == group_id)
126       return 1;
127   }
128 
129   return 0;
130 }
131 
132 
print_groups(pam_handle_t * pamh,const my_gid_t * user_groups,int ng)133 static void print_groups(pam_handle_t *pamh, const my_gid_t *user_groups, int ng)
134 {
135   char buf[256];
136   char *c_buf= buf, *buf_end= buf+sizeof(buf)-2;
137   struct group *gr;
138   int cg;
139 
140   for (cg=0; cg < ng; cg++)
141   {
142     char *c;
143     if (c_buf == buf_end)
144       break;
145     *(c_buf++)= ',';
146     if (!(gr= getgrgid(user_groups[cg])) ||
147         !(c= gr->gr_name))
148       continue;
149     while (*c)
150     {
151       if (c_buf == buf_end)
152         break;
153       *(c_buf++)= *(c++);
154     }
155   }
156   c_buf[0]= c_buf[1]= 0;
157   pam_syslog(pamh, LOG_DEBUG, "User belongs to %d %s [%s].\n",
158                                  ng, (ng == 1) ? "group" : "groups", buf+1);
159 }
160 
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char * argv[])161 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
162     int argc, const char *argv[])
163 {
164   int mode_debug= 0;
165   int pam_err, line= 0;
166   const char *username;
167   char buf[256];
168   FILE *f;
169   my_gid_t group_buffer[GROUP_BUFFER_SIZE];
170   my_gid_t *groups= group_buffer;
171   int n_groups= -1;
172 
173   for (; argc > 0; argc--)
174   {
175     if (strcasecmp(argv[argc-1], debug_keyword) == 0)
176       mode_debug= 1;
177   }
178 
179   SYSLOG_DEBUG(pamh, LOG_DEBUG, "Opening file '%s'.\n", FILENAME);
180 
181   f= fopen(FILENAME, "r");
182   if (f == NULL)
183   {
184     pam_syslog(pamh, LOG_ERR, "Cannot open '%s'\n", FILENAME);
185     return PAM_SYSTEM_ERR;
186   }
187 
188   pam_err = pam_get_item(pamh, PAM_USER, (const void**)&username);
189   if (pam_err != PAM_SUCCESS)
190   {
191     pam_syslog(pamh, LOG_ERR, "Cannot get username.\n");
192     goto ret;
193   }
194 
195   SYSLOG_DEBUG(pamh, LOG_DEBUG, "Incoming username '%s'.\n", username);
196 
197   while (fgets(buf, sizeof(buf), f) != NULL)
198   {
199     char *s= buf, *from, *to, *end_from, *end_to;
200     int check_group;
201     int cmp_result;
202 
203     line++;
204 
205     skip(isspace(*s));
206     if (*s == '#' || *s == 0) continue;
207     if ((check_group= *s == '@'))
208     {
209       if (n_groups < 0)
210       {
211         n_groups= populate_user_groups(username, &groups);
212         if (mode_debug)
213           print_groups(pamh, groups, n_groups);
214       }
215       s++;
216     }
217     from= s;
218     skip(isalnum(*s) || (*s == '_') || (*s == '.') || (*s == '-') ||
219          (*s == '$') || (*s == '\\') || (*s == '/'));
220     end_from= s;
221     skip(isspace(*s));
222     if (end_from == from || *s++ != ':') goto syntax_error;
223     skip(isspace(*s));
224     to= s;
225     skip(isalnum(*s) || (*s == '_') || (*s == '.') || (*s == '-') ||
226          (*s == '$'));
227     end_to= s;
228     if (end_to == to) goto syntax_error;
229 
230     *end_from= *end_to= 0;
231 
232     if (check_group)
233     {
234       cmp_result= user_in_group(groups, n_groups, from);
235       SYSLOG_DEBUG(pamh, LOG_DEBUG, "Check if user is in group '%s': %s\n",
236                                     from, cmp_result ? "YES":"NO");
237     }
238     else
239     {
240       cmp_result= (strcmp(username, from) == 0);
241       SYSLOG_DEBUG(pamh, LOG_DEBUG, "Check if username '%s': %s\n",
242                                     from, cmp_result ? "YES":"NO");
243     }
244     if (cmp_result)
245     {
246       pam_err= pam_set_item(pamh, PAM_USER, to);
247       SYSLOG_DEBUG(pamh, LOG_DEBUG,
248           (pam_err == PAM_SUCCESS) ? "User mapped as '%s'\n" :
249                                      "Couldn't map as '%s'\n", to);
250       goto ret;
251     }
252   }
253 
254   SYSLOG_DEBUG(pamh, LOG_DEBUG, "User not found in the list.\n");
255   pam_err= PAM_AUTH_ERR;
256   goto ret;
257 
258 syntax_error:
259   pam_syslog(pamh, LOG_ERR, "Syntax error at %s:%d", FILENAME, line);
260   pam_err= PAM_SYSTEM_ERR;
261 ret:
262   if (groups != group_buffer)
263     free(groups);
264 
265   fclose(f);
266 
267   return pam_err;
268 }
269 
270 
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char * argv[])271 int pam_sm_setcred(pam_handle_t *pamh, int flags,
272                    int argc, const char *argv[])
273 {
274 
275     return PAM_SUCCESS;
276 }
277 
278