1 /*
2 (C) 2012, 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 @file
20
21 PAM authentication for MySQL, server-side plugin for the
22 production use.
23
24 Oracle MySQL-compatible plugin. Acts as a mediator
25 between the MySQL server, the MySQL client, and the PAM backend.
26
27 The server plugin requests authentication from the PAM backend, and reads one
28 phrase from client plugin. mysql_clear_password plugin used as client plugin.
29
30 This plugin does not encrypt the communication channel in any way. If this is
31 required, a SSL connection should be used.
32
33 To install this plugin, copy the .so file to the plugin directory and do
34
35 INSTALL PLUGIN auth_pam SONAME 'auth_pam_compat.so';
36
37 To use this plugin for one particular user, specify it at user's creation time
38 (TODO: tested with localhost only):
39
40 CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam_compat;
41
42 Alternatively UPDATE the mysql.user table to set the plugin value for an
43 existing user.
44
45 Also it is possible to use this plugin to authenticate anonymous users:
46
47 CREATE USER ''@'hostname' IDENTIFIED WITH auth_pam_compat;
48
49 */
50
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54
55 #include <string.h>
56 #include "auth_pam_common.h"
57
auth_pam_client_talk_init(void ** talk_data)58 int auth_pam_client_talk_init(void **talk_data)
59 {
60 int *num_talks= calloc(1, sizeof(int));
61 *talk_data= (void*)num_talks;
62 return (num_talks != NULL) ? PAM_SUCCESS : PAM_BUF_ERR;
63 }
64
auth_pam_client_talk_finalize(void * talk_data)65 void auth_pam_client_talk_finalize(void *talk_data)
66 {
67 free(talk_data);
68 }
69
auth_pam_talk_perform(const struct pam_message * msg,struct pam_response * resp,struct pam_conv_data * data,void * talk_data)70 int auth_pam_talk_perform(const struct pam_message *msg,
71 struct pam_response *resp,
72 struct pam_conv_data *data,
73 void *talk_data)
74 {
75 int pkt_len;
76 unsigned char *pkt;
77 int *num_talks= (int*)talk_data;
78
79 if (msg->msg_style == PAM_PROMPT_ECHO_OFF
80 || msg->msg_style == PAM_PROMPT_ECHO_ON)
81 {
82 /* mysql_clear_password plugin has support for only single phrase */
83 if (*num_talks > 1)
84 return PAM_CONV_ERR;
85
86 /* Read the answer */
87 if ((pkt_len= data->vio->read_packet(data->vio, &pkt))
88 < 0)
89 return PAM_CONV_ERR;
90
91 resp->resp= malloc(pkt_len + 1);
92 if (resp->resp == NULL)
93 return PAM_BUF_ERR;
94
95 strncpy(resp->resp, (char *)pkt, pkt_len);
96 resp->resp[pkt_len]= '\0';
97
98 /**
99 we could only guess whether password was used or not
100 normally we would set PASSWORD_USED_NO_MENTION but
101 because of http://bugs.mysql.com/bug.php?id=72536
102 we set PASSWORD_USED_YES.
103 */
104 data->info->password_used= PASSWORD_USED_YES;
105 ++(*num_talks);
106 }
107
108 return PAM_SUCCESS;
109 }
110
111 static struct st_mysql_auth pam_auth_handler=
112 {
113 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
114 "mysql_clear_password",
115 &authenticate_user_with_pam_server
116 };
117
mysql_declare_plugin(auth_pam)118 mysql_declare_plugin(auth_pam)
119 {
120 MYSQL_AUTHENTICATION_PLUGIN,
121 &pam_auth_handler,
122 "auth_pam_compat",
123 "Percona, Inc.",
124 "PAM authentication plugin",
125 PLUGIN_LICENSE_GPL,
126 NULL,
127 NULL,
128 0x0001,
129 NULL,
130 NULL,
131 NULL
132 #if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103
133 ,
134 0
135 #endif
136 }
137 mysql_declare_plugin_end;
138