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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: develop at kristov dot de
New email:
PHP Version: OS:

 

 [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

Pull Requests

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 Nov 22 17:01:31 2024 UTC