0

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="foo@DOMAIN.COM";
    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 krbtgt@DOMAIN.COM". What am I missing? (DOMAIN.COM is not my real domain)

Thank you very much!

cs crypt
  • 1
  • 2
  • Why are you using a low level API instead of high level (GSS-API)? – Michael-O Jan 01 '20 at 09:30
  • Hi @Michael-O , I want to acccess internal structures inside the kerberos ticket. As far as I know, the GSS-API is suitable for people who want less overhead. – cs crypt Jan 01 '20 at 09:52
  • What is the value of your SPN? I am not familiar with the C code, however I had this error. The reason was incorrect SPN. try this command and let me know if this works -> "kinit " and provide password if prompted.. (note if you are asking for ticket for self, then username@domain is your SPN in above command) – Bhushan Karmarkar Jan 03 '20 at 06:08

1 Answers1

0

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

Ashok Prajapati
  • 374
  • 2
  • 7
  • Hi. I have a working kerberos and I'm able to create tickets with the kinit command. What is perplexing me is the fact that it doesn't work with the C api – cs crypt Dec 31 '19 at 15:30
  • `kinit` uses a set of rules to find its configuration file -- `KRB5_CONFIG` env variable, then system-specific defaults (e.g. `/etc/krb5.conf` for Linux). Does the C API use the same rules, or do you have to give it **explicitly** the location of `krb5.conf`?? – Samson Scharfrichter Jan 05 '20 at 13:57