I'm trying to implement the programming logic for requesting TGT for my application, thus it wouldn't be necessary to call kinit
from command line before authenticate to an LDAP server via GSSAPI and GSS-SPNEGO mechanisms.
I create an in-memory ccache, request a TGT with username and password, then import the credentials using gss_krb5_import_cred
. The GSSAPI context is set for the LDAP structure before starting the authentication.
The example code below is working fine with GSSAPI, but when I try to change the mechanism to GSS-SPNEGO I get a local LDAP error (-2) with the following message:
SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure. Minor code may provide more information (SPNEGO cannot find mechanisms to negotiate)
int create_krb5_cred(krb5_context ctx, char *realm, char *user,
char *password, krb5_ccache *ccache, gss_cred_id_t *gsscred) {
int rc = 0, minor_stat = 0;
int len = 0;
const char *cname = NULL;
krb5_get_init_creds_opt *cred_opt;
krb5_creds creds;
krb5_principal princ = NULL;
if (realm == NULL || user == NULL || password == NULL) return -1;
rc = krb5_cc_new_unique(ctx, "MEMORY", NULL, ccache);
if (rc != 0) goto clear;
len = strlen(realm);
rc = krb5_build_principal(ctx, &princ, len, realm, user, NULL);
if (rc != 0) goto clear;
rc = krb5_cc_initialize(ctx, *ccache, princ);
if (rc != 0) goto clear;
rc = krb5_get_init_creds_opt_alloc(ctx, &cred_opt);
if (rc != 0) goto clear;
rc = krb5_get_init_creds_password(ctx, &creds, princ, password, 0, NULL, 0, NULL, NULL);
if (rc != 0) goto clear;
rc= krb5_cc_store_cred(ctx, *ccache, &creds);
if (rc != 0) goto clear;
cname = krb5_cc_get_name(ctx, *ccache);
if (cname == NULL) goto clear;
rc = gss_krb5_ccache_name(&minor_stat, cname, NULL);
if (rc != 0) goto clear;
rc = gss_krb5_import_cred(&minor_stat, *ccache, princ, 0, gsscred);
clear:
if (princ != NULL) krb5_free_principal(ctx, princ);
return rc;
}
int remove_krb5_cred(krb5_context ctx, krb5_ccache ccache, gss_cred_id_t *gsscred) {
int rc = 0;
rc = gss_release_cred(NULL, gsscred);
if (rc != 0) return rc;
rc = krb5_cc_destroy(ctx, ccache);
krb5_free_context(ctx);
return rc;
}
int main(void) {
int rc = 0;
krb5_context ctx;
krb5_ccache ccache;
gss_cred_id_t gsscred = NULL;
rc = krb5_init_context(&ctx);
create_krb5_cred(ctx, "EXAMPLE.ORG", "testuser", "secret", &ccache, &gsscred);
LDAP *ld = NULL;
const int version = LDAP_VERSION3;
void *defaults = NULL;
ldap_initialize(&ld, "ldap://example.org");
ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
/* Setting the credentials and handling the SASL binding with the `interact` function
(with setting the new GSS context) is not disclosed here...*/
rc = ldap_sasl_interactive_bind_s(ld, NULL, "GSSAPI", NULL, NULL, 0, interact, defaults);
printf("Connect 0x%x\n", rc);
remove_krb5_cred(ctx, ccache, &gsscred);
struct berval *authzid;
rc = ldap_whoami_s(ld, &authzid, NULL, NULL);
printf("RC %d %s\n\n", rc, authzid->bv_val);
}
This old forum post suggest that the SPNEGO oid is not set for the imported credentials, thus the LDAP will ignore it during the authentication.
I've tried to use gss_acquire_cred
function instead of gss_krb5_import_cred
, but I have no success receiving a TGT with it (not even for GSSAPI).
Any ideas to how to achieve successful authentication with both GSSAPI and GSS-SPNEGO would be welcome.
Update: I've managed to use gss_acquire_cred
with GSSAPI and GSS-SPNEGO, but I had to use file based credential cache instead of an in-memory based.