Merge lp:~qiush-summer/maria/krb_auth_plugin into lp:maria

Proposed by Shuang Qiu
Status: Needs review
Proposed branch: lp:~qiush-summer/maria/krb_auth_plugin
Merge into: lp:maria
Diff against target: 1703 lines (+1662/-0)
8 files modified
mysql-test/suite/plugins/r/kerberos.result (+12/-0)
mysql-test/suite/plugins/t/kerberos.test (+30/-0)
plugin/auth_kerberos/CMakeLists.txt (+24/-0)
plugin/auth_kerberos/README.md (+211/-0)
plugin/auth_kerberos/kerberos_client.c (+419/-0)
plugin/auth_kerberos/kerberos_common.c (+152/-0)
plugin/auth_kerberos/kerberos_common.h (+89/-0)
plugin/auth_kerberos/kerberos_server.c (+725/-0)
To merge this branch: bzr merge lp:~qiush-summer/maria/krb_auth_plugin
Reviewer Review Type Date Requested Status
Maria-captains Pending
Review via email: mp+188061@code.launchpad.net

Commit message

Enable Kerberos authentication on MariaDB.

Description of the change

This patch add Kerberos authentication support to MariaDB.

The authentication on *nix and Windows environment are featured.
Interoperation between hybrid OS environment is also allowed.

To post a comment you must log in.
3837. By Shuang Qiu

Change the licensing from GPL to BSD.

3838. By Shuang Qiu

Change the Kerberos authentication plugin licensing from GPL to BSD.

Unmerged revisions

3838. By Shuang Qiu

Change the Kerberos authentication plugin licensing from GPL to BSD.

3837. By Shuang Qiu

Change the licensing from GPL to BSD.

3836. By Shuang Qiu

remove deprecated working directory.

3835. By Shuang Qiu

add user manual for Kerberos authentication plugin.

3834. By Shuang Qiu

minor update.

3833. By Shuang Qiu

Cleanup kerberos_client.c, add cross-platform support to CMakeList.txt.

3832. By Shuang Qiu

update Kerberos unittest file and add unittest result.

3831. By Shuang Qiu

Dot is a valid alphabet in principal names.

3830. By Shuang Qiu

Normailize comments.

3829. By Shuang Qiu

Update Kerberos authentication test script.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'mysql-test/suite/plugins/r/kerberos.result'
--- mysql-test/suite/plugins/r/kerberos.result 1970-01-01 00:00:00 +0000
+++ mysql-test/suite/plugins/r/kerberos.result 2015-10-18 04:21:02 +0000
@@ -0,0 +1,12 @@
1INSTALL PLUGIN kerberos SONAME 'kerberos';
2CREATE USER test_kerberos IDENTIFIED VIA kerberos AS 'MTR_KERBEROS_UPN';
3SET GLOBAL kerberos_principal_name='MTR_KERBEROS_SPN';
4#
5# user principal is accepted, and authentication is successful.
6#
7SELECT USER();
8USER()
9test_kerberos@localhost
10# connection is persistent.
11DROP USER test_kerberos;
12UNINSTALL PLUGIN kerberos;
013
=== added file 'mysql-test/suite/plugins/t/kerberos.test'
--- mysql-test/suite/plugins/t/kerberos.test 1970-01-01 00:00:00 +0000
+++ mysql-test/suite/plugins/t/kerberos.test 2015-10-18 04:21:02 +0000
@@ -0,0 +1,30 @@
1
2--source include/not_embedded.inc
3
4if (!$MTR_KERBEROS_ENABLED) {
5 skip No Kerberos auth plugin;
6}
7
8eval INSTALL PLUGIN kerberos SONAME 'kerberos';
9--replace_result $MTR_KERBEROS_UPN MTR_KERBEROS_UPN
10eval CREATE USER test_kerberos IDENTIFIED VIA kerberos AS '$MTR_KERBEROS_UPN';
11
12--replace_result $MTR_KERBEROS_SPN MTR_KERBEROS_SPN
13eval SET GLOBAL kerberos_principal_name='$MTR_KERBEROS_SPN';
14
15let $plugindir=`SELECT @@global.plugin_dir`;
16
17--echo #
18--echo # user principal is accepted, and authentication is successful.
19--echo #
20--exec echo "SELECT USER();" | $MYSQL_TEST -u test_kerberos --plugin-dir=$plugindir
21--echo # connection is persistent.
22
23# --echo #
24# --echo # athentication is unsuccessful
25# --echo #
26# --error 1
27# --exec $MYSQL_TEST -u test_kerberos --plugin-dir=$plugindir
28
29DROP USER test_kerberos;
30UNINSTALL PLUGIN kerberos;
031
=== modified file 'mysys/charset.c' (properties changed: -x to +x)
=== added directory 'plugin/auth_kerberos'
=== added file 'plugin/auth_kerberos/CMakeLists.txt'
--- plugin/auth_kerberos/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugin/auth_kerberos/CMakeLists.txt 2015-10-18 04:21:02 +0000
@@ -0,0 +1,24 @@
1IF (WIN32)
2 # on Windows-platform, secur32.lib is certainly there.
3 SET(KRB_LIB secur32)
4ELSE (WIN32)
5 # check include file
6 CHECK_INCLUDE_FILES("gssapi/gssapi.h;gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" HAVE_GSSAPI_H)
7 IF (NOT HAVE_GSSAPI_H)
8 RETURN()
9 ENDIF (NOT HAVE_GSSAPI_H)
10 # on non-Windows-platform, check the existence of library gssapi_krb5.
11 FIND_LIBRARY(KRB_LIB gssapi_krb5)
12 IF (NOT KRB_LIB)
13 # library is not there on the PATH.
14 message("gssapi_krb5 not found.")
15 RETURN()
16 ENDIF (NOT KRB_LIB)
17ENDIF (WIN32)
18
19MYSQL_ADD_PLUGIN(kerberos kerberos_server.c kerberos_common.c
20 LINK_LIBRARIES ${KRB_LIB}
21 MODULE_ONLY COMPONENT SharedLibraries)
22MYSQL_ADD_PLUGIN(kerberos_client kerberos_client.c kerberos_common.c
23 LINK_LIBRARIES ${KRB_LIB}
24 MODULE_ONLY COMPONENT SharedLibraries)
025
=== added file 'plugin/auth_kerberos/README.md'
--- plugin/auth_kerberos/README.md 1970-01-01 00:00:00 +0000
+++ plugin/auth_kerberos/README.md 2015-10-18 04:21:02 +0000
@@ -0,0 +1,211 @@
1## Kerberos Authentication Plugin for MariaDB
2
3This article gives instructions on configuring Kerberos authentication
4plugin for MariaDB. With Kerberos authentication plugin enabled, you can
5login MariaDB as a Kerberos domain user passwordlessly.
6
7### System Settings
8
9To use the full feature of Kerberos authentication plugin, make sure a
10Kerberos authentication domain is properly set up.
11
12* For a pure *nix Kerberos domain, an [MIT Kerberos Key Distribution Centre][1]
13(krb5kdc service) should be running.
14
15* As to a pure Windows domain, an active directory domain controller should
16be connectable.
17
18* Hybrid Kerberos domains are also allowed.
19
20Detailed guides to set up a Kerberos authentication domain is beyond the scope
21of this document. You can refer to the links in the References section on
22how to setup a Kerberos authentication domain.
23
24### Compile
25
26The compilation of Kerberos authentication plugin has been integrated into
27the CMake building framework of MariaDB.
28
29##### *nix
30
31If you are a *nix user, guarantee the Kerberos libraries and headers are
32installed.
33
34##### Windows
35
36The Windows version requires no additional libraries.
37
38The Kerberos authentication plugin is separated into two isolated parts:
39the server-side plugin and client-side plugin. If no errors occur
40during compilation, two shared libraries will be created, `kerberos` and
41`kerberos_client` for server-side and client-side plugin respectively.
42
43### Installation
44
45Install the server-side Kerberos authentication plugin before used.
46Client-side plugin will be automatically loaded when required.
47
48Connect MariaDB server as a superuser, then issue the command
49
50 INSTALL PLUGIN kerberos SONAME 'kerberos';
51
52This will instruct MariaDB server to load the Kerberos authentication plugin.
53
54### Set a Service Principal Name to MariaDB
55
56Before we can authenticate against Kerberos service, the last step is assign
57a service principal name to MariaDB server.
58
59#### Figure out a Valid Principal Name
60
61Services and users are identified via principal names internally in Kerberos
62authentication service. Generally, a principal name is in the format of
63`username/hostname@DOMAIN.NAME`.
64
65##### *nix
66
67For Kerberos services on *nix platform, a user specified service principal
68name is required, since no default principal name can be derived from the
69effective user of the server process.
70
71##### Windows
72
73As to Windows Active Directory services
74
75* if MariaDB is running as `NetworkService` by default, the principal name
76is akin `host$@DOMAIN.NAME`. In this case, no need to specify service
77principal name manually. The Kerberos plugin can derive a valid default
78service principal name.
79
80* Otherwise, if you run MariaDB as a customized domain user, the principal
81name is `username@DOMAIN.NAME` by default. In this case, no need to specify
82service principal name manually. The Kerberos plugin can derive a valid
83default service principal name.
84
85* Finally, if a principal name is preferred over the default `username@DOMAIN`,
86feel free to update it with the `setspn.exe` tool. Correspondingly, the
87new principal name must be specified in the MariaDB configuration file to
88have it work.
89
90A valid Kerberos principal name is case sensitive in both *nix and Windows.
91For example, on *nix platform a valid service principal name is like
92`MySQL/localhost@EXAMPLE.COM` (capitalised realm name is recommended by
93Kerberos service) or `MySQL@EXAMPLE.COM` on Windows (`hostname` is not
94emphasised).
95
96#### Assign the Principal Name
97
98To assign a service principal name to server, Kerberos authentication plugin
99exposes a system variable `named kerberos_principal_name`.
100
101One can specify the name (say, `MySQL/localhost@EXAMPLE.COM`) in three ways:
102
103* Specify the name in configure file: edit local configure file ~/.my.cnf by
104inserting the line `kerberos_principal_name=MySQL/localhost@EXAMPLE.COM` in the
105server section. * Pass as command line parameter: start MariaDB server with
106command line parameter `--kerberos_principal_name=MySQL/localhost@EXAMPLE.COM`.
107* If you can login MariaDB as a superuser, you can set by the following
108commands: `SET GLOBAL kerberos_principal_name='MySQL/localhost@EXAMPLE.COM';`
109The parameter should be set each time after the service restarts.
110
111You can verify service principal name is properly set by
112
113 SELECT @@global.kerberos_principal_name;
114
115#### Create New MariaDB Users
116
117If all the steps above are completed, you can now create a new user identified
118via Kerberos authentication plugin.
119
120 CREATE USER user_name IDENTIFIED VIA kerberos AS 'user_principal_name';
121
122We need the `AS` clause to specify the principal name instead of embedded
123into `user_name` directly for the length gap between MariaDB username and
124Kerberos principal name.
125
126#### Connect and Login as Kerboers Principal
127
128To connect to MariaDB as `user_name`, first check a valid Kerberos tgt ticket
129is cached with `klist`. You can obtain a tgt ticket either by login as a
130domain user on Windows platform or by `kinit principal_name` on Linux box.
131
132If all these steps are done, you should now connect to MariaDB as `user_name`
133successfully.
134
135### Run Test Suite
136
137This section describes how to run unit test for Kerberos authentication plugin.
138In case of setting up a Kerberos domain, which is tedious to an ordinary user,
139the unit test for Kerberos authentication plugin is by default skipped.
140
141To run the unit test, an OS environment variable `MTR_KERBEROS_ENABLED`
142should be set to a valid value
143
144 MTR_KERBEROS_ENABLED=1
145
146Two more OS environment variables `MTR_KERBEROS_SPN` and `MTR_KERBEROS_UPN`
147should be set for MariaDB service principal name and login user principal
148name respectively.
149
150#### Extending the Ticket Lifetime
151
152Make sure the tgt ticket is not expired for login user when running the
153unit test. In case early expiration of the ticket, we can extend the ticket
154lifetime in configuration.
155
156##### *nix
157
158You can extend the ticket lifetime by editing `/etc/krb5.conf` in *nix by
159updating two parameter `ticket_lifetime` and `renew_lifetime`.
160
161##### Windows
162
163Extending ticket lifetime can also be done within several clicks in Windows,
164here is a step-by-step instruction:
165
166 1. Open "Group Policy Management" (Start -> All Programs -> Administrative
167 Tools -> Group Policy Management). 2. Select default domain (Domains
168 -> default.domain -> Domain Controllers -> Default Domain Controllers;
169 then on the right detail panel Settings -> Policies -> Windows Settings
170 -> Security Settings -> right click "Local Policies/Security Options"
171 -> Edit... to open Group Policy Management Editor). 3. Then in the GPM
172 Editor Default Domain Controllers Policy -> Computer Configure -> Polices
173 -> Windows Settings -> Security Settings -> Account Policies -> Kerberos
174 Policies -> Maximum lifetime for service/user ticket.
175
176Once the tgt ticket is expired, on Linux use command `kinit -R` to renew
177the ticket, while on Windows, one should logout and logon again.
178
179### Trouble Shoot Authentication Problems
180
181 1. Check the Kerberos KDC log first, on Linux it is `/var/log/krb5kdc.log`
182 by default or specified in /etc/krb5.conf. On Windows, steps are: Start
183 -> Administrative Tools -> Event Viewer, Windows Logs -> Security, to see
184 whether the ticket to requested service has been issued. If not issued,
185 verify whether both service principal name and user principal name are
186 correct. In addition, corresponding principals have been created in the
187 KDC server. 2. If tickets are issued, while authentication still fails
188 and you're on *nix box, make sure the initial credential to the service
189 principal's ticket is saved in the keytab. What's more, the keytab can be
190 read by Kerberos KDC.
191
192### References
193
194* [MIT Kerberos Official Documents for Administrators][2]
195* [A Step-by-step Guide for Windows Server 2008 Domain Controller and DNS Setup][3]
196* [Add Windows Box to a *nix krb5kdc Domain][5]
197* [Authenticate Linux Clients with Active Directory][6]
198* [Install Plugin in MariaDB][7]
199* [Kerberos Principal Name Specification][8]
200* [Renew a ticket on Windows][9]
201* [\*nix krenew command: Extend Ticket Lifetime on \*nix][10]
202
203[1]: http://web.mit.edu/kerberos/
204[2]: http://web.mit.edu/kerberos/krb5-latest/doc/admin/index.html
205[3]: http://www.windowsreference.com/windows-server-2008/step-by-step-guide-for-windows-server-2008-domain-controller-and-dns-server-setup/
206[5]: http://social.technet.microsoft.com/wiki/contents/articles/2751.kerberos-interoperability-step-by-step-guide-for-windows-server-2003.aspx#Using_an_MIT_KDC_with_a_Stand-alone_Windows_Server_TwentyOhThree_Client
207[6]: http://technet.microsoft.com/en-us/magazine/2008.12.linux.aspx
208[7]: https://kb.askmonty.org/en/plugin-overview/#installing-plugins
209[8]: http://pic.dhe.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/cl/addkrbtkt.htm
210[9]: http://technet.microsoft.com/en-us/library/cc738673(v=ws.10).aspx#w2k8tr_kerb_tools_iybi
211[10]: http://stackoverflow.com/questions/14682153/lifetime-of-kerberos-tickets#15457265
0212
=== added file 'plugin/auth_kerberos/kerberos_client.c'
--- plugin/auth_kerberos/kerberos_client.c 1970-01-01 00:00:00 +0000
+++ plugin/auth_kerberos/kerberos_client.c 2015-10-18 04:21:02 +0000
@@ -0,0 +1,419 @@
1/* Copyright (c) 2015, Shuang Qiu
2 All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6
7 1. Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 POSSIBILITY OF SUCH DAMAGE.
25*/
26
27/**
28 @file
29
30 Kerberos server authentication plugin
31
32 kerberos_server is a general purpose server authentication plugin, it
33 authenticates user against Kerberos principal.
34
35 This is the client side implementation.
36*/
37#include <stdarg.h>
38#include <mysqld_error.h>
39#include <mysql/client_plugin.h>
40#include "kerberos_common.h"
41
42#define KERBEROS_UNKNOWN_ERROR "HY000"
43#define KERBEROS_OUTOFMEMORY_ERROR "HY001"
44#define KERBEROS_NET_ERROR_ON_WRITE "08S01"
45#define KERBEROS_NET_READ_ERROR "08S01"
46
47/**
48 * set client error message
49 */
50static void set_krb_client_auth_error(MYSQL *, int, const char *, const char *, ...);
51
52#ifdef _WIN32
53static int sspi_kerberos_auth_client(const char *spn, MYSQL *mysql,
54 MYSQL_PLUGIN_VIO *vio)
55{
56 int read_len= 0;
57 int max_token_sz= SSPI_MAX_TOKEN_SIZE;
58 int ret= 0; /* return code */
59 const char *err_msg= NULL;
60 /* SSPI related */
61 BOOL have_ctxt= FALSE;
62 BOOL have_cred= FALSE;
63 BOOL have_input= FALSE;
64 BOOL context_established= FALSE;
65 ULONG attribs= 0;
66 TimeStamp lifetime;
67
68 SECURITY_STATUS ss;
69 CredHandle cred_handle; /* credential handle */
70 CtxtHandle ctxt_handle; /* security context */
71
72 SecPkgInfo *sec_pkg_info; /* package information */
73 SecBufferDesc input_buf_desc;
74 SecBuffer input_buf;
75 SecBufferDesc output_buf_desc;
76 SecBuffer output_buf;
77 PBYTE output_con;
78
79 /* query package information */
80 ss= QuerySecurityPackageInfo(SECURITY_PACKAGE_NAME, &sec_pkg_info);
81 if (ss == SEC_E_OK)
82 {
83 max_token_sz= sec_pkg_info->cbMaxToken;
84 }
85
86 /* allocate memory */
87 output_con= (PBYTE) calloc(max_token_sz, sizeof(BYTE));
88 if (!output_con)
89 {
90 set_krb_client_auth_error(mysql, ER_OUTOFMEMORY, \
91 KERBEROS_OUTOFMEMORY_ERROR, \
92 "Kerberos: insufficient memory to allocate for output token buffer.");
93 return CR_ERROR;
94 }
95
96 /* acquire credentials */
97 ss= AcquireCredentialsHandle(
98 NULL,
99 SECURITY_PACKAGE_NAME,
100 SECPKG_CRED_OUTBOUND,
101 NULL,
102 NULL,
103 NULL,
104 NULL,
105 &cred_handle,
106 &lifetime);
107 if (SEC_ERROR(ss))
108 {
109 err_msg= error_msg(ss);
110 set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR,
111 KERBEROS_UNKNOWN_ERROR, err_msg);
112 return CR_ERROR;
113 }
114 else
115 {
116 have_cred= TRUE;
117 }
118
119 /* prepare input buffer */
120 input_buf_desc.ulVersion= SECBUFFER_VERSION;
121 input_buf_desc.cBuffers= 1;
122 input_buf_desc.pBuffers= &input_buf;
123 input_buf.BufferType= SECBUFFER_TOKEN;
124 input_buf.cbBuffer= 0;
125 input_buf.pvBuffer= NULL;
126
127 /* prepare output buffer */
128 output_buf_desc.ulVersion= SECBUFFER_VERSION;
129 output_buf_desc.cBuffers= 1;
130 output_buf_desc.pBuffers= &output_buf;
131
132 output_buf.BufferType= SECBUFFER_TOKEN;
133 output_buf.cbBuffer= max_token_sz;
134 output_buf.pvBuffer= output_con;
135
136 do
137 {
138 ss= InitializeSecurityContext(
139 &cred_handle, /* credential handle */
140 have_ctxt?&ctxt_handle:NULL, /* context handle */
141 (SEC_CHAR *) spn, /* target principal name */
142 0, /* no special attributes req-ed*/
143 0, /* reserved */
144 SECURITY_NATIVE_DREP,
145 have_input ? &input_buf_desc : NULL,
146 0, /* reserved */
147 &ctxt_handle,
148 &output_buf_desc,
149 &attribs,
150 &lifetime);
151 /* reset used flag */
152 have_input= FALSE;
153
154 if (output_buf.cbBuffer)
155 {
156 /* send credential to server */
157 if (vio->write_packet(vio,
158 (const unsigned char *) output_buf.pvBuffer,
159 output_buf.cbBuffer))
160 {
161 ret= CR_ERROR;
162 set_krb_client_auth_error(mysql, ER_NET_ERROR_ON_WRITE,
163 KERBEROS_NET_ERROR_ON_WRITE,
164 "Kerberos: fail send credentials to server.");
165 goto cleanup;
166 }
167 }
168
169 if (SEC_ERROR(ss))
170 {
171 err_msg= error_msg(ss);
172 ret= CR_ERROR;
173 set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR,
174 KERBEROS_UNKNOWN_ERROR, err_msg);
175 goto cleanup;
176 }
177
178 have_ctxt= TRUE;
179
180 if ((ss == SEC_I_COMPLETE_NEEDED) ||
181 (ss == SEC_I_COMPLETE_AND_CONTINUE))
182 {
183 ss= CompleteAuthToken(&ctxt_handle, &output_buf_desc);
184
185 if (SEC_ERROR(ss))
186 {
187 err_msg= error_msg(ss);
188 ret= CR_ERROR;
189 set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR,
190 KERBEROS_UNKNOWN_ERROR, err_msg);
191 goto cleanup;
192 }
193 }
194
195 context_established= !((ss == SEC_I_CONTINUE_NEEDED) ||
196 (ss == SEC_I_COMPLETE_AND_CONTINUE));
197 if (!context_established)
198 {
199 read_len= vio->read_packet(vio, (unsigned char **) &input_buf.pvBuffer);
200 if (read_len < 0)
201 {
202 ret= CR_ERROR;
203 set_krb_client_auth_error(mysql, ER_NET_READ_ERROR,
204 KERBEROS_NET_READ_ERROR,
205 "Kerberos: fail to read credential from server.");
206 goto cleanup;
207 }
208 else
209 {
210 input_buf.cbBuffer= read_len;
211 have_input= TRUE;
212 }
213 }
214
215 output_buf.cbBuffer= max_token_sz;
216 } while (!context_established);
217
218 ret= CR_OK;
219
220cleanup:
221 /* free dynamic memory */
222 if (have_ctxt) DeleteSecurityContext(&ctxt_handle);
223 free(output_con);
224 ss= FreeContextBuffer(sec_pkg_info);
225 if (ss != SEC_E_OK)
226 {
227 set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR,
228 KERBEROS_UNKNOWN_ERROR,
229 "Kerberos: fail to free SecurityPackageInfo object.");
230 }
231
232 return ret;
233}
234#else /* !_WIN32 */
235static int gssapi_kerberos_auth_client(const char *spn, MYSQL *mysql,
236 MYSQL_PLUGIN_VIO *vio)
237{
238 int r_len= 0; /* packet read length */
239 int context_established= 0; /* indicate ctxt avail */
240 int rc= CR_OK;
241 int have_cred= FALSE;
242 int have_ctxt= FALSE;
243 int have_name= FALSE;
244 const char *err_msg= NULL;
245 /* GSSAPI related fields */
246 OM_uint32 major= 0, minor= 0;
247 gss_name_t service_name;
248 gss_ctx_id_t ctxt;
249 gss_cred_id_t cred= GSS_C_NO_CREDENTIAL; /* use default credential */
250 gss_buffer_desc spn_buf, input, output;
251
252 /* import principal from plain text */
253 /* initialize plain text service principal name */
254 spn_buf.length= strlen(spn);
255 spn_buf.value= (void *) spn;
256 /* import service principal */
257 major= gss_import_name(&minor,
258 &spn_buf,
259 (gss_OID) gss_nt_user_name,
260 &service_name);
261 /* gss_import_name error checking */
262 if (GSS_ERROR(major))
263 {
264 err_msg= error_msg(major, minor);
265 rc= CR_ERROR;
266 set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR,
267 KERBEROS_UNKNOWN_ERROR, err_msg);
268 goto cleanup;
269 }
270 have_name= TRUE;
271
272 /* initial context */
273 ctxt= GSS_C_NO_CONTEXT;
274 input.length= 0;
275 input.value= NULL;
276
277 while (!context_established)
278 {
279 major= gss_init_sec_context(&minor,
280 cred,
281 &ctxt,
282 service_name,
283 GSS_C_NO_OID, /* for an impl-spec mech */
284 0, /* no flags requested */
285 0, /* request default time */
286 GSS_C_NO_CHANNEL_BINDINGS,
287 &input, /* token input */
288 NULL, /* actual mech */
289 &output, /* token output */
290 NULL, /* actual flags */
291 NULL); /* actual valid time */
292
293 if (output.length)
294 {
295 /* send credential */
296 if (vio->write_packet(vio, output.value, output.length))
297 {
298 gss_release_buffer(&minor, &output);
299 rc= CR_ERROR;
300 set_krb_client_auth_error(mysql, ER_NET_ERROR_ON_WRITE,
301 KERBEROS_NET_ERROR_ON_WRITE,
302 "Kerberos: fail to send credential to server.");
303 goto cleanup;
304 }
305 gss_release_buffer(&minor, &output);
306 }
307
308 if (GSS_ERROR(major))
309 {
310 /* fatal error */
311 if (ctxt != GSS_C_NO_CONTEXT)
312 {
313 gss_delete_sec_context(&minor,
314 &ctxt,
315 GSS_C_NO_BUFFER);
316 }
317 err_msg= error_msg(major, minor);
318 rc= CR_ERROR;
319 set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR,
320 KERBEROS_UNKNOWN_ERROR, err_msg);
321 goto cleanup;
322 }
323
324 if (major & GSS_S_CONTINUE_NEEDED)
325 {
326 r_len= vio->read_packet(vio, (unsigned char **) &input.value);
327 if (r_len < 0)
328 {
329 rc= CR_ERROR;
330 set_krb_client_auth_error(mysql, ER_NET_READ_ERROR,
331 KERBEROS_NET_READ_ERROR,
332 "Error read credential packet from server.");
333 goto cleanup;
334 }
335 }
336 else
337 {
338 context_established= 1;
339 }
340 }
341
342cleanup:
343 if (have_name) gss_release_name(&minor, &service_name);
344 if (have_ctxt) gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER);
345 if (have_cred) gss_release_cred(&minor, &cred);
346
347 return rc;
348}
349#endif /* _WIN32 */
350
351/**
352 * The main client function of the Kerberos plugin.
353 */
354static int kerberos_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
355{
356 int r_len= 0;
357 int rc= CR_OK;
358 /* service principal name */
359 char *spn= NULL;
360 char *spn_buff= (char *) malloc(PRINCIPAL_NAME_LEN);
361
362 /* read from server for service principal name */
363 r_len= vio->read_packet(vio, (unsigned char **) &spn);
364 if (r_len < 0)
365 {
366 set_krb_client_auth_error(mysql, ER_NET_READ_ERROR,
367 KERBEROS_NET_READ_ERROR,
368 "Kerberos: fail to read service principal name.");
369
370 return CR_ERROR;
371 }
372 strncpy(spn_buff, spn, PRINCIPAL_NAME_LEN);
373
374 rc =
375#ifdef _WIN32
376 sspi_kerberos_auth_client((const char *) spn_buff, mysql, vio);
377#else /* !_WIN32 */
378 gssapi_kerberos_auth_client((const char *) spn_buff, mysql, vio);
379#endif /* _WIN32 */
380
381 free(spn_buff);
382 return rc;
383}
384
385/**
386 * set client error message.
387 * Param:
388 * mysql connection handle
389 * errno extended error number
390 * format error message template
391 * ... variable argument list
392 */
393static void set_krb_client_auth_error(MYSQL *mysql, int errcode,
394 const char *sqlstate, const char *format, ...)
395{
396 NET *net= &mysql->net;
397 va_list args;
398
399 net->last_errno= errcode;
400 va_start(args, format);
401 vsnprintf(net->last_error, sizeof(net->last_error) - 1,
402 format, args);
403 va_end(args);
404 memcpy(net->sqlstate, sqlstate, sizeof(net->sqlstate));
405}
406
407/* register client plugin */
408mysql_declare_client_plugin(AUTHENTICATION)
409 "kerberos_client",
410 "Shuang Qiu",
411 "Kerberos based authentication",
412 {0, 1, 0},
413 "BSD",
414 NULL,
415 NULL,
416 NULL,
417 NULL,
418 kerberos_auth_client
419mysql_end_client_plugin;
0420
=== added file 'plugin/auth_kerberos/kerberos_common.c'
--- plugin/auth_kerberos/kerberos_common.c 1970-01-01 00:00:00 +0000
+++ plugin/auth_kerberos/kerberos_common.c 2015-10-18 04:21:02 +0000
@@ -0,0 +1,152 @@
1/* Copyright (c) 2015, Shuang Qiu
2 All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6
7 1. Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 POSSIBILITY OF SUCH DAMAGE.
25*/
26
27/**
28 @file
29
30 Kerberos authentication utilities
31
32 Utility functions are defined.
33*/
34#include "kerberos_common.h"
35
36#ifdef _WIN32
37/* translate status code to error message */
38const char *error_msg(SECURITY_STATUS ss)
39{
40 const char *err_msg= NULL;
41
42 switch (ss)
43 {
44 case SEC_E_INSUFFICIENT_MEMORY:
45 err_msg= "Kerberos: insufficient memory to complete the request.";
46 break;
47 case SEC_E_INTERNAL_ERROR:
48 err_msg= "Kerberos: an internal error occurs.";
49 break;
50 case SEC_E_NO_CREDENTIALS:
51 err_msg= "Kerberos: no credentials are available.";
52 break;
53 case SEC_E_NOT_OWNER:
54 err_msg= "Kerberos: necessary credentials to "
55 "acquire the new credential are not found.";
56 break;
57 case SEC_E_UNKNOWN_CREDENTIALS:
58 err_msg= "Kerberos: credentials supplied were not recognized.";
59 break;
60 case SEC_E_INVALID_HANDLE:
61 err_msg= "Kerberos: an invalid handle is provided.";
62 break;
63 case SEC_E_INVALID_TOKEN:
64 err_msg= "Kerberos: an invalid token is provided.";
65 break;
66 case SEC_E_LOGON_DENIED:
67 err_msg= "Kerberos: logon as specified principal failed.";
68 break;
69 case SEC_E_NO_AUTHENTICATING_AUTHORITY:
70 err_msg= "Kerberos: the domain name is invalid.";
71 break;
72 case SEC_E_TARGET_UNKNOWN:
73 err_msg= "Kerberos: the target principal is unknown.";
74 break;
75 case SEC_E_WRONG_PRINCIPAL:
76 err_msg= "Kerberos: the target principle does not "
77 "match with expected one.";
78 break;
79 case SEC_E_TIME_SKEW:
80 err_msg= "Kerberos: a time skew is detected.";
81 break;
82 default:
83 err_msg = "Kerberos: unknown error.";
84 break;
85 }
86
87 return err_msg;
88}
89#else /* _WIN32 */
90#define ERR_MSG_BUF_LEN 1024
91static char err_msg_buf[ERR_MSG_BUF_LEN];
92
93const char *error_msg(OM_uint32 major, OM_uint32 minor __attribute__((unused)))
94{
95 const char *err_msg= NULL;
96
97 switch (major)
98 {
99 case GSS_S_BAD_NAMETYPE:
100 case GSS_S_BAD_NAME:
101 err_msg= "Kerberos: input name could not be recognied.";
102 break;
103 case GSS_S_BAD_MECH:
104 err_msg= "Kerberos: a bad mechanism is requested.";
105 break;
106 case GSS_S_CREDENTIALS_EXPIRED:
107 err_msg = "Kerberos: the credentials could not be acquired "
108 "for expiration.";
109 break;
110 case GSS_S_NO_CRED:
111 err_msg = "Kerberos: no credentials were found for the specified name.";
112 break;
113 case GSS_S_DEFECTIVE_TOKEN:
114 err_msg = "Kerberos: consistency checks performed on "
115 "the input token failed.";
116 break;
117 case GSS_S_DEFECTIVE_CREDENTIAL:
118 err_msg = "Kerberos: consistency checks performed on "
119 "the credential failed.";
120 break;
121 case GSS_S_BAD_BINDINGS:
122 err_msg = "Kerberos: the input token contains "
123 "different channel bindings as specified.";
124 break;
125 case GSS_S_NO_CONTEXT:
126 err_msg = "Kerberos: the supplied context handle is invalid.";
127 break;
128 case GSS_S_BAD_SIG:
129 err_msg = "Kerberos: input token contains an invalid MIC.";
130 break;
131 case GSS_S_OLD_TOKEN:
132 err_msg = "Kerberos: input token is too old.";
133 break;
134 case GSS_S_DUPLICATE_TOKEN:
135 err_msg = "Kerberos: input token is a duplicate of a token "
136 "already processed.";
137 break;
138 case GSS_S_FAILURE:
139 snprintf(err_msg_buf, ERR_MSG_BUF_LEN,
140 "Kerberos: undefined Kerberos error. "
141 "Make sure a valid ticket-grant-ticket is acquired "
142 "and refer minor error code %d for details.", minor);
143 err_msg = err_msg_buf;
144 break;
145 default:
146 err_msg = "Kerberos: unknown error.";
147 break;
148 }
149
150 return err_msg;
151}
152#endif /* _WIN32 */
0153
=== added file 'plugin/auth_kerberos/kerberos_common.h'
--- plugin/auth_kerberos/kerberos_common.h 1970-01-01 00:00:00 +0000
+++ plugin/auth_kerberos/kerberos_common.h 2015-10-18 04:21:02 +0000
@@ -0,0 +1,89 @@
1/* Copyright (c) 2015, Shuang Qiu
2 All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6
7 1. Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 POSSIBILITY OF SUCH DAMAGE.
25*/
26
27/**
28 @file
29
30 Kerberos authentication utilities
31
32 Utility functions are declared.
33*/
34#ifndef KERBEROS_COMMON_H
35#define KERBEROS_COMMON_H
36
37#include <my_sys.h>
38#include <my_global.h>
39#include <mysql.h>
40#include <mysql/plugin_auth_common.h>
41#include <ctype.h>
42#include <string.h>
43
44/* global define directives */
45#define PRINCIPAL_NAME_LEN 256 /* TODO need a reference */
46
47#define MF_ERROR MYF(0)
48#define MF_WARNING MYF(1)
49
50/* platform dependent header */
51#ifdef _WIN32
52 /* on Windows platform, SSPI is used to perform the authentication */
53 #include <windows.h>
54
55 #define SECURITY_WIN32 /* User-mode SSPI application */
56 #include <Security.h>
57 #include <SecExt.h>
58 #include <sspi.h>
59#else /* !_WIN32 */
60 /**
61 * on other platform, make sure the Kerberos environment is pre-configured
62 * GSSAPI is used for inter-operation purpose between Windows platform
63 */
64 #include <gssapi/gssapi.h>
65 #include <gssapi/gssapi_generic.h>
66 #include <gssapi/gssapi_krb5.h>
67#endif /* _WIN32 */
68
69/* platform dependent define directives */
70#ifdef _WIN32
71 #define UNUSED(x) __pragma(warning(suppress:4100)) x /* warns suppressor */
72#else
73 #define UNUSED(x) x __attribute__((unused))
74#endif /* _WIN32 */
75
76#ifdef _WIN32
77 #define SECURITY_PACKAGE_NAME "Kerberos"
78 #define SSPI_MAX_TOKEN_SIZE 12000
79
80 #define SEC_ERROR(ss) ((ss) < 0)
81
82 /* translate SECURITY_STATUS to error text */
83 const char *error_msg(SECURITY_STATUS);
84#else /* _WIN32 */
85 /* translate symbolic error number to text error message */
86 const char *error_msg(OM_uint32, OM_uint32);
87#endif /* _WIN32 */
88
89#endif /* KERBEROS_COMMON_H */
090
=== added file 'plugin/auth_kerberos/kerberos_server.c'
--- plugin/auth_kerberos/kerberos_server.c 1970-01-01 00:00:00 +0000
+++ plugin/auth_kerberos/kerberos_server.c 2015-10-18 04:21:02 +0000
@@ -0,0 +1,725 @@
1/* Copyright (c) 2015, Shuang Qiu
2 All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6
7 1. Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 POSSIBILITY OF SUCH DAMAGE.
25*/
26
27/**
28 @file
29
30 Kerberos server authentication plugin
31
32 kerberos_server is a general purpose server authentication plugin, it
33 authenticates user against Kerberos principal.
34
35 This is the server side implementation.
36*/
37#include <mysql/plugin_auth.h>
38
39#include "kerberos_common.h"
40
41#define KRB_SERVER_AUTH_ERROR 1
42
43/* plugin global variables */
44char *kerberos_principal_name; /* system-wise declaration for spn */
45/**
46 * underlying storage for Kerberos service principal name system variable
47 */
48static char kerberos_spn_storage[PRINCIPAL_NAME_LEN];
49
50#ifdef _WIN32
51static int sspi_kerberos_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) {
52 int read_len= 0;
53 int max_token_sz= SSPI_MAX_TOKEN_SIZE;
54 int ret= CR_OK; /* return code */
55 const char *err_msg= NULL; /* error message */
56 /* SSPI related fields */
57 SECURITY_STATUS ss = 0;
58 BOOL have_ctxt= FALSE;
59 BOOL have_cred= FALSE;
60 BOOL context_established= FALSE;
61 ULONG attribs= 0;
62 TimeStamp lifetime;
63
64 CredHandle cred_handle; /* credential handle */
65 CtxtHandle ctxt_handle; /* context handle */
66 SecPkgContext_NativeNames native_names;
67
68 SecPkgInfo *sec_pkg_info; /* Packet information */
69 SecBufferDesc input_buf_desc; /* Input token */
70 SecBuffer input_buf;
71 SecBufferDesc output_buf_desc; /* Output token */
72 SecBuffer output_buf;
73 PBYTE output_con;
74
75 /* query packet information */
76 ss= QuerySecurityPackageInfo(SECURITY_PACKAGE_NAME, &sec_pkg_info);
77
78 if (ss != SEC_E_OK)
79 {
80 /* error query package information */
81 my_error(KRB_SERVER_AUTH_ERROR, MF_WARNING,
82 "Kerberos: fail to get maximum token size, use default: %d.",
83 max_token_sz);
84 }
85 else
86 {
87 max_token_sz= sec_pkg_info->cbMaxToken;
88 }
89
90 /* allocate memory */
91 output_con= (PBYTE) calloc(max_token_sz, sizeof(BYTE));
92 if (!output_con)
93 {
94 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, "Kerberos: no more memory.");
95 return CR_ERROR;
96 }
97
98 /* acquire initial credential */
99 ss= AcquireCredentialsHandle(NULL, /* use default credential */
100 SECURITY_PACKAGE_NAME,
101 SECPKG_CRED_INBOUND, /* cred usage */
102 NULL, /* locally unique id */
103 NULL, /* use default credential */
104 NULL, /* get key func */
105 NULL, /* get key argument func */
106 &cred_handle,
107 &lifetime);
108
109 /* AcquireCredentialsHandle error checking */
110 if (SEC_ERROR(ss))
111 {
112 err_msg= error_msg(ss);
113 ret= CR_ERROR;
114 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg);
115 goto cleanup;
116 }
117 else
118 {
119 have_cred= TRUE;
120 }
121
122 /* prepare input buffer */
123 input_buf_desc.ulVersion= SECBUFFER_VERSION;
124 input_buf_desc.cBuffers= 1;
125 input_buf_desc.pBuffers= &input_buf;
126
127 input_buf.cbBuffer= 0;
128 input_buf.BufferType= SECBUFFER_TOKEN;
129 input_buf.pvBuffer= NULL;
130
131 /* prepare output buffer */
132 output_buf_desc.ulVersion= SECBUFFER_VERSION;
133 output_buf_desc.cBuffers= 1;
134 output_buf_desc.pBuffers= &output_buf;
135
136 output_buf.BufferType= SECBUFFER_TOKEN;
137 output_buf.cbBuffer= max_token_sz;
138 output_buf.pvBuffer= output_con;
139
140 do
141 {
142 /* read credential from client */
143 read_len= vio->read_packet(vio, (unsigned char **) &input_buf.pvBuffer);
144 if (read_len < 0) {
145 ret= CR_ERROR;
146 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
147 "Kerberos: fail read client credentials.");
148 goto cleanup;
149 }
150 input_buf.cbBuffer= read_len;
151
152 ss= AcceptSecurityContext(
153 &cred_handle, /* credential */
154 have_ctxt ? &ctxt_handle : NULL,
155 &input_buf_desc, /* input credentials */
156 attribs,
157 SECURITY_NATIVE_DREP,
158 &ctxt_handle, /* secure context */
159 &output_buf_desc, /* output credentials */
160 &attribs,
161 &lifetime);
162
163 /* AcceptSecurityContext error checking */
164 if (SEC_ERROR(ss))
165 {
166 err_msg= error_msg(ss);
167 ret= CR_ERROR;
168 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg);
169 goto cleanup;
170 }
171 /* Security context established (partially) */
172 have_ctxt= TRUE;
173
174 if (output_buf.cbBuffer)
175 {
176 /* write credential packet */
177 if (vio->write_packet(vio,
178 (const unsigned char *) output_buf.pvBuffer,
179 output_buf.cbBuffer))
180 {
181 ret= CR_ERROR;
182 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
183 "Kerberos: fail send crednetials to client.");
184 goto cleanup;
185 }
186 }
187 output_buf.cbBuffer= max_token_sz;
188
189 if ((ss == SEC_I_COMPLETE_NEEDED) ||
190 (ss == SEC_I_COMPLETE_AND_CONTINUE))
191 {
192 ss= CompleteAuthToken(&ctxt_handle, &output_buf_desc);
193 if (SEC_ERROR(ss))
194 {
195 err_msg= error_msg(ss);
196 ret= CR_ERROR;
197 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg);
198 goto cleanup;
199 }
200 }
201
202 context_established= !((ss == SEC_I_CONTINUE_NEEDED) ||
203 (ss == SEC_I_COMPLETE_AND_CONTINUE));
204 } while (!context_established);
205
206 /* check principal name */
207 ss= QueryContextAttributes(&ctxt_handle, SECPKG_ATTR_NATIVE_NAMES, &native_names);
208
209 if (SEC_ERROR(ss))
210 {
211 err_msg= error_msg(ss);
212 ret= CR_ERROR;
213 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg);
214 goto cleanup;
215 }
216
217 if (strcmp(native_names.sClientName, info->auth_string))
218 {
219 ret= CR_ERROR;
220 }
221 else
222 {
223 ret= CR_OK;
224 }
225
226cleanup:
227 /* free dynamic memory */
228 if (have_ctxt) DeleteSecurityContext(&ctxt_handle);
229 if (have_cred) FreeCredentialsHandle(&cred_handle);
230 free(output_con);
231 ss= FreeContextBuffer(sec_pkg_info);
232 if (ss != SEC_E_OK) {
233 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
234 "Kerberos: fail to free SecurityPackageInfo object.");
235 /* return code is not modified */
236 }
237
238 return ret;
239}
240#else /* !_WIN32 */
241static int gssapi_kerberos_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) {
242 int r_len= 0; /* packet read length */
243 int rc= CR_OK; /* return code */
244 int have_cred= FALSE;
245 int have_ctxt= FALSE;
246 const char *err_msg= NULL; /* error message text */
247 /* GSSAPI related fields */
248 OM_uint32 major= 0, minor= 0, flags= 0;
249 gss_cred_id_t cred; /* credential identifier */
250 gss_ctx_id_t ctxt; /* context identifier */
251 gss_name_t client_name, service_name;
252 gss_buffer_desc principal_name_buf, client_name_buf, input, output;
253
254 /* import service principal from plain text */
255 /* initialize principal name */
256 principal_name_buf.length= strlen(kerberos_principal_name);
257 principal_name_buf.value= kerberos_principal_name;
258 major= gss_import_name(&minor,
259 &principal_name_buf,
260 (gss_OID) gss_nt_user_name,
261 &service_name);
262 /* gss_import_name error checking */
263 if (GSS_ERROR(major))
264 {
265 err_msg= error_msg(major, minor);
266 rc= CR_ERROR;
267 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg);
268 goto cleanup;
269 }
270
271 /* server acquires credential */
272 major= gss_acquire_cred(&minor,
273 service_name,
274 GSS_C_INDEFINITE, /* time_req, ALAP */
275 GSS_C_NO_OID_SET, /* desired_mechs */
276 GSS_C_ACCEPT, /* cred_usage, ACCEPT only */
277 &cred,
278 NULL, /* actual_mechs */
279 NULL); /* time_rec */
280
281 /* gss_acquire_cred error checking */
282 if (GSS_ERROR(major))
283 {
284 err_msg= error_msg(major, minor);
285 rc= CR_ERROR;
286 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg);
287 goto cleanup;
288 }
289 else
290 {
291 have_cred= TRUE;
292 }
293
294 major= gss_release_name(&minor, &service_name);
295 if (major == GSS_S_BAD_NAME)
296 {
297 rc= CR_ERROR;
298 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
299 "Kerbeos: fail when invoke gss_release_name, no valid name found.");
300 goto cleanup;
301 }
302
303 /* accept security context */
304 ctxt= GSS_C_NO_CONTEXT;
305 /* first trial */
306 input.length= 0;
307 input.value= NULL;
308 do {
309 /* receive token from peer first */
310 r_len= vio->read_packet(vio, (unsigned char **) &input.value);
311 if (r_len < 0) {
312 rc= CR_ERROR;
313 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
314 "Kerberos: fail to read token from client.");
315 goto cleanup;
316 }
317 else
318 {
319 /* make length consistent with value */
320 input.length= r_len;
321 }
322
323 major= gss_accept_sec_context(&minor,
324 &ctxt, /* ctxt handle */
325 cred,
326 &input, /* input buffer */
327 GSS_C_NO_CHANNEL_BINDINGS,
328 &client_name, /* source name */
329 NULL, /* mech type */
330 &output, /* output buffer */
331 &flags, /* return flag */
332 NULL, /* time rec */
333 NULL);
334 if (GSS_ERROR(major))
335 {
336 if (ctxt != GSS_C_NO_CONTEXT)
337 {
338 gss_delete_sec_context(&minor,
339 &ctxt,
340 GSS_C_NO_BUFFER);
341 }
342 err_msg= error_msg(major, minor);
343 rc= CR_ERROR;
344 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg);
345 goto cleanup;
346 }
347 /* security context established (partially) */
348 have_ctxt= TRUE;
349
350 /* send token to peer */
351 if (output.length)
352 {
353 if (vio->write_packet(vio, output.value, output.length))
354 {
355 gss_release_buffer(&minor, &output);
356 rc= CR_ERROR;
357 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
358 "Kerberos: fail to send authentication token.");
359 goto cleanup;
360 }
361 gss_release_buffer(&minor, &output);
362 }
363 } while (major & GSS_S_CONTINUE_NEEDED);
364
365 /* extrac plain text client name */
366 major= gss_display_name(&minor, client_name, &client_name_buf, NULL);
367 if (major == GSS_S_BAD_NAME)
368 {
369 rc= CR_ERROR;
370 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
371 "Kerberos: fail to display an ill-formed principal name.");
372 goto cleanup;
373 }
374
375 /* expected user? */
376 if (strncmp(client_name_buf.value, info->auth_string, PRINCIPAL_NAME_LEN))
377 {
378 gss_release_buffer(&minor, &client_name_buf);
379 rc= CR_ERROR;
380 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
381 "Kerberos: fail authentication user.");
382 goto cleanup;
383 }
384 gss_release_buffer(&minor, &client_name_buf);
385
386cleanup:
387 if (have_ctxt) gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER);
388 if (have_cred) gss_release_cred(&minor, &cred);
389
390 return rc;
391}
392#endif /* _WIN32 */
393
394/**
395 * The main server function of the Kerberos plugin.
396 */
397static int kerberos_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
398{
399 size_t p_len= 0; /* length of principal name */
400 int rc= CR_OK; /* return code */
401 char *principal_name= NULL;
402
403 /* server sends service principal name first. */
404 p_len= strlen(kerberos_principal_name);
405 if (!p_len)
406 {
407 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
408 "Kerberos: no principal name specified.");
409 return CR_ERROR;
410 }
411
412 if (vio->write_packet(vio,
413 (unsigned char *) kerberos_principal_name,
414 (int) p_len) < 0)
415 {
416 my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR,
417 "Kerberos: fail to send service principal name.");
418 return CR_ERROR;
419 }
420
421 rc =
422#ifdef _WIN32
423 sspi_kerberos_auth(vio, info);
424#else /* !_WIN32 */
425 gssapi_kerberos_auth(vio, info);
426#endif /* _WIN32 */
427
428 free(principal_name);
429
430 return rc;
431}
432
433#ifdef _WIN32
434static BOOL GetLogonSID (PSID *ppsid)
435{
436 BOOL succ= FALSE;
437 HANDLE token;
438 DWORD index;
439 DWORD length= 0;
440 PTOKEN_GROUPS ptg= NULL;
441
442 /* Verify the parameter passed in is not NULL. */
443 if (ppsid == NULL)
444 goto cleanup;
445
446 /* Open a handle to the access token for the calling process. */
447 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token ))
448 goto cleanup;
449
450 /* Get required buffer size and allocate the TOKEN_GROUPS buffer. */
451 if (!GetTokenInformation(
452 token, /* handle to the access token */
453 TokenGroups, /* get information about the token's groups */
454 (LPVOID) ptg, /* pointer to TOKEN_GROUPS buffer */
455 0, /* size of buffer */
456 &length /* receives required buffer size */
457 ))
458 {
459 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
460 goto cleanup;
461
462 ptg= (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
463 HEAP_ZERO_MEMORY, length);
464
465 if (ptg == NULL)
466 goto cleanup;
467 }
468
469 /* Get the token group information from the access token. */
470 if (!GetTokenInformation(
471 token, /* handle to the access token */
472 TokenGroups, /* get information about the token's groups */
473 (LPVOID) ptg, /* pointer to TOKEN_GROUPS buffer */
474 length, /* size of buffer */
475 &length /* receives required buffer size */
476 ))
477 {
478 goto cleanup;
479 }
480
481 /* Loop through the groups to find the logon SID. */
482 for (index= 0; index < ptg->GroupCount; index++)
483 {
484 if ((ptg->Groups[index].Attributes & SE_GROUP_LOGON_ID)
485 == SE_GROUP_LOGON_ID)
486 {
487 /* Found the logon SID; make a copy of it. */
488 length= GetLengthSid(ptg->Groups[index].Sid);
489 *ppsid= (PSID) HeapAlloc(GetProcessHeap(),
490 HEAP_ZERO_MEMORY, length);
491 if (*ppsid == NULL)
492 goto cleanup;
493 if (!CopySid(length, *ppsid, ptg->Groups[index].Sid))
494 {
495 HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
496 goto cleanup;
497 }
498 break;
499 }
500 }
501 succ= TRUE;
502
503cleanup:
504 /* Free the buffer for the token groups. */
505 if (ptg != NULL)
506 HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);
507
508 return succ;
509}
510
511static VOID FreeLogonSID (PSID *ppsid)
512{
513 HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
514}
515
516static BOOLEAN LogonAsNetworkService(void)
517{
518 SID sNetServ;
519 PSID psLogon= NULL;
520 DWORD szSid= sizeof(sNetServ);
521 BOOLEAN bRetCode= FALSE;
522
523 if (GetLogonSID(&psLogon) &&
524 CreateWellKnownSid(WinNetworkServiceSid, NULL, &sNetServ, &szSid) &&
525 EqualSid(psLogon, &sNetServ))
526 {
527 bRetCode= TRUE;
528 }
529
530 if (psLogon) FreeLogonSID(&psLogon);
531
532 return bRetCode;
533}
534
535static int initialize_principal_name(void *unused)
536{
537 int ret= 0;
538 ULONG len= sizeof(kerberos_spn_storage);
539 CHAR *computer_name= NULL;
540 CHAR *domain_name= NULL;
541 BOOLEAN api_rc= TRUE;
542
543#define KRB_AUTH_SERVER_INIT_ERROR(ret, msg) \
544 do { ret= -1; \
545 my_error(ret, MF_ERROR, msg); \
546 } while(0)
547
548 /* principal name has already been set */
549 if (kerberos_principal_name && kerberos_principal_name[0]) return ret;
550
551 api_rc= GetUserNameEx(NameUserPrincipal, kerberos_spn_storage, &len);
552 if (!api_rc)
553 {
554 switch (GetLastError())
555 {
556 case ERROR_NO_SUCH_DOMAIN:
557 ret = CR_ERROR;
558 my_error(KRB_SERVER_AUTH_ERROR, MF_WARNING,
559 "Kerberos: the domain controller is not up.");
560 break;
561 case ERROR_NONE_MAPPED:
562 /* cannot find UPN for logon user */
563 /*
564 * If logon sid is NetworkService, a fallback is construct
565 * UPN (computer$@domain) manually.
566 */
567 if (LogonAsNetworkService())
568 {
569 BOOLEAN done= TRUE;
570
571 len= PRINCIPAL_NAME_LEN;
572 computer_name= (CHAR *) calloc(len, sizeof(CHAR));
573 done= (computer_name == NULL);
574 if (done && (done= GetComputerNameEx(ComputerNameDnsHostname,
575 computer_name, &len)))
576 {
577 len= PRINCIPAL_NAME_LEN;
578 domain_name= (CHAR *) calloc(len, sizeof(CHAR));
579 done= (domain_name == NULL);
580 if (done && (done= GetComputerNameEx(ComputerNameDnsDomain,
581 domain_name, &len)))
582 {
583 sprintf_s(kerberos_spn_storage,
584 sizeof(kerberos_spn_storage),
585 "%s$@%s", computer_name, domain_name);
586 }
587 }
588
589 /* Release heap memory */
590 if (computer_name) free(computer_name);
591 if (domain_name) free(domain_name);
592
593 if (!done)
594 {
595 KRB_AUTH_SERVER_INIT_ERROR(ret,
596 "Kerberos: the name is not available in specific format.");
597 }
598 else
599 {
600 /* redirect the variable */
601 kerberos_principal_name= kerberos_spn_storage;
602 }
603 }
604 else
605 {
606 KRB_AUTH_SERVER_INIT_ERROR(ret,
607 "Kerberos: the name is not available in specific format.");
608 }
609 break;
610 default:
611 break;
612 }
613 }
614 else
615 {
616 /* redirect the variable */
617 kerberos_principal_name= kerberos_spn_storage;
618 }
619
620/* localize macro KRB_AUTH_SERVER_INIT_ERROR */
621#undef KRB_AUTH_SERVER_INIT_ERROR
622
623 return ret;
624}
625#endif /* _WIN32 */
626
627static int verify_principal_name(UNUSED(MYSQL_THD thd),
628 UNUSED(struct st_mysql_sys_var UNUSED(*var)), UNUSED(void *save),
629 UNUSED(struct st_mysql_value *value)) {
630 char upn_buf[PRINCIPAL_NAME_LEN];
631 int buf_len= PRINCIPAL_NAME_LEN;
632 /* UPN should in the form `user@domain` or `user/host@domain` */
633 const char *ptr= value->val_str(value, upn_buf, &buf_len);
634 const char *itr= ptr;
635
636#define FWD_ITER(iter) while (*iter && (isalpha(*iter)||(*itr)=='.')) iter++
637 /* user part */
638 if (*itr && isalpha(*itr))
639 {
640 FWD_ITER(itr);
641 }
642 else
643 {
644 /* name part is required */
645 return 1;
646 }
647
648 /* host part, which is optional */
649 if (*itr && *itr == '/')
650 {
651 itr++;
652 FWD_ITER(itr);
653 }
654
655 /* domain part */
656 if (*itr && *itr == '@')
657 {
658 itr++;
659 FWD_ITER(itr);
660 }
661 else
662 {
663 /* domain part is required */
664 return 1;
665 }
666
667 /* if validated return 0, or any non-zero value */
668 if (!*itr)
669 {
670 strncpy(kerberos_spn_storage, ptr, PRINCIPAL_NAME_LEN);
671 }
672 return *itr;
673}
674
675static void update_principal_name(UNUSED(MYSQL_THD thd),
676 UNUSED(struct st_mysql_sys_var* var), UNUSED(void * var_ptr),
677 UNUSED(const void * save))
678{
679 kerberos_principal_name= kerberos_spn_storage;
680}
681
682/* system variable */
683static MYSQL_SYSVAR_STR(principal_name, kerberos_principal_name,
684 PLUGIN_VAR_RQCMDARG,
685 "Service principal name in Kerberos authentication.",
686 verify_principal_name, /* check function */
687 update_principal_name, /* update function */
688 "");
689
690static struct st_mysql_sys_var *system_variables[]= {
691 MYSQL_SYSVAR(principal_name),
692 NULL
693};
694
695/* register Kerberos authentication plugin */
696static struct st_mysql_auth server_handler= {
697 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
698 "kerberos_client",
699 kerberos_auth
700};
701
702maria_declare_plugin(kerberos_server)
703{
704 MYSQL_AUTHENTICATION_PLUGIN,
705 &server_handler,
706 "kerberos",
707 "Shuang Qiu",
708 "Plugin for Kerberos based authentication.",
709 PLUGIN_LICENSE_BSD,
710#ifdef _WIN32
711 initialize_principal_name,
712#else /* _WIN32 */
713 NULL,
714#endif /* _WIN32 */
715 NULL, /* destructor */
716 0x0100, /* version */
717 NULL, /* status variables */
718 system_variables, /* system variables */
719 "Kerberos authentication plugin 1.0",
720 MariaDB_PLUGIN_MATURITY_EXPERIMENTAL /* TODO change when release */
721}
722maria_declare_plugin_end;
723
724/* localize macro KRB_AUTH_SERVER_ERROR */
725#undef KRB_AUTH_SERVER_ERROR

Subscribers

People subscribed via source and target branches