php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #45496 allow a client certificate when connecting to an IMAP server using imap_open
Submitted: 2008-07-13 06:08 UTC Modified: 2016-12-31 00:22 UTC
Votes:4
Avg. Score:5.0 ± 0.0
Reproduced:3 of 3 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: develop at kristov dot de Assigned:
Status: Open Package: IMAP related
PHP Version: 5.2.6 OS: Gentoo Linux
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2008-07-13 06:08 UTC] develop at kristov dot de
Description:
------------
Currently, imap_open does not allow to specify a client certificate to be used for a SSL/TLS-encrypted connection. Newer c-client versions do support this (using the GET_SSLCLIENTCERT and GET_SSLCLIENTKEY callbacks). I propose a patch which addresses this issue:

- it adds an additional parameter "keycert" to imap_open which receives the combined client certificate and key (similar to the "local_cert" option used e.g. by stream_socket_client/stream_context_create)

- it adds an additional IMAP global variable imap_keycert to hold the value of this parameter until being used by the callback

- it defines and registers a callback function mail_getkeycert which passes the combined client certificate and key to the c-client library if supported (the existence of both of the #defines SET_SSLCLIENTCERT and SET_SSLCLIENTKEY enables the callback)

- it links in the external authenticator auth_ext as client certificates/keys often replace user/password combinations (the user is identified by the CN of the client certificate and the password is substituted by the valid client key)

One technical detail: The external authenticator of the c-client library does not do a mm_login callback (see src/c-client/auth_ext.c, function auth_external_client). So the user name for the connection has to be set some other way. This is done by extending the caller's mailbox name by a user specification "/user=<user>" which is parsed and handled by the c-client library. The code to do this string insertion is a bit clumsy and can probably be improved; I'm afraid I do not know Zend very well and I was lucky to find a way for this string manipulation at all :-)

The rest of the patch is trivial and consists mainly of memory management and parameter checks.



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2008-07-13 06:10 UTC] develop at kristov dot de
Somehow I cannot add a patch file to this change request. So I include it here:

diff -urN php-5.2.6/ext/imap/php_imap.c php-5.2.6_p1/ext/imap/php_imap.c
--- php-5.2.6/ext/imap/php_imap.c	2008-04-17 13:04:49.000000000 +0200
+++ php-5.2.6_p1/ext/imap/php_imap.c	2008-07-12 11:42:12.000000000 +0200
@@ -229,6 +229,10 @@
 		efree(IMAPG(imap_password));
 		IMAPG(imap_password) = 0;
 	}
+	if (IMAPG(imap_keycert)) {
+		efree(IMAPG(imap_keycert));
+		IMAPG(imap_keycert) = 0;
+	}
 
 	efree(imap_le_struct);
 }
@@ -413,6 +417,21 @@
 }
 /* }}} */
 
+#ifdef SET_SSLCLIENTCERT
+#ifdef SET_SSLCLIENTKEY
+/* {{{ mail_getkeycert
+ *
+ * Mail GET_SSLCLIENTCERT and GET_SSLCLIENTKEY callback
+ */
+char *mail_getkeycert(void)
+{
+	TSRMLS_FETCH();
+	return IMAPG(imap_keycert);
+}
+/* }}} */
+#endif
+#endif
+
 #endif
 
 
@@ -422,6 +441,7 @@
 {
 	imap_globals->imap_user = NIL;
 	imap_globals->imap_password = NIL;
+	imap_globals->imap_keycert = NIL;
 
 	imap_globals->imap_alertstack = NIL;
 	imap_globals->imap_errorstack = NIL;
@@ -469,6 +489,7 @@
 	mail_link(&dummydriver);	/* link in the dummy driver */
 
 #ifndef PHP_WIN32
+	auth_link(&auth_ext);		/* link in the external authenticator */
 	auth_link(&auth_log);		/* link in the log authenticator */
 	auth_link(&auth_md5);       /* link in the cram-md5 authenticator */ 
 #if HAVE_IMAP_KRB && defined(HAVE_IMAP_AUTH_GSS)
@@ -748,14 +769,15 @@
  */
 static void php_imap_do_open(INTERNAL_FUNCTION_PARAMETERS, int persistent)
 {
-	zval **mailbox, **user, **passwd, **options, **retries;
+	zval **mailbox, **user, **passwd, **options, **retries, **keycert;
 	MAILSTREAM *imap_stream;
 	pils *imap_le_struct;
 	long flags=NIL;
 	long cl_flags=NIL;
 	int myargc = ZEND_NUM_ARGS();
-	
-	if (myargc < 3 || myargc > 5 || zend_get_parameters_ex(myargc, &mailbox, &user, &passwd, &options, &retries) == FAILURE) {
+	char *found = NULL;
+
+	if (myargc < 3 || myargc > 6 || zend_get_parameters_ex(myargc, &mailbox, &user, &passwd, &options, &retries, &keycert) == FAILURE) {
 		ZEND_WRONG_PARAM_COUNT();
 	}
 
@@ -770,6 +792,9 @@
 			flags ^= PHP_EXPUNGE;
 		}
 	}
+	if (myargc >= 6) {
+		convert_to_string_ex(keycert);
+	}
 
 	if (IMAPG(imap_user)) { 
 		efree(IMAPG(imap_user));
@@ -779,6 +804,10 @@
 		efree(IMAPG(imap_password));
 	}
 
+	if (IMAPG(imap_keycert)) { 
+		efree(IMAPG(imap_keycert));
+	}
+
 	/* local filename, need to perform open_basedir and safe_mode checks */
 	if (Z_STRVAL_PP(mailbox)[0] != '{' && 
 			(php_check_open_basedir(Z_STRVAL_PP(mailbox) TSRMLS_CC) || 
@@ -789,8 +818,40 @@
 	IMAPG(imap_user)     = estrndup(Z_STRVAL_PP(user), Z_STRLEN_PP(user));
 	IMAPG(imap_password) = estrndup(Z_STRVAL_PP(passwd), Z_STRLEN_PP(passwd));
 
+	found = php_memnstr(Z_STRVAL_PP(mailbox),
+				"}",
+				1,
+				Z_STRVAL_PP(mailbox) + Z_STRLEN_PP(mailbox));
+	if (found) {
+		zval *mailboxwithuser, *tmp;
+		MAKE_STD_ZVAL(mailboxwithuser);
+		ZVAL_STRINGL(mailboxwithuser,
+			Z_STRVAL_PP(mailbox),
+			found - Z_STRVAL_PP(mailbox),
+			1);
+
+		MAKE_STD_ZVAL(tmp);
+		ZVAL_STRING(tmp, "/user=", 0);
+		add_string_to_string(mailboxwithuser, mailboxwithuser, tmp);
+		ZVAL_STRING(tmp, IMAPG(imap_user), 0);
+		add_string_to_string(mailboxwithuser, mailboxwithuser, tmp);
+		ZVAL_STRINGL(tmp,
+			found,
+			Z_STRLEN_PP(mailbox) - (found - Z_STRVAL_PP(mailbox)),
+			0);
+		add_string_to_string(mailboxwithuser, mailboxwithuser, tmp);
+		FREE_ZVAL(tmp);
+
+		REPLACE_ZVAL_VALUE(mailbox, mailboxwithuser, 0);
+		FREE_ZVAL(mailboxwithuser);
+	}
+
+	if (myargc >= 6) {
+		IMAPG(imap_keycert) = estrndup(Z_STRVAL_PP(keycert), Z_STRLEN_PP(keycert));
+	}
+
 #ifdef SET_MAXLOGINTRIALS
-	if (myargc == 5) {
+	if (myargc >= 5) {
 		convert_to_long_ex(retries);
 		if (Z_LVAL_PP(retries) < 0) {
 			php_error_docref(NULL TSRMLS_CC, E_WARNING ,"Retries must be greater or equal to 0");
@@ -800,12 +861,24 @@
 	}
 #endif
 
+#ifdef SET_SSLCLIENTCERT
+#ifdef SET_SSLCLIENTKEY
+	if (myargc >= 6) {
+		mail_parameters(NIL, SET_SSLCLIENTCERT, (void *) mail_getkeycert);
+		mail_parameters(NIL, SET_SSLCLIENTKEY, (void *) mail_getkeycert);
+	}
+#endif
+#endif
+
 	imap_stream = mail_open(NIL, Z_STRVAL_PP(mailbox), flags);
 
 	if (imap_stream == NIL) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't open stream %s", Z_STRVAL_PP(mailbox));
 		efree(IMAPG(imap_user)); IMAPG(imap_user) = 0;
 		efree(IMAPG(imap_password)); IMAPG(imap_password) = 0;
+		if (myargc >= 6) {
+			efree(IMAPG(imap_keycert)); IMAPG(imap_keycert) = 0;
+		}
 		RETURN_FALSE;
 	}
 
diff -urN php-5.2.6/ext/imap/php_imap.h php-5.2.6_p1/ext/imap/php_imap.h
--- php-5.2.6/ext/imap/php_imap.h	2007-12-31 08:20:07.000000000 +0100
+++ php-5.2.6_p1/ext/imap/php_imap.h	2008-07-12 11:03:40.000000000 +0200
@@ -180,6 +180,7 @@
 ZEND_BEGIN_MODULE_GLOBALS(imap)
 	char *imap_user;
 	char *imap_password;
+	char *imap_keycert;
 
 	STRINGLIST *imap_alertstack;
 	ERRORLIST *imap_errorstack;
 [2008-07-13 06:56 UTC] develop at kristov dot de
I forgot to mention that this patch has been successfully tested with Cyrus 2.3.9 for months (started as a patch for PHP 5.2.5).
 [2016-12-31 00:22 UTC] cmb@php.net
-Package: Feature/Change Request +Package: IMAP related
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 22:01:28 2024 UTC