1 /*
2 (C) 2011-2013 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 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include <string.h>
24 #include "auth_pam_common.h"
25 #include "auth_mapping.h"
26 #include "groups.h"
27 
28 /* The server plugin */
29 
30 /** The MySQL service name for PAM configuration */
31 static const char* service_name_default= "mysqld";
32 
valid_pam_msg_style(int pam_msg_style)33 static int valid_pam_msg_style (int pam_msg_style)
34 {
35   switch (pam_msg_style)
36   {
37   case PAM_PROMPT_ECHO_OFF:
38   case PAM_PROMPT_ECHO_ON:
39   case PAM_ERROR_MSG:
40   case PAM_TEXT_INFO:
41     return 1;
42   default:
43     return 0;
44   }
45 }
46 
47 /** The maximum length of service name. It shouldn't be too long as it's
48     filename in pam.d directory also */
49 enum { max_pam_service_name_len = 64 };
50 
free_pam_response(struct pam_response ** resp,int n)51 static void free_pam_response (struct pam_response ** resp, int n)
52 {
53   int i;
54   for (i = 0; i < n; i++)
55   {
56     free((*resp)[i].resp);
57   }
58   free(*resp);
59   *resp= NULL;
60 }
61 
vio_server_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)62 static int vio_server_conv (int num_msg, const struct pam_message **msg,
63                             struct pam_response ** resp, void *appdata_ptr)
64 {
65   int i;
66   int error;
67   void *talk_data;
68   struct pam_conv_data *data= (struct pam_conv_data *)appdata_ptr;
69 
70   if (data == NULL)
71   {
72     MY_ASSERT_UNREACHABLE();
73     return PAM_CONV_ERR;
74   }
75 
76   *resp = calloc (sizeof (struct pam_response), num_msg);
77   if (*resp == NULL)
78     return PAM_BUF_ERR;
79 
80   error= auth_pam_client_talk_init(&talk_data);
81   if (error != PAM_SUCCESS)
82   {
83     free_pam_response(resp, 0);
84     return error;
85   }
86 
87   for (i = 0; i < num_msg; i++)
88   {
89     if (!valid_pam_msg_style(msg[i]->msg_style))
90     {
91       auth_pam_client_talk_finalize(talk_data);
92       free_pam_response(resp, i);
93       return PAM_CONV_ERR;
94     }
95 
96     error= auth_pam_talk_perform(msg[i], &(*resp)[i], data, talk_data);
97     if (error != PAM_SUCCESS)
98     {
99       auth_pam_client_talk_finalize(talk_data);
100       free_pam_response(resp, i);
101       return error;
102     }
103   }
104   auth_pam_client_talk_finalize(talk_data);
105   return PAM_SUCCESS;
106 }
107 
authenticate_user_with_pam_server(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)108 int authenticate_user_with_pam_server (MYSQL_PLUGIN_VIO *vio,
109                                        MYSQL_SERVER_AUTH_INFO *info)
110 {
111   pam_handle_t *pam_handle;
112   struct pam_conv_data data= { vio, info };
113   struct pam_conv conv_func_info= { &vio_server_conv, &data };
114   int error;
115   char *pam_mapped_user_name;
116   char service_name[max_pam_service_name_len];
117 
118   /* Set service name as specified in auth_string. If no auth_string
119   provided or parsing error occurs, then keep default value */
120   strcpy(service_name, service_name_default);
121   if (info->auth_string)
122     mapping_get_service_name(service_name, sizeof(service_name), info->auth_string);
123 
124   info->password_used= PASSWORD_USED_NO_MENTION;
125 
126   error= pam_start(service_name,
127                    info->user_name,
128                    &conv_func_info, &pam_handle);
129   if (error != PAM_SUCCESS)
130     return CR_ERROR;
131 
132   error= pam_set_item(pam_handle, PAM_RUSER, info->user_name);
133   if (error != PAM_SUCCESS)
134   {
135     pam_end(pam_handle, error);
136     return CR_ERROR;
137   }
138 
139   error= pam_set_item(pam_handle, PAM_RHOST, info->host_or_ip);
140   if (error != PAM_SUCCESS)
141   {
142     pam_end(pam_handle, error);
143     return CR_ERROR;
144   }
145 
146   error= pam_authenticate(pam_handle, 0);
147   if (error != PAM_SUCCESS)
148   {
149     pam_end(pam_handle, error);
150     return CR_ERROR;
151   }
152 
153   error= pam_acct_mgmt(pam_handle, 0);
154   if (error != PAM_SUCCESS)
155   {
156     pam_end(pam_handle, error);
157     return CR_ERROR;
158   }
159 
160   /* Get the authenticated user name from PAM */
161   error= pam_get_item(pam_handle, PAM_USER, (void *)&pam_mapped_user_name);
162   if (error != PAM_SUCCESS)
163   {
164     pam_end(pam_handle, error);
165     return CR_ERROR;
166   }
167 
168   /* Check if user name from PAM is the same as provided for MySQL.  If
169   different, use the new user name for MySQL authorization and as
170   CURRENT_USER() value.  */
171   if (strcmp(info->user_name, pam_mapped_user_name))
172   {
173     strncpy(info->authenticated_as, pam_mapped_user_name,
174             MYSQL_USERNAME_LENGTH);
175     info->authenticated_as[MYSQL_USERNAME_LENGTH]= '\0';
176   }
177 
178   if (info->auth_string)
179   {
180     mapping_lookup_user(pam_mapped_user_name, info->authenticated_as,
181                         MYSQL_USERNAME_LENGTH, info->auth_string);
182   }
183 
184   error= pam_end(pam_handle, error);
185   if (error != PAM_SUCCESS)
186     return CR_ERROR;
187 
188   return CR_OK;
189 }
190