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