代码实现kinit后无法将Kerberos票据存入默认缓存的问题
问题分析与解决:代码实现kinit后klist无法看到Kerberos票据
问题背景
在Linux Rocky 8.5系统上,使用krb5库通过代码实现kinit功能,程序运行无报错,但执行klist命令无法看到Kerberos票据;直接执行kinit -kt test.keytab username@DOMAIN命令后,klist能正常显示票据,需要让代码实现的kinit像原生命令一样将票据存入默认缓存。
核心问题定位
你的代码中存在一个关键错误:错误调用了krb5_cc_destroy销毁缓存,而不是关闭缓存句柄。
krb5_cc_destroy函数的作用是删除整个缓存文件(包括其中的所有票据),这会导致你刚写入的票据被直接删除,所以klist无法读取到。- 原生命令kinit在写入票据后,只是关闭缓存句柄,不会销毁缓存文件。
解决方案
将代码中的krb5_cc_destroy(context, cache)替换为krb5_cc_close(context, cache)。krb5_cc_close是正确的关闭缓存句柄的方法,只会释放相关资源,不会删除缓存文件,这样写入的票据就能被保留下来。
修改后的完整代码
#include <krb5.h> #include <iostream> #include <string> #define RESULT_OK(X) ((X)==0) int kinit(const std::string& username, const std::string& domain, const std::string& password, bool password_is_keytab) { krb5_context context=NULL; krb5_principal principal=NULL; krb5_creds creds; krb5_keytab keytab=NULL; // Init Kerberos context krb5_error_code ret = krb5_init_context(&context); if (RESULT_OK(ret)) { // Parse the principal name, create principal ret = krb5_parse_name(context, (username + "@" + domain).c_str(), &principal); if(RESULT_OK(ret)) { if(password_is_keytab) { ret = krb5_kt_resolve(context, password.c_str(), &keytab); if(RESULT_OK(ret)) { ret = krb5_get_init_creds_keytab(context, &creds, principal, keytab, 0, NULL, NULL); if(RESULT_OK(ret)) { //OK } else { std::cerr<<"krb5_get_init_creds_keytab:"<<krb5_get_error_message(context,ret)<<std::endl; } } else { std::cerr<<"krb5_kt_resolve:"<<krb5_get_error_message(context,ret)<<std::endl; } } else { ret = krb5_get_init_creds_password(context, &creds, principal, password.c_str(),NULL, NULL, 0, NULL, NULL); if(RESULT_OK(ret)) { //OK } else { std::cerr<<"krb5_get_init_creds_password:"<<krb5_get_error_message(context,ret)<<std::endl; } } } else { std::cerr<<"krb5_parse_name:"<<krb5_get_error_message(context,ret)<<std::endl; } } else { // Handle error std::cerr<<"krb5_init_context:"<<krb5_get_error_message(context,ret)<<std::endl; } if(RESULT_OK(ret)) { krb5_ccache cache = NULL; ret = krb5_cc_default(context, &cache); if(RESULT_OK(ret)) { ret = krb5_cc_initialize(context, cache, principal); if(RESULT_OK(ret)) { ret = krb5_cc_store_cred(context, cache, &creds); if(RESULT_OK(ret)) { std::cout<<"kinit succeeded"<<std::endl; } else { std::cerr<<"krb5_cc_store_cred:"<<krb5_get_error_message(context,ret)<<std::endl; } } else { std::cerr<<"krb5_cc_initialize:"<<krb5_get_error_message(context,ret)<<std::endl; } // 替换为krb5_cc_close,不再销毁缓存 if(cache) krb5_cc_close(context, cache); } else { std::cerr<<"krb5_cc_default:"<<krb5_get_error_message(context,ret)<<std::endl; } } if(keytab) krb5_kt_close(context, keytab); krb5_free_cred_contents(context, &creds); if(principal) krb5_free_principal(context, principal); if(context) krb5_free_context(context); return ret; } int main() { std::string username = "username"; std::string domain = "DOMAIN"; std::string keytab = "/etc/keytab/test.keytab"; int res = kinit(username, domain, keytab, 1); std::cout<<"res = "<<res<<std::endl; return res; }
额外注意事项
- 确保运行程序的用户和执行
klist的用户是同一个,不同用户的默认缓存路径不同(通常为/tmp/krb5cc_<UID>),跨用户无法读取对方的缓存。 - 检查默认缓存路径的权限,确保程序有写入权限,避免因为权限问题导致票据写入失败。
内容的提问来源于stack exchange,提问作者SHR
相关产品推荐
相关产品推荐

