1 /*
2 Copyright (c) 2011, 2018 MariaDB Corporation
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 02111-1301 USA */
16
17 /*
18 This file contains code to interact with the PAM module.
19 To be included into auth_pam_tool.c and auth_pam_v2.c,
20
21 Before the #include these sould be defined:
22
23 struct param {
24 unsigned char buf[10240], *ptr;
25 MYSQL_PLUGIN_VIO *vio;
26 ... other arbitrary fields allowed.
27 };
28 static int write_packet(struct param *param, const unsigned char *buf,
29 int buf_len)
30 static int read_packet(struct param *param, unsigned char **pkt)
31 */
32
33 #include <config_auth_pam.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <security/pam_appl.h>
37 #include <security/pam_modules.h>
38
39 /* It least solaris doesn't have strndup */
40
41 #ifndef HAVE_STRNDUP
strndup(const char * from,size_t length)42 char *strndup(const char *from, size_t length)
43 {
44 char *ptr;
45 size_t max_length= strlen(from);
46 if (length > max_length)
47 length= max_length;
48 if ((ptr= (char*) malloc(length+1)) != 0)
49 {
50 memcpy((char*) ptr, (char*) from, length);
51 ptr[length]=0;
52 }
53 return ptr;
54 }
55 #endif
56
57 #ifndef DBUG_OFF
58 static char pam_debug = 0;
59 #define PAM_DEBUG(X) do { if (pam_debug) { fprintf X; } } while(0)
60 #else
61 #define PAM_DEBUG(X) /* no-op */
62 #endif
63
64 static char winbind_hack = 0;
65
conv(int n,const struct pam_message ** msg,struct pam_response ** resp,void * data)66 static int conv(int n, const struct pam_message **msg,
67 struct pam_response **resp, void *data)
68 {
69 struct param *param = (struct param *)data;
70 unsigned char *end = param->buf + sizeof(param->buf) - 1;
71 int i;
72
73 *resp = 0;
74
75 for (i = 0; i < n; i++)
76 {
77 /* if there's a message - append it to the buffer */
78 if (msg[i]->msg)
79 {
80 int len = strlen(msg[i]->msg);
81 if (len > end - param->ptr)
82 len = end - param->ptr;
83 if (len > 0)
84 {
85 memcpy(param->ptr, msg[i]->msg, len);
86 param->ptr+= len;
87 *(param->ptr)++ = '\n';
88 }
89 }
90 /* if the message style is *_PROMPT_*, meaning PAM asks a question,
91 send the accumulated text to the client, read the reply */
92 if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
93 msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
94 {
95 int pkt_len;
96 unsigned char *pkt;
97
98 /* allocate the response array.
99 freeing it is the responsibility of the caller */
100 if (*resp == 0)
101 {
102 *resp = calloc(sizeof(struct pam_response), n);
103 if (*resp == 0)
104 return PAM_BUF_ERR;
105 }
106
107 /* dialog plugin interprets the first byte of the packet
108 as the magic number.
109 2 means "read the input with the echo enabled"
110 4 means "password-like input, echo disabled"
111 C'est la vie. */
112 param->buf[0] = msg[i]->msg_style == PAM_PROMPT_ECHO_ON ? 2 : 4;
113 PAM_DEBUG((stderr, "PAM: conv: send(%.*s)\n",
114 (int)(param->ptr - param->buf - 1), param->buf));
115 pkt_len= roundtrip(param, param->buf, param->ptr - param->buf - 1, &pkt);
116 if (pkt_len < 0)
117 return PAM_CONV_ERR;
118
119 PAM_DEBUG((stderr, "PAM: conv: recv(%.*s)\n", pkt_len, pkt));
120 /* allocate and copy the reply to the response array */
121 if (!((*resp)[i].resp= strndup((char*) pkt, pkt_len)))
122 return PAM_CONV_ERR;
123 param->ptr = param->buf + 1;
124 }
125 }
126 return PAM_SUCCESS;
127 }
128
129 #define DO(X) if ((status = (X)) != PAM_SUCCESS) goto end
130
131 #if defined(SOLARIS) || defined(__sun)
132 typedef void** pam_get_item_3_arg;
133 #else
134 typedef const void** pam_get_item_3_arg;
135 #endif
136
pam_auth_base(struct param * param,MYSQL_SERVER_AUTH_INFO * info)137 static int pam_auth_base(struct param *param, MYSQL_SERVER_AUTH_INFO *info)
138 {
139 pam_handle_t *pamh = NULL;
140 int status;
141 const char *new_username= NULL;
142 /* The following is written in such a way to make also solaris happy */
143 struct pam_conv pam_start_arg = { &conv, (char*) param };
144
145 /*
146 get the service name, as specified in
147
148 CREATE USER ... IDENTIFIED WITH pam AS "service"
149 */
150 const char *service = info->auth_string && info->auth_string[0]
151 ? info->auth_string : "mysql";
152
153 param->ptr = param->buf + 1;
154
155 PAM_DEBUG((stderr, "PAM: pam_start(%s, %s)\n", service, info->user_name));
156 DO( pam_start(service, info->user_name, &pam_start_arg, &pamh) );
157
158 PAM_DEBUG((stderr, "PAM: pam_authenticate(0)\n"));
159 DO( pam_authenticate (pamh, 0) );
160
161 PAM_DEBUG((stderr, "PAM: pam_acct_mgmt(0)\n"));
162 DO( pam_acct_mgmt(pamh, 0) );
163
164 PAM_DEBUG((stderr, "PAM: pam_get_item(PAM_USER)\n"));
165 DO( pam_get_item(pamh, PAM_USER, (pam_get_item_3_arg) &new_username) );
166
167 if (new_username &&
168 (winbind_hack ? strcasecmp : strcmp)(new_username, info->user_name))
169 strncpy(info->authenticated_as, new_username,
170 sizeof(info->authenticated_as));
171 info->authenticated_as[sizeof(info->authenticated_as)-1]= 0;
172
173 end:
174 PAM_DEBUG((stderr, "PAM: status = %d (%s) user = %s\n",
175 status, pam_strerror(pamh, status), info->authenticated_as));
176 pam_end(pamh, status);
177 return status == PAM_SUCCESS ? CR_OK : CR_ERROR;
178 }
179
180