[PECL-CVS] [pecl-authentication-krb5] master: Add GSSAPIContext::exportCredentials() and importCredentials()
[email protected] (David Härdeman via Moritz Bechler) Mon, 8 Jun 2026 15:50:16 +0000
| Newsgroups | php.pecl.cvs |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> 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)