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