krb5_error_code krb5_kt_find_realm(krb5_context context, krb5_keytab keytab, krb5_principal princ, krb5_data *realm) { krb5_kt_cursor cur; krb5_keytab_entry ent; krb5_boolean match; krb5_data tmp_realm; krb5_error_code ret, ret2; ret = krb5_kt_start_seq_get(context, keytab, &cur); if (ret != 0) { return (ret); } while ((ret = krb5_kt_next_entry(context, keytab, &ent, &cur)) == 0) { /* For the comparison the realms should be the same. */ memcpy(&tmp_realm, &ent.principal->realm, sizeof (krb5_data)); memcpy(&ent.principal->realm, &princ->realm, sizeof (krb5_data)); match = krb5_principal_compare(context, ent.principal, princ); /* Copy the realm back */ memcpy(&ent.principal->realm, &tmp_realm, sizeof (krb5_data)); if (match) { /* * A suitable entry was found in the keytab. * Copy its realm */ ret = krb5int_copy_data_contents(context, &ent.principal->realm, realm); if (ret) { krb5_kt_free_entry(context, &ent); krb5_kt_end_seq_get(context, keytab, &cur); return (ret); } krb5_kt_free_entry(context, &ent); break; } krb5_kt_free_entry(context, &ent); } ret2 = krb5_kt_end_seq_get(context, keytab, &cur); if (ret == KRB5_KT_END) { return (KRB5_KT_NOTFOUND); } return (ret ? ret : ret2); }
krb5_error_code _adcli_krb5_keytab_clear (krb5_context k5, krb5_keytab keytab, krb5_boolean (* match_func) (krb5_context, krb5_keytab_entry *, void *), void *match_data) { krb5_kt_cursor cursor; krb5_keytab_entry entry; krb5_error_code code; code = krb5_kt_start_seq_get (k5, keytab, &cursor); if (code == KRB5_KT_END || code == ENOENT) return 0; else if (code != 0) return code; for (;;) { code = krb5_kt_next_entry (k5, keytab, &entry, &cursor); if (code != 0) break; /* See if we should remove this entry */ if (!match_func (k5, &entry, match_data)) { krb5_free_keytab_entry_contents (k5, &entry); continue; } /* * Here we close the cursor, remove the entry and then * start all over again from the beginning. Dumb but works. */ code = krb5_kt_end_seq_get (k5, keytab, &cursor); return_val_if_fail (code == 0, code); code = krb5_kt_remove_entry (k5, keytab, &entry); krb5_free_keytab_entry_contents (k5, &entry); if (code != 0) return code; code = krb5_kt_start_seq_get (k5, keytab, &cursor); return_val_if_fail (code == 0, code); } if (code == KRB5_KT_END) code = 0; krb5_kt_end_seq_get (k5, keytab, &cursor); return code; }
static krb5_error_code krb5_kt_get_entry_wrapped(krb5_context context, krb5_keytab id, krb5_const_principal principal, krb5_kvno kvno, krb5_enctype enctype, krb5_keytab_entry *entry) { krb5_keytab_entry tmp; krb5_error_code ret; krb5_kt_cursor cursor; if(id->get) return (*id->get)(context, id, principal, kvno, enctype, entry); ret = krb5_kt_start_seq_get (context, id, &cursor); if (ret) { /* This is needed for krb5_verify_init_creds, but keep error * string from previous error for the human. */ context->error_code = KRB5_KT_NOTFOUND; return KRB5_KT_NOTFOUND; } entry->vno = 0; while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { /* the file keytab might only store the lower 8 bits of the kvno, so only compare those bits */ if (kvno == tmp.vno || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { krb5_kt_copy_entry_contents (context, &tmp, entry); krb5_kt_free_entry (context, &tmp); krb5_kt_end_seq_get(context, id, &cursor); return 0; } else if (kvno == 0 && tmp.vno > entry->vno) { if (entry->vno) krb5_kt_free_entry (context, entry); krb5_kt_copy_entry_contents (context, &tmp, entry); } } krb5_kt_free_entry(context, &tmp); } krb5_kt_end_seq_get (context, id, &cursor); if (entry->vno == 0) return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND, id, principal, enctype, kvno); return 0; }
static krb5_error_code KRB5_CALLCONV any_next_entry (krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor) { krb5_error_code ret, ret2; struct any_cursor_extra_data *ed; ed = (struct any_cursor_extra_data *)cursor->data; do { ret = krb5_kt_next_entry(context, ed->a->kt, entry, &ed->cursor); if (ret == 0) return 0; else if (ret != KRB5_KT_END) return ret; ret2 = krb5_kt_end_seq_get (context, ed->a->kt, &ed->cursor); if (ret2) return ret2; while ((ed->a = ed->a->next) != NULL) { ret2 = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor); if (ret2 == 0) break; } if (ed->a == NULL) { krb5_clear_error_message (context); return KRB5_KT_END; } } while (1); }
int kerberos5_init(Authenticator *ap, int server) { krb5_error_code ret; ret = krb5_init_context(&context); if (ret) return 0; if (server) { krb5_keytab kt; krb5_kt_cursor cursor; ret = krb5_kt_default(context, &kt); if (ret) return 0; ret = krb5_kt_start_seq_get (context, kt, &cursor); if (ret) { krb5_kt_close (context, kt); return 0; } krb5_kt_end_seq_get (context, kt, &cursor); krb5_kt_close (context, kt); str_data[3] = TELQUAL_REPLY; } else str_data[3] = TELQUAL_IS; return(1); }
/* * Given two files containing keytab data, second keytab, merge the keys into * the new file. Currently, this doesn't do any cleanup of old kvnos and * doesn't handle duplicate kvnos correctly. Dies on any error. */ static void merge_keytab(krb5_context ctx, const char *newfile, const char *file) { char *oldfile; krb5_keytab old = NULL, temp = NULL; krb5_kt_cursor cursor; krb5_keytab_entry entry; krb5_error_code status; memset(&entry, 0, sizeof(entry)); xasprintf(&oldfile, "WRFILE:%s", file); status = krb5_kt_resolve(ctx, oldfile, &old); if (status != 0) die_krb5(ctx, status, "cannot open keytab %s", file); free(oldfile); status = krb5_kt_resolve(ctx, newfile, &temp); if (status != 0) die_krb5(ctx, status, "cannot open temporary keytab %s", newfile); status = krb5_kt_start_seq_get(ctx, temp, &cursor); if (status != 0) die_krb5(ctx, status, "cannot read temporary keytab %s", newfile); while ((status = krb5_kt_next_entry(ctx, temp, &entry, &cursor)) == 0) { status = krb5_kt_add_entry(ctx, old, &entry); if (status != 0) die_krb5(ctx, status, "cannot write to keytab %s", file); krb5_kt_free_entry(ctx, &entry); } if (status != KRB5_KT_END) die_krb5(ctx, status, "error reading temporary keytab %s", newfile); krb5_kt_end_seq_get(ctx, temp, &cursor); if (old != NULL) krb5_kt_close(ctx, old); if (temp != NULL) krb5_kt_close(ctx, temp); }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_have_content(krb5_context context, krb5_keytab id) { krb5_keytab_entry entry; krb5_kt_cursor cursor; krb5_error_code ret; char *name; ret = krb5_kt_start_seq_get(context, id, &cursor); if (ret) goto notfound; ret = krb5_kt_next_entry(context, id, &entry, &cursor); krb5_kt_end_seq_get(context, id, &cursor); if (ret) goto notfound; krb5_kt_free_entry(context, &entry); return 0; notfound: ret = krb5_kt_get_full_name(context, id, &name); if (ret == 0) { krb5_set_error_message(context, KRB5_KT_NOTFOUND, N_("No entry in keytab: %s", ""), name); free(name); } return KRB5_KT_NOTFOUND; }
/* * Decrypt the ticket in req using an entry in keytab matching server (if * given). Set req->ticket->server to the principal of the keytab entry used. * Store the decrypting key in *keyblock_out if it is not NULL. */ static krb5_error_code decrypt_ticket(krb5_context context, const krb5_ap_req *req, krb5_const_principal server, krb5_keytab keytab, krb5_keyblock *keyblock_out) { krb5_error_code ret; krb5_keytab_entry ent; krb5_kt_cursor cursor; #ifdef LEAN_CLIENT return KRB5KRB_AP_WRONG_PRINC; #else /* If we have an explicit server principal, try just that one. */ if (!is_matching(context, server)) return try_one_princ(context, req, server, keytab, keyblock_out); if (keytab->ops->start_seq_get == NULL) { /* We can't iterate over the keytab. Try the principal asserted by the * client if it's allowed by the server parameter. */ if (!krb5_sname_match(context, server, req->ticket->server)) return KRB5KRB_AP_WRONG_PRINC; return try_one_princ(context, req, req->ticket->server, keytab, keyblock_out); } ret = krb5_kt_start_seq_get(context, keytab, &cursor); if (ret) goto cleanup; while ((ret = krb5_kt_next_entry(context, keytab, &ent, &cursor)) == 0) { if (ent.key.enctype == req->ticket->enc_part.enctype && krb5_sname_match(context, server, ent.principal)) { ret = try_one_entry(context, req, &ent, keyblock_out); if (ret == 0) { TRACE_RD_REQ_DECRYPT_ANY(context, ent.principal, &ent.key); (void)krb5_free_keytab_entry_contents(context, &ent); break; } } (void)krb5_free_keytab_entry_contents(context, &ent); } (void)krb5_kt_end_seq_get(context, keytab, &cursor); cleanup: switch (ret) { case KRB5_KT_KVNONOTFOUND: case KRB5_KT_NOTFOUND: case KRB5_KT_END: case KRB5KRB_AP_ERR_BAD_INTEGRITY: ret = KRB5KRB_AP_WRONG_PRINC; break; default: break; } return ret; #endif /* LEAN_CLIENT */ }
/* * Find the principal of the first entry of a keytab and return it. The * caller is responsible for freeing the result with krb5_free_principal. * Exit on error. */ krb5_principal kerberos_keytab_principal(krb5_context ctx, const char *path) { krb5_keytab keytab; krb5_kt_cursor cursor; krb5_keytab_entry entry; krb5_principal princ; krb5_error_code status; status = krb5_kt_resolve(ctx, path, &keytab); if (status != 0) bail_krb5(ctx, status, "error opening %s", path); status = krb5_kt_start_seq_get(ctx, keytab, &cursor); if (status != 0) bail_krb5(ctx, status, "error reading %s", path); status = krb5_kt_next_entry(ctx, keytab, &entry, &cursor); if (status == 0) { status = krb5_copy_principal(ctx, entry.principal, &princ); if (status != 0) bail_krb5(ctx, status, "error copying principal from %s", path); krb5_kt_free_entry(ctx, &entry); } if (status != 0) bail("no principal found in keytab file %s", path); krb5_kt_end_seq_get(ctx, keytab, &cursor); krb5_kt_close(ctx, keytab); return princ; }
/* * Given a context, a keytab file, and a realm, return a list of all * principals in that file. */ static struct principal_name * keytab_principals(krb5_context ctx, const char *file, char *realm) { char *princname = NULL, *princrealm = NULL; bool found; krb5_keytab keytab = NULL; krb5_kt_cursor cursor; krb5_keytab_entry entry; krb5_error_code status; struct principal_name *names = NULL, *current = NULL, *last = NULL; memset(&entry, 0, sizeof(entry)); status = krb5_kt_resolve(ctx, file, &keytab); if (status != 0) die_krb5(ctx, status, "cannot open keytab %s", file); status = krb5_kt_start_seq_get(ctx, keytab, &cursor); if (status != 0) die_krb5(ctx, status, "cannot read keytab %s", file); while ((status = krb5_kt_next_entry(ctx, keytab, &entry, &cursor)) == 0) { status = krb5_unparse_name(ctx, entry.principal, &princname); if (status != 0) die_krb5(ctx, status, "cannot unparse name for a principal"); /* Separate into principal and realm. */ princrealm = strchr(princname, '@'); if (princrealm != NULL) { *princrealm = '\0'; princrealm++; } if (princrealm == NULL || strcmp(princrealm, realm) != 0) continue; /* Check to see if the principal has already been listed. */ found = false; for (current = names; current != NULL; current = current->next) { if (strcmp(current->princ, princname) == 0) { found = true; break; } last = current; } if (found == false) { current = xmalloc(sizeof(struct principal_name)); current->princ = xstrdup(princname); current->next = NULL; if (last == NULL) names = current; else last->next = current; } krb5_kt_free_entry(ctx, &entry); free(princname); } if (status != KRB5_KT_END) die_krb5(ctx, status, "error reading keytab %s", file); krb5_kt_end_seq_get(ctx, keytab, &cursor); krb5_kt_close(ctx, keytab); return names; }
static OM_uint32 acquire_acceptor_cred (OM_uint32 * minor_status, krb5_context context, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gsskrb5_cred handle, gss_OID_set * actual_mechs, OM_uint32 * time_rec ) { OM_uint32 ret; krb5_error_code kret; ret = GSS_S_FAILURE; kret = get_keytab(context, &handle->keytab); if (kret) goto end; /* check that the requested principal exists in the keytab */ if (handle->principal) { krb5_keytab_entry entry; kret = krb5_kt_get_entry(context, handle->keytab, handle->principal, 0, 0, &entry); if (kret) goto end; krb5_kt_free_entry(context, &entry); ret = GSS_S_COMPLETE; } else { /* * Check if there is at least one entry in the keytab before * declaring it as an useful keytab. */ krb5_keytab_entry tmp; krb5_kt_cursor c; kret = krb5_kt_start_seq_get (context, handle->keytab, &c); if (kret) goto end; if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) { krb5_kt_free_entry(context, &tmp); ret = GSS_S_COMPLETE; /* ok found one entry */ } krb5_kt_end_seq_get (context, handle->keytab, &c); } end: if (ret != GSS_S_COMPLETE) { if (handle->keytab != NULL) krb5_kt_close(context, handle->keytab); if (kret != 0) { *minor_status = kret; } } return (ret); }
mit_krb5_error_code KRB5_CALLCONV krb5_kt_get_entry(mit_krb5_context context, mit_krb5_keytab id, mit_krb5_const_principal principal, mit_krb5_kvno kvno, mit_krb5_enctype enctype, mit_krb5_keytab_entry *entry) { mit_krb5_keytab_entry tmp; mit_krb5_error_code ret; mit_krb5_kt_cursor cursor; LOG_ENTRY(); memset(entry, 0, sizeof(*entry)); ret = krb5_kt_start_seq_get (context, id, &cursor); if (ret) return KRB5_KT_NOTFOUND; entry->vno = 0; while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { /* the file keytab might only store the lower 8 bits of the kvno, so only compare those bits */ if (kvno == tmp.vno || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { krb5_kt_copy_entry_contents (context, &tmp, entry); krb5_kt_free_entry (context, &tmp); krb5_kt_end_seq_get(context, id, &cursor); return 0; } else if (kvno == 0 && tmp.vno > entry->vno) { if (entry->vno) krb5_kt_free_entry (context, entry); krb5_kt_copy_entry_contents (context, &tmp, entry); } } krb5_kt_free_entry(context, &tmp); } krb5_kt_end_seq_get (context, id, &cursor); if (entry->vno == 0) return KRB5_KT_NOTFOUND; return 0; }
KRB5Keytab::cursor::~cursor() { krb5_free_keytab_entry_contents(g_context.get(), &m_entry); memset(&m_entry, 0, sizeof(m_entry)); // Tell m_princ to not free its contents! m_princ.reset_no_free(NULL); krb5_error_code ret = krb5_kt_end_seq_get(g_context.get(), m_keytab.m_keytab, &m_cursor); if (ret) { // FIXME: shouldn't throw from destructor... throw KRB5Exception("krb5_kt_end_seq_get", ret); } }
static krb5_error_code KRB5_CALLCONV any_end_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor) { krb5_error_code ret = 0; struct any_cursor_extra_data *ed; ed = (struct any_cursor_extra_data *)cursor->data; if (ed->a != NULL) ret = krb5_kt_end_seq_get(context, ed->a->kt, &ed->cursor); free (ed); cursor->data = NULL; return ret; }
/* * Given a keytab, extract the principal name of the (first) entry with * the highest kvno in the keytab. This provides compatibility with the * rxkad KeyFile behavior of always using the highest kvno entry when * printing tickets. We could return the kvno as well, but krb5_kt_get_entry * can find the highest kvno on its own. * * Returns 0 on success, krb5 errors on failure. */ static int pick_principal(krb5_context context, krb5_keytab kt, krb5_principal *service_principal) { krb5_error_code code; krb5_kvno vno = 0; krb5_kt_cursor c; krb5_keytab_entry n_entry; /* Nothing to do */ if (*service_principal != NULL) return 0; memset(&n_entry, 0, sizeof(n_entry)); code = krb5_kt_start_seq_get(context, kt, &c); if (code != 0) goto cleanup; while (code == 0 && krb5_kt_next_entry(context, kt, &n_entry, &c) == 0) { if (n_entry.vno > vno) { vno = n_entry.vno; (void)krb5_free_principal(context, *service_principal); code = krb5_copy_principal(context, n_entry.principal, service_principal); } (void)krb5_free_keytab_entry_contents(context, &n_entry); } if (code != 0) { (void)krb5_kt_end_seq_get(context, kt, &c); goto cleanup; } code = krb5_kt_end_seq_get(context, kt, &c); cleanup: return code; }
static krb5_error_code copy_keytab(krb5_context context, krb5_keytab from, krb5_keytab to) { krb5_keytab_entry entry; krb5_kt_cursor cursor; krb5_error_code ret; ret = krb5_kt_start_seq_get(context, from, &cursor); if (ret) return ret; while((ret = krb5_kt_next_entry(context, from, &entry, &cursor)) == 0){ krb5_kt_add_entry(context, to, &entry); krb5_kt_free_entry(context, &entry); } return krb5_kt_end_seq_get(context, from, &cursor); }
krb5_error_code kt_copy (krb5_context context, const char *from, const char *to) { krb5_error_code ret; krb5_keytab src_keytab, dst_keytab; krb5_kt_cursor cursor; krb5_keytab_entry entry; ret = krb5_kt_resolve (context, from, &src_keytab); if (ret) { krb5_set_error_message (context, ret, "resolving src keytab `%s'", from); return ret; } ret = krb5_kt_resolve (context, to, &dst_keytab); if (ret) { krb5_kt_close (context, src_keytab); krb5_set_error_message (context, ret, "resolving dst keytab `%s'", to); return ret; } ret = krb5_kt_start_seq_get (context, src_keytab, &cursor); if (ret) { krb5_set_error_message (context, ret, "krb5_kt_start_seq_get %s", from); goto out; } while((ret = krb5_kt_next_entry(context, src_keytab, &entry, &cursor)) == 0) { ret = copy_one_entry(context, src_keytab, dst_keytab, entry); if (ret) { break; } } krb5_kt_end_seq_get (context, src_keytab, &cursor); out: krb5_kt_close (context, src_keytab); krb5_kt_close (context, dst_keytab); if (ret == KRB5_KT_END) { return 0; } else if (ret == 0) { return EINVAL; } return ret; }
long kerberos_server_valid () { krb5_context ctx; krb5_keytab kt; krb5_kt_cursor csr; long ret = NIL; /* make a context */ if (!krb5_init_context (&ctx)) { /* get default keytab */ if (!krb5_kt_default (ctx,&kt)) { /* can do server if have good keytab */ if (!krb5_kt_start_seq_get (ctx,kt,&csr) && !krb5_kt_end_seq_get (ctx,kt,&csr)) ret = LONGT; krb5_kt_close (ctx,kt); /* finished with keytab */ } krb5_free_context (ctx); /* finished with context */ } return ret; }
static krb5_error_code KRB5_CALLCONV fkt_remove_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) { krb5_keytab_entry e; krb5_kt_cursor cursor; off_t pos_start, pos_end; int found = 0; krb5_error_code ret; ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY | O_CLOEXEC, 1, &cursor); if(ret != 0) goto out; /* return other error here? */ while(fkt_next_entry_int(context, id, &e, &cursor, &pos_start, &pos_end) == 0) { if(krb5_kt_compare(context, &e, entry->principal, entry->vno, entry->keyblock.keytype)) { int32_t len; unsigned char buf[128]; found = 1; krb5_storage_seek(cursor.sp, pos_start, SEEK_SET); len = pos_end - pos_start - 4; krb5_store_int32(cursor.sp, -len); memset(buf, 0, sizeof(buf)); while(len > 0) { krb5_storage_write(cursor.sp, buf, min((size_t)len, sizeof(buf))); len -= min((size_t)len, sizeof(buf)); } } krb5_kt_free_entry(context, &e); } krb5_kt_end_seq_get(context, id, &cursor); out: if (!found) { krb5_clear_error_message (context); return KRB5_KT_NOTFOUND; } return 0; }
krb5_error_code sss_krb5_kt_have_content(krb5_context context, krb5_keytab keytab) { #ifdef HAVE_KRB5_KT_HAVE_CONTENT return krb5_kt_have_content(context, keytab); #else krb5_keytab_entry entry; krb5_kt_cursor cursor; krb5_error_code kerr; krb5_error_code kerr_end; kerr = krb5_kt_start_seq_get(context, keytab, &cursor); if (kerr != 0) { DEBUG(SSSDBG_OP_FAILURE, "krb5_kt_start_seq_get failed, assuming no entries.\n"); return KRB5_KT_NOTFOUND; } kerr = krb5_kt_next_entry(context, keytab, &entry, &cursor); kerr_end = krb5_kt_end_seq_get(context, keytab, &cursor); if (kerr != 0) { DEBUG(SSSDBG_OP_FAILURE, "krb5_kt_next_entry failed, assuming no entries.\n"); return KRB5_KT_NOTFOUND; } kerr = krb5_free_keytab_entry_contents(context, &entry); if (kerr_end != 0) { DEBUG(SSSDBG_TRACE_FUNC, "krb5_kt_end_seq_get failed, ignored.\n"); } if (kerr != 0) { DEBUG(SSSDBG_TRACE_FUNC, "krb5_free_keytab_entry_contents failed, ignored.\n"); } return 0; #endif }
/* * In a couple of places we need to get a principal name from a keytab: when * verifying credentials against a keytab, and when querying the name of a * default GSS acceptor cred. Keytabs do not have the concept of a default * principal like ccaches do, so for now we just return the first principal * listed in the keytab, or an error if it's not iterable. In the future we * could consider elevating this to a public API and giving keytab types an * operation to return a default principal, and maybe extending the file format * and tools to support it. Returns KRB5_KT_NOTFOUND if the keytab is empty * or non-iterable. */ krb5_error_code k5_kt_get_principal(krb5_context context, krb5_keytab keytab, krb5_principal *princ_out) { krb5_error_code ret; krb5_kt_cursor cursor; krb5_keytab_entry kte; *princ_out = NULL; if (keytab->ops->start_seq_get == NULL) return KRB5_KT_NOTFOUND; ret = krb5_kt_start_seq_get(context, keytab, &cursor); if (ret) return ret; ret = krb5_kt_next_entry(context, keytab, &kte, &cursor); (void)krb5_kt_end_seq_get(context, keytab, &cursor); if (ret) return (ret == KRB5_KT_END) ? KRB5_KT_NOTFOUND : ret; ret = krb5_copy_principal(context, kte.principal, princ_out); krb5_kt_free_entry(context, &kte); return ret; }
int main(int argc, char **argv) { krb5_context kcontext = NULL; krb5_keytab kt = NULL; krb5_keytab_entry entry; krb5_kt_cursor cursor = NULL; krb5_error_code krb5_err; int matched = 0; char svc_name[] = "host"; int svc_name_len = strlen (svc_name); krb5_err = krb5_init_context(&kcontext); if (krb5_err) { goto Error; } krb5_err = krb5_kt_default(kcontext, &kt); if (krb5_err) { goto Error; } krb5_err = krb5_kt_start_seq_get(kcontext, kt, &cursor); if (krb5_err) { goto Error; } while ((matched == 0) && (krb5_err = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0) { krb5_data *nameData = krb5_princ_name (kcontext, entry.principal); if (NULL != nameData->data && svc_name_len == nameData->length && 0 == strncmp (svc_name, nameData->data, nameData->length)) { matched = 1; } krb5_free_keytab_entry_contents(kcontext, &entry); } krb5_err = krb5_kt_end_seq_get(kcontext, kt, &cursor); Error: if (NULL != kt) { krb5_kt_close (kcontext, kt); } if (NULL != kcontext) { krb5_free_context (kcontext); } // Return 0 if we got match or -1 if err or no match return (0 != krb5_err) ? -1 : matched ? 0 : -1; }
/* Return a list of all unique host service princs in keytab. */ static krb5_error_code get_host_princs_from_keytab(krb5_context context, krb5_keytab keytab, krb5_principal **princ_list_out) { krb5_error_code ret; krb5_kt_cursor cursor; krb5_keytab_entry kte; krb5_principal *plist = NULL, p; *princ_list_out = NULL; ret = krb5_kt_start_seq_get(context, keytab, &cursor); if (ret) goto cleanup; while ((ret = krb5_kt_next_entry(context, keytab, &kte, &cursor)) == 0) { p = kte.principal; if (p->length == 2 && data_eq_string(p->data[0], "host")) ret = add_princ_list(context, p, &plist); krb5_kt_free_entry(context, &kte); if (ret) break; } (void)krb5_kt_end_seq_get(context, keytab, &cursor); if (ret == KRB5_KT_END) ret = 0; if (ret) goto cleanup; *princ_list_out = plist; plist = NULL; cleanup: free_princ_list(context, plist); return ret; }
static int check_keytab(krb5_context context, gsskrb5_cred handle, const char *service, int require_lkdc) { krb5_keytab_entry tmp; krb5_error_code ret; krb5_kt_cursor c; int found = 0; ret = krb5_kt_start_seq_get (context, handle->keytab, &c); if (ret) return 0; while (!found && krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) { krb5_principal principal = tmp.principal; if (service) { if (principal->name.name_string.len < 1 || strcmp(principal->name.name_string.val[0], service) != 0) goto next; } if (require_lkdc) { if (krb5_principal_is_lkdc(context, principal)) found = 1; if (krb5_principal_is_pku2u(context, principal)) found = 1; } else found = 1; next: krb5_kt_free_entry(context, &tmp); } krb5_kt_end_seq_get (context, handle->keytab, &c); return found; }
static void get_princ_kt(krb5_context context, krb5_principal *principal, char *name) { krb5_error_code ret; krb5_principal tmp; krb5_ccache ccache; krb5_kt_cursor cursor; krb5_keytab_entry entry; char *def_realm; if (name == NULL) { /* * If the credential cache exists and specifies a client principal, * use that. */ if (krb5_cc_default(context, &ccache) == 0) { ret = krb5_cc_get_principal(context, ccache, principal); krb5_cc_close(context, ccache); if (ret == 0) return; } } if (name) { /* If the principal specifies an explicit realm, just use that. */ int parseflags = KRB5_PRINCIPAL_PARSE_NO_DEF_REALM; parse_name_realm(context, name, parseflags, NULL, &tmp); if (krb5_principal_get_realm(context, tmp) != NULL) { *principal = tmp; return; } } else { /* Otherwise, search keytab for bare name of the default principal. */ get_default_principal(context, &tmp); set_princ_realm(context, tmp, NULL); } def_realm = get_default_realm(context); ret = krb5_kt_start_seq_get(context, kt, &cursor); if (ret) krb5_err(context, 1, ret, "krb5_kt_start_seq_get"); while (ret == 0 && krb5_kt_next_entry(context, kt, &entry, &cursor) == 0) { const char *realm; if (!krb5_principal_compare_any_realm(context, tmp, entry.principal)) continue; if (*principal && krb5_principal_compare(context, *principal, entry.principal)) continue; /* The default realm takes precedence */ realm = krb5_principal_get_realm(context, entry.principal); if (*principal && strcmp(def_realm, realm) == 0) { krb5_free_principal(context, *principal); ret = krb5_copy_principal(context, entry.principal, principal); break; } if (!*principal) ret = krb5_copy_principal(context, entry.principal, principal); } if (ret != 0 || (ret = krb5_kt_end_seq_get(context, kt, &cursor)) != 0) krb5_err(context, 1, ret, "get_princ_kt"); if (!*principal) { if (name) parse_name_realm(context, name, 0, NULL, principal); else krb5_err(context, 1, KRB5_CC_NOTFOUND, "get_princ_kt"); } krb5_free_principal(context, tmp); free(def_realm); }
int print_keytab (const char *in_name) { krb5_error_code err = 0; krb5_keytab kt; krb5_keytab_entry entry; krb5_kt_cursor cursor; char keytab_name[BUFSIZ]; /* hopefully large enough for any type */ if (!err) { if (!in_name) { err = krb5_kt_default (kcontext, &kt); printiferr (err, "while resolving default keytab"); } else { err = krb5_kt_resolve (kcontext, in_name, &kt); printiferr (err, "while resolving keytab %s", in_name); } } if (!err) { err = krb5_kt_get_name (kcontext, kt, keytab_name, sizeof (keytab_name)); printiferr (err, "while getting keytab name"); } if (!err) { printmsg ("Keytab name: %s\n", keytab_name); } if (!err) { err = krb5_kt_start_seq_get (kcontext, kt, &cursor); printiferr (err, "while starting scan of keytab %s", keytab_name); } if (!err) { if (show_entry_timestamps) { printmsg ("KVNO Timestamp"); printfiller (' ', get_timestamp_width () - sizeof ("Timestamp") + 2); printmsg ("Principal\n"); printmsg ("---- "); printfiller ('-', get_timestamp_width ()); printmsg (" "); printfiller ('-', 78 - get_timestamp_width () - sizeof ("KVNO")); printmsg ("\n"); } else { printmsg("KVNO Principal\n"); printmsg("---- --------------------------------------------------------------------------\n"); } } while (!err) { char *principal_name = NULL; err = krb5_kt_next_entry (kcontext, kt, &entry, &cursor); if (err == KRB5_KT_END) { err = 0; break; } if (!err) { err = krb5_unparse_name (kcontext, entry.principal, &principal_name); printiferr (err, "while unparsing principal name"); } if (!err) { printmsg ("%4d ", entry.vno); if (show_entry_timestamps) { printtime (entry.timestamp); printmsg (" "); } printmsg ("%s", principal_name); if (show_enctypes) { printmsg (" (%s) ", enctype_to_string (entry.key.enctype)); } if (show_entry_DES_keys) { unsigned int i; printmsg (" (0x"); for (i = 0; i < entry.key.length; i++) { printmsg ("%02x", entry.key.contents[i]); } printmsg (")"); } printmsg ("\n"); } printiferr (err, "while scanning keytab %s", keytab_name); if (principal_name) { krb5_free_unparsed_name (kcontext, principal_name); } } if (!err) { err = krb5_kt_end_seq_get (kcontext, kt, &cursor); printiferr (err, "while ending scan of keytab %s", keytab_name); } return err ? 1 : 0; }
int main (int argc, char **argv) { TALLOC_CTX *mem_ctx = talloc_init("ktutil"); krb5_context context; krb5_keytab keytab; krb5_kt_cursor cursor; krb5_keytab_entry entry; krb5_error_code ret; char *keytab_name = NULL; if (mem_ctx == NULL) { printf("talloc_init() failed\n"); exit(1); } if (argc != 2) { printf("Usage: %s KEYTAB\n", argv[0]); exit(1); } keytab_name = argv[1]; initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { smb_krb5_err(mem_ctx, context, 1, ret, "krb5_context"); } ret = smb_krb5_open_keytab_relative(context, keytab_name, false, &keytab); if (ret) { smb_krb5_err(mem_ctx, context, 1, ret, "open keytab"); } ret = krb5_kt_start_seq_get(context, keytab, &cursor); if (ret) { smb_krb5_err(mem_ctx, context, 1, ret, "krb5_kt_start_seq_get"); } for (ret = krb5_kt_next_entry(context, keytab, &entry, &cursor); ret == 0; ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) { char *principal = NULL; char *enctype_str = NULL; krb5_enctype enctype = smb_get_enctype_from_kt_entry(&entry); ret = smb_krb5_unparse_name(mem_ctx, context, entry.principal, &principal); if (ret) { smb_krb5_err(mem_ctx, context, 1, ret, "krb5_enctype_to_string"); } ret = smb_krb5_enctype_to_string(context, enctype, &enctype_str); if (ret) { smb_krb5_err(mem_ctx, context, 1, ret, "krb5_enctype_to_string"); } printf("%s (%s)\n", principal, enctype_str); TALLOC_FREE(principal); SAFE_FREE(enctype_str); smb_krb5_kt_free_entry(context, &entry); } ret = krb5_kt_end_seq_get(context, keytab, &cursor); if (ret) { smb_krb5_err(mem_ctx, context, 1, ret, "krb5_kt_end_seq_get"); } ret = krb5_kt_close(context, keytab); if (ret) { smb_krb5_err(mem_ctx, context, 1, ret, "krb5_kt_close"); } krb5_free_context(context); talloc_free(mem_ctx); return 0; }
/* Return the list of etypes available for client in keytab. */ static krb5_error_code lookup_etypes_for_keytab(krb5_context context, krb5_keytab keytab, krb5_principal client, krb5_enctype **etypes_out) { krb5_kt_cursor cursor; krb5_keytab_entry entry; krb5_enctype *p, *etypes = NULL, etype; krb5_kvno max_kvno = 0, vno; krb5_error_code ret; krb5_boolean match; size_t count = 0; *etypes_out = NULL; if (keytab->ops->start_seq_get == NULL) return EINVAL; ret = krb5_kt_start_seq_get(context, keytab, &cursor); if (ret != 0) return ret; while (!(ret = krb5_kt_next_entry(context, keytab, &entry, &cursor))) { /* Extract what we need from the entry and free it. */ etype = entry.key.enctype; vno = entry.vno; match = krb5_principal_compare(context, entry.principal, client); krb5_free_keytab_entry_contents(context, &entry); /* Filter out old or non-matching entries and invalid enctypes. */ if (vno < max_kvno || !match || !krb5_c_valid_enctype(etype)) continue; /* Update max_kvno and reset the list if we find a newer kvno. */ if (vno > max_kvno) { max_kvno = vno; free(etypes); etypes = NULL; count = 0; } /* Leave room for the terminator and possibly a second entry. */ p = realloc(etypes, (count + 3) * sizeof(*etypes)); if (p == NULL) { ret = ENOMEM; goto cleanup; } etypes = p; etypes[count++] = etype; /* All DES key types work with des-cbc-crc, which is more likely to be * accepted by the KDC (since MIT KDCs refuse des-cbc-md5). */ if (etype == ENCTYPE_DES_CBC_MD5 || etype == ENCTYPE_DES_CBC_MD4) etypes[count++] = ENCTYPE_DES_CBC_CRC; etypes[count] = 0; } if (ret != KRB5_KT_END) goto cleanup; ret = 0; *etypes_out = etypes; etypes = NULL; cleanup: krb5_kt_end_seq_get(context, keytab, &cursor); free(etypes); return ret; }
krb5_error_code KRB5_LIB_FUNCTION krb5_kt_get_entry(krb5_context context, krb5_keytab id, krb5_const_principal principal, krb5_kvno kvno, krb5_enctype enctype, krb5_keytab_entry *entry) { krb5_keytab_entry tmp; krb5_error_code ret; krb5_kt_cursor cursor; if(id->get) return (*id->get)(context, id, principal, kvno, enctype, entry); ret = krb5_kt_start_seq_get (context, id, &cursor); if (ret) { /* This is needed for krb5_verify_init_creds, but keep error * string from previous error for the human. */ context->error_code = KRB5_KT_NOTFOUND; return KRB5_KT_NOTFOUND; } entry->vno = 0; while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { /* the file keytab might only store the lower 8 bits of the kvno, so only compare those bits */ if (kvno == tmp.vno || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { krb5_kt_copy_entry_contents (context, &tmp, entry); krb5_kt_free_entry (context, &tmp); krb5_kt_end_seq_get(context, id, &cursor); return 0; } else if (kvno == 0 && tmp.vno > entry->vno) { if (entry->vno) krb5_kt_free_entry (context, entry); krb5_kt_copy_entry_contents (context, &tmp, entry); } } krb5_kt_free_entry(context, &tmp); } krb5_kt_end_seq_get (context, id, &cursor); if (entry->vno) { return 0; } else { char princ[256], kvno_str[25], *kt_name; char *enctype_str = NULL; krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); krb5_kt_get_full_name (context, id, &kt_name); krb5_enctype_to_string(context, enctype, &enctype_str); if (kvno) snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); else kvno_str[0] = '\0'; krb5_set_error_message (context, KRB5_KT_NOTFOUND, N_("Failed to find %s%s in keytab %s (%s)", "principal, kvno, keytab file, enctype"), princ, kvno_str, kt_name ? kt_name : "unknown keytab", enctype_str ? enctype_str : "unknown enctype"); free(kt_name); free(enctype_str); return KRB5_KT_NOTFOUND; } }
static void remove_principal(char *keytab_str, krb5_keytab keytab, char *princ_str, char *kvno_str) { krb5_principal princ; krb5_keytab_entry entry; krb5_kt_cursor cursor; enum { UNDEF, SPEC, HIGH, ALL, OLD } mode; int code, did_something; krb5_kvno kvno; code = krb5_parse_name(context, princ_str, &princ); if (code != 0) { com_err(whoami, code, "while parsing principal name %s", princ_str); return; } mode = UNDEF; if (kvno_str == NULL) { mode = HIGH; kvno = 0; } else if (strcmp(kvno_str, "all") == 0) { mode = ALL; kvno = 0; } else if (strcmp(kvno_str, "old") == 0) { mode = OLD; kvno = 0; } else { mode = SPEC; kvno = atoi(kvno_str); } /* kvno is set to specified value for SPEC, 0 otherwise */ code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); if (code != 0) { if (code == ENOENT) { fprintf(stderr, "%s: Keytab %s does not exist.\n", whoami, keytab_str); } else if (code == KRB5_KT_NOTFOUND) { if (mode != SPEC) { fprintf(stderr, "%s: No entry for principal " "%s exists in keytab %s\n", whoami, princ_str, keytab_str); } else { fprintf(stderr, "%s: No entry for principal " "%s with kvno %d exists in keytab " "%s.\n", whoami, princ_str, kvno, keytab_str); } } else com_err(whoami, code, "while retrieving highest kvno from keytab"); return; } /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ kvno = entry.vno; krb5_kt_free_entry(context, &entry); code = krb5_kt_start_seq_get(context, keytab, &cursor); if (code != 0) { com_err(whoami, code, "while starting keytab scan"); return; } did_something = 0; while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) { if (krb5_principal_compare(context, princ, entry.principal) && ((mode == ALL) || (mode == SPEC && entry.vno == kvno) || (mode == OLD && entry.vno != kvno) || (mode == HIGH && entry.vno == kvno))) { /* * Ack! What a kludge... the scanning functions lock * the keytab so entries cannot be removed while they * are operating. */ code = krb5_kt_end_seq_get(context, keytab, &cursor); if (code != 0) { com_err(whoami, code, "while temporarily ending keytab scan"); return; } code = krb5_kt_remove_entry(context, keytab, &entry); if (code != 0) { com_err(whoami, code, "while deleting entry from keytab"); return; } code = krb5_kt_start_seq_get(context, keytab, &cursor); if (code != 0) { com_err(whoami, code, "while restarting keytab scan"); return; } did_something++; if (!quiet) printf("Entry for principal %s with kvno %d " "removed from keytab %s.\n", princ_str, entry.vno, keytab_str); } krb5_kt_free_entry(context, &entry); } if (code && code != KRB5_KT_END) { com_err(whoami, code, "while scanning keytab"); return; } code = krb5_kt_end_seq_get(context, keytab, &cursor); if (code) { com_err(whoami, code, "while ending keytab scan"); return; } /* * If !did_someting then mode must be OLD or we would have * already returned with an error. But check it anyway just to * prevent unexpected error messages... */ if (!did_something && mode == OLD) { fprintf(stderr, "%s: There is only one entry for principal " "%s in keytab %s\n", whoami, princ_str, keytab_str); } }