c++linuxkerberos

"kinit" program doesn't add to cache?


I've tried to implement the kinit command by code.

After struggling a bit I can run it successfully using krb5 on Linux Rocky 8.5.

My problem is that after running my program without any error, I can't see anything when running klist, While when running the kinit -kt test.keytab username@DOMAIN I can see the Kerberos ticket when running klist.

I'm Just wondering what I've been missing. I want it to run with the default cache exactly as the above kinit does.

#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;
            }
        }
        else
        {
            std::cerr<<"krb5_cc_default:"<<krb5_get_error_message(context,ret)<<std::endl;
        }

        if(cache) krb5_cc_destroy(context, cache);
    }


    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;
} 

Solution

  • You're calling krb5_cc_destroy() at the end. As the documentation says, it works like kdestroy:

    This function destroys any existing contents of cache and closes the handle to it.

    To close the handle without destroying contents, use krb5_cc_close().