Merge lp:~qiush-summer/maria/krb_auth_plugin into lp:maria
- krb_auth_plugin
- Merge into 10.0
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 |
Related bugs: |
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.
- 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
1 | === added file 'mysql-test/suite/plugins/r/kerberos.result' |
2 | --- mysql-test/suite/plugins/r/kerberos.result 1970-01-01 00:00:00 +0000 |
3 | +++ mysql-test/suite/plugins/r/kerberos.result 2015-10-18 04:21:02 +0000 |
4 | @@ -0,0 +1,12 @@ |
5 | +INSTALL PLUGIN kerberos SONAME 'kerberos'; |
6 | +CREATE USER test_kerberos IDENTIFIED VIA kerberos AS 'MTR_KERBEROS_UPN'; |
7 | +SET GLOBAL kerberos_principal_name='MTR_KERBEROS_SPN'; |
8 | +# |
9 | +# user principal is accepted, and authentication is successful. |
10 | +# |
11 | +SELECT USER(); |
12 | +USER() |
13 | +test_kerberos@localhost |
14 | +# connection is persistent. |
15 | +DROP USER test_kerberos; |
16 | +UNINSTALL PLUGIN kerberos; |
17 | |
18 | === added file 'mysql-test/suite/plugins/t/kerberos.test' |
19 | --- mysql-test/suite/plugins/t/kerberos.test 1970-01-01 00:00:00 +0000 |
20 | +++ mysql-test/suite/plugins/t/kerberos.test 2015-10-18 04:21:02 +0000 |
21 | @@ -0,0 +1,30 @@ |
22 | + |
23 | +--source include/not_embedded.inc |
24 | + |
25 | +if (!$MTR_KERBEROS_ENABLED) { |
26 | + skip No Kerberos auth plugin; |
27 | +} |
28 | + |
29 | +eval INSTALL PLUGIN kerberos SONAME 'kerberos'; |
30 | +--replace_result $MTR_KERBEROS_UPN MTR_KERBEROS_UPN |
31 | +eval CREATE USER test_kerberos IDENTIFIED VIA kerberos AS '$MTR_KERBEROS_UPN'; |
32 | + |
33 | +--replace_result $MTR_KERBEROS_SPN MTR_KERBEROS_SPN |
34 | +eval SET GLOBAL kerberos_principal_name='$MTR_KERBEROS_SPN'; |
35 | + |
36 | +let $plugindir=`SELECT @@global.plugin_dir`; |
37 | + |
38 | +--echo # |
39 | +--echo # user principal is accepted, and authentication is successful. |
40 | +--echo # |
41 | +--exec echo "SELECT USER();" | $MYSQL_TEST -u test_kerberos --plugin-dir=$plugindir |
42 | +--echo # connection is persistent. |
43 | + |
44 | +# --echo # |
45 | +# --echo # athentication is unsuccessful |
46 | +# --echo # |
47 | +# --error 1 |
48 | +# --exec $MYSQL_TEST -u test_kerberos --plugin-dir=$plugindir |
49 | + |
50 | +DROP USER test_kerberos; |
51 | +UNINSTALL PLUGIN kerberos; |
52 | |
53 | === modified file 'mysys/charset.c' (properties changed: -x to +x) |
54 | === added directory 'plugin/auth_kerberos' |
55 | === added file 'plugin/auth_kerberos/CMakeLists.txt' |
56 | --- plugin/auth_kerberos/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
57 | +++ plugin/auth_kerberos/CMakeLists.txt 2015-10-18 04:21:02 +0000 |
58 | @@ -0,0 +1,24 @@ |
59 | +IF (WIN32) |
60 | + # on Windows-platform, secur32.lib is certainly there. |
61 | + SET(KRB_LIB secur32) |
62 | +ELSE (WIN32) |
63 | + # check include file |
64 | + CHECK_INCLUDE_FILES("gssapi/gssapi.h;gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" HAVE_GSSAPI_H) |
65 | + IF (NOT HAVE_GSSAPI_H) |
66 | + RETURN() |
67 | + ENDIF (NOT HAVE_GSSAPI_H) |
68 | + # on non-Windows-platform, check the existence of library gssapi_krb5. |
69 | + FIND_LIBRARY(KRB_LIB gssapi_krb5) |
70 | + IF (NOT KRB_LIB) |
71 | + # library is not there on the PATH. |
72 | + message("gssapi_krb5 not found.") |
73 | + RETURN() |
74 | + ENDIF (NOT KRB_LIB) |
75 | +ENDIF (WIN32) |
76 | + |
77 | +MYSQL_ADD_PLUGIN(kerberos kerberos_server.c kerberos_common.c |
78 | + LINK_LIBRARIES ${KRB_LIB} |
79 | + MODULE_ONLY COMPONENT SharedLibraries) |
80 | +MYSQL_ADD_PLUGIN(kerberos_client kerberos_client.c kerberos_common.c |
81 | + LINK_LIBRARIES ${KRB_LIB} |
82 | + MODULE_ONLY COMPONENT SharedLibraries) |
83 | |
84 | === added file 'plugin/auth_kerberos/README.md' |
85 | --- plugin/auth_kerberos/README.md 1970-01-01 00:00:00 +0000 |
86 | +++ plugin/auth_kerberos/README.md 2015-10-18 04:21:02 +0000 |
87 | @@ -0,0 +1,211 @@ |
88 | +## Kerberos Authentication Plugin for MariaDB |
89 | + |
90 | +This article gives instructions on configuring Kerberos authentication |
91 | +plugin for MariaDB. With Kerberos authentication plugin enabled, you can |
92 | +login MariaDB as a Kerberos domain user passwordlessly. |
93 | + |
94 | +### System Settings |
95 | + |
96 | +To use the full feature of Kerberos authentication plugin, make sure a |
97 | +Kerberos authentication domain is properly set up. |
98 | + |
99 | +* For a pure *nix Kerberos domain, an [MIT Kerberos Key Distribution Centre][1] |
100 | +(krb5kdc service) should be running. |
101 | + |
102 | +* As to a pure Windows domain, an active directory domain controller should |
103 | +be connectable. |
104 | + |
105 | +* Hybrid Kerberos domains are also allowed. |
106 | + |
107 | +Detailed guides to set up a Kerberos authentication domain is beyond the scope |
108 | +of this document. You can refer to the links in the References section on |
109 | +how to setup a Kerberos authentication domain. |
110 | + |
111 | +### Compile |
112 | + |
113 | +The compilation of Kerberos authentication plugin has been integrated into |
114 | +the CMake building framework of MariaDB. |
115 | + |
116 | +##### *nix |
117 | + |
118 | +If you are a *nix user, guarantee the Kerberos libraries and headers are |
119 | +installed. |
120 | + |
121 | +##### Windows |
122 | + |
123 | +The Windows version requires no additional libraries. |
124 | + |
125 | +The Kerberos authentication plugin is separated into two isolated parts: |
126 | +the server-side plugin and client-side plugin. If no errors occur |
127 | +during compilation, two shared libraries will be created, `kerberos` and |
128 | +`kerberos_client` for server-side and client-side plugin respectively. |
129 | + |
130 | +### Installation |
131 | + |
132 | +Install the server-side Kerberos authentication plugin before used. |
133 | +Client-side plugin will be automatically loaded when required. |
134 | + |
135 | +Connect MariaDB server as a superuser, then issue the command |
136 | + |
137 | + INSTALL PLUGIN kerberos SONAME 'kerberos'; |
138 | + |
139 | +This will instruct MariaDB server to load the Kerberos authentication plugin. |
140 | + |
141 | +### Set a Service Principal Name to MariaDB |
142 | + |
143 | +Before we can authenticate against Kerberos service, the last step is assign |
144 | +a service principal name to MariaDB server. |
145 | + |
146 | +#### Figure out a Valid Principal Name |
147 | + |
148 | +Services and users are identified via principal names internally in Kerberos |
149 | +authentication service. Generally, a principal name is in the format of |
150 | +`username/hostname@DOMAIN.NAME`. |
151 | + |
152 | +##### *nix |
153 | + |
154 | +For Kerberos services on *nix platform, a user specified service principal |
155 | +name is required, since no default principal name can be derived from the |
156 | +effective user of the server process. |
157 | + |
158 | +##### Windows |
159 | + |
160 | +As to Windows Active Directory services |
161 | + |
162 | +* if MariaDB is running as `NetworkService` by default, the principal name |
163 | +is akin `host$@DOMAIN.NAME`. In this case, no need to specify service |
164 | +principal name manually. The Kerberos plugin can derive a valid default |
165 | +service principal name. |
166 | + |
167 | +* Otherwise, if you run MariaDB as a customized domain user, the principal |
168 | +name is `username@DOMAIN.NAME` by default. In this case, no need to specify |
169 | +service principal name manually. The Kerberos plugin can derive a valid |
170 | +default service principal name. |
171 | + |
172 | +* Finally, if a principal name is preferred over the default `username@DOMAIN`, |
173 | +feel free to update it with the `setspn.exe` tool. Correspondingly, the |
174 | +new principal name must be specified in the MariaDB configuration file to |
175 | +have it work. |
176 | + |
177 | +A valid Kerberos principal name is case sensitive in both *nix and Windows. |
178 | +For example, on *nix platform a valid service principal name is like |
179 | +`MySQL/localhost@EXAMPLE.COM` (capitalised realm name is recommended by |
180 | +Kerberos service) or `MySQL@EXAMPLE.COM` on Windows (`hostname` is not |
181 | +emphasised). |
182 | + |
183 | +#### Assign the Principal Name |
184 | + |
185 | +To assign a service principal name to server, Kerberos authentication plugin |
186 | +exposes a system variable `named kerberos_principal_name`. |
187 | + |
188 | +One can specify the name (say, `MySQL/localhost@EXAMPLE.COM`) in three ways: |
189 | + |
190 | +* Specify the name in configure file: edit local configure file ~/.my.cnf by |
191 | +inserting the line `kerberos_principal_name=MySQL/localhost@EXAMPLE.COM` in the |
192 | +server section. * Pass as command line parameter: start MariaDB server with |
193 | +command line parameter `--kerberos_principal_name=MySQL/localhost@EXAMPLE.COM`. |
194 | +* If you can login MariaDB as a superuser, you can set by the following |
195 | +commands: `SET GLOBAL kerberos_principal_name='MySQL/localhost@EXAMPLE.COM';` |
196 | +The parameter should be set each time after the service restarts. |
197 | + |
198 | +You can verify service principal name is properly set by |
199 | + |
200 | + SELECT @@global.kerberos_principal_name; |
201 | + |
202 | +#### Create New MariaDB Users |
203 | + |
204 | +If all the steps above are completed, you can now create a new user identified |
205 | +via Kerberos authentication plugin. |
206 | + |
207 | + CREATE USER user_name IDENTIFIED VIA kerberos AS 'user_principal_name'; |
208 | + |
209 | +We need the `AS` clause to specify the principal name instead of embedded |
210 | +into `user_name` directly for the length gap between MariaDB username and |
211 | +Kerberos principal name. |
212 | + |
213 | +#### Connect and Login as Kerboers Principal |
214 | + |
215 | +To connect to MariaDB as `user_name`, first check a valid Kerberos tgt ticket |
216 | +is cached with `klist`. You can obtain a tgt ticket either by login as a |
217 | +domain user on Windows platform or by `kinit principal_name` on Linux box. |
218 | + |
219 | +If all these steps are done, you should now connect to MariaDB as `user_name` |
220 | +successfully. |
221 | + |
222 | +### Run Test Suite |
223 | + |
224 | +This section describes how to run unit test for Kerberos authentication plugin. |
225 | +In case of setting up a Kerberos domain, which is tedious to an ordinary user, |
226 | +the unit test for Kerberos authentication plugin is by default skipped. |
227 | + |
228 | +To run the unit test, an OS environment variable `MTR_KERBEROS_ENABLED` |
229 | +should be set to a valid value |
230 | + |
231 | + MTR_KERBEROS_ENABLED=1 |
232 | + |
233 | +Two more OS environment variables `MTR_KERBEROS_SPN` and `MTR_KERBEROS_UPN` |
234 | +should be set for MariaDB service principal name and login user principal |
235 | +name respectively. |
236 | + |
237 | +#### Extending the Ticket Lifetime |
238 | + |
239 | +Make sure the tgt ticket is not expired for login user when running the |
240 | +unit test. In case early expiration of the ticket, we can extend the ticket |
241 | +lifetime in configuration. |
242 | + |
243 | +##### *nix |
244 | + |
245 | +You can extend the ticket lifetime by editing `/etc/krb5.conf` in *nix by |
246 | +updating two parameter `ticket_lifetime` and `renew_lifetime`. |
247 | + |
248 | +##### Windows |
249 | + |
250 | +Extending ticket lifetime can also be done within several clicks in Windows, |
251 | +here is a step-by-step instruction: |
252 | + |
253 | + 1. Open "Group Policy Management" (Start -> All Programs -> Administrative |
254 | + Tools -> Group Policy Management). 2. Select default domain (Domains |
255 | + -> default.domain -> Domain Controllers -> Default Domain Controllers; |
256 | + then on the right detail panel Settings -> Policies -> Windows Settings |
257 | + -> Security Settings -> right click "Local Policies/Security Options" |
258 | + -> Edit... to open Group Policy Management Editor). 3. Then in the GPM |
259 | + Editor Default Domain Controllers Policy -> Computer Configure -> Polices |
260 | + -> Windows Settings -> Security Settings -> Account Policies -> Kerberos |
261 | + Policies -> Maximum lifetime for service/user ticket. |
262 | + |
263 | +Once the tgt ticket is expired, on Linux use command `kinit -R` to renew |
264 | +the ticket, while on Windows, one should logout and logon again. |
265 | + |
266 | +### Trouble Shoot Authentication Problems |
267 | + |
268 | + 1. Check the Kerberos KDC log first, on Linux it is `/var/log/krb5kdc.log` |
269 | + by default or specified in /etc/krb5.conf. On Windows, steps are: Start |
270 | + -> Administrative Tools -> Event Viewer, Windows Logs -> Security, to see |
271 | + whether the ticket to requested service has been issued. If not issued, |
272 | + verify whether both service principal name and user principal name are |
273 | + correct. In addition, corresponding principals have been created in the |
274 | + KDC server. 2. If tickets are issued, while authentication still fails |
275 | + and you're on *nix box, make sure the initial credential to the service |
276 | + principal's ticket is saved in the keytab. What's more, the keytab can be |
277 | + read by Kerberos KDC. |
278 | + |
279 | +### References |
280 | + |
281 | +* [MIT Kerberos Official Documents for Administrators][2] |
282 | +* [A Step-by-step Guide for Windows Server 2008 Domain Controller and DNS Setup][3] |
283 | +* [Add Windows Box to a *nix krb5kdc Domain][5] |
284 | +* [Authenticate Linux Clients with Active Directory][6] |
285 | +* [Install Plugin in MariaDB][7] |
286 | +* [Kerberos Principal Name Specification][8] |
287 | +* [Renew a ticket on Windows][9] |
288 | +* [\*nix krenew command: Extend Ticket Lifetime on \*nix][10] |
289 | + |
290 | +[1]: http://web.mit.edu/kerberos/ |
291 | +[2]: http://web.mit.edu/kerberos/krb5-latest/doc/admin/index.html |
292 | +[3]: http://www.windowsreference.com/windows-server-2008/step-by-step-guide-for-windows-server-2008-domain-controller-and-dns-server-setup/ |
293 | +[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 |
294 | +[6]: http://technet.microsoft.com/en-us/magazine/2008.12.linux.aspx |
295 | +[7]: https://kb.askmonty.org/en/plugin-overview/#installing-plugins |
296 | +[8]: http://pic.dhe.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/cl/addkrbtkt.htm |
297 | +[9]: http://technet.microsoft.com/en-us/library/cc738673(v=ws.10).aspx#w2k8tr_kerb_tools_iybi |
298 | +[10]: http://stackoverflow.com/questions/14682153/lifetime-of-kerberos-tickets#15457265 |
299 | |
300 | === added file 'plugin/auth_kerberos/kerberos_client.c' |
301 | --- plugin/auth_kerberos/kerberos_client.c 1970-01-01 00:00:00 +0000 |
302 | +++ plugin/auth_kerberos/kerberos_client.c 2015-10-18 04:21:02 +0000 |
303 | @@ -0,0 +1,419 @@ |
304 | +/* Copyright (c) 2015, Shuang Qiu |
305 | + All rights reserved. |
306 | + |
307 | + Redistribution and use in source and binary forms, with or without |
308 | + modification, are permitted provided that the following conditions are met: |
309 | + |
310 | + 1. Redistributions of source code must retain the above copyright notice, |
311 | + this list of conditions and the following disclaimer. |
312 | + |
313 | + 2. Redistributions in binary form must reproduce the above copyright notice, |
314 | + this list of conditions and the following disclaimer in the documentation |
315 | + and/or other materials provided with the distribution. |
316 | + |
317 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
318 | + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
319 | + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
320 | + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
321 | + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
322 | + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
323 | + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
324 | + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
325 | + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
326 | + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
327 | + POSSIBILITY OF SUCH DAMAGE. |
328 | +*/ |
329 | + |
330 | +/** |
331 | + @file |
332 | + |
333 | + Kerberos server authentication plugin |
334 | + |
335 | + kerberos_server is a general purpose server authentication plugin, it |
336 | + authenticates user against Kerberos principal. |
337 | + |
338 | + This is the client side implementation. |
339 | +*/ |
340 | +#include <stdarg.h> |
341 | +#include <mysqld_error.h> |
342 | +#include <mysql/client_plugin.h> |
343 | +#include "kerberos_common.h" |
344 | + |
345 | +#define KERBEROS_UNKNOWN_ERROR "HY000" |
346 | +#define KERBEROS_OUTOFMEMORY_ERROR "HY001" |
347 | +#define KERBEROS_NET_ERROR_ON_WRITE "08S01" |
348 | +#define KERBEROS_NET_READ_ERROR "08S01" |
349 | + |
350 | +/** |
351 | + * set client error message |
352 | + */ |
353 | +static void set_krb_client_auth_error(MYSQL *, int, const char *, const char *, ...); |
354 | + |
355 | +#ifdef _WIN32 |
356 | +static int sspi_kerberos_auth_client(const char *spn, MYSQL *mysql, |
357 | + MYSQL_PLUGIN_VIO *vio) |
358 | +{ |
359 | + int read_len= 0; |
360 | + int max_token_sz= SSPI_MAX_TOKEN_SIZE; |
361 | + int ret= 0; /* return code */ |
362 | + const char *err_msg= NULL; |
363 | + /* SSPI related */ |
364 | + BOOL have_ctxt= FALSE; |
365 | + BOOL have_cred= FALSE; |
366 | + BOOL have_input= FALSE; |
367 | + BOOL context_established= FALSE; |
368 | + ULONG attribs= 0; |
369 | + TimeStamp lifetime; |
370 | + |
371 | + SECURITY_STATUS ss; |
372 | + CredHandle cred_handle; /* credential handle */ |
373 | + CtxtHandle ctxt_handle; /* security context */ |
374 | + |
375 | + SecPkgInfo *sec_pkg_info; /* package information */ |
376 | + SecBufferDesc input_buf_desc; |
377 | + SecBuffer input_buf; |
378 | + SecBufferDesc output_buf_desc; |
379 | + SecBuffer output_buf; |
380 | + PBYTE output_con; |
381 | + |
382 | + /* query package information */ |
383 | + ss= QuerySecurityPackageInfo(SECURITY_PACKAGE_NAME, &sec_pkg_info); |
384 | + if (ss == SEC_E_OK) |
385 | + { |
386 | + max_token_sz= sec_pkg_info->cbMaxToken; |
387 | + } |
388 | + |
389 | + /* allocate memory */ |
390 | + output_con= (PBYTE) calloc(max_token_sz, sizeof(BYTE)); |
391 | + if (!output_con) |
392 | + { |
393 | + set_krb_client_auth_error(mysql, ER_OUTOFMEMORY, \ |
394 | + KERBEROS_OUTOFMEMORY_ERROR, \ |
395 | + "Kerberos: insufficient memory to allocate for output token buffer."); |
396 | + return CR_ERROR; |
397 | + } |
398 | + |
399 | + /* acquire credentials */ |
400 | + ss= AcquireCredentialsHandle( |
401 | + NULL, |
402 | + SECURITY_PACKAGE_NAME, |
403 | + SECPKG_CRED_OUTBOUND, |
404 | + NULL, |
405 | + NULL, |
406 | + NULL, |
407 | + NULL, |
408 | + &cred_handle, |
409 | + &lifetime); |
410 | + if (SEC_ERROR(ss)) |
411 | + { |
412 | + err_msg= error_msg(ss); |
413 | + set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR, |
414 | + KERBEROS_UNKNOWN_ERROR, err_msg); |
415 | + return CR_ERROR; |
416 | + } |
417 | + else |
418 | + { |
419 | + have_cred= TRUE; |
420 | + } |
421 | + |
422 | + /* prepare input buffer */ |
423 | + input_buf_desc.ulVersion= SECBUFFER_VERSION; |
424 | + input_buf_desc.cBuffers= 1; |
425 | + input_buf_desc.pBuffers= &input_buf; |
426 | + input_buf.BufferType= SECBUFFER_TOKEN; |
427 | + input_buf.cbBuffer= 0; |
428 | + input_buf.pvBuffer= NULL; |
429 | + |
430 | + /* prepare output buffer */ |
431 | + output_buf_desc.ulVersion= SECBUFFER_VERSION; |
432 | + output_buf_desc.cBuffers= 1; |
433 | + output_buf_desc.pBuffers= &output_buf; |
434 | + |
435 | + output_buf.BufferType= SECBUFFER_TOKEN; |
436 | + output_buf.cbBuffer= max_token_sz; |
437 | + output_buf.pvBuffer= output_con; |
438 | + |
439 | + do |
440 | + { |
441 | + ss= InitializeSecurityContext( |
442 | + &cred_handle, /* credential handle */ |
443 | + have_ctxt?&ctxt_handle:NULL, /* context handle */ |
444 | + (SEC_CHAR *) spn, /* target principal name */ |
445 | + 0, /* no special attributes req-ed*/ |
446 | + 0, /* reserved */ |
447 | + SECURITY_NATIVE_DREP, |
448 | + have_input ? &input_buf_desc : NULL, |
449 | + 0, /* reserved */ |
450 | + &ctxt_handle, |
451 | + &output_buf_desc, |
452 | + &attribs, |
453 | + &lifetime); |
454 | + /* reset used flag */ |
455 | + have_input= FALSE; |
456 | + |
457 | + if (output_buf.cbBuffer) |
458 | + { |
459 | + /* send credential to server */ |
460 | + if (vio->write_packet(vio, |
461 | + (const unsigned char *) output_buf.pvBuffer, |
462 | + output_buf.cbBuffer)) |
463 | + { |
464 | + ret= CR_ERROR; |
465 | + set_krb_client_auth_error(mysql, ER_NET_ERROR_ON_WRITE, |
466 | + KERBEROS_NET_ERROR_ON_WRITE, |
467 | + "Kerberos: fail send credentials to server."); |
468 | + goto cleanup; |
469 | + } |
470 | + } |
471 | + |
472 | + if (SEC_ERROR(ss)) |
473 | + { |
474 | + err_msg= error_msg(ss); |
475 | + ret= CR_ERROR; |
476 | + set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR, |
477 | + KERBEROS_UNKNOWN_ERROR, err_msg); |
478 | + goto cleanup; |
479 | + } |
480 | + |
481 | + have_ctxt= TRUE; |
482 | + |
483 | + if ((ss == SEC_I_COMPLETE_NEEDED) || |
484 | + (ss == SEC_I_COMPLETE_AND_CONTINUE)) |
485 | + { |
486 | + ss= CompleteAuthToken(&ctxt_handle, &output_buf_desc); |
487 | + |
488 | + if (SEC_ERROR(ss)) |
489 | + { |
490 | + err_msg= error_msg(ss); |
491 | + ret= CR_ERROR; |
492 | + set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR, |
493 | + KERBEROS_UNKNOWN_ERROR, err_msg); |
494 | + goto cleanup; |
495 | + } |
496 | + } |
497 | + |
498 | + context_established= !((ss == SEC_I_CONTINUE_NEEDED) || |
499 | + (ss == SEC_I_COMPLETE_AND_CONTINUE)); |
500 | + if (!context_established) |
501 | + { |
502 | + read_len= vio->read_packet(vio, (unsigned char **) &input_buf.pvBuffer); |
503 | + if (read_len < 0) |
504 | + { |
505 | + ret= CR_ERROR; |
506 | + set_krb_client_auth_error(mysql, ER_NET_READ_ERROR, |
507 | + KERBEROS_NET_READ_ERROR, |
508 | + "Kerberos: fail to read credential from server."); |
509 | + goto cleanup; |
510 | + } |
511 | + else |
512 | + { |
513 | + input_buf.cbBuffer= read_len; |
514 | + have_input= TRUE; |
515 | + } |
516 | + } |
517 | + |
518 | + output_buf.cbBuffer= max_token_sz; |
519 | + } while (!context_established); |
520 | + |
521 | + ret= CR_OK; |
522 | + |
523 | +cleanup: |
524 | + /* free dynamic memory */ |
525 | + if (have_ctxt) DeleteSecurityContext(&ctxt_handle); |
526 | + free(output_con); |
527 | + ss= FreeContextBuffer(sec_pkg_info); |
528 | + if (ss != SEC_E_OK) |
529 | + { |
530 | + set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR, |
531 | + KERBEROS_UNKNOWN_ERROR, |
532 | + "Kerberos: fail to free SecurityPackageInfo object."); |
533 | + } |
534 | + |
535 | + return ret; |
536 | +} |
537 | +#else /* !_WIN32 */ |
538 | +static int gssapi_kerberos_auth_client(const char *spn, MYSQL *mysql, |
539 | + MYSQL_PLUGIN_VIO *vio) |
540 | +{ |
541 | + int r_len= 0; /* packet read length */ |
542 | + int context_established= 0; /* indicate ctxt avail */ |
543 | + int rc= CR_OK; |
544 | + int have_cred= FALSE; |
545 | + int have_ctxt= FALSE; |
546 | + int have_name= FALSE; |
547 | + const char *err_msg= NULL; |
548 | + /* GSSAPI related fields */ |
549 | + OM_uint32 major= 0, minor= 0; |
550 | + gss_name_t service_name; |
551 | + gss_ctx_id_t ctxt; |
552 | + gss_cred_id_t cred= GSS_C_NO_CREDENTIAL; /* use default credential */ |
553 | + gss_buffer_desc spn_buf, input, output; |
554 | + |
555 | + /* import principal from plain text */ |
556 | + /* initialize plain text service principal name */ |
557 | + spn_buf.length= strlen(spn); |
558 | + spn_buf.value= (void *) spn; |
559 | + /* import service principal */ |
560 | + major= gss_import_name(&minor, |
561 | + &spn_buf, |
562 | + (gss_OID) gss_nt_user_name, |
563 | + &service_name); |
564 | + /* gss_import_name error checking */ |
565 | + if (GSS_ERROR(major)) |
566 | + { |
567 | + err_msg= error_msg(major, minor); |
568 | + rc= CR_ERROR; |
569 | + set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR, |
570 | + KERBEROS_UNKNOWN_ERROR, err_msg); |
571 | + goto cleanup; |
572 | + } |
573 | + have_name= TRUE; |
574 | + |
575 | + /* initial context */ |
576 | + ctxt= GSS_C_NO_CONTEXT; |
577 | + input.length= 0; |
578 | + input.value= NULL; |
579 | + |
580 | + while (!context_established) |
581 | + { |
582 | + major= gss_init_sec_context(&minor, |
583 | + cred, |
584 | + &ctxt, |
585 | + service_name, |
586 | + GSS_C_NO_OID, /* for an impl-spec mech */ |
587 | + 0, /* no flags requested */ |
588 | + 0, /* request default time */ |
589 | + GSS_C_NO_CHANNEL_BINDINGS, |
590 | + &input, /* token input */ |
591 | + NULL, /* actual mech */ |
592 | + &output, /* token output */ |
593 | + NULL, /* actual flags */ |
594 | + NULL); /* actual valid time */ |
595 | + |
596 | + if (output.length) |
597 | + { |
598 | + /* send credential */ |
599 | + if (vio->write_packet(vio, output.value, output.length)) |
600 | + { |
601 | + gss_release_buffer(&minor, &output); |
602 | + rc= CR_ERROR; |
603 | + set_krb_client_auth_error(mysql, ER_NET_ERROR_ON_WRITE, |
604 | + KERBEROS_NET_ERROR_ON_WRITE, |
605 | + "Kerberos: fail to send credential to server."); |
606 | + goto cleanup; |
607 | + } |
608 | + gss_release_buffer(&minor, &output); |
609 | + } |
610 | + |
611 | + if (GSS_ERROR(major)) |
612 | + { |
613 | + /* fatal error */ |
614 | + if (ctxt != GSS_C_NO_CONTEXT) |
615 | + { |
616 | + gss_delete_sec_context(&minor, |
617 | + &ctxt, |
618 | + GSS_C_NO_BUFFER); |
619 | + } |
620 | + err_msg= error_msg(major, minor); |
621 | + rc= CR_ERROR; |
622 | + set_krb_client_auth_error(mysql, ER_UNKNOWN_ERROR, |
623 | + KERBEROS_UNKNOWN_ERROR, err_msg); |
624 | + goto cleanup; |
625 | + } |
626 | + |
627 | + if (major & GSS_S_CONTINUE_NEEDED) |
628 | + { |
629 | + r_len= vio->read_packet(vio, (unsigned char **) &input.value); |
630 | + if (r_len < 0) |
631 | + { |
632 | + rc= CR_ERROR; |
633 | + set_krb_client_auth_error(mysql, ER_NET_READ_ERROR, |
634 | + KERBEROS_NET_READ_ERROR, |
635 | + "Error read credential packet from server."); |
636 | + goto cleanup; |
637 | + } |
638 | + } |
639 | + else |
640 | + { |
641 | + context_established= 1; |
642 | + } |
643 | + } |
644 | + |
645 | +cleanup: |
646 | + if (have_name) gss_release_name(&minor, &service_name); |
647 | + if (have_ctxt) gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER); |
648 | + if (have_cred) gss_release_cred(&minor, &cred); |
649 | + |
650 | + return rc; |
651 | +} |
652 | +#endif /* _WIN32 */ |
653 | + |
654 | +/** |
655 | + * The main client function of the Kerberos plugin. |
656 | + */ |
657 | +static int kerberos_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) |
658 | +{ |
659 | + int r_len= 0; |
660 | + int rc= CR_OK; |
661 | + /* service principal name */ |
662 | + char *spn= NULL; |
663 | + char *spn_buff= (char *) malloc(PRINCIPAL_NAME_LEN); |
664 | + |
665 | + /* read from server for service principal name */ |
666 | + r_len= vio->read_packet(vio, (unsigned char **) &spn); |
667 | + if (r_len < 0) |
668 | + { |
669 | + set_krb_client_auth_error(mysql, ER_NET_READ_ERROR, |
670 | + KERBEROS_NET_READ_ERROR, |
671 | + "Kerberos: fail to read service principal name."); |
672 | + |
673 | + return CR_ERROR; |
674 | + } |
675 | + strncpy(spn_buff, spn, PRINCIPAL_NAME_LEN); |
676 | + |
677 | + rc = |
678 | +#ifdef _WIN32 |
679 | + sspi_kerberos_auth_client((const char *) spn_buff, mysql, vio); |
680 | +#else /* !_WIN32 */ |
681 | + gssapi_kerberos_auth_client((const char *) spn_buff, mysql, vio); |
682 | +#endif /* _WIN32 */ |
683 | + |
684 | + free(spn_buff); |
685 | + return rc; |
686 | +} |
687 | + |
688 | +/** |
689 | + * set client error message. |
690 | + * Param: |
691 | + * mysql connection handle |
692 | + * errno extended error number |
693 | + * format error message template |
694 | + * ... variable argument list |
695 | + */ |
696 | +static void set_krb_client_auth_error(MYSQL *mysql, int errcode, |
697 | + const char *sqlstate, const char *format, ...) |
698 | +{ |
699 | + NET *net= &mysql->net; |
700 | + va_list args; |
701 | + |
702 | + net->last_errno= errcode; |
703 | + va_start(args, format); |
704 | + vsnprintf(net->last_error, sizeof(net->last_error) - 1, |
705 | + format, args); |
706 | + va_end(args); |
707 | + memcpy(net->sqlstate, sqlstate, sizeof(net->sqlstate)); |
708 | +} |
709 | + |
710 | +/* register client plugin */ |
711 | +mysql_declare_client_plugin(AUTHENTICATION) |
712 | + "kerberos_client", |
713 | + "Shuang Qiu", |
714 | + "Kerberos based authentication", |
715 | + {0, 1, 0}, |
716 | + "BSD", |
717 | + NULL, |
718 | + NULL, |
719 | + NULL, |
720 | + NULL, |
721 | + kerberos_auth_client |
722 | +mysql_end_client_plugin; |
723 | |
724 | === added file 'plugin/auth_kerberos/kerberos_common.c' |
725 | --- plugin/auth_kerberos/kerberos_common.c 1970-01-01 00:00:00 +0000 |
726 | +++ plugin/auth_kerberos/kerberos_common.c 2015-10-18 04:21:02 +0000 |
727 | @@ -0,0 +1,152 @@ |
728 | +/* Copyright (c) 2015, Shuang Qiu |
729 | + All rights reserved. |
730 | + |
731 | + Redistribution and use in source and binary forms, with or without |
732 | + modification, are permitted provided that the following conditions are met: |
733 | + |
734 | + 1. Redistributions of source code must retain the above copyright notice, |
735 | + this list of conditions and the following disclaimer. |
736 | + |
737 | + 2. Redistributions in binary form must reproduce the above copyright notice, |
738 | + this list of conditions and the following disclaimer in the documentation |
739 | + and/or other materials provided with the distribution. |
740 | + |
741 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
742 | + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
743 | + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
744 | + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
745 | + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
746 | + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
747 | + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
748 | + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
749 | + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
750 | + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
751 | + POSSIBILITY OF SUCH DAMAGE. |
752 | +*/ |
753 | + |
754 | +/** |
755 | + @file |
756 | + |
757 | + Kerberos authentication utilities |
758 | + |
759 | + Utility functions are defined. |
760 | +*/ |
761 | +#include "kerberos_common.h" |
762 | + |
763 | +#ifdef _WIN32 |
764 | +/* translate status code to error message */ |
765 | +const char *error_msg(SECURITY_STATUS ss) |
766 | +{ |
767 | + const char *err_msg= NULL; |
768 | + |
769 | + switch (ss) |
770 | + { |
771 | + case SEC_E_INSUFFICIENT_MEMORY: |
772 | + err_msg= "Kerberos: insufficient memory to complete the request."; |
773 | + break; |
774 | + case SEC_E_INTERNAL_ERROR: |
775 | + err_msg= "Kerberos: an internal error occurs."; |
776 | + break; |
777 | + case SEC_E_NO_CREDENTIALS: |
778 | + err_msg= "Kerberos: no credentials are available."; |
779 | + break; |
780 | + case SEC_E_NOT_OWNER: |
781 | + err_msg= "Kerberos: necessary credentials to " |
782 | + "acquire the new credential are not found."; |
783 | + break; |
784 | + case SEC_E_UNKNOWN_CREDENTIALS: |
785 | + err_msg= "Kerberos: credentials supplied were not recognized."; |
786 | + break; |
787 | + case SEC_E_INVALID_HANDLE: |
788 | + err_msg= "Kerberos: an invalid handle is provided."; |
789 | + break; |
790 | + case SEC_E_INVALID_TOKEN: |
791 | + err_msg= "Kerberos: an invalid token is provided."; |
792 | + break; |
793 | + case SEC_E_LOGON_DENIED: |
794 | + err_msg= "Kerberos: logon as specified principal failed."; |
795 | + break; |
796 | + case SEC_E_NO_AUTHENTICATING_AUTHORITY: |
797 | + err_msg= "Kerberos: the domain name is invalid."; |
798 | + break; |
799 | + case SEC_E_TARGET_UNKNOWN: |
800 | + err_msg= "Kerberos: the target principal is unknown."; |
801 | + break; |
802 | + case SEC_E_WRONG_PRINCIPAL: |
803 | + err_msg= "Kerberos: the target principle does not " |
804 | + "match with expected one."; |
805 | + break; |
806 | + case SEC_E_TIME_SKEW: |
807 | + err_msg= "Kerberos: a time skew is detected."; |
808 | + break; |
809 | + default: |
810 | + err_msg = "Kerberos: unknown error."; |
811 | + break; |
812 | + } |
813 | + |
814 | + return err_msg; |
815 | +} |
816 | +#else /* _WIN32 */ |
817 | +#define ERR_MSG_BUF_LEN 1024 |
818 | +static char err_msg_buf[ERR_MSG_BUF_LEN]; |
819 | + |
820 | +const char *error_msg(OM_uint32 major, OM_uint32 minor __attribute__((unused))) |
821 | +{ |
822 | + const char *err_msg= NULL; |
823 | + |
824 | + switch (major) |
825 | + { |
826 | + case GSS_S_BAD_NAMETYPE: |
827 | + case GSS_S_BAD_NAME: |
828 | + err_msg= "Kerberos: input name could not be recognied."; |
829 | + break; |
830 | + case GSS_S_BAD_MECH: |
831 | + err_msg= "Kerberos: a bad mechanism is requested."; |
832 | + break; |
833 | + case GSS_S_CREDENTIALS_EXPIRED: |
834 | + err_msg = "Kerberos: the credentials could not be acquired " |
835 | + "for expiration."; |
836 | + break; |
837 | + case GSS_S_NO_CRED: |
838 | + err_msg = "Kerberos: no credentials were found for the specified name."; |
839 | + break; |
840 | + case GSS_S_DEFECTIVE_TOKEN: |
841 | + err_msg = "Kerberos: consistency checks performed on " |
842 | + "the input token failed."; |
843 | + break; |
844 | + case GSS_S_DEFECTIVE_CREDENTIAL: |
845 | + err_msg = "Kerberos: consistency checks performed on " |
846 | + "the credential failed."; |
847 | + break; |
848 | + case GSS_S_BAD_BINDINGS: |
849 | + err_msg = "Kerberos: the input token contains " |
850 | + "different channel bindings as specified."; |
851 | + break; |
852 | + case GSS_S_NO_CONTEXT: |
853 | + err_msg = "Kerberos: the supplied context handle is invalid."; |
854 | + break; |
855 | + case GSS_S_BAD_SIG: |
856 | + err_msg = "Kerberos: input token contains an invalid MIC."; |
857 | + break; |
858 | + case GSS_S_OLD_TOKEN: |
859 | + err_msg = "Kerberos: input token is too old."; |
860 | + break; |
861 | + case GSS_S_DUPLICATE_TOKEN: |
862 | + err_msg = "Kerberos: input token is a duplicate of a token " |
863 | + "already processed."; |
864 | + break; |
865 | + case GSS_S_FAILURE: |
866 | + snprintf(err_msg_buf, ERR_MSG_BUF_LEN, |
867 | + "Kerberos: undefined Kerberos error. " |
868 | + "Make sure a valid ticket-grant-ticket is acquired " |
869 | + "and refer minor error code %d for details.", minor); |
870 | + err_msg = err_msg_buf; |
871 | + break; |
872 | + default: |
873 | + err_msg = "Kerberos: unknown error."; |
874 | + break; |
875 | + } |
876 | + |
877 | + return err_msg; |
878 | +} |
879 | +#endif /* _WIN32 */ |
880 | |
881 | === added file 'plugin/auth_kerberos/kerberos_common.h' |
882 | --- plugin/auth_kerberos/kerberos_common.h 1970-01-01 00:00:00 +0000 |
883 | +++ plugin/auth_kerberos/kerberos_common.h 2015-10-18 04:21:02 +0000 |
884 | @@ -0,0 +1,89 @@ |
885 | +/* Copyright (c) 2015, Shuang Qiu |
886 | + All rights reserved. |
887 | + |
888 | + Redistribution and use in source and binary forms, with or without |
889 | + modification, are permitted provided that the following conditions are met: |
890 | + |
891 | + 1. Redistributions of source code must retain the above copyright notice, |
892 | + this list of conditions and the following disclaimer. |
893 | + |
894 | + 2. Redistributions in binary form must reproduce the above copyright notice, |
895 | + this list of conditions and the following disclaimer in the documentation |
896 | + and/or other materials provided with the distribution. |
897 | + |
898 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
899 | + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
900 | + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
901 | + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
902 | + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
903 | + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
904 | + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
905 | + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
906 | + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
907 | + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
908 | + POSSIBILITY OF SUCH DAMAGE. |
909 | +*/ |
910 | + |
911 | +/** |
912 | + @file |
913 | + |
914 | + Kerberos authentication utilities |
915 | + |
916 | + Utility functions are declared. |
917 | +*/ |
918 | +#ifndef KERBEROS_COMMON_H |
919 | +#define KERBEROS_COMMON_H |
920 | + |
921 | +#include <my_sys.h> |
922 | +#include <my_global.h> |
923 | +#include <mysql.h> |
924 | +#include <mysql/plugin_auth_common.h> |
925 | +#include <ctype.h> |
926 | +#include <string.h> |
927 | + |
928 | +/* global define directives */ |
929 | +#define PRINCIPAL_NAME_LEN 256 /* TODO need a reference */ |
930 | + |
931 | +#define MF_ERROR MYF(0) |
932 | +#define MF_WARNING MYF(1) |
933 | + |
934 | +/* platform dependent header */ |
935 | +#ifdef _WIN32 |
936 | + /* on Windows platform, SSPI is used to perform the authentication */ |
937 | + #include <windows.h> |
938 | + |
939 | + #define SECURITY_WIN32 /* User-mode SSPI application */ |
940 | + #include <Security.h> |
941 | + #include <SecExt.h> |
942 | + #include <sspi.h> |
943 | +#else /* !_WIN32 */ |
944 | + /** |
945 | + * on other platform, make sure the Kerberos environment is pre-configured |
946 | + * GSSAPI is used for inter-operation purpose between Windows platform |
947 | + */ |
948 | + #include <gssapi/gssapi.h> |
949 | + #include <gssapi/gssapi_generic.h> |
950 | + #include <gssapi/gssapi_krb5.h> |
951 | +#endif /* _WIN32 */ |
952 | + |
953 | +/* platform dependent define directives */ |
954 | +#ifdef _WIN32 |
955 | + #define UNUSED(x) __pragma(warning(suppress:4100)) x /* warns suppressor */ |
956 | +#else |
957 | + #define UNUSED(x) x __attribute__((unused)) |
958 | +#endif /* _WIN32 */ |
959 | + |
960 | +#ifdef _WIN32 |
961 | + #define SECURITY_PACKAGE_NAME "Kerberos" |
962 | + #define SSPI_MAX_TOKEN_SIZE 12000 |
963 | + |
964 | + #define SEC_ERROR(ss) ((ss) < 0) |
965 | + |
966 | + /* translate SECURITY_STATUS to error text */ |
967 | + const char *error_msg(SECURITY_STATUS); |
968 | +#else /* _WIN32 */ |
969 | + /* translate symbolic error number to text error message */ |
970 | + const char *error_msg(OM_uint32, OM_uint32); |
971 | +#endif /* _WIN32 */ |
972 | + |
973 | +#endif /* KERBEROS_COMMON_H */ |
974 | |
975 | === added file 'plugin/auth_kerberos/kerberos_server.c' |
976 | --- plugin/auth_kerberos/kerberos_server.c 1970-01-01 00:00:00 +0000 |
977 | +++ plugin/auth_kerberos/kerberos_server.c 2015-10-18 04:21:02 +0000 |
978 | @@ -0,0 +1,725 @@ |
979 | +/* Copyright (c) 2015, Shuang Qiu |
980 | + All rights reserved. |
981 | + |
982 | + Redistribution and use in source and binary forms, with or without |
983 | + modification, are permitted provided that the following conditions are met: |
984 | + |
985 | + 1. Redistributions of source code must retain the above copyright notice, |
986 | + this list of conditions and the following disclaimer. |
987 | + |
988 | + 2. Redistributions in binary form must reproduce the above copyright notice, |
989 | + this list of conditions and the following disclaimer in the documentation |
990 | + and/or other materials provided with the distribution. |
991 | + |
992 | + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
993 | + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
994 | + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
995 | + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
996 | + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
997 | + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
998 | + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
999 | + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
1000 | + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
1001 | + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
1002 | + POSSIBILITY OF SUCH DAMAGE. |
1003 | +*/ |
1004 | + |
1005 | +/** |
1006 | + @file |
1007 | + |
1008 | + Kerberos server authentication plugin |
1009 | + |
1010 | + kerberos_server is a general purpose server authentication plugin, it |
1011 | + authenticates user against Kerberos principal. |
1012 | + |
1013 | + This is the server side implementation. |
1014 | +*/ |
1015 | +#include <mysql/plugin_auth.h> |
1016 | + |
1017 | +#include "kerberos_common.h" |
1018 | + |
1019 | +#define KRB_SERVER_AUTH_ERROR 1 |
1020 | + |
1021 | +/* plugin global variables */ |
1022 | +char *kerberos_principal_name; /* system-wise declaration for spn */ |
1023 | +/** |
1024 | + * underlying storage for Kerberos service principal name system variable |
1025 | + */ |
1026 | +static char kerberos_spn_storage[PRINCIPAL_NAME_LEN]; |
1027 | + |
1028 | +#ifdef _WIN32 |
1029 | +static int sspi_kerberos_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { |
1030 | + int read_len= 0; |
1031 | + int max_token_sz= SSPI_MAX_TOKEN_SIZE; |
1032 | + int ret= CR_OK; /* return code */ |
1033 | + const char *err_msg= NULL; /* error message */ |
1034 | + /* SSPI related fields */ |
1035 | + SECURITY_STATUS ss = 0; |
1036 | + BOOL have_ctxt= FALSE; |
1037 | + BOOL have_cred= FALSE; |
1038 | + BOOL context_established= FALSE; |
1039 | + ULONG attribs= 0; |
1040 | + TimeStamp lifetime; |
1041 | + |
1042 | + CredHandle cred_handle; /* credential handle */ |
1043 | + CtxtHandle ctxt_handle; /* context handle */ |
1044 | + SecPkgContext_NativeNames native_names; |
1045 | + |
1046 | + SecPkgInfo *sec_pkg_info; /* Packet information */ |
1047 | + SecBufferDesc input_buf_desc; /* Input token */ |
1048 | + SecBuffer input_buf; |
1049 | + SecBufferDesc output_buf_desc; /* Output token */ |
1050 | + SecBuffer output_buf; |
1051 | + PBYTE output_con; |
1052 | + |
1053 | + /* query packet information */ |
1054 | + ss= QuerySecurityPackageInfo(SECURITY_PACKAGE_NAME, &sec_pkg_info); |
1055 | + |
1056 | + if (ss != SEC_E_OK) |
1057 | + { |
1058 | + /* error query package information */ |
1059 | + my_error(KRB_SERVER_AUTH_ERROR, MF_WARNING, |
1060 | + "Kerberos: fail to get maximum token size, use default: %d.", |
1061 | + max_token_sz); |
1062 | + } |
1063 | + else |
1064 | + { |
1065 | + max_token_sz= sec_pkg_info->cbMaxToken; |
1066 | + } |
1067 | + |
1068 | + /* allocate memory */ |
1069 | + output_con= (PBYTE) calloc(max_token_sz, sizeof(BYTE)); |
1070 | + if (!output_con) |
1071 | + { |
1072 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, "Kerberos: no more memory."); |
1073 | + return CR_ERROR; |
1074 | + } |
1075 | + |
1076 | + /* acquire initial credential */ |
1077 | + ss= AcquireCredentialsHandle(NULL, /* use default credential */ |
1078 | + SECURITY_PACKAGE_NAME, |
1079 | + SECPKG_CRED_INBOUND, /* cred usage */ |
1080 | + NULL, /* locally unique id */ |
1081 | + NULL, /* use default credential */ |
1082 | + NULL, /* get key func */ |
1083 | + NULL, /* get key argument func */ |
1084 | + &cred_handle, |
1085 | + &lifetime); |
1086 | + |
1087 | + /* AcquireCredentialsHandle error checking */ |
1088 | + if (SEC_ERROR(ss)) |
1089 | + { |
1090 | + err_msg= error_msg(ss); |
1091 | + ret= CR_ERROR; |
1092 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg); |
1093 | + goto cleanup; |
1094 | + } |
1095 | + else |
1096 | + { |
1097 | + have_cred= TRUE; |
1098 | + } |
1099 | + |
1100 | + /* prepare input buffer */ |
1101 | + input_buf_desc.ulVersion= SECBUFFER_VERSION; |
1102 | + input_buf_desc.cBuffers= 1; |
1103 | + input_buf_desc.pBuffers= &input_buf; |
1104 | + |
1105 | + input_buf.cbBuffer= 0; |
1106 | + input_buf.BufferType= SECBUFFER_TOKEN; |
1107 | + input_buf.pvBuffer= NULL; |
1108 | + |
1109 | + /* prepare output buffer */ |
1110 | + output_buf_desc.ulVersion= SECBUFFER_VERSION; |
1111 | + output_buf_desc.cBuffers= 1; |
1112 | + output_buf_desc.pBuffers= &output_buf; |
1113 | + |
1114 | + output_buf.BufferType= SECBUFFER_TOKEN; |
1115 | + output_buf.cbBuffer= max_token_sz; |
1116 | + output_buf.pvBuffer= output_con; |
1117 | + |
1118 | + do |
1119 | + { |
1120 | + /* read credential from client */ |
1121 | + read_len= vio->read_packet(vio, (unsigned char **) &input_buf.pvBuffer); |
1122 | + if (read_len < 0) { |
1123 | + ret= CR_ERROR; |
1124 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1125 | + "Kerberos: fail read client credentials."); |
1126 | + goto cleanup; |
1127 | + } |
1128 | + input_buf.cbBuffer= read_len; |
1129 | + |
1130 | + ss= AcceptSecurityContext( |
1131 | + &cred_handle, /* credential */ |
1132 | + have_ctxt ? &ctxt_handle : NULL, |
1133 | + &input_buf_desc, /* input credentials */ |
1134 | + attribs, |
1135 | + SECURITY_NATIVE_DREP, |
1136 | + &ctxt_handle, /* secure context */ |
1137 | + &output_buf_desc, /* output credentials */ |
1138 | + &attribs, |
1139 | + &lifetime); |
1140 | + |
1141 | + /* AcceptSecurityContext error checking */ |
1142 | + if (SEC_ERROR(ss)) |
1143 | + { |
1144 | + err_msg= error_msg(ss); |
1145 | + ret= CR_ERROR; |
1146 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg); |
1147 | + goto cleanup; |
1148 | + } |
1149 | + /* Security context established (partially) */ |
1150 | + have_ctxt= TRUE; |
1151 | + |
1152 | + if (output_buf.cbBuffer) |
1153 | + { |
1154 | + /* write credential packet */ |
1155 | + if (vio->write_packet(vio, |
1156 | + (const unsigned char *) output_buf.pvBuffer, |
1157 | + output_buf.cbBuffer)) |
1158 | + { |
1159 | + ret= CR_ERROR; |
1160 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1161 | + "Kerberos: fail send crednetials to client."); |
1162 | + goto cleanup; |
1163 | + } |
1164 | + } |
1165 | + output_buf.cbBuffer= max_token_sz; |
1166 | + |
1167 | + if ((ss == SEC_I_COMPLETE_NEEDED) || |
1168 | + (ss == SEC_I_COMPLETE_AND_CONTINUE)) |
1169 | + { |
1170 | + ss= CompleteAuthToken(&ctxt_handle, &output_buf_desc); |
1171 | + if (SEC_ERROR(ss)) |
1172 | + { |
1173 | + err_msg= error_msg(ss); |
1174 | + ret= CR_ERROR; |
1175 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg); |
1176 | + goto cleanup; |
1177 | + } |
1178 | + } |
1179 | + |
1180 | + context_established= !((ss == SEC_I_CONTINUE_NEEDED) || |
1181 | + (ss == SEC_I_COMPLETE_AND_CONTINUE)); |
1182 | + } while (!context_established); |
1183 | + |
1184 | + /* check principal name */ |
1185 | + ss= QueryContextAttributes(&ctxt_handle, SECPKG_ATTR_NATIVE_NAMES, &native_names); |
1186 | + |
1187 | + if (SEC_ERROR(ss)) |
1188 | + { |
1189 | + err_msg= error_msg(ss); |
1190 | + ret= CR_ERROR; |
1191 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg); |
1192 | + goto cleanup; |
1193 | + } |
1194 | + |
1195 | + if (strcmp(native_names.sClientName, info->auth_string)) |
1196 | + { |
1197 | + ret= CR_ERROR; |
1198 | + } |
1199 | + else |
1200 | + { |
1201 | + ret= CR_OK; |
1202 | + } |
1203 | + |
1204 | +cleanup: |
1205 | + /* free dynamic memory */ |
1206 | + if (have_ctxt) DeleteSecurityContext(&ctxt_handle); |
1207 | + if (have_cred) FreeCredentialsHandle(&cred_handle); |
1208 | + free(output_con); |
1209 | + ss= FreeContextBuffer(sec_pkg_info); |
1210 | + if (ss != SEC_E_OK) { |
1211 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1212 | + "Kerberos: fail to free SecurityPackageInfo object."); |
1213 | + /* return code is not modified */ |
1214 | + } |
1215 | + |
1216 | + return ret; |
1217 | +} |
1218 | +#else /* !_WIN32 */ |
1219 | +static int gssapi_kerberos_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { |
1220 | + int r_len= 0; /* packet read length */ |
1221 | + int rc= CR_OK; /* return code */ |
1222 | + int have_cred= FALSE; |
1223 | + int have_ctxt= FALSE; |
1224 | + const char *err_msg= NULL; /* error message text */ |
1225 | + /* GSSAPI related fields */ |
1226 | + OM_uint32 major= 0, minor= 0, flags= 0; |
1227 | + gss_cred_id_t cred; /* credential identifier */ |
1228 | + gss_ctx_id_t ctxt; /* context identifier */ |
1229 | + gss_name_t client_name, service_name; |
1230 | + gss_buffer_desc principal_name_buf, client_name_buf, input, output; |
1231 | + |
1232 | + /* import service principal from plain text */ |
1233 | + /* initialize principal name */ |
1234 | + principal_name_buf.length= strlen(kerberos_principal_name); |
1235 | + principal_name_buf.value= kerberos_principal_name; |
1236 | + major= gss_import_name(&minor, |
1237 | + &principal_name_buf, |
1238 | + (gss_OID) gss_nt_user_name, |
1239 | + &service_name); |
1240 | + /* gss_import_name error checking */ |
1241 | + if (GSS_ERROR(major)) |
1242 | + { |
1243 | + err_msg= error_msg(major, minor); |
1244 | + rc= CR_ERROR; |
1245 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg); |
1246 | + goto cleanup; |
1247 | + } |
1248 | + |
1249 | + /* server acquires credential */ |
1250 | + major= gss_acquire_cred(&minor, |
1251 | + service_name, |
1252 | + GSS_C_INDEFINITE, /* time_req, ALAP */ |
1253 | + GSS_C_NO_OID_SET, /* desired_mechs */ |
1254 | + GSS_C_ACCEPT, /* cred_usage, ACCEPT only */ |
1255 | + &cred, |
1256 | + NULL, /* actual_mechs */ |
1257 | + NULL); /* time_rec */ |
1258 | + |
1259 | + /* gss_acquire_cred error checking */ |
1260 | + if (GSS_ERROR(major)) |
1261 | + { |
1262 | + err_msg= error_msg(major, minor); |
1263 | + rc= CR_ERROR; |
1264 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg); |
1265 | + goto cleanup; |
1266 | + } |
1267 | + else |
1268 | + { |
1269 | + have_cred= TRUE; |
1270 | + } |
1271 | + |
1272 | + major= gss_release_name(&minor, &service_name); |
1273 | + if (major == GSS_S_BAD_NAME) |
1274 | + { |
1275 | + rc= CR_ERROR; |
1276 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1277 | + "Kerbeos: fail when invoke gss_release_name, no valid name found."); |
1278 | + goto cleanup; |
1279 | + } |
1280 | + |
1281 | + /* accept security context */ |
1282 | + ctxt= GSS_C_NO_CONTEXT; |
1283 | + /* first trial */ |
1284 | + input.length= 0; |
1285 | + input.value= NULL; |
1286 | + do { |
1287 | + /* receive token from peer first */ |
1288 | + r_len= vio->read_packet(vio, (unsigned char **) &input.value); |
1289 | + if (r_len < 0) { |
1290 | + rc= CR_ERROR; |
1291 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1292 | + "Kerberos: fail to read token from client."); |
1293 | + goto cleanup; |
1294 | + } |
1295 | + else |
1296 | + { |
1297 | + /* make length consistent with value */ |
1298 | + input.length= r_len; |
1299 | + } |
1300 | + |
1301 | + major= gss_accept_sec_context(&minor, |
1302 | + &ctxt, /* ctxt handle */ |
1303 | + cred, |
1304 | + &input, /* input buffer */ |
1305 | + GSS_C_NO_CHANNEL_BINDINGS, |
1306 | + &client_name, /* source name */ |
1307 | + NULL, /* mech type */ |
1308 | + &output, /* output buffer */ |
1309 | + &flags, /* return flag */ |
1310 | + NULL, /* time rec */ |
1311 | + NULL); |
1312 | + if (GSS_ERROR(major)) |
1313 | + { |
1314 | + if (ctxt != GSS_C_NO_CONTEXT) |
1315 | + { |
1316 | + gss_delete_sec_context(&minor, |
1317 | + &ctxt, |
1318 | + GSS_C_NO_BUFFER); |
1319 | + } |
1320 | + err_msg= error_msg(major, minor); |
1321 | + rc= CR_ERROR; |
1322 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, err_msg); |
1323 | + goto cleanup; |
1324 | + } |
1325 | + /* security context established (partially) */ |
1326 | + have_ctxt= TRUE; |
1327 | + |
1328 | + /* send token to peer */ |
1329 | + if (output.length) |
1330 | + { |
1331 | + if (vio->write_packet(vio, output.value, output.length)) |
1332 | + { |
1333 | + gss_release_buffer(&minor, &output); |
1334 | + rc= CR_ERROR; |
1335 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1336 | + "Kerberos: fail to send authentication token."); |
1337 | + goto cleanup; |
1338 | + } |
1339 | + gss_release_buffer(&minor, &output); |
1340 | + } |
1341 | + } while (major & GSS_S_CONTINUE_NEEDED); |
1342 | + |
1343 | + /* extrac plain text client name */ |
1344 | + major= gss_display_name(&minor, client_name, &client_name_buf, NULL); |
1345 | + if (major == GSS_S_BAD_NAME) |
1346 | + { |
1347 | + rc= CR_ERROR; |
1348 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1349 | + "Kerberos: fail to display an ill-formed principal name."); |
1350 | + goto cleanup; |
1351 | + } |
1352 | + |
1353 | + /* expected user? */ |
1354 | + if (strncmp(client_name_buf.value, info->auth_string, PRINCIPAL_NAME_LEN)) |
1355 | + { |
1356 | + gss_release_buffer(&minor, &client_name_buf); |
1357 | + rc= CR_ERROR; |
1358 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1359 | + "Kerberos: fail authentication user."); |
1360 | + goto cleanup; |
1361 | + } |
1362 | + gss_release_buffer(&minor, &client_name_buf); |
1363 | + |
1364 | +cleanup: |
1365 | + if (have_ctxt) gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER); |
1366 | + if (have_cred) gss_release_cred(&minor, &cred); |
1367 | + |
1368 | + return rc; |
1369 | +} |
1370 | +#endif /* _WIN32 */ |
1371 | + |
1372 | +/** |
1373 | + * The main server function of the Kerberos plugin. |
1374 | + */ |
1375 | +static int kerberos_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) |
1376 | +{ |
1377 | + size_t p_len= 0; /* length of principal name */ |
1378 | + int rc= CR_OK; /* return code */ |
1379 | + char *principal_name= NULL; |
1380 | + |
1381 | + /* server sends service principal name first. */ |
1382 | + p_len= strlen(kerberos_principal_name); |
1383 | + if (!p_len) |
1384 | + { |
1385 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1386 | + "Kerberos: no principal name specified."); |
1387 | + return CR_ERROR; |
1388 | + } |
1389 | + |
1390 | + if (vio->write_packet(vio, |
1391 | + (unsigned char *) kerberos_principal_name, |
1392 | + (int) p_len) < 0) |
1393 | + { |
1394 | + my_error(KRB_SERVER_AUTH_ERROR, MF_ERROR, |
1395 | + "Kerberos: fail to send service principal name."); |
1396 | + return CR_ERROR; |
1397 | + } |
1398 | + |
1399 | + rc = |
1400 | +#ifdef _WIN32 |
1401 | + sspi_kerberos_auth(vio, info); |
1402 | +#else /* !_WIN32 */ |
1403 | + gssapi_kerberos_auth(vio, info); |
1404 | +#endif /* _WIN32 */ |
1405 | + |
1406 | + free(principal_name); |
1407 | + |
1408 | + return rc; |
1409 | +} |
1410 | + |
1411 | +#ifdef _WIN32 |
1412 | +static BOOL GetLogonSID (PSID *ppsid) |
1413 | +{ |
1414 | + BOOL succ= FALSE; |
1415 | + HANDLE token; |
1416 | + DWORD index; |
1417 | + DWORD length= 0; |
1418 | + PTOKEN_GROUPS ptg= NULL; |
1419 | + |
1420 | + /* Verify the parameter passed in is not NULL. */ |
1421 | + if (ppsid == NULL) |
1422 | + goto cleanup; |
1423 | + |
1424 | + /* Open a handle to the access token for the calling process. */ |
1425 | + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token )) |
1426 | + goto cleanup; |
1427 | + |
1428 | + /* Get required buffer size and allocate the TOKEN_GROUPS buffer. */ |
1429 | + if (!GetTokenInformation( |
1430 | + token, /* handle to the access token */ |
1431 | + TokenGroups, /* get information about the token's groups */ |
1432 | + (LPVOID) ptg, /* pointer to TOKEN_GROUPS buffer */ |
1433 | + 0, /* size of buffer */ |
1434 | + &length /* receives required buffer size */ |
1435 | + )) |
1436 | + { |
1437 | + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) |
1438 | + goto cleanup; |
1439 | + |
1440 | + ptg= (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), |
1441 | + HEAP_ZERO_MEMORY, length); |
1442 | + |
1443 | + if (ptg == NULL) |
1444 | + goto cleanup; |
1445 | + } |
1446 | + |
1447 | + /* Get the token group information from the access token. */ |
1448 | + if (!GetTokenInformation( |
1449 | + token, /* handle to the access token */ |
1450 | + TokenGroups, /* get information about the token's groups */ |
1451 | + (LPVOID) ptg, /* pointer to TOKEN_GROUPS buffer */ |
1452 | + length, /* size of buffer */ |
1453 | + &length /* receives required buffer size */ |
1454 | + )) |
1455 | + { |
1456 | + goto cleanup; |
1457 | + } |
1458 | + |
1459 | + /* Loop through the groups to find the logon SID. */ |
1460 | + for (index= 0; index < ptg->GroupCount; index++) |
1461 | + { |
1462 | + if ((ptg->Groups[index].Attributes & SE_GROUP_LOGON_ID) |
1463 | + == SE_GROUP_LOGON_ID) |
1464 | + { |
1465 | + /* Found the logon SID; make a copy of it. */ |
1466 | + length= GetLengthSid(ptg->Groups[index].Sid); |
1467 | + *ppsid= (PSID) HeapAlloc(GetProcessHeap(), |
1468 | + HEAP_ZERO_MEMORY, length); |
1469 | + if (*ppsid == NULL) |
1470 | + goto cleanup; |
1471 | + if (!CopySid(length, *ppsid, ptg->Groups[index].Sid)) |
1472 | + { |
1473 | + HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid); |
1474 | + goto cleanup; |
1475 | + } |
1476 | + break; |
1477 | + } |
1478 | + } |
1479 | + succ= TRUE; |
1480 | + |
1481 | +cleanup: |
1482 | + /* Free the buffer for the token groups. */ |
1483 | + if (ptg != NULL) |
1484 | + HeapFree(GetProcessHeap(), 0, (LPVOID)ptg); |
1485 | + |
1486 | + return succ; |
1487 | +} |
1488 | + |
1489 | +static VOID FreeLogonSID (PSID *ppsid) |
1490 | +{ |
1491 | + HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid); |
1492 | +} |
1493 | + |
1494 | +static BOOLEAN LogonAsNetworkService(void) |
1495 | +{ |
1496 | + SID sNetServ; |
1497 | + PSID psLogon= NULL; |
1498 | + DWORD szSid= sizeof(sNetServ); |
1499 | + BOOLEAN bRetCode= FALSE; |
1500 | + |
1501 | + if (GetLogonSID(&psLogon) && |
1502 | + CreateWellKnownSid(WinNetworkServiceSid, NULL, &sNetServ, &szSid) && |
1503 | + EqualSid(psLogon, &sNetServ)) |
1504 | + { |
1505 | + bRetCode= TRUE; |
1506 | + } |
1507 | + |
1508 | + if (psLogon) FreeLogonSID(&psLogon); |
1509 | + |
1510 | + return bRetCode; |
1511 | +} |
1512 | + |
1513 | +static int initialize_principal_name(void *unused) |
1514 | +{ |
1515 | + int ret= 0; |
1516 | + ULONG len= sizeof(kerberos_spn_storage); |
1517 | + CHAR *computer_name= NULL; |
1518 | + CHAR *domain_name= NULL; |
1519 | + BOOLEAN api_rc= TRUE; |
1520 | + |
1521 | +#define KRB_AUTH_SERVER_INIT_ERROR(ret, msg) \ |
1522 | + do { ret= -1; \ |
1523 | + my_error(ret, MF_ERROR, msg); \ |
1524 | + } while(0) |
1525 | + |
1526 | + /* principal name has already been set */ |
1527 | + if (kerberos_principal_name && kerberos_principal_name[0]) return ret; |
1528 | + |
1529 | + api_rc= GetUserNameEx(NameUserPrincipal, kerberos_spn_storage, &len); |
1530 | + if (!api_rc) |
1531 | + { |
1532 | + switch (GetLastError()) |
1533 | + { |
1534 | + case ERROR_NO_SUCH_DOMAIN: |
1535 | + ret = CR_ERROR; |
1536 | + my_error(KRB_SERVER_AUTH_ERROR, MF_WARNING, |
1537 | + "Kerberos: the domain controller is not up."); |
1538 | + break; |
1539 | + case ERROR_NONE_MAPPED: |
1540 | + /* cannot find UPN for logon user */ |
1541 | + /* |
1542 | + * If logon sid is NetworkService, a fallback is construct |
1543 | + * UPN (computer$@domain) manually. |
1544 | + */ |
1545 | + if (LogonAsNetworkService()) |
1546 | + { |
1547 | + BOOLEAN done= TRUE; |
1548 | + |
1549 | + len= PRINCIPAL_NAME_LEN; |
1550 | + computer_name= (CHAR *) calloc(len, sizeof(CHAR)); |
1551 | + done= (computer_name == NULL); |
1552 | + if (done && (done= GetComputerNameEx(ComputerNameDnsHostname, |
1553 | + computer_name, &len))) |
1554 | + { |
1555 | + len= PRINCIPAL_NAME_LEN; |
1556 | + domain_name= (CHAR *) calloc(len, sizeof(CHAR)); |
1557 | + done= (domain_name == NULL); |
1558 | + if (done && (done= GetComputerNameEx(ComputerNameDnsDomain, |
1559 | + domain_name, &len))) |
1560 | + { |
1561 | + sprintf_s(kerberos_spn_storage, |
1562 | + sizeof(kerberos_spn_storage), |
1563 | + "%s$@%s", computer_name, domain_name); |
1564 | + } |
1565 | + } |
1566 | + |
1567 | + /* Release heap memory */ |
1568 | + if (computer_name) free(computer_name); |
1569 | + if (domain_name) free(domain_name); |
1570 | + |
1571 | + if (!done) |
1572 | + { |
1573 | + KRB_AUTH_SERVER_INIT_ERROR(ret, |
1574 | + "Kerberos: the name is not available in specific format."); |
1575 | + } |
1576 | + else |
1577 | + { |
1578 | + /* redirect the variable */ |
1579 | + kerberos_principal_name= kerberos_spn_storage; |
1580 | + } |
1581 | + } |
1582 | + else |
1583 | + { |
1584 | + KRB_AUTH_SERVER_INIT_ERROR(ret, |
1585 | + "Kerberos: the name is not available in specific format."); |
1586 | + } |
1587 | + break; |
1588 | + default: |
1589 | + break; |
1590 | + } |
1591 | + } |
1592 | + else |
1593 | + { |
1594 | + /* redirect the variable */ |
1595 | + kerberos_principal_name= kerberos_spn_storage; |
1596 | + } |
1597 | + |
1598 | +/* localize macro KRB_AUTH_SERVER_INIT_ERROR */ |
1599 | +#undef KRB_AUTH_SERVER_INIT_ERROR |
1600 | + |
1601 | + return ret; |
1602 | +} |
1603 | +#endif /* _WIN32 */ |
1604 | + |
1605 | +static int verify_principal_name(UNUSED(MYSQL_THD thd), |
1606 | + UNUSED(struct st_mysql_sys_var UNUSED(*var)), UNUSED(void *save), |
1607 | + UNUSED(struct st_mysql_value *value)) { |
1608 | + char upn_buf[PRINCIPAL_NAME_LEN]; |
1609 | + int buf_len= PRINCIPAL_NAME_LEN; |
1610 | + /* UPN should in the form `user@domain` or `user/host@domain` */ |
1611 | + const char *ptr= value->val_str(value, upn_buf, &buf_len); |
1612 | + const char *itr= ptr; |
1613 | + |
1614 | +#define FWD_ITER(iter) while (*iter && (isalpha(*iter)||(*itr)=='.')) iter++ |
1615 | + /* user part */ |
1616 | + if (*itr && isalpha(*itr)) |
1617 | + { |
1618 | + FWD_ITER(itr); |
1619 | + } |
1620 | + else |
1621 | + { |
1622 | + /* name part is required */ |
1623 | + return 1; |
1624 | + } |
1625 | + |
1626 | + /* host part, which is optional */ |
1627 | + if (*itr && *itr == '/') |
1628 | + { |
1629 | + itr++; |
1630 | + FWD_ITER(itr); |
1631 | + } |
1632 | + |
1633 | + /* domain part */ |
1634 | + if (*itr && *itr == '@') |
1635 | + { |
1636 | + itr++; |
1637 | + FWD_ITER(itr); |
1638 | + } |
1639 | + else |
1640 | + { |
1641 | + /* domain part is required */ |
1642 | + return 1; |
1643 | + } |
1644 | + |
1645 | + /* if validated return 0, or any non-zero value */ |
1646 | + if (!*itr) |
1647 | + { |
1648 | + strncpy(kerberos_spn_storage, ptr, PRINCIPAL_NAME_LEN); |
1649 | + } |
1650 | + return *itr; |
1651 | +} |
1652 | + |
1653 | +static void update_principal_name(UNUSED(MYSQL_THD thd), |
1654 | + UNUSED(struct st_mysql_sys_var* var), UNUSED(void * var_ptr), |
1655 | + UNUSED(const void * save)) |
1656 | +{ |
1657 | + kerberos_principal_name= kerberos_spn_storage; |
1658 | +} |
1659 | + |
1660 | +/* system variable */ |
1661 | +static MYSQL_SYSVAR_STR(principal_name, kerberos_principal_name, |
1662 | + PLUGIN_VAR_RQCMDARG, |
1663 | + "Service principal name in Kerberos authentication.", |
1664 | + verify_principal_name, /* check function */ |
1665 | + update_principal_name, /* update function */ |
1666 | + ""); |
1667 | + |
1668 | +static struct st_mysql_sys_var *system_variables[]= { |
1669 | + MYSQL_SYSVAR(principal_name), |
1670 | + NULL |
1671 | +}; |
1672 | + |
1673 | +/* register Kerberos authentication plugin */ |
1674 | +static struct st_mysql_auth server_handler= { |
1675 | + MYSQL_AUTHENTICATION_INTERFACE_VERSION, |
1676 | + "kerberos_client", |
1677 | + kerberos_auth |
1678 | +}; |
1679 | + |
1680 | +maria_declare_plugin(kerberos_server) |
1681 | +{ |
1682 | + MYSQL_AUTHENTICATION_PLUGIN, |
1683 | + &server_handler, |
1684 | + "kerberos", |
1685 | + "Shuang Qiu", |
1686 | + "Plugin for Kerberos based authentication.", |
1687 | + PLUGIN_LICENSE_BSD, |
1688 | +#ifdef _WIN32 |
1689 | + initialize_principal_name, |
1690 | +#else /* _WIN32 */ |
1691 | + NULL, |
1692 | +#endif /* _WIN32 */ |
1693 | + NULL, /* destructor */ |
1694 | + 0x0100, /* version */ |
1695 | + NULL, /* status variables */ |
1696 | + system_variables, /* system variables */ |
1697 | + "Kerberos authentication plugin 1.0", |
1698 | + MariaDB_PLUGIN_MATURITY_EXPERIMENTAL /* TODO change when release */ |
1699 | +} |
1700 | +maria_declare_plugin_end; |
1701 | + |
1702 | +/* localize macro KRB_AUTH_SERVER_ERROR */ |
1703 | +#undef KRB_AUTH_SERVER_ERROR |