xref: /PHP-5.4/ext/mysqlnd/mysqlnd_auth.c (revision c0d060f5)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Georg Richter <georg@mysql.com>                             |
16   |          Andrey Hristov <andrey@mysql.com>                           |
17   |          Ulf Wendel <uwendel@mysql.com>                              |
18   +----------------------------------------------------------------------+
19 */
20 
21 /* $Id: mysqlnd.c 307377 2011-01-11 13:02:57Z andrey $ */
22 #include "php.h"
23 #include "mysqlnd.h"
24 #include "mysqlnd_structs.h"
25 #include "mysqlnd_wireprotocol.h"
26 #include "mysqlnd_priv.h"
27 #include "mysqlnd_result.h"
28 #include "mysqlnd_charset.h"
29 #include "mysqlnd_debug.h"
30 
31 
32 /* {{{ mysqlnd_auth_handshake */
33 enum_func_status
mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,const char * const user,const char * const passwd,const size_t passwd_len,const char * const db,const size_t db_len,const MYSQLND_OPTIONS * const options,unsigned long mysql_flags,unsigned int server_charset_no,zend_bool use_full_blown_auth_packet,const char * const auth_protocol,const zend_uchar * const auth_plugin_data,const size_t auth_plugin_data_len,char ** switch_to_auth_protocol,size_t * switch_to_auth_protocol_len,zend_uchar ** switch_to_auth_protocol_data,size_t * switch_to_auth_protocol_data_len TSRMLS_DC)34 mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn,
35 							  const char * const user,
36 							  const char * const passwd,
37 							  const size_t passwd_len,
38 							  const char * const db,
39 							  const size_t db_len,
40 							  const MYSQLND_OPTIONS * const options,
41 							  unsigned long mysql_flags,
42 							  unsigned int server_charset_no,
43 							  zend_bool use_full_blown_auth_packet,
44 							  const char * const auth_protocol,
45 							  const zend_uchar * const auth_plugin_data,
46 							  const size_t auth_plugin_data_len,
47 							  char ** switch_to_auth_protocol,
48 							  size_t * switch_to_auth_protocol_len,
49 							  zend_uchar ** switch_to_auth_protocol_data,
50 							  size_t * switch_to_auth_protocol_data_len
51 							  TSRMLS_DC)
52 {
53 	enum_func_status ret = FAIL;
54 	const MYSQLND_CHARSET * charset = NULL;
55 	MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
56 	MYSQLND_PACKET_AUTH_RESPONSE * auth_resp_packet = NULL;
57 	MYSQLND_PACKET_AUTH * auth_packet = NULL;
58 
59 	DBG_ENTER("mysqlnd_auth_handshake");
60 
61 	auth_resp_packet = conn->protocol->m.get_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
62 
63 	if (!auth_resp_packet) {
64 		SET_OOM_ERROR(*conn->error_info);
65 		goto end;
66 	}
67 
68 	if (use_full_blown_auth_packet != TRUE) {
69 		change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
70 		if (!change_auth_resp_packet) {
71 			SET_OOM_ERROR(*conn->error_info);
72 			goto end;
73 		}
74 
75 		change_auth_resp_packet->auth_data = auth_plugin_data;
76 		change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
77 
78 		if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
79 			CONN_SET_STATE(conn, CONN_QUIT_SENT);
80 			SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
81 			goto end;
82 		}
83 	} else {
84 		auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
85 
86 		auth_packet->client_flags = mysql_flags;
87 		auth_packet->max_packet_size = options->max_allowed_packet;
88 		if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
89 			auth_packet->charset_no	= charset->nr;
90 		} else {
91 #if MYSQLND_UNICODE
92 			auth_packet->charset_no	= 200;/* utf8 - swedish collation, check mysqlnd_charset.c */
93 #else
94 			auth_packet->charset_no	= server_charset_no;
95 #endif
96 		}
97 
98 		auth_packet->send_auth_data = TRUE;
99 		auth_packet->user		= user;
100 		auth_packet->db			= db;
101 		auth_packet->db_len		= db_len;
102 
103 		auth_packet->auth_data = auth_plugin_data;
104 		auth_packet->auth_data_len = auth_plugin_data_len;
105 		auth_packet->auth_plugin_name = auth_protocol;
106 
107 		if (!PACKET_WRITE(auth_packet, conn)) {
108 			goto end;
109 		}
110 	}
111 	if (use_full_blown_auth_packet == TRUE) {
112 		conn->charset = mysqlnd_find_charset_nr(auth_packet->charset_no);
113 	}
114 
115 	if (FAIL == PACKET_READ(auth_resp_packet, conn) || auth_resp_packet->response_code >= 0xFE) {
116 		if (auth_resp_packet->response_code == 0xFE) {
117 			/* old authentication with new server  !*/
118 			if (!auth_resp_packet->new_auth_protocol) {
119 				DBG_ERR(mysqlnd_old_passwd);
120 				SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
121 			} else {
122 				*switch_to_auth_protocol = mnd_pestrndup(auth_resp_packet->new_auth_protocol, auth_resp_packet->new_auth_protocol_len, FALSE);
123 				*switch_to_auth_protocol_len = auth_resp_packet->new_auth_protocol_len;
124 				if (auth_resp_packet->new_auth_protocol_data) {
125 					*switch_to_auth_protocol_data_len = auth_resp_packet->new_auth_protocol_data_len;
126 					*switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
127 					memcpy(*switch_to_auth_protocol_data, auth_resp_packet->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
128 				} else {
129 					*switch_to_auth_protocol_data = NULL;
130 					*switch_to_auth_protocol_data_len = 0;
131 				}
132 			}
133 		} else if (auth_resp_packet->response_code == 0xFF) {
134 			if (auth_resp_packet->sqlstate[0]) {
135 				strlcpy(conn->error_info->sqlstate, auth_resp_packet->sqlstate, sizeof(conn->error_info->sqlstate));
136 				DBG_ERR_FMT("ERROR:%u [SQLSTATE:%s] %s", auth_resp_packet->error_no, auth_resp_packet->sqlstate, auth_resp_packet->error);
137 			}
138 			SET_CLIENT_ERROR(*conn->error_info, auth_resp_packet->error_no, UNKNOWN_SQLSTATE, auth_resp_packet->error);
139 		}
140 		goto end;
141 	}
142 
143 	SET_NEW_MESSAGE(conn->last_message, conn->last_message_len, auth_resp_packet->message, auth_resp_packet->message_len, conn->persistent);
144 	ret = PASS;
145 end:
146 	PACKET_FREE(change_auth_resp_packet);
147 	PACKET_FREE(auth_packet);
148 	PACKET_FREE(auth_resp_packet);
149 	DBG_RETURN(ret);
150 }
151 /* }}} */
152 
153 
154 /* {{{ mysqlnd_auth_change_user */
155 enum_func_status
mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,const char * const user,const size_t user_len,const char * const passwd,const size_t passwd_len,const char * const db,const size_t db_len,const zend_bool silent,zend_bool use_full_blown_auth_packet,const char * const auth_protocol,zend_uchar * auth_plugin_data,size_t auth_plugin_data_len,char ** switch_to_auth_protocol,size_t * switch_to_auth_protocol_len,zend_uchar ** switch_to_auth_protocol_data,size_t * switch_to_auth_protocol_data_len TSRMLS_DC)156 mysqlnd_auth_change_user(MYSQLND_CONN_DATA * const conn,
157 								const char * const user,
158 								const size_t user_len,
159 								const char * const passwd,
160 								const size_t passwd_len,
161 								const char * const db,
162 								const size_t db_len,
163 								const zend_bool silent,
164 								zend_bool use_full_blown_auth_packet,
165 								const char * const auth_protocol,
166 								zend_uchar * auth_plugin_data,
167 								size_t auth_plugin_data_len,
168 								char ** switch_to_auth_protocol,
169 								size_t * switch_to_auth_protocol_len,
170 								zend_uchar ** switch_to_auth_protocol_data,
171 								size_t * switch_to_auth_protocol_data_len
172 								TSRMLS_DC)
173 {
174 	enum_func_status ret = FAIL;
175 	const MYSQLND_CHARSET * old_cs = conn->charset;
176 	MYSQLND_PACKET_CHANGE_AUTH_RESPONSE * change_auth_resp_packet = NULL;
177 	MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp = NULL;
178 	MYSQLND_PACKET_AUTH * auth_packet = NULL;
179 
180 	DBG_ENTER("mysqlnd_auth_change_user");
181 
182 	chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
183 
184 	if (!chg_user_resp) {
185 		SET_OOM_ERROR(*conn->error_info);
186 		goto end;
187 	}
188 
189 	if (use_full_blown_auth_packet != TRUE) {
190 		change_auth_resp_packet = conn->protocol->m.get_change_auth_response_packet(conn->protocol, FALSE TSRMLS_CC);
191 		if (!change_auth_resp_packet) {
192 			SET_OOM_ERROR(*conn->error_info);
193 			goto end;
194 		}
195 
196 		change_auth_resp_packet->auth_data = auth_plugin_data;
197 		change_auth_resp_packet->auth_data_len = auth_plugin_data_len;
198 
199 		if (!PACKET_WRITE(change_auth_resp_packet, conn)) {
200 			CONN_SET_STATE(conn, CONN_QUIT_SENT);
201 			SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
202 			goto end;
203 		}
204 	} else {
205 		auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
206 
207 		if (!auth_packet) {
208 			SET_OOM_ERROR(*conn->error_info);
209 			goto end;
210 		}
211 
212 		auth_packet->is_change_user_packet = TRUE;
213 		auth_packet->user		= user;
214 		auth_packet->db			= db;
215 		auth_packet->db_len		= db_len;
216 		auth_packet->silent		= silent;
217 
218 		auth_packet->auth_data = auth_plugin_data;
219 		auth_packet->auth_data_len = auth_plugin_data_len;
220 		auth_packet->auth_plugin_name = auth_protocol;
221 
222 
223 		if (conn->m->get_server_version(conn TSRMLS_CC) >= 50123) {
224 			auth_packet->charset_no	= conn->charset->nr;
225 		}
226 
227 		if (!PACKET_WRITE(auth_packet, conn)) {
228 			CONN_SET_STATE(conn, CONN_QUIT_SENT);
229 				SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
230 			goto end;
231 		}
232 	}
233 
234 	ret = PACKET_READ(chg_user_resp, conn);
235 	COPY_CLIENT_ERROR(*conn->error_info, chg_user_resp->error_info);
236 
237 	if (0xFE == chg_user_resp->response_code) {
238 		ret = FAIL;
239 		if (!chg_user_resp->new_auth_protocol) {
240 			DBG_ERR(mysqlnd_old_passwd);
241 			SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
242 		} else {
243 			*switch_to_auth_protocol = mnd_pestrndup(chg_user_resp->new_auth_protocol, chg_user_resp->new_auth_protocol_len, FALSE);
244 			*switch_to_auth_protocol_len = chg_user_resp->new_auth_protocol_len;
245 			if (chg_user_resp->new_auth_protocol_data) {
246 				*switch_to_auth_protocol_data_len = chg_user_resp->new_auth_protocol_data_len;
247 				*switch_to_auth_protocol_data = mnd_emalloc(*switch_to_auth_protocol_data_len);
248 				memcpy(*switch_to_auth_protocol_data, chg_user_resp->new_auth_protocol_data, *switch_to_auth_protocol_data_len);
249 			} else {
250 				*switch_to_auth_protocol_data = NULL;
251 				*switch_to_auth_protocol_data_len = 0;
252 			}
253 		}
254 	}
255 
256 	if (conn->error_info->error_no) {
257 		ret = FAIL;
258 		/*
259 		  COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
260 		  bug#25371 mysql_change_user() triggers "packets out of sync"
261 		  When it gets fixed, there should be one more check here
262 		*/
263 		if (conn->m->get_server_version(conn TSRMLS_CC) > 50113L &&conn->m->get_server_version(conn TSRMLS_CC) < 50118L) {
264 			MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
265 			if (redundant_error_packet) {
266 				PACKET_READ(redundant_error_packet, conn);
267 				PACKET_FREE(redundant_error_packet);
268 				DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", conn->m->get_server_version(conn TSRMLS_CC));
269 			} else {
270 				SET_OOM_ERROR(*conn->error_info);
271 			}
272 		}
273 	}
274 	if (ret == PASS) {
275 		char * tmp = NULL;
276 		/* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
277 		tmp = mnd_pestrndup(user, user_len, conn->persistent);
278 		if (conn->user) {
279 			mnd_pefree(conn->user, conn->persistent);
280 		}
281 		conn->user = tmp;
282 
283 		tmp = mnd_pestrdup(passwd, conn->persistent);
284 		if (conn->passwd) {
285 			mnd_pefree(conn->passwd, conn->persistent);
286 		}
287 		conn->passwd = tmp;
288 
289 		if (conn->last_message) {
290 			mnd_pefree(conn->last_message, conn->persistent);
291 			conn->last_message = NULL;
292 		}
293 		memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
294 		/* set charset for old servers */
295 		if (conn->m->get_server_version(conn TSRMLS_CC) < 50123) {
296 			ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC);
297 		}
298 	} else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
299 		/* old authentication with new server  !*/
300 		DBG_ERR(mysqlnd_old_passwd);
301 		SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
302 	}
303 end:
304 	PACKET_FREE(change_auth_resp_packet);
305 	PACKET_FREE(auth_packet);
306 	PACKET_FREE(chg_user_resp);
307 	DBG_RETURN(ret);
308 }
309 /* }}} */
310 
311 
312 /******************************************* MySQL Native Password ***********************************/
313 
314 #include "ext/standard/sha1.h"
315 
316 /* {{{ php_mysqlnd_crypt */
317 static void
php_mysqlnd_crypt(zend_uchar * buffer,const zend_uchar * s1,const zend_uchar * s2,size_t len)318 php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
319 {
320 	const zend_uchar *s1_end = s1 + len;
321 	while (s1 < s1_end) {
322 		*buffer++= *s1++ ^ *s2++;
323 	}
324 }
325 /* }}} */
326 
327 
328 /* {{{ php_mysqlnd_scramble */
php_mysqlnd_scramble(zend_uchar * const buffer,const zend_uchar * const scramble,const zend_uchar * const password,size_t password_len)329 void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password, size_t password_len)
330 {
331 	PHP_SHA1_CTX context;
332 	zend_uchar sha1[SHA1_MAX_LENGTH];
333 	zend_uchar sha2[SHA1_MAX_LENGTH];
334 
335 	/* Phase 1: hash password */
336 	PHP_SHA1Init(&context);
337 	PHP_SHA1Update(&context, password, password_len);
338 	PHP_SHA1Final(sha1, &context);
339 
340 	/* Phase 2: hash sha1 */
341 	PHP_SHA1Init(&context);
342 	PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
343 	PHP_SHA1Final(sha2, &context);
344 
345 	/* Phase 3: hash scramble + sha2 */
346 	PHP_SHA1Init(&context);
347 	PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
348 	PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
349 	PHP_SHA1Final(buffer, &context);
350 
351 	/* let's crypt buffer now */
352 	php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
353 }
354 /* }}} */
355 
356 
357 /* {{{ mysqlnd_native_auth_get_auth_data */
358 static zend_uchar *
mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,size_t * auth_data_len,MYSQLND_CONN_DATA * conn,const char * const user,const char * const passwd,const size_t passwd_len,zend_uchar * auth_plugin_data,size_t auth_plugin_data_len,const MYSQLND_OPTIONS * const options,unsigned long mysql_flags TSRMLS_DC)359 mysqlnd_native_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
360 								  size_t * auth_data_len,
361 								  MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
362 								  const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
363 								  const MYSQLND_OPTIONS * const options, unsigned long mysql_flags
364 								  TSRMLS_DC)
365 {
366 	zend_uchar * ret = NULL;
367 	DBG_ENTER("mysqlnd_native_auth_get_auth_data");
368 	*auth_data_len = 0;
369 
370 	/* 5.5.x reports 21 as scramble length because it needs to show the length of the data before the plugin name */
371 	if (auth_plugin_data_len < SCRAMBLE_LENGTH) {
372 		/* mysql_native_password only works with SCRAMBLE_LENGTH scramble */
373 		SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "The server sent wrong length for scramble");
374 		DBG_ERR_FMT("The server sent wrong length for scramble %u. Expected %u", auth_plugin_data_len, SCRAMBLE_LENGTH);
375 		DBG_RETURN(NULL);
376 	}
377 
378 	/* copy scrambled pass*/
379 	if (passwd && passwd_len) {
380 		ret = malloc(SCRAMBLE_LENGTH);
381 		*auth_data_len = SCRAMBLE_LENGTH;
382 		/* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
383 		php_mysqlnd_scramble((zend_uchar*)ret, auth_plugin_data, (zend_uchar*)passwd, passwd_len);
384 	}
385 	DBG_RETURN(ret);
386 }
387 /* }}} */
388 
389 
390 static struct st_mysqlnd_authentication_plugin mysqlnd_native_auth_plugin =
391 {
392 	{
393 		MYSQLND_PLUGIN_API_VERSION,
394 		"auth_plugin_mysql_native_password",
395 		MYSQLND_VERSION_ID,
396 		MYSQLND_VERSION,
397 		"PHP License 3.01",
398 		"Andrey Hristov <andrey@mysql.com>,  Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
399 		{
400 			NULL, /* no statistics , will be filled later if there are some */
401 			NULL, /* no statistics */
402 		},
403 		{
404 			NULL /* plugin shutdown */
405 		}
406 	},
407 	{/* methods */
408 		mysqlnd_native_auth_get_auth_data
409 	}
410 };
411 
412 
413 /******************************************* PAM Authentication ***********************************/
414 
415 /* {{{ mysqlnd_pam_auth_get_auth_data */
416 static zend_uchar *
mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,size_t * auth_data_len,MYSQLND_CONN_DATA * conn,const char * const user,const char * const passwd,const size_t passwd_len,zend_uchar * auth_plugin_data,size_t auth_plugin_data_len,const MYSQLND_OPTIONS * const options,unsigned long mysql_flags TSRMLS_DC)417 mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self,
418 							   size_t * auth_data_len,
419 							   MYSQLND_CONN_DATA * conn, const char * const user, const char * const passwd,
420 							   const size_t passwd_len, zend_uchar * auth_plugin_data, size_t auth_plugin_data_len,
421 							   const MYSQLND_OPTIONS * const options, unsigned long mysql_flags
422 							   TSRMLS_DC)
423 {
424 	zend_uchar * ret = NULL;
425 
426 	/* copy pass*/
427 	if (passwd && passwd_len) {
428 		ret = (zend_uchar*) zend_strndup(passwd, passwd_len);
429 	}
430 	*auth_data_len = passwd_len;
431 
432 	return ret;
433 }
434 /* }}} */
435 
436 
437 static struct st_mysqlnd_authentication_plugin mysqlnd_pam_authentication_plugin =
438 {
439 	{
440 		MYSQLND_PLUGIN_API_VERSION,
441 		"auth_plugin_mysql_clear_password",
442 		MYSQLND_VERSION_ID,
443 		MYSQLND_VERSION,
444 		"PHP License 3.01",
445 		"Andrey Hristov <andrey@mysql.com>,  Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
446 		{
447 			NULL, /* no statistics , will be filled later if there are some */
448 			NULL, /* no statistics */
449 		},
450 		{
451 			NULL /* plugin shutdown */
452 		}
453 	},
454 	{/* methods */
455 		mysqlnd_pam_auth_get_auth_data
456 	}
457 };
458 
459 
460 /* {{{ mysqlnd_register_builtin_authentication_plugins */
461 void
mysqlnd_register_builtin_authentication_plugins(TSRMLS_D)462 mysqlnd_register_builtin_authentication_plugins(TSRMLS_D)
463 {
464 	mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_native_auth_plugin TSRMLS_CC);
465 	mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_pam_authentication_plugin TSRMLS_CC);
466 }
467 /* }}} */
468 
469 
470 /*
471  * Local variables:
472  * tab-width: 4
473  * c-basic-offset: 4
474  * End:
475  * vim600: noet sw=4 ts=4 fdm=marker
476  * vim<600: noet sw=4 ts=4
477  */
478