Example #1
0
File: sss_krb5.c Project: 3van/sssd
char * sss_get_ccache_name_for_principal(TALLOC_CTX *mem_ctx,
                                         krb5_context ctx,
                                         krb5_principal principal,
                                         const char *location)
{
#ifdef HAVE_KRB5_CC_COLLECTION
    krb5_error_code kerr;
    krb5_ccache tmp_cc = NULL;
    char *tmp_ccname = NULL;
    char *ret_ccname = NULL;

    DEBUG(SSSDBG_TRACE_ALL,
          "Location: [%s]\n", location);

    kerr = krb5_cc_set_default_name(ctx, location);
    if (kerr != 0) {
        KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
        return NULL;
    }

    kerr = krb5_cc_cache_match(ctx, principal, &tmp_cc);
    if (kerr != 0) {
        const char *err_msg = sss_krb5_get_error_message(ctx, kerr);
        DEBUG(SSSDBG_TRACE_INTERNAL,
              "krb5_cc_cache_match failed: [%d][%s]\n", kerr, err_msg);
        sss_krb5_free_error_message(ctx, err_msg);
        return NULL;
    }

    kerr = krb5_cc_get_full_name(ctx, tmp_cc, &tmp_ccname);
    if (kerr != 0) {
        KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
        goto done;
    }

    DEBUG(SSSDBG_TRACE_ALL,
          "tmp_ccname: [%s]\n", tmp_ccname);

    ret_ccname = talloc_strdup(mem_ctx, tmp_ccname);
    if (ret_ccname == NULL) {
        DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed (ENOMEM).\n");
    }

done:
    if (tmp_cc != NULL) {
        kerr = krb5_cc_close(ctx, tmp_cc);
        if (kerr != 0) {
            KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
        }
    }
    krb5_free_string(ctx, tmp_ccname);

    return ret_ccname;
#else
    return NULL;
#endif /* HAVE_KRB5_CC_COLLECTION */
}
Example #2
0
static int prepare_response(TALLOC_CTX *mem_ctx,
                            const char *ccname,
                            time_t expire_time,
                            krb5_error_code kerr,
                            struct response **rsp)
{
    int ret;
    struct response *r = NULL;
    const char *krb5_msg = NULL;

    r = talloc_zero(mem_ctx, struct response);
    if (!r) return ENOMEM;

    r->buf = NULL;
    r->size = 0;

    DEBUG(SSSDBG_TRACE_FUNC, ("Building response for result [%d]\n", kerr));

    if (kerr == 0) {
        ret = pack_buffer(r, EOK, kerr, ccname, expire_time);
    } else {
        krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr);
        if (krb5_msg == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                    ("sss_krb5_get_error_message failed.\n"));
            return ENOMEM;
        }

        ret = pack_buffer(r, EFAULT, kerr, krb5_msg, 0);
        sss_krb5_free_error_message(krb5_error_ctx, krb5_msg);
    }

    if (ret != EOK) {
        DEBUG(1, ("pack_buffer failed\n"));
        return ret;
    }

    *rsp = r;
    return EOK;
}
Example #3
0
static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
                                               const char *realm_str,
                                               const char *princ_str,
                                               const char *keytab_name,
                                               const krb5_deltat lifetime,
                                               const char **ccname_out,
                                               time_t *expire_time_out)
{
    char *ccname;
    char *realm_name = NULL;
    char *full_princ = NULL;
    char *default_realm = NULL;
    char *tmp_str = NULL;
    krb5_context context = NULL;
    krb5_keytab keytab = NULL;
    krb5_ccache ccache = NULL;
    krb5_principal kprinc;
    krb5_creds my_creds;
    krb5_get_init_creds_opt options;
    krb5_error_code krberr;
    krb5_timestamp kdc_time_offset;
    int canonicalize = 0;
    int kdc_time_offset_usec;
    int ret;

    krberr = krb5_init_context(&context);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, ("Failed to init kerberos context\n"));
        return krberr;
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, ("Kerberos context initialized\n"));

    krberr = set_child_debugging(context);
    if (krberr != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE, ("Cannot set krb5_child debugging\n"));
    }

    if (!realm_str) {
        krberr = krb5_get_default_realm(context, &default_realm);
        if (krberr) {
            DEBUG(SSSDBG_OP_FAILURE, ("Failed to get default realm name: %s\n",
                      sss_krb5_get_error_message(context, krberr)));
            goto done;
        }

        realm_name = talloc_strdup(memctx, default_realm);
        krb5_free_default_realm(context, default_realm);
        if (!realm_name) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }
    } else {
        realm_name = talloc_strdup(memctx, realm_str);
        if (!realm_name) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }
    }

    DEBUG(SSSDBG_TRACE_INTERNAL, ("got realm_name: [%s]\n", realm_name));

    if (princ_str) {
        if (!strchr(princ_str, '@')) {
            full_princ = talloc_asprintf(memctx, "%s@%s",
                                         princ_str, realm_name);
        } else {
            full_princ = talloc_strdup(memctx, princ_str);
        }
    } else {
        char hostname[512];

        ret = gethostname(hostname, 511);
        if (ret == -1) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }
        hostname[511] = '\0';

        DEBUG(SSSDBG_TRACE_LIBS, ("got hostname: [%s]\n", hostname));

        ret = select_principal_from_keytab(memctx, hostname, realm_name,
                keytab_name, &full_princ, NULL, NULL);
        if (ret) {
            krberr = KRB5_KT_IOERR;
            goto done;
        }
    }
    if (!full_princ) {
        krberr = KRB5KRB_ERR_GENERIC;
        goto done;
    }
    DEBUG(SSSDBG_CONF_SETTINGS, ("Principal name is: [%s]\n", full_princ));

    krberr = krb5_parse_name(context, full_princ, &kprinc);
    if (krberr) {
        DEBUG(2, ("Unable to build principal: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    if (keytab_name) {
        krberr = krb5_kt_resolve(context, keytab_name, &keytab);
    } else {
        krberr = krb5_kt_default(context, &keytab);
    }
    DEBUG(SSSDBG_CONF_SETTINGS, ("Using keytab [%s]\n", KEYTAB_CLEAN_NAME));
    if (krberr) {
        DEBUG(SSSDBG_FATAL_FAILURE,
              ("Failed to read keytab file [%s]: %s\n",
               KEYTAB_CLEAN_NAME,
               sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    /* Verify the keytab */
    ret = sss_krb5_verify_keytab_ex(full_princ, keytab_name, context, keytab);
    if (ret) {
        DEBUG(SSSDBG_OP_FAILURE,
                ("Unable to verify principal is present in the keytab\n"));
        krberr = KRB5_KT_IOERR;
        goto done;
    }

    ccname = talloc_asprintf(memctx, "FILE:%s/ccache_%s", DB_PATH, realm_name);
    if (!ccname) {
        krberr = KRB5KRB_ERR_GENERIC;
        goto done;
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, ("keytab ccname: [%s]\n", ccname));

    krberr = krb5_cc_resolve(context, ccname, &ccache);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, ("Failed to set cache name: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    memset(&my_creds, 0, sizeof(my_creds));
    memset(&options, 0, sizeof(options));

    krb5_get_init_creds_opt_set_address_list(&options, NULL);
    krb5_get_init_creds_opt_set_forwardable(&options, 0);
    krb5_get_init_creds_opt_set_proxiable(&options, 0);
    krb5_get_init_creds_opt_set_tkt_life(&options, lifetime);

    tmp_str = getenv("KRB5_CANONICALIZE");
    if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
        DEBUG(SSSDBG_CONF_SETTINGS, ("Will canonicalize principals\n"));
        canonicalize = 1;
    }
    sss_krb5_get_init_creds_opt_set_canonicalize(&options, canonicalize);

    krberr = krb5_get_init_creds_keytab(context, &my_creds, kprinc,
                                        keytab, 0, NULL, &options);
    if (krberr) {
        DEBUG(SSSDBG_FATAL_FAILURE,
              ("Failed to init credentials: %s\n",
               sss_krb5_get_error_message(context, krberr)));
        sss_log(SSS_LOG_ERR,
                "Failed to initialize credentials using keytab [%s]: %s. "
                "Unable to create GSSAPI-encrypted LDAP connection.",
                KEYTAB_CLEAN_NAME,
                sss_krb5_get_error_message(context, krberr));
        goto done;
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, ("credentials initialized\n"));

    /* Use updated principal if changed due to canonicalization. */
    krberr = krb5_cc_initialize(context, ccache, my_creds.client);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, ("Failed to init ccache: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    krberr = krb5_cc_store_cred(context, ccache, &my_creds);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, ("Failed to store creds: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, ("credentials stored\n"));

#ifdef HAVE_KRB5_GET_TIME_OFFSETS
    krberr = krb5_get_time_offsets(context, &kdc_time_offset,
            &kdc_time_offset_usec);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, ("Failed to get KDC time offset: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        kdc_time_offset = 0;
    } else {
        if (kdc_time_offset_usec > 0) {
            kdc_time_offset++;
        }
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, ("Got KDC time offset\n"));
#else
    /* If we don't have this function, just assume no offset */
    kdc_time_offset = 0;
#endif

    krberr = 0;
    *ccname_out = ccname;
    *expire_time_out = my_creds.times.endtime - kdc_time_offset;

done:
    if (krberr != 0) KRB5_SYSLOG(krberr);
    if (keytab) krb5_kt_close(context, keytab);
    if (context) krb5_free_context(context);
    return krberr;
}
Example #4
0
static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
                                               krb5_context context,
                                               const char *realm_str,
                                               const char *princ_str,
                                               const char *keytab_name,
                                               const krb5_deltat lifetime,
                                               const char **ccname_out,
                                               time_t *expire_time_out)
{
    int fd;
    char *ccname;
    char *ccname_dummy;
    char *realm_name = NULL;
    char *full_princ = NULL;
    char *default_realm = NULL;
    char *tmp_str = NULL;
    krb5_keytab keytab = NULL;
    krb5_ccache ccache = NULL;
    krb5_principal kprinc;
    krb5_creds my_creds;
    krb5_get_init_creds_opt options;
    krb5_error_code krberr;
    krb5_timestamp kdc_time_offset;
    int canonicalize = 0;
    int kdc_time_offset_usec;
    int ret;
    TALLOC_CTX *tmp_ctx;
    char *ccname_file_dummy = NULL;
    char *ccname_file;
    mode_t old_umask;

    tmp_ctx = talloc_new(memctx);
    if (tmp_ctx == NULL) {
        krberr = KRB5KRB_ERR_GENERIC;
        goto done;
    }

    krberr = set_child_debugging(context);
    if (krberr != EOK) {
        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set krb5_child debugging\n");
    }

    if (!realm_str) {
        krberr = krb5_get_default_realm(context, &default_realm);
        if (krberr) {
            DEBUG(SSSDBG_OP_FAILURE, "Failed to get default realm name: %s\n",
                      sss_krb5_get_error_message(context, krberr));
            goto done;
        }

        realm_name = talloc_strdup(tmp_ctx, default_realm);
        krb5_free_default_realm(context, default_realm);
        if (!realm_name) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }
    } else {
        realm_name = talloc_strdup(tmp_ctx, realm_str);
        if (!realm_name) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }
    }

    DEBUG(SSSDBG_TRACE_INTERNAL, "got realm_name: [%s]\n", realm_name);

    if (princ_str) {
        if (!strchr(princ_str, '@')) {
            full_princ = talloc_asprintf(tmp_ctx, "%s@%s",
                                         princ_str, realm_name);
        } else {
            full_princ = talloc_strdup(tmp_ctx, princ_str);
        }
    } else {
        char hostname[HOST_NAME_MAX + 1];

        ret = gethostname(hostname, HOST_NAME_MAX);
        if (ret == -1) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }
        hostname[HOST_NAME_MAX] = '\0';

        DEBUG(SSSDBG_TRACE_LIBS, "got hostname: [%s]\n", hostname);

        ret = select_principal_from_keytab(tmp_ctx, hostname, realm_name,
                keytab_name, &full_princ, NULL, NULL);
        if (ret) {
            krberr = KRB5_KT_IOERR;
            goto done;
        }
    }
    if (!full_princ) {
        krberr = KRB5KRB_ERR_GENERIC;
        goto done;
    }
    DEBUG(SSSDBG_CONF_SETTINGS, "Principal name is: [%s]\n", full_princ);

    krberr = krb5_parse_name(context, full_princ, &kprinc);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, "Unable to build principal: %s\n",
                  sss_krb5_get_error_message(context, krberr));
        goto done;
    }

    if (keytab_name) {
        krberr = krb5_kt_resolve(context, keytab_name, &keytab);
    } else {
        krberr = krb5_kt_default(context, &keytab);
    }
    DEBUG(SSSDBG_CONF_SETTINGS, "Using keytab [%s]\n", KEYTAB_CLEAN_NAME);
    if (krberr) {
        DEBUG(SSSDBG_FATAL_FAILURE,
              "Failed to read keytab file [%s]: %s\n",
               KEYTAB_CLEAN_NAME,
               sss_krb5_get_error_message(context, krberr));
        goto done;
    }

    /* Verify the keytab */
    ret = lc_verify_keytab_ex(full_princ, keytab_name, context, keytab);
    if (ret) {
        DEBUG(SSSDBG_OP_FAILURE,
                "Unable to verify principal is present in the keytab\n");
        krberr = KRB5_KT_IOERR;
        goto done;
    }

    memset(&my_creds, 0, sizeof(my_creds));
    memset(&options, 0, sizeof(options));

    krb5_get_init_creds_opt_set_address_list(&options, NULL);
    krb5_get_init_creds_opt_set_forwardable(&options, 0);
    krb5_get_init_creds_opt_set_proxiable(&options, 0);
    krb5_get_init_creds_opt_set_tkt_life(&options, lifetime);

    tmp_str = getenv("KRB5_CANONICALIZE");
    if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
        DEBUG(SSSDBG_CONF_SETTINGS, "Will canonicalize principals\n");
        canonicalize = 1;
    }
    sss_krb5_get_init_creds_opt_set_canonicalize(&options, canonicalize);

    ccname_file = talloc_asprintf(tmp_ctx, "%s/ccache_%s",
                                  DB_PATH, realm_name);
    if (ccname_file == NULL) {
        krberr = ENOMEM;
        DEBUG(SSSDBG_CRIT_FAILURE,
              "talloc_asprintf failed: %s:[%d].\n",
              strerror(krberr), krberr);
        goto done;
    }

    ccname_file_dummy = talloc_asprintf(tmp_ctx, "%s/ccache_%s_XXXXXX",
                                        DB_PATH, realm_name);
    if (ccname_file_dummy == NULL) {
        krberr = ENOMEM;
        DEBUG(SSSDBG_CRIT_FAILURE,
              "talloc_asprintf failed: %s:[%d].\n",
              strerror(krberr), krberr);
        goto done;
    }

    old_umask = umask(077);
    fd = mkstemp(ccname_file_dummy);
    umask(old_umask);
    if (fd == -1) {
        ret = errno;
        DEBUG(SSSDBG_CRIT_FAILURE,
              "mkstemp failed: %s:[%d].\n",
              strerror(ret), ret);
        krberr = KRB5KRB_ERR_GENERIC;
        goto done;
    }
    /* We only care about creating a unique file name here, we don't
     * need the fd
     */
    close(fd);

    krberr = krb5_get_init_creds_keytab(context, &my_creds, kprinc,
                                        keytab, 0, NULL, &options);
    krb5_kt_close(context, keytab);
    keytab = NULL;
    if (krberr) {
        DEBUG(SSSDBG_FATAL_FAILURE,
              "Failed to init credentials: %s\n",
               sss_krb5_get_error_message(context, krberr));
        sss_log(SSS_LOG_ERR,
                "Failed to initialize credentials using keytab [%s]: %s. "
                "Unable to create GSSAPI-encrypted LDAP connection.",
                KEYTAB_CLEAN_NAME,
                sss_krb5_get_error_message(context, krberr));
        goto done;
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, "credentials initialized\n");

    ccname_dummy = talloc_asprintf(tmp_ctx, "FILE:%s", ccname_file_dummy);
    ccname = talloc_asprintf(tmp_ctx, "FILE:%s", ccname_file);
    if (ccname_dummy == NULL || ccname == NULL) {
        krberr = ENOMEM;
        goto done;
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, "keytab ccname: [%s]\n", ccname_dummy);

    krberr = krb5_cc_resolve(context, ccname_dummy, &ccache);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, "Failed to set cache name: %s\n",
                  sss_krb5_get_error_message(context, krberr));
        goto done;
    }

    /* Use updated principal if changed due to canonicalization. */
    krberr = krb5_cc_initialize(context, ccache, my_creds.client);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, "Failed to init ccache: %s\n",
                  sss_krb5_get_error_message(context, krberr));
        goto done;
    }

    krberr = krb5_cc_store_cred(context, ccache, &my_creds);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, "Failed to store creds: %s\n",
                  sss_krb5_get_error_message(context, krberr));
        goto done;
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, "credentials stored\n");

#ifdef HAVE_KRB5_GET_TIME_OFFSETS
    krberr = krb5_get_time_offsets(context, &kdc_time_offset,
            &kdc_time_offset_usec);
    if (krberr) {
        DEBUG(SSSDBG_OP_FAILURE, "Failed to get KDC time offset: %s\n",
                  sss_krb5_get_error_message(context, krberr));
        kdc_time_offset = 0;
    } else {
        if (kdc_time_offset_usec > 0) {
            kdc_time_offset++;
        }
    }
    DEBUG(SSSDBG_TRACE_INTERNAL, "Got KDC time offset\n");
#else
    /* If we don't have this function, just assume no offset */
    kdc_time_offset = 0;
#endif

    DEBUG(SSSDBG_TRACE_INTERNAL,
          "Renaming [%s] to [%s]\n", ccname_file_dummy, ccname_file);
    ret = rename(ccname_file_dummy, ccname_file);
    if (ret == -1) {
        ret = errno;
        DEBUG(SSSDBG_CRIT_FAILURE,
              "rename failed [%d][%s].\n", ret, strerror(ret));
        goto done;
    }
    ccname_file_dummy = NULL;

    krberr = 0;
    *ccname_out = talloc_steal(memctx, ccname);
    *expire_time_out = my_creds.times.endtime - kdc_time_offset;

done:
    if (krberr != 0) KRB5_SYSLOG(krberr);
    if (keytab) krb5_kt_close(context, keytab);
    if (context) krb5_free_context(context);
    if (ccname_file_dummy) {
        DEBUG(SSSDBG_TRACE_INTERNAL, "Unlinking [%s]\n", ccname_file_dummy);
        ret = unlink(ccname_file_dummy);
        if (ret == -1) {
            ret = errno;
            DEBUG(SSSDBG_MINOR_FAILURE,
                  "Unlink failed [%d][%s].\n", ret, strerror(ret));
        }
    }
    talloc_free(tmp_ctx);
    return krberr;
}
Example #5
0
static int lc_verify_keytab_ex(const char *principal,
                               const char *keytab_name,
                               krb5_context context,
                               krb5_keytab keytab)
{
    bool found;
    char *kt_principal;
    krb5_error_code krberr;
    krb5_kt_cursor cursor;
    krb5_keytab_entry entry;

    krberr = krb5_kt_start_seq_get(context, keytab, &cursor);
    if (krberr) {
        DEBUG(SSSDBG_FATAL_FAILURE,
              "Cannot read keytab [%s].\n", KEYTAB_CLEAN_NAME);

        sss_log(SSS_LOG_ERR, "Error reading keytab file [%s]: [%d][%s]. "
                             "Unable to create GSSAPI-encrypted LDAP "
                             "connection.",
                             KEYTAB_CLEAN_NAME, krberr,
                             sss_krb5_get_error_message(context, krberr));

        return EIO;
    }

    found = false;
    while ((krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
        krberr = krb5_unparse_name(context, entry.principal, &kt_principal);
        if (krberr) {
            DEBUG(SSSDBG_FATAL_FAILURE,
                  "Could not parse keytab entry\n");
            sss_log(SSS_LOG_ERR, "Could not parse keytab entry\n");
            return EIO;
        }

        if (strcmp(principal, kt_principal) == 0) {
            found = true;
        }
        free(kt_principal);
        krberr = sss_krb5_free_keytab_entry_contents(context, &entry);
        if (krberr) {
            /* This should never happen. The API docs for this function
             * specify only success for this function
             */
            DEBUG(SSSDBG_CRIT_FAILURE,"Could not free keytab entry contents\n");
            /* This is non-fatal, so we'll continue here */
        }

        if (found) {
            break;
        }
    }

    krberr = krb5_kt_end_seq_get(context, keytab, &cursor);
    if (krberr) {
        DEBUG(SSSDBG_FATAL_FAILURE, "Could not close keytab.\n");
        sss_log(SSS_LOG_ERR, "Could not close keytab file [%s].",
                             KEYTAB_CLEAN_NAME);
        return EIO;
    }

    if (!found) {
        DEBUG(SSSDBG_FATAL_FAILURE,
              "Principal [%s] not found in keytab [%s]\n",
               principal,
               KEYTAB_CLEAN_NAME);
        sss_log(SSS_LOG_ERR, "Error processing keytab file [%s]: "
                             "Principal [%s] was not found. "
                             "Unable to create GSSAPI-encrypted LDAP connection.",
                             KEYTAB_CLEAN_NAME, principal);

        return EFAULT;
    }

    return EOK;
}
Example #6
0
File: sss_krb5.c Project: 3van/sssd
errno_t select_principal_from_keytab(TALLOC_CTX *mem_ctx,
                                     const char *hostname,
                                     const char *desired_realm,
                                     const char *keytab_name,
                                     char **_principal,
                                     char **_primary,
                                     char **_realm)
{
    krb5_error_code kerr = 0;
    krb5_context krb_ctx = NULL;
    krb5_keytab keytab = NULL;
    krb5_principal client_princ = NULL;
    TALLOC_CTX *tmp_ctx;
    char *primary = NULL;
    char *realm = NULL;
    int i = 0;
    errno_t ret;
    char *principal_string;
    const char *realm_name;
    int realm_len;

    /**
     * The %s conversion is passed as-is, the %S conversion is translated to
     * "short host name"
     *
     * Priority of lookup:
     * - our.hostname@REALM or host/our.hostname@REALM depending on the input
     * - SHORT.HOSTNAME$@REALM (AD domain)
     * - host/our.hostname@REALM
     * - foobar$@REALM (AD domain)
     * - host/foobar@REALM
     * - host/foo@BAR
     * - pick the first principal in the keytab
     */
    const char *primary_patterns[] = {"%s", "%S$", "host/%s", "*$", "host/*",
                                      "host/*", NULL};
    const char *realm_patterns[] =   {"%s", "%s",  "%s",      "%s", "%s",
                                      NULL,     NULL};

    DEBUG(SSSDBG_FUNC_DATA,
          "trying to select the most appropriate principal from keytab\n");
    tmp_ctx = talloc_new(NULL);
    if (!tmp_ctx) {
        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n");
        return ENOMEM;
    }

    kerr = krb5_init_context(&krb_ctx);
    if (kerr) {
        DEBUG(SSSDBG_OP_FAILURE, "Failed to init kerberos context\n");
        ret = EFAULT;
        goto done;
    }

    if (keytab_name != NULL) {
        kerr = krb5_kt_resolve(krb_ctx, keytab_name, &keytab);
    } else {
        kerr = krb5_kt_default(krb_ctx, &keytab);
    }
    if (kerr) {
        DEBUG(SSSDBG_FATAL_FAILURE,
              "Failed to read keytab [%s]: %s\n",
               KEYTAB_CLEAN_NAME,
               sss_krb5_get_error_message(krb_ctx, kerr));
        ret = EFAULT;
        goto done;
    }

    if (!desired_realm) {
        desired_realm = "*";
    }
    if (!hostname) {
        hostname = "*";
    }

    do {
        if (primary_patterns[i]) {
            primary = sss_krb5_get_primary(tmp_ctx,
                                           primary_patterns[i],
                                           hostname);
            if (primary == NULL) {
                ret = ENOMEM;
                goto done;
            }
        } else {
            primary = NULL;
        }
        if (realm_patterns[i]) {
            realm = talloc_asprintf(tmp_ctx, realm_patterns[i], desired_realm);
            if (realm == NULL) {
                ret = ENOMEM;
                goto done;
            }
        } else {
            realm = NULL;
        }

        kerr = find_principal_in_keytab(krb_ctx, keytab, primary, realm,
                                        &client_princ);
        talloc_zfree(primary);
        talloc_zfree(realm);
        if (kerr == 0) {
            break;
        }
        if (client_princ != NULL) {
            krb5_free_principal(krb_ctx, client_princ);
            client_princ = NULL;
        }
        i++;
    } while(primary_patterns[i-1] != NULL || realm_patterns[i-1] != NULL);

    if (kerr == 0) {
        if (_principal) {
            kerr = krb5_unparse_name(krb_ctx, client_princ, &principal_string);
            if (kerr) {
                DEBUG(SSSDBG_CRIT_FAILURE, "krb5_unparse_name failed\n");
                ret = EFAULT;
                goto done;
            }

            *_principal = talloc_strdup(mem_ctx, principal_string);
            free(principal_string);
            if (!*_principal) {
                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed\n");
                ret = ENOMEM;
                goto done;
            }
            DEBUG(SSSDBG_FUNC_DATA, "Selected principal: %s\n", *_principal);
        }

        if (_primary) {
            kerr = sss_krb5_unparse_name_flags(krb_ctx, client_princ,
                                               KRB5_PRINCIPAL_UNPARSE_NO_REALM,
                                               &principal_string);
            if (kerr) {
                DEBUG(SSSDBG_CRIT_FAILURE, "krb5_unparse_name failed\n");
                ret = EFAULT;
                goto done;
            }

            *_primary = talloc_strdup(mem_ctx, principal_string);
            free(principal_string);
            if (!*_primary) {
                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed\n");
                if (_principal) talloc_zfree(*_principal);
                ret = ENOMEM;
                goto done;
            }
            DEBUG(SSSDBG_FUNC_DATA, "Selected primary: %s\n", *_primary);
        }

        if (_realm) {
            sss_krb5_princ_realm(krb_ctx, client_princ,
                                 &realm_name,
                                 &realm_len);
            if (realm_len == 0) {
                DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_princ_realm failed.\n");
                if (_principal) talloc_zfree(*_principal);
                if (_primary) talloc_zfree(*_primary);
                ret = EINVAL;
                goto done;
            }

            *_realm = talloc_asprintf(mem_ctx, "%.*s",
                                      realm_len, realm_name);
            if (!*_realm) {
                DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n");
                if (_principal) talloc_zfree(*_principal);
                if (_primary) talloc_zfree(*_primary);
                ret = ENOMEM;
                goto done;
            }
            DEBUG(SSSDBG_FUNC_DATA, "Selected realm: %s\n", *_realm);
        }

        ret = EOK;
    } else {
        DEBUG(SSSDBG_MINOR_FAILURE, "No suitable principal found in keytab\n");
        ret = ENOENT;
    }

done:
    if (ret != EOK) {
        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to read keytab [%s]: %s\n",
               KEYTAB_CLEAN_NAME, strerror(ret));
        sss_log(SSS_LOG_ERR, "Failed to read keytab [%s]: %s\n",
                KEYTAB_CLEAN_NAME, strerror(ret));
    }
    if (keytab) krb5_kt_close(krb_ctx, keytab);
    if (krb_ctx) krb5_free_context(krb_ctx);
    if (client_princ != NULL) {
        krb5_free_principal(krb_ctx, client_princ);
        client_princ = NULL;
    }
    talloc_free(tmp_ctx);
    return ret;
}
Example #7
0
static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
                                               const char *realm_str,
                                               const char *princ_str,
                                               const char *keytab_name,
                                               const krb5_deltat lifetime,
                                               const char **ccname_out,
                                               time_t *expire_time_out)
{
    char *ccname;
    char *realm_name = NULL;
    char *full_princ = NULL;
    char *default_realm = NULL;
    krb5_context context = NULL;
    krb5_keytab keytab = NULL;
    krb5_ccache ccache = NULL;
    krb5_principal kprinc;
    krb5_creds my_creds;
    krb5_get_init_creds_opt options;
    krb5_error_code krberr;
    krb5_timestamp kdc_time_offset;
    int kdc_time_offset_usec;
    int ret;

    krberr = krb5_init_context(&context);
    if (krberr) {
        DEBUG(2, ("Failed to init kerberos context\n"));
        return krberr;
    }

    if (!realm_str) {
        krberr = krb5_get_default_realm(context, &default_realm);
        if (krberr) {
            DEBUG(2, ("Failed to get default realm name: %s\n",
                      sss_krb5_get_error_message(context, krberr)));
            goto done;
        }

        realm_name = talloc_strdup(memctx, default_realm);
        krb5_free_default_realm(context, default_realm);
        if (!realm_name) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }

    } else {
        realm_name = talloc_strdup(memctx, realm_str);
        if (!realm_name) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }
    }

    if (princ_str) {
        if (!strchr(princ_str, '@')) {
            full_princ = talloc_asprintf(memctx, "%s@%s",
                                         princ_str, realm_name);
        } else {
            full_princ = talloc_strdup(memctx, princ_str);
        }
    } else {
        char hostname[512];

        ret = gethostname(hostname, 511);
        if (ret == -1) {
            krberr = KRB5KRB_ERR_GENERIC;
            goto done;
        }
        hostname[511] = '\0';

        ret = select_principal_from_keytab(memctx, hostname, realm_name,
                                           keytab_name, &full_princ, NULL, NULL);
        if (ret) goto done;
    }
    if (!full_princ) {
        krberr = KRB5KRB_ERR_GENERIC;
        goto done;
    }
    DEBUG(4, ("Principal name is: [%s]\n", full_princ));

    krberr = krb5_parse_name(context, full_princ, &kprinc);
    if (krberr) {
        DEBUG(2, ("Unable to build principal: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    if (keytab_name) {
        krberr = krb5_kt_resolve(context, keytab_name, &keytab);
    } else {
        krberr = krb5_kt_default(context, &keytab);
    }
    if (krberr) {
        DEBUG(0, ("Failed to read keytab file: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    /* Verify the keytab */
    ret = sss_krb5_verify_keytab_ex(full_princ, keytab_name, context, keytab);
    if (ret) {
        DEBUG(2, ("Unable to verify principal is present in the keytab\n"));
        krberr = KRB5_KT_IOERR;
        goto done;
    }

    ccname = talloc_asprintf(memctx, "FILE:%s/ccache_%s", DB_PATH, realm_name);
    if (!ccname) {
        krberr = KRB5KRB_ERR_GENERIC;
        goto done;
    }

    krberr = krb5_cc_resolve(context, ccname, &ccache);
    if (krberr) {
        DEBUG(2, ("Failed to set cache name: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    memset(&my_creds, 0, sizeof(my_creds));
    memset(&options, 0, sizeof(options));

    krb5_get_init_creds_opt_set_address_list(&options, NULL);
    krb5_get_init_creds_opt_set_forwardable(&options, 0);
    krb5_get_init_creds_opt_set_proxiable(&options, 0);
    krb5_get_init_creds_opt_set_tkt_life(&options, lifetime);

    krberr = krb5_get_init_creds_keytab(context, &my_creds, kprinc,
                                        keytab, 0, NULL, &options);

    if (krberr) {
        DEBUG(0, ("Failed to init credentials: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        sss_log(SSS_LOG_ERR, "Failed to initialize credentials using keytab [%s]: %s. "
                             "Unable to create GSSAPI-encrypted LDAP connection.",
                             keytab_name, sss_krb5_get_error_message(context, krberr));
        goto done;
    }

    krberr = krb5_cc_initialize(context, ccache, kprinc);
    if (krberr) {
        DEBUG(2, ("Failed to init ccache: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    krberr = krb5_cc_store_cred(context, ccache, &my_creds);
    if (krberr) {
        DEBUG(2, ("Failed to store creds: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        goto done;
    }

    krberr = krb5_get_time_offsets(context, &kdc_time_offset, &kdc_time_offset_usec);
    if (krberr) {
        DEBUG(2, ("Failed to get KDC time offset: %s\n",
                  sss_krb5_get_error_message(context, krberr)));
        kdc_time_offset = 0;
    } else {
        if (kdc_time_offset_usec > 0) {
            kdc_time_offset++;
        }
    }

    krberr = 0;
    *ccname_out = ccname;
    *expire_time_out = my_creds.times.endtime - kdc_time_offset;

done:
    if (keytab) krb5_kt_close(context, keytab);
    if (context) krb5_free_context(context);
    return krberr;
}