[pecl-authentication-krb5] master: Add GSSAPIContext::exportCredentials() and importCredentials()
| From: | David Härdeman via Moritz Bechler | Date: | Mon, 08 Jun 2026 15:50:16 +0000 |
| Subject: | [pecl-authentication-krb5] master: Add GSSAPIContext::exportCredentials() and importCredentials() | ||
| Groups: | php.pecl.cvs | ||
| Request: | Send a blank email to pecl-cvs+get-28566@lists.php.net to get a copy of this message | ||
Author: David Härdeman (Alphix)
Committer: Moritz Bechler (mbechler)
Date: 2026-06-08T17:22:59+02:00
Commit: https://github.com/php/pecl-authentication-krb5/commit/5c572644e778f35e940f731998fc14f85632ba3c
Raw diff: https://github.com/php/pecl-authentication-krb5/commit/5c572644e778f35e940f731998fc14f85632ba3c.diff
Add GSSAPIContext::exportCredentials() and importCredentials()
Expose gss_export_cred()/gss_import_cred() to PHP, allowing delegated
credentials to be serialised and restored across process boundaries.
Both methods are conditionally compiled under HAVE_GSS_EXPORT_CRED.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Changed paths:
A tests/007.phpt
M config.m4
M gssapi.c
Diff:
diff --git a/config.m4 b/config.m4
index e6972be..40f2e7c 100644
--- a/config.m4
+++ b/config.m4
@@ -62,6 +62,7 @@ if test "$PHP_KRB5" != "no" -o "$PHP_KRB5KADM" !=
"no"; then
AC_CHECK_FUNCS(krb5_free_string)
AC_CHECK_FUNCS(gss_acquire_cred_from)
+ AC_CHECK_FUNCS(gss_export_cred)
AC_CHECK_FUNCS(krb5_chpw_message)
AC_CHECK_FUNCS(krb5_principal_get_realm)
AC_CHECK_FUNCS(krb5_get_init_creds_opt_set_expire_callback)
diff --git a/gssapi.c b/gssapi.c
index 627dfd1..ebf2c68 100644
--- a/gssapi.c
+++ b/gssapi.c
@@ -21,6 +21,9 @@
**/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
#include "php.h"
#include "php_krb5.h"
@@ -89,6 +92,11 @@ ZEND_BEGIN_ARG_INFO_EX(krb5_GSSAPIContext_unwrapArgs, 0, 0, 2)
ZEND_ARG_INFO(1, output)
ZEND_END_ARG_INFO()
+#ifdef HAVE_GSS_EXPORT_CRED
+ZEND_BEGIN_ARG_INFO_EX(krb5_GSSAPIContext_importCredentials, 0, 0, 1)
+ ZEND_ARG_INFO(0, token)
+ZEND_END_ARG_INFO()
+#endif
PHP_METHOD(GSSAPIContext, registerAcceptorIdentity);
PHP_METHOD(GSSAPIContext, acquireCredentials);
@@ -100,6 +108,10 @@ PHP_METHOD(GSSAPIContext, verifyMic);
PHP_METHOD(GSSAPIContext, wrap);
PHP_METHOD(GSSAPIContext, unwrap);
PHP_METHOD(GSSAPIContext, getTimeRemaining);
+#ifdef HAVE_GSS_EXPORT_CRED
+PHP_METHOD(GSSAPIContext, exportCredentials);
+PHP_METHOD(GSSAPIContext, importCredentials);
+#endif
static zend_function_entry krb5_gssapi_context_functions[] = {
PHP_ME(GSSAPIContext, registerAcceptorIdentity, krb5_GSSAPIContext_registerAcceptorIdentity,
ZEND_ACC_PUBLIC)
@@ -112,6 +124,10 @@ static zend_function_entry krb5_gssapi_context_functions[] = {
PHP_ME(GSSAPIContext, wrap, krb5_GSSAPIContext_wrapArgs,
ZEND_ACC_PUBLIC)
PHP_ME(GSSAPIContext, unwrap, krb5_GSSAPIContext_unwrapArgs,
ZEND_ACC_PUBLIC)
PHP_ME(GSSAPIContext, getTimeRemaining, krb5_GSSAPIContext_none,
ZEND_ACC_PUBLIC)
+#ifdef HAVE_GSS_EXPORT_CRED
+ PHP_ME(GSSAPIContext, exportCredentials, krb5_GSSAPIContext_none,
ZEND_ACC_PUBLIC)
+ PHP_ME(GSSAPIContext, importCredentials, krb5_GSSAPIContext_importCredentials,
ZEND_ACC_PUBLIC)
+#endif
PHP_FE_END
};
@@ -933,3 +949,64 @@ PHP_METHOD(GSSAPIContext, unwrap)
status = gss_release_buffer(&minor_status, &output);
ASSERT_GSS_SUCCESS(status,minor_status,);
} /* }}} */
+
+#ifdef HAVE_GSS_EXPORT_CRED
+/* {{{ proto string GSSAPIContext::exportCredentials( )
+ Exports the current credentials to an opaque token that can be stored or passed to another
process */
+PHP_METHOD(GSSAPIContext, exportCredentials)
+{
+ OM_uint32 status = 0;
+ OM_uint32 minor_status = 0;
+ gss_buffer_desc token;
+ krb5_gssapi_context_object *context = KRB5_THIS_GSSAPI_CONTEXT;
+
+ memset(&token, 0, sizeof(token));
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ if (context->creds == GSS_C_NO_CREDENTIAL) {
+ zend_throw_exception(NULL, "No credentials to export", 0 TSRMLS_CC);
+ return;
+ }
+
+ status = gss_export_cred(&minor_status, context->creds, &token);
+ ASSERT_GSS_SUCCESS(status, minor_status,);
+
+ _RETVAL_STRINGL(token.value, token.length);
+
+ status = gss_release_buffer(&minor_status, &token);
+ ASSERT_GSS_SUCCESS(status, minor_status,);
+} /* }}} */
+
+/* {{{ proto bool GSSAPIContext::importCredentials( string $token )
+ Imports credentials from a token previously created by exportCredentials() */
+PHP_METHOD(GSSAPIContext, importCredentials)
+{
+ OM_uint32 status = 0;
+ OM_uint32 minor_status = 0;
+ gss_buffer_desc token;
+ gss_cred_id_t new_creds = GSS_C_NO_CREDENTIAL;
+ krb5_gssapi_context_object *context = KRB5_THIS_GSSAPI_CONTEXT;
+ strsize_t token_len = 0;
+
+ memset(&token, 0, sizeof(token));
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &(token.value), &token_len) == FAILURE) {
+ RETURN_FALSE;
+ }
+ token.length = token_len;
+
+ status = gss_import_cred(&minor_status, &token, &new_creds);
+ ASSERT_GSS_SUCCESS(status, minor_status,);
+
+ if (context->creds != GSS_C_NO_CREDENTIAL) {
+ gss_release_cred(&minor_status, &(context->creds));
+ }
+ context->creds = new_creds;
+
+ RETURN_TRUE;
+} /* }}} */
+#endif
diff --git a/tests/007.phpt b/tests/007.phpt
new file mode 100644
index 0000000..447ba55
--- /dev/null
+++ b/tests/007.phpt
@@ -0,0 +1,75 @@
+--TEST--
+Testing for credential export and import
+--SKIPIF--
+<?php
+if(!file_exists(dirname(__FILE__) . '/config.php')) { echo "skip config
missing"; return; }
+if(!include(dirname(__FILE__) . '/config.php')) return;
+if(!method_exists('GSSAPIContext', 'exportCredentials')) { echo "skip
gss_export_cred not available"; return; }
+?>
+--FILE--
+<?php
+include(dirname(__FILE__) . '/config.php');
+$client = new KRB5CCache();
+if($use_config) {
+ $client->setConfig(dirname(__FILE__) . '/krb5.ini');
+}
+
+$client->initPassword($client_principal, $client_password, array('forwardable' =>
true, 'proxiable' => true));
+
+$server = new KRB5CCache();
+if($use_config) {
+ $server->setConfig(dirname(__FILE__) . '/krb5.ini');
+}
+
+$server->initKeytab($server_principal, $server_keytab);
+
+$cgssapi = new GSSAPIContext();
+$sgssapi = new GSSAPIContext();
+
+$cgssapi->acquireCredentials($client);
+$sgssapi->acquireCredentials($server);
+
+$token = '';
+$token2 = '';
+$principal = '';
+$ret_flags = 0;
+$time_rec = 0;
+$deleg = new KRB5CCache();
+
+// Establish a context with delegation to obtain delegated credentials
+var_dump($cgssapi->initSecContext($server_principal, null, GSS_C_DELEG_FLAG, null, $token));
+var_dump($sgssapi->acceptSecContext($token, $token2, $principal, $ret_flags, $time_rec,
$deleg));
+var_dump(count($deleg->getEntries()));
+
+// Acquire credentials from the delegated ccache, then export them
+$dgssapi = new GSSAPIContext();
+$dgssapi->acquireCredentials($deleg, $principal, GSS_C_INITIATE);
+
+$exported = $dgssapi->exportCredentials();
+var_dump(is_string($exported) && strlen($exported) > 0);
+
+// Import the exported credentials into a fresh context and use them
+$igssapi = new GSSAPIContext();
+var_dump($igssapi->importCredentials($exported));
+
+$s2gssapi = new GSSAPIContext();
+$s2gssapi->acquireCredentials($server);
+
+$token = '';
+$token2 = '';
+$principal2 = '';
+
+var_dump($igssapi->initSecContext($server_principal, null, null, null, $token));
+var_dump($s2gssapi->acceptSecContext($token, $token2, $principal2, $ret_flags, $time_rec,
$deleg));
+var_dump($principal2 === $principal);
+
+?>
+--EXPECTF--
+bool(true)
+bool(true)
+int(1)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)