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;
}
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()
.