I have kerberos installed with a kdc server and a client. They both work and I can generate ticket without any problem via kinit. However, I cannot do that via the MIT Kerberos API. I have the following code, which should generate a ticket with the kdc:
#include <stdio.h>
#include <krb5.h>
#include <com_err.h>
#include <string.h>
#include <unistd.h>
#define TKT_LIFETIME 30
#define NO_REPLAYCACHE
static void syslog_err(const char* tag, long code, const char*format, va_list args){
printf("%s: %s in %s\n", tag, error_message(code), format);
}
#define check_code(err, tag)\
if(err){\
void (*proc)(const char*, long, const char*, va_list);\
proc=set_com_err_hook(syslog_err);\
com_err("Error: ",(err),(tag));\
(void)set_com_err_hook(proc);\
goto cleanup;\
}
int main() {
const char *username="[email protected]";
const char *password="pass";
//const char *host="";
const char kt_pathname[256] = "/etc/krb5.keytab";
const char service[256]="krbtgt/DOMAIN.COM";
const char host[256]="DOMAIN.COM";
krb5_error_code err;
krb5_context context;
krb5_auth_context auth_context = NULL;
krb5_creds credentials;
krb5_principal user_principal,
service_principal;
krb5_keytab keytab=NULL;
krb5_ccache ccache;
char ccache_name[L_tmpnam + 8];
krb5_get_init_creds_opt * gic_options;
#ifndef NO_REPLAYCACHE
krb5_verify_init_creds_opt vic_options;
#endif
krb5_data apreq_pkt;
char myhostname[256],sprinc[256];
int have_user_principal =0,
have_service_principal=0,
have_keytab=0,
have_credentials=0,
success=-1;
apreq_pkt.data=NULL;
/* --------------------------------------------------------------------------------- */
err = krb5_init_context(&context);
check_code(err, "init context");
(void) memset(ccache_name,0,sizeof(ccache_name));
(void) strcpy(ccache_name, "MEMORY:");
(void) mkstemp(&ccache_name[7]);
err = krb5_cc_resolve(context,ccache_name,&ccache);
/* else:
* err = krb5_cc_default(context,&ccache);
*/
if(err != 0) printf("[*] Error resolving credential cache name. Code: %d\n", err);
err = krb5_parse_name(context, username, &user_principal);
if(err != 0) printf("[*] Error parsing kerberos name. Code: %d\n", err);
else have_user_principal=1;
err = krb5_cc_initialize(context,ccache,user_principal);
if(err != 0) printf("[*] Error initializing credential cache. Code: %d\n", err);
(void) memset( (char*)&credentials, 0, sizeof(credentials));
// TODO: swtich later to: (void) gethostname(myhostname, sizeof(myhostname));
// TODO: not sure if double pointer.
krb5_get_init_creds_opt_alloc(context, &gic_options); // Allocate a new initial credential options structure
krb5_get_init_creds_opt_set_tkt_life(gic_options, TKT_LIFETIME); // Set the ticket lifetime in initial credential options
err = krb5_get_init_creds_password(context, &credentials, user_principal,password,0,0,0,NULL,gic_options); //Get initial credentials using a password
switch(err){
case 0:
have_credentials=1;
printf("[*] Successfully received credentials\n");
break;
case KRB5KDC_ERR_PREAUTH_FAILED:
printf("[*] Generic Pre-athentication failure. Unable to login. Access denied.\n");
case KRB5KRB_AP_ERR_BAD_INTEGRITY:
printf("[*] Decrypt integrity check failed - no domain controller\n");
case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
printf("[*] Bad username or password\n");
case KRB5_KDC_UNREACH:
printf("[*] Cannot contact any KDC for requested realm\n");
case KRB5_LIBOS_PWDINTR:
printf("[*] Password read interrupted\n");
case KRB5_REALM_CANT_RESOLVE:
printf("[*] Cannot resolve network address for KDC in requested realm\n");
case KRB5KDC_ERR_KEY_EXP:
printf("[*] Password has expired\n");
case KRB5_LIBOS_BADPWDMATCH:
printf("[*] Password mismatch\n");
case KRB5_CHPW_PWDNULL:
printf("[*] New password cannot be zero length\n");
case KRB5_CHPW_FAIL:
printf("[*] Password change failed\n");
default:
success=0;
break;
}
err = krb5_cc_store_cred(context, ccache, &credentials); // Store credentials in a credential cache
if (err != 0)
printf("[*] Failed storing credentials in credential cache\n");
char ***realmsp=malloc(sizeof(***realmsp));
err = krb5_get_host_realm(context, NULL,realmsp);
check_code(err, "krb5_get_host_realm");
free(realmsp);
err = krb5_sname_to_principal(context,host,service,KRB5_NT_SRV_HST,&service_principal); //Generate a full principal name from a service name KRB5_NT_UNKNOWN
check_code(err, "sname_to_principal");
have_service_principal=1;
err = krb5_kt_resolve(context,kt_pathname,&keytab); //Get a handle for a key table
check_code(err, "kt_resolve");
have_keytab=1;
/*
* A replay cache keeps track of all authenticators recenntly presented to a service. If a duplicate authneitcation request is detected in the replay cache,
* an error message is sent to application program
*/
#ifndef NO_REPLAYCACHE
krb5_verify_init_creds_opt_init(&vic_options); //Initialize a credential verification options structure
krb5_verify_init_creds_opt_set_ap_req_nofail(&vic_options,1); //Set whether credential verification is required
err = krb5_verify_init_creds(context, &credentials, service_principal, keytab,0,&vic_options); //Verify initial credentials against a keytab
check_code(err,"verify init credentials");
#else
//**********************************************CODE FAILES IN THE FOLLOWING FUNCTION **********************
err = krb5_mk_req(context,&auth_context,0,service,host,NULL,ccache,&apreq_pkt);
if(auth_context){
krb5_auth_con_free(context,auth_context); //Free a krb5_auth_context structure.
auth_context=NULL;
}
err = krb5_auth_con_init(context,&auth_context); //Create and initialize an authentication context
if (err!=0)
printf("[*] Failed to initialize an authentication context\n");
err = krb5_auth_con_setflags(context,auth_context,~KRB5_AUTH_CONTEXT_DO_TIME); //Set a flags field in a krb5_auth_context structure
if (err!=0)
printf("[*] Failed to set flags in context structure\n");
err = krb5_rd_req(context,&auth_context,&apreq_pkt,service_principal,keytab,NULL,NULL); // This function parses, decrypts and verifies a AP-REQ message
if (err!=0)
printf("[*] Failed to decrypt/parse AP-REQ message \n");
if(auth_context){
krb5_auth_con_free(context,auth_context); //Free a krb5_auth_context structure.
auth_context=NULL;
}
#endif
err = krb5_cc_destroy(context,ccache);
if (err!=0)
printf("[*] Failed to destroy kerberos context \n");
else
success=1;
cleanup:
if(apreq_pkt.data)
krb5_free_data_contents(context,&apreq_pkt); // Free the contents of a krb5_data structure and zero the data field
if(have_keytab){
krb5_kt_close(context,keytab);
if (err!=0)
printf("[*] Failed to destroy the keytab \n"); //Close a key table handle
}
if(have_user_principal)
krb5_free_principal(context,user_principal);
if(have_service_principal)
krb5_free_principal(context,service_principal);
if(have_credentials)
krb5_free_cred_contents(context,&credentials);
if(context)
krb5_free_context(context);
return 0;
}
when the code reaches the "krb5_mk_req" function, it fails with the error: "server not found in Kerberos database". I tried many combination with service + host, non of them worked. In kinit, I can generate a valid ticket with "kinit -S [email protected]". What am I missing? (DOMAIN.COM is not my real domain)
Thank you very much!
Seems either you have to create database if it is not already available otherwise you have to configure it in kerberos configuration file. Please check below link which explains how to create or configure kdc database. https://www.thetechnojournals.com/2019/12/setting-up-kerberos-in-mac-os-x.html