static krb5_error_code find_cred(krb5_context context, krb5_ccache id, krb5_principal server, krb5_creds **tgts, krb5_creds *out_creds) { krb5_error_code ret; krb5_creds mcreds; krb5_cc_clear_mcred(&mcreds); mcreds.server = server; ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM, &mcreds, out_creds); if(ret == 0) return 0; while(tgts && *tgts){ if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, &mcreds, *tgts)){ ret = krb5_copy_creds_contents(context, *tgts, out_creds); return ret; } tgts++; } return not_found(context, server, KRB5_CC_NOTFOUND); }
krb5_error_code kcm_ccache_retrieve_cred_internal(krb5_context context, kcm_ccache ccache, krb5_flags whichfields, const krb5_creds *mcreds, krb5_creds **creds) { krb5_boolean match; struct kcm_creds *c; krb5_error_code ret; memset(creds, 0, sizeof(*creds)); ret = KRB5_CC_END; match = FALSE; for (c = ccache->creds; c != NULL; c = c->next) { match = krb5_compare_creds(context, whichfields, mcreds, &c->cred); if (match) break; } if (match) { ret = 0; *creds = &c->cred; } return ret; }
krb5_error_code kcm_ccache_remove_cred_internal(krb5_context context, kcm_ccache ccache, krb5_flags whichfields, const krb5_creds *mcreds) { krb5_error_code ret; struct kcm_creds **c; ret = KRB5_CC_NOTFOUND; for (c = &ccache->creds; *c != NULL; c = &(*c)->next) { if (krb5_compare_creds(context, whichfields, mcreds, &(*c)->cred)) { struct kcm_creds *cred = *c; *c = cred->next; krb5_free_cred_contents(context, &cred->cred); free(cred); ret = 0; if (*c == NULL) break; } } return ret; }
static krb5_boolean matchfunc(krb5_context context, void *ptr, const krb5_creds *creds) { struct ctx *ctx = ptr; if (krb5_compare_creds(context, ctx->whichfields, &ctx->mcreds, creds)) return TRUE; return FALSE; }
static krb5_error_code mcc_remove_cred(krb5_context context, krb5_ccache id, krb5_flags which, krb5_creds *mcreds) { krb5_mcache *m = MCACHE(id); struct link **q, *p; for(q = &m->creds, p = *q; p; p = *q) { if(krb5_compare_creds(context, which, mcreds, &p->cred)) { *q = p->next; krb5_free_cred_contents(context, &p->cred); free(p); } else q = &p->next; } return 0; }
static int is_primary_credential_p(krb5_context context, kcm_ccache ccache, krb5_creds *newcred) { krb5_flags whichfields; if (ccache->client == NULL) return 0; if (newcred->client == NULL || !krb5_principal_compare(context, ccache->client, newcred->client)) return 0; /* XXX just checks whether it's the first credential in the cache */ if (ccache->creds == NULL) return 0; whichfields = KRB5_TC_MATCH_KEYTYPE | KRB5_TC_MATCH_FLAGS_EXACT | KRB5_TC_MATCH_TIMES_EXACT | KRB5_TC_MATCH_AUTHDATA | KRB5_TC_MATCH_2ND_TKT | KRB5_TC_MATCH_IS_SKEY; return krb5_compare_creds(context, whichfields, newcred, &ccache->creds->cred); }
static krb5_error_code get_cred_kdc_referral(krb5_context context, krb5_kdc_flags flags, krb5_ccache ccache, krb5_creds *in_creds, krb5_principal impersonate_principal, Ticket *second_ticket, krb5_creds **out_creds, krb5_creds ***ret_tgts) { krb5_const_realm client_realm; krb5_error_code ret; krb5_creds tgt, referral, ticket; int loop = 0; int ok_as_delegate = 1; if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) { krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED, N_("Name too short to do referals, skipping", "")); return KRB5KDC_ERR_PATH_NOT_ACCEPTED; } memset(&tgt, 0, sizeof(tgt)); memset(&ticket, 0, sizeof(ticket)); flags.b.canonicalize = 1; *out_creds = NULL; client_realm = krb5_principal_get_realm(context, in_creds->client); /* find tgt for the clients base realm */ { krb5_principal tgtname; ret = krb5_make_principal(context, &tgtname, client_realm, KRB5_TGS_NAME, client_realm, NULL); if(ret) return ret; ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt); krb5_free_principal(context, tgtname); if (ret) return ret; } referral = *in_creds; ret = krb5_copy_principal(context, in_creds->server, &referral.server); if (ret) { krb5_free_cred_contents(context, &tgt); return ret; } ret = krb5_principal_set_realm(context, referral.server, client_realm); if (ret) { krb5_free_cred_contents(context, &tgt); krb5_free_principal(context, referral.server); return ret; } while (loop++ < 17) { krb5_creds **tickets; krb5_creds mcreds; char *referral_realm; /* Use cache if we are not doing impersonation or contrainte deleg */ if (impersonate_principal == NULL || flags.b.constrained_delegation) { krb5_cc_clear_mcred(&mcreds); mcreds.server = referral.server; ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket); } else ret = EINVAL; if (ret) { ret = get_cred_kdc_address(context, ccache, flags, NULL, &referral, &tgt, impersonate_principal, second_ticket, &ticket); if (ret) goto out; } /* Did we get the right ticket ? */ if (krb5_principal_compare_any_realm(context, referral.server, ticket.server)) break; if (!krb5_principal_is_krbtgt(context, ticket.server)) { krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, N_("Got back an non krbtgt " "ticket referrals", "")); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } referral_realm = ticket.server->name.name_string.val[1]; /* check that there are no referrals loops */ tickets = *ret_tgts; krb5_cc_clear_mcred(&mcreds); mcreds.server = ticket.server; while(tickets && *tickets){ if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, &mcreds, *tickets)) { krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, N_("Referral from %s " "loops back to realm %s", ""), tgt.server->realm, referral_realm); ret = KRB5_GET_IN_TKT_LOOP; goto out; } tickets++; } /* * if either of the chain or the ok_as_delegate was stripped * by the kdc, make sure we strip it too. */ if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) { ok_as_delegate = 0; ticket.flags.b.ok_as_delegate = 0; } ret = add_cred(context, &ticket, ret_tgts); if (ret) goto out; /* try realm in the referral */ ret = krb5_principal_set_realm(context, referral.server, referral_realm); krb5_free_cred_contents(context, &tgt); tgt = ticket; memset(&ticket, 0, sizeof(ticket)); if (ret) goto out; } ret = krb5_copy_creds(context, &ticket, out_creds); out: krb5_free_principal(context, referral.server); krb5_free_cred_contents(context, &tgt); krb5_free_cred_contents(context, &ticket); return ret; }
static krb5_error_code tkt_referral_recv(krb5_context context, krb5_tkt_creds_context ctx, krb5_data *in, krb5_data *out, krb5_realm *realm, unsigned int *flags) { krb5_error_code ret; krb5_creds outcred, mcred; unsigned long n; _krb5_debugx(context, 10, "tkt_referral_recv: %s", ctx->server_name); memset(&outcred, 0, sizeof(outcred)); ret = parse_tgs_rep(context, ctx, in, &outcred); if (ret) { _krb5_debugx(context, 10, "tkt_referral_recv: parse_tgs_rep %d", ret); tkt_reset(context, ctx); ctx->state = tkt_capath_init; return 0; } /* * Check if we found the right ticket */ if (krb5_principal_compare_any_realm(context, ctx->next.server, outcred.server)) { ret = krb5_copy_creds(context, &outcred, &ctx->cred); if (ret) return (ctx->error = ret); krb5_free_cred_contents(context, &outcred); ctx->state = tkt_store; return 0; } if (!krb5_principal_is_krbtgt(context, outcred.server)) { krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, N_("Got back an non krbtgt " "ticket referrals", "")); krb5_free_cred_contents(context, &outcred); ctx->state = tkt_capath_init; return 0; } _krb5_debugx(context, 10, "KDC for realm %s sends a referrals to %s", ctx->tgt.server->realm, outcred.server->name.name_string.val[1]); /* * check if there is a loop */ krb5_cc_clear_mcred(&mcred); mcred.server = outcred.server; for (n = 0; ctx->tickets && ctx->tickets[n]; n++) { if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, &mcred, ctx->tickets[n])) { _krb5_debugx(context, 5, "Referral from %s loops back to realm %s", ctx->tgt.server->realm, outcred.server->realm); ctx->state = tkt_capath_init; return 0; } } #define MAX_KDC_REFERRALS_LOOPS 15 if (n > MAX_KDC_REFERRALS_LOOPS) { ctx->state = tkt_capath_init; return 0; } /* * filter out ok-as-delegate if needed */ if (ctx->ok_as_delegate == 0 || outcred.flags.b.ok_as_delegate == 0) { ctx->ok_as_delegate = 0; outcred.flags.b.ok_as_delegate = 0; } /* add to iteration cache */ ret = add_cred(context, &outcred, &ctx->tickets); if (ret) { ctx->state = tkt_capath_init; return 0; } /* set up next server to talk to */ krb5_free_cred_contents(context, &ctx->tgt); ctx->tgt = outcred; /* * Setup next target principal to target */ ret = krb5_principal_set_realm(context, ctx->next.server, ctx->tgt.server->realm); if (ret) { ctx->state = tkt_capath_init; return 0; } ctx->state = tkt_referral_send; return 0; }
static krb5_error_code KRB5_CALLCONV scc_remove_cred(krb5_context context, krb5_ccache id, krb5_flags which, krb5_creds *mcreds) { krb5_scache *s = SCACHE(id); krb5_error_code ret; sqlite3_stmt *stmt; sqlite_uint64 credid = 0; const void *data = NULL; size_t len = 0; ret = make_database(context, s); if (ret) return ret; ret = prepare_stmt(context, s->db, &stmt, "SELECT cred,oid FROM credentials " "WHERE cid = ?"); if (ret) return ret; sqlite3_bind_int(stmt, 1, s->cid); /* find credential... */ while (1) { krb5_creds creds; ret = sqlite3_step(stmt); if (ret == SQLITE_DONE) { ret = 0; break; } else if (ret != SQLITE_ROW) { ret = KRB5_CC_IO; krb5_set_error_message(context, ret, N_("scache Database failed: %s", ""), sqlite3_errmsg(s->db)); break; } if (sqlite3_column_type(stmt, 0) != SQLITE_BLOB) { ret = KRB5_CC_END; krb5_set_error_message(context, ret, N_("Credential of wrong type " "for SCC:%s:%s", ""), s->name, s->file); break; } data = sqlite3_column_blob(stmt, 0); len = sqlite3_column_bytes(stmt, 0); ret = decode_creds(context, data, len, &creds); if (ret) break; ret = krb5_compare_creds(context, which, mcreds, &creds); krb5_free_cred_contents(context, &creds); if (ret) { credid = sqlite3_column_int64(stmt, 1); ret = 0; break; } } sqlite3_finalize(stmt); if (id) { ret = prepare_stmt(context, s->db, &stmt, "DELETE FROM credentials WHERE oid=?"); if (ret) return ret; sqlite3_bind_int(stmt, 1, credid); do { ret = sqlite3_step(stmt); } while (ret == SQLITE_ROW); sqlite3_finalize(stmt); if (ret != SQLITE_DONE) { ret = KRB5_CC_IO; krb5_set_error_message(context, ret, N_("failed to delete scache credental", "")); } else ret = 0; } return ret; }