You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

代码实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 22:47:13