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