0

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.

noirello
  • 51
  • 1
  • 11

1 Answers1

0

There are several actions necessary to achieve this:

  1. Obtain a regular TGT.
  2. Make sure that Cyrus SASL is properly compiled against MIT Kerberos. You can check that with the pluginviewer
  3. Make OpenLDAP properly compile against Cyrus SASL.
  4. Test your setup with ldapsearch to make sure that Kerberos actually work.

If all is well, go on to:

  1. Use the public function gss_acquire_cred_with_password from gssapi_ext.h.
  2. Now prepare the LDAP handle with an interactive bind, nothing else will work.
  3. This handle can passthrough auth to Cyrus SASL which expects it in the format of the mechanism.
  4. Now you need to pass your gss_cred_t to the berval struct with a pointer and sizeof.
  5. Cyrus SASL will pick it up and pass it to the GSS context.

Unfortunately, both OpenLDAP and Cyrus SASL are terrible documented. How do I know that it will work? I read the source code of both to understand and make use of that usecase.

Start by reading the header files and you will get to a result. Reask again if you are stuck.

Michael-O
  • 18,123
  • 6
  • 55
  • 121