Example #1
0
int
main (int argc, char **argv)
{
    rtbl_t table;

    table = rtbl_create ();
    rtbl_add_column_by_id (table, 0, "Issued", 0);
    rtbl_add_column_by_id (table, 1, "Expires", 0);
    rtbl_add_column_by_id (table, 2, "Foo", RTBL_ALIGN_RIGHT);
    rtbl_add_column_by_id (table, 3, "Principal", 0);

    rtbl_add_column_entry_by_id (table, 0, "Jul  7 21:19:29");
    rtbl_add_column_entry_by_id (table, 1, "Jul  8 07:19:29");
    rtbl_add_column_entry_by_id (table, 2, "73");
    rtbl_add_column_entry_by_id (table, 2, "0");
    rtbl_add_column_entry_by_id (table, 2, "-2000");
    rtbl_add_column_entry_by_id (table, 3, "krbtgt/[email protected]");

    rtbl_add_column_entry_by_id (table, 0, "Jul  7 21:19:29");
    rtbl_add_column_entry_by_id (table, 1, "Jul  8 07:19:29");
    rtbl_add_column_entry_by_id (table, 3, "afs/[email protected]");

    rtbl_add_column_entry_by_id (table, 0, "Jul  7 21:19:29");
    rtbl_add_column_entry_by_id (table, 1, "Jul  8 07:19:29");
    rtbl_add_column_entry_by_id (table, 3, "*****@*****.**");

    rtbl_set_separator (table, "  ");

    rtbl_format (table, stdout);

    rtbl_destroy (table);

    printf("\n");

    table = rtbl_create ();
    rtbl_add_column_by_id (table, 0, "Column A", 0);
    rtbl_set_column_affix_by_id (table, 0, "<", ">");
    rtbl_add_column_by_id (table, 1, "Column B", 0);
    rtbl_set_column_affix_by_id (table, 1, "[", "]");
    rtbl_add_column_by_id (table, 2, "Column C", 0);
    rtbl_set_column_affix_by_id (table, 2, "(", ")");

    rtbl_add_column_entry_by_id (table, 0, "1");
    rtbl_new_row(table);
    rtbl_add_column_entry_by_id (table, 1, "2");
    rtbl_new_row(table);
    rtbl_add_column_entry_by_id (table, 2, "3");
    rtbl_new_row(table);

    rtbl_set_separator (table, "  ");
    rtbl_format (table, stdout);

    rtbl_destroy (table);

    return 0;
}
Example #2
0
int
supported_mechanisms(void *argptr, int argc, char **argv)
{
    OM_uint32 maj_stat, min_stat;
    gss_OID_set mechs;
    rtbl_t ct;
    size_t i;

    maj_stat = gss_indicate_mechs(&min_stat, &mechs);
    if (maj_stat != GSS_S_COMPLETE)
	errx(1, "gss_indicate_mechs failed");

    printf("Supported mechanisms:\n");

    ct = rtbl_create();
    if (ct == NULL)
	errx(1, "rtbl_create");

    rtbl_set_separator(ct, "  ");
    rtbl_add_column(ct, COL_OID, 0);
    rtbl_add_column(ct, COL_NAME, 0);
    rtbl_add_column(ct, COL_DESC, 0);
    rtbl_add_column(ct, COL_SASL, 0);

    for (i = 0; i < mechs->count; i++) {
	gss_buffer_desc str, sasl_name, mech_name, mech_desc;

	maj_stat = gss_oid_to_str(&min_stat, &mechs->elements[i], &str);
	if (maj_stat != GSS_S_COMPLETE)
	    errx(1, "gss_oid_to_str failed");

	rtbl_add_column_entryv(ct, COL_OID, "%.*s",
			       (int)str.length, (char *)str.value);
	gss_release_buffer(&min_stat, &str);

	(void)gss_inquire_saslname_for_mech(&min_stat,
					    &mechs->elements[i],
					    &sasl_name,
					    &mech_name,
					    &mech_desc);

	rtbl_add_column_entryv(ct, COL_NAME, "%.*s",
			       (int)mech_name.length, (char *)mech_name.value);
	rtbl_add_column_entryv(ct, COL_DESC, "%.*s",
			       (int)mech_desc.length, (char *)mech_desc.value);
	rtbl_add_column_entryv(ct, COL_SASL, "%.*s",
			       (int)sasl_name.length, (char *)sasl_name.value);

	gss_release_buffer(&min_stat, &mech_name);
	gss_release_buffer(&min_stat, &mech_desc);
	gss_release_buffer(&min_stat, &sasl_name);

    }
    gss_release_oid_set(&min_stat, &mechs);

    rtbl_format(ct, stdout);
    rtbl_destroy(ct);

    return 0;
}
Example #3
0
static int
getit(struct get_options *opt, const char *name, int argc, char **argv)
{
    int i;
    krb5_error_code ret;
    struct get_entry_data data;

    if(opt->long_flag == -1 && (opt->short_flag == 1 || opt->terse_flag == 1))
	opt->long_flag = 0;
    if(opt->short_flag == -1 && (opt->long_flag == 1 || opt->terse_flag == 1))
	opt->short_flag = 0;
    if(opt->terse_flag == -1 && (opt->long_flag == 1 || opt->short_flag == 1))
	opt->terse_flag = 0;
    if(opt->long_flag == 0 && opt->short_flag == 0 && opt->terse_flag == 0)
	opt->short_flag = 1;

    if (opt->terse_flag)
        return listit(name, argc, argv);

    data.table = NULL;
    data.chead = NULL;
    data.ctail = &data.chead;
    data.mask = 0;
    data.extra_mask = 0;

    if(opt->short_flag) {
	data.table = rtbl_create();
	rtbl_set_separator(data.table, "  ");
	data.format = print_entry_short;
    } else
	data.format = print_entry_long;
    if(opt->column_info_string == NULL) {
	if(opt->long_flag)
	    ret = setup_columns(&data, DEFAULT_COLUMNS_LONG);
	else
	    ret = setup_columns(&data, DEFAULT_COLUMNS_SHORT);
    } else
	ret = setup_columns(&data, opt->column_info_string);

    if(ret != 0) {
	if(data.table != NULL)
	    rtbl_destroy(data.table);
	return 0;
    }

    for(i = 0; i < argc; i++)
	ret = foreach_principal(argv[i], do_get_entry, name, &data);

    if(data.table != NULL) {
	rtbl_format(data.table, stdout);
	rtbl_destroy(data.table);
    }
    free_columns(&data);
    return ret != 0;
}
Example #4
0
void static 
print_mech_attr(const char *mechname, gss_const_OID mech, gss_OID_set set)
{
    gss_buffer_desc name, desc;
    OM_uint32 major, minor;
    rtbl_t ct;
    size_t n;

    ct = rtbl_create();
    if (ct == NULL)
	errx(1, "rtbl_create");

    rtbl_set_separator(ct, "  ");
    rtbl_add_column(ct, COL_OID, 0);
    rtbl_add_column(ct, COL_DESC, 0);
    if (mech)
	rtbl_add_column(ct, COL_VALUE, 0);

    for (n = 0; n < set->count; n++) {
	major = gss_display_mech_attr(&minor, &set->elements[n], &name, &desc, NULL);
	if (major)
	    continue;
	
	rtbl_add_column_entryv(ct, COL_OID, "%.*s",
			       (int)name.length, (char *)name.value);
	rtbl_add_column_entryv(ct, COL_DESC, "%.*s",
			       (int)desc.length, (char *)desc.value);
	if (mech) {
	    gss_buffer_desc value;
	    
	    if (gss_mo_get(mech, &set->elements[n], &value) != 0)
		value.length = 0;

	    if (value.length)
		rtbl_add_column_entryv(ct, COL_VALUE, "%.*s",
				       (int)value.length, (char *)value.value);
	    else
		rtbl_add_column_entryv(ct, COL_VALUE, "<>");
	    gss_release_buffer(&minor, &value);
	}

	gss_release_buffer(&minor, &name);
	gss_release_buffer(&minor, &desc);
    }

    printf("attributes for: %s\n", mechname);
    rtbl_format(ct, stdout);
    rtbl_destroy(ct);
}
Example #5
0
int
supported_mechanisms(void *argptr, int argc, char **argv)
{
    OM_uint32 maj_stat, min_stat;
    gss_OID_set mechs;
    rtbl_t ct;
    size_t i;

    maj_stat = gss_indicate_mechs(&min_stat, &mechs);
    if (maj_stat != GSS_S_COMPLETE)
	errx(1, "gss_indicate_mechs failed");

    printf("Supported mechanisms:\n");

    ct = rtbl_create();
    if (ct == NULL)
	errx(1, "rtbl_create");

    rtbl_set_separator(ct, "  ");
    rtbl_add_column(ct, COL_OID, 0);
    rtbl_add_column(ct, COL_NAME, 0);

    for (i = 0; i < mechs->count; i++) {
	gss_buffer_desc name;

	maj_stat = gss_oid_to_str(&min_stat, &mechs->elements[i], &name);
	if (maj_stat != GSS_S_COMPLETE)
	    errx(1, "gss_oid_to_str failed");

	rtbl_add_column_entryv(ct, COL_OID, "%.*s",
			       (int)name.length, (char *)name.value);
	gss_release_buffer(&min_stat, &name);

	if (gss_oid_equal(&mechs->elements[i], GSS_KRB5_MECHANISM))
	    rtbl_add_column_entry(ct, COL_NAME, "Kerberos 5");
	else if (gss_oid_equal(&mechs->elements[i], GSS_SPNEGO_MECHANISM))
	    rtbl_add_column_entry(ct, COL_NAME, "SPNEGO");
	else if (gss_oid_equal(&mechs->elements[i], GSS_NTLM_MECHANISM))
	    rtbl_add_column_entry(ct, COL_NAME, "NTLM");
    }
    gss_release_oid_set(&min_stat, &mechs);

    rtbl_format(ct, stdout);
    rtbl_destroy(ct);

    return 0;
}
Example #6
0
static void
write_stats(krb5_context context, slave *slaves, u_int32_t current_version)
{
    char str[100];
    rtbl_t tbl;
    time_t t = time(NULL);
    FILE *fp;

    fp = fopen(slave_stats_file, "w");
    if (fp == NULL)
	return;

    krb5_format_time(context, t, str, sizeof(str), TRUE); 
    fprintf(fp, "Status for slaves, last updated: %s\n\n", str);

    fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version);

    tbl = rtbl_create();
    if (tbl == NULL) {
	fclose(fp);
	return;
    }

    rtbl_add_column(tbl, SLAVE_NAME, 0);
    rtbl_add_column(tbl, SLAVE_ADDRESS, 0);
    rtbl_add_column(tbl, SLAVE_VERSION, RTBL_ALIGN_RIGHT);
    rtbl_add_column(tbl, SLAVE_STATUS, 0);
    rtbl_add_column(tbl, SLAVE_SEEN, 0);

    rtbl_set_prefix(tbl, "  ");
    rtbl_set_column_prefix(tbl, SLAVE_NAME, "");

    while (slaves) {
	krb5_address addr;
	krb5_error_code ret;
	rtbl_add_column_entry(tbl, SLAVE_NAME, slaves->name);
	ret = krb5_sockaddr2address (context, 
				     (struct sockaddr*)&slaves->addr, &addr);
	if(ret == 0) {
	    krb5_print_address(&addr, str, sizeof(str), NULL);
	    krb5_free_address(context, &addr);
	    rtbl_add_column_entry(tbl, SLAVE_ADDRESS, str);
	} else
	    rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>");
	
	snprintf(str, sizeof(str), "%u", (unsigned)slaves->version);
	rtbl_add_column_entry(tbl, SLAVE_VERSION, str);

	if (slaves->flags & SLAVE_F_DEAD)
	    rtbl_add_column_entry(tbl, SLAVE_STATUS, "Down");
	else
	    rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up");

	ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE); 
	rtbl_add_column_entry(tbl, SLAVE_SEEN, str);

	slaves = slaves->next;
    }

    rtbl_format(tbl, fp);
    rtbl_destroy(tbl);

    fclose(fp);
}
Example #7
0
int
kswitch(struct kswitch_options *opt, int argc, char **argv)
{
    krb5_error_code ret;
    krb5_ccache id = NULL;

    if (opt->cache_string && opt->principal_string)
	krb5_errx(kcc_context, 1,
		  N_("Both --cache and --principal given, choose one", ""));

    if (opt->interactive_flag) {
	krb5_cc_cache_cursor cursor;
	krb5_ccache *ids = NULL;
	size_t i, len = 0;
	char *name;
	rtbl_t ct;

	ct = rtbl_create();

	rtbl_add_column_by_id(ct, 0, "#", 0);
	rtbl_add_column_by_id(ct, 1, "Principal", 0);
	rtbl_set_column_affix_by_id(ct, 1, "    ", "");
        rtbl_add_column_by_id(ct, 2, "Type", 0);
        rtbl_set_column_affix_by_id(ct, 2, "  ", "");

	ret = krb5_cc_cache_get_first(kcc_context, NULL, &cursor);
	if (ret)
	    krb5_err(kcc_context, 1, ret, "krb5_cc_cache_get_first");

	while (krb5_cc_cache_next(kcc_context, cursor, &id) == 0) {
	    krb5_principal p;
	    char num[10];

	    ret = krb5_cc_get_principal(kcc_context, id, &p);
	    if (ret)
		continue;

	    ret = krb5_unparse_name(kcc_context, p, &name);
	    krb5_free_principal(kcc_context, p);

	    snprintf(num, sizeof(num), "%d", (int)(len + 1));
	    rtbl_add_column_entry_by_id(ct, 0, num);
	    rtbl_add_column_entry_by_id(ct, 1, name);
            rtbl_add_column_entry_by_id(ct, 2, krb5_cc_get_type(kcc_context, id));
	    free(name);

	    ids = erealloc(ids, (len + 1) * sizeof(ids[0]));
	    ids[len] = id;
	    len++;
	}
	krb5_cc_cache_end_seq_get(kcc_context, cursor);

	rtbl_format(ct, stdout);
	rtbl_destroy(ct);

	name = readline("Select number: ");
	if (name) {
	    i = atoi(name);
	    if (i == 0)
		krb5_errx(kcc_context, 1, "Cache number '%s' is invalid", name);
	    if (i > len)
		krb5_errx(kcc_context, 1, "Cache number '%s' is too large", name);

	    id = ids[i - 1];
	    ids[i - 1] = NULL;
	} else
	    krb5_errx(kcc_context, 1, "No cache selected");
	for (i = 0; i < len; i++)
	    if (ids[i])
		krb5_cc_close(kcc_context, ids[i]);

    } else if (opt->principal_string) {
	krb5_principal p;

	ret = krb5_parse_name(kcc_context, opt->principal_string, &p);
	if (ret)
	    krb5_err(kcc_context, 1, ret, "krb5_parse_name: %s",
		     opt->principal_string);

	ret = krb5_cc_cache_match(kcc_context, p, &id);
	if (ret)
	    krb5_err(kcc_context, 1, ret,
		     N_("Did not find principal: %s", ""),
		     opt->principal_string);

	krb5_free_principal(kcc_context, p);

    } else if (opt->cache_string) {
	const krb5_cc_ops *ops;
	char *str;

	ops = krb5_cc_get_prefix_ops(kcc_context, opt->type_string);
	if (ops == NULL)
	    krb5_err(kcc_context, 1, 0, "krb5_cc_get_prefix_ops");

	asprintf(&str, "%s:%s", ops->prefix, opt->cache_string);
	if (str == NULL)
	    krb5_errx(kcc_context, 1, N_("out of memory", ""));

	ret = krb5_cc_resolve(kcc_context, str, &id);
	if (ret)
	    krb5_err(kcc_context, 1, ret, "krb5_cc_resolve: %s", str);

	free(str);
    } else {
	krb5_errx(kcc_context, 1, "missing option for kswitch");
    }

    ret = krb5_cc_switch(kcc_context, id);
    if (ret)
	krb5_err(kcc_context, 1, ret, "krb5_cc_switch");

    return 0;
}
Example #8
0
static int
list_caches(krb5_context context)
{
    krb5_cc_cache_cursor cursor;
    const char *cdef_name;
    char *def_name;
    krb5_error_code ret;
    krb5_ccache id;
    rtbl_t ct;

    cdef_name = krb5_cc_default_name(context);
    if (cdef_name == NULL)
	krb5_errx(context, 1, "krb5_cc_default_name");
    def_name = strdup(cdef_name);

    ret = krb5_cc_cache_get_first (context, NULL, &cursor);
    if (ret == KRB5_CC_NOSUPP)
	return 0;
    else if (ret)
	krb5_err (context, 1, ret, "krb5_cc_cache_get_first");

    ct = rtbl_create();
    rtbl_add_column(ct, COL_NAME, 0);
    rtbl_add_column(ct, COL_CACHENAME, 0);
    rtbl_add_column(ct, COL_EXPIRES, 0);
    rtbl_add_column(ct, COL_DEFCACHE, 0);
    rtbl_set_prefix(ct, "   ");
    rtbl_set_column_prefix(ct, COL_NAME, "");

    while (krb5_cc_cache_next (context, cursor, &id) == 0) {
	krb5_principal principal = NULL;
	int expired = 0;
	char *name;
	time_t t;

	ret = krb5_cc_get_principal(context, id, &principal);
	if (ret)
	    continue;

	expired = check_for_tgt (context, id, principal, &t);

	ret = krb5_cc_get_friendly_name(context, id, &name);
	if (ret == 0) {
	    const char *str;
	    char *fname;
	    rtbl_add_column_entry(ct, COL_NAME, name);
	    rtbl_add_column_entry(ct, COL_CACHENAME,
				  krb5_cc_get_name(context, id));
	    if (expired)
		str = N_(">>> Expired <<<", "");
	    else
		str = printable_time(t);
	    rtbl_add_column_entry(ct, COL_EXPIRES, str);
	    free(name);

	    ret = krb5_cc_get_full_name(context, id, &fname);
	    if (ret)
		krb5_err (context, 1, ret, "krb5_cc_get_full_name");

	    if (strcmp(fname, def_name) == 0)
		rtbl_add_column_entry(ct, COL_DEFCACHE, "*");
	    else
		rtbl_add_column_entry(ct, COL_DEFCACHE, "");

	    krb5_xfree(fname);
	}
	krb5_cc_close(context, id);

	krb5_free_principal(context, principal);
    }

    krb5_cc_cache_end_seq_get(context, cursor);

    free(def_name);
    rtbl_format(ct, stdout);
    rtbl_destroy(ct);

    return 0;
}
Example #9
0
static void
print_tickets (krb5_context context,
	       krb5_ccache ccache,
	       krb5_principal principal,
	       int do_verbose,
	       int do_flags,
	       int do_hidden)
{
    krb5_error_code ret;
    char *str, *name;
    krb5_cc_cursor cursor;
    krb5_creds creds;
    krb5_deltat sec;

    rtbl_t ct = NULL;

    ret = krb5_unparse_name (context, principal, &str);
    if (ret)
	krb5_err (context, 1, ret, "krb5_unparse_name");

    printf ("%17s: %s:%s\n",
	    N_("Credentials cache", ""),
	    krb5_cc_get_type(context, ccache),
	    krb5_cc_get_name(context, ccache));
    printf ("%17s: %s\n", N_("Principal", ""), str);

    ret = krb5_cc_get_friendly_name(context, ccache, &name);
    if (ret == 0) {
	if (strcmp(name, str) != 0)
	    printf ("%17s: %s\n", N_("Friendly name", ""), name);
	free(name);
    }
    free (str);

    if(do_verbose) {
	printf ("%17s: %d\n", N_("Cache version", ""),
		krb5_cc_get_version(context, ccache));
    } else {
        krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET);
    }

    ret = krb5_cc_get_kdc_offset(context, ccache, &sec);

    if (ret == 0 && do_verbose && sec != 0) {
	char buf[BUFSIZ];
	int val;
	int sig;

	val = sec;
	sig = 1;
	if (val < 0) {
	    sig = -1;
	    val = -val;
	}
	
	unparse_time (val, buf, sizeof(buf));

	printf ("%17s: %s%s\n", N_("KDC time offset", ""),
		sig == -1 ? "-" : "", buf);
    }

    printf("\n");

    ret = krb5_cc_start_seq_get (context, ccache, &cursor);
    if (ret)
	krb5_err(context, 1, ret, "krb5_cc_start_seq_get");

    if(!do_verbose) {
	ct = rtbl_create();
	rtbl_add_column(ct, COL_ISSUED, 0);
	rtbl_add_column(ct, COL_EXPIRES, 0);
	if(do_flags)
	    rtbl_add_column(ct, COL_FLAGS, 0);
	rtbl_add_column(ct, COL_PRINCIPAL, 0);
	rtbl_set_separator(ct, "  ");
    }
    while ((ret = krb5_cc_next_cred (context,
				     ccache,
				     &cursor,
				     &creds)) == 0) {
	if (!do_hidden && krb5_is_config_principal(context, creds.server)) {
	    ;
	}else if(do_verbose){
	    print_cred_verbose(context, &creds);
	}else{
	    print_cred(context, &creds, ct, do_flags);
	}
	krb5_free_cred_contents (context, &creds);
    }
    if(ret != KRB5_CC_END)
	krb5_err(context, 1, ret, "krb5_cc_get_next");
    ret = krb5_cc_end_seq_get (context, ccache, &cursor);
    if (ret)
	krb5_err (context, 1, ret, "krb5_cc_end_seq_get");
    if(!do_verbose) {
	rtbl_format(ct, stdout);
	rtbl_destroy(ct);
    }
}
Example #10
0
static int
list_caches(krb5_context context, struct klist_options *opt)
{
    krb5_cccol_cursor cursor;
    const char *cdef_name;
    char *def_name;
    krb5_error_code ret;
    krb5_ccache id;
    rtbl_t ct;

    cdef_name = krb5_cc_default_name(context);
    if (cdef_name == NULL)
	krb5_errx(context, 1, "krb5_cc_default_name");
    def_name = strdup(cdef_name);

    ret = krb5_cccol_cursor_new(context, &cursor);
    if (ret == KRB5_CC_NOSUPP)
	return 0;
    else if (ret)
	krb5_err (context, 1, ret, "krb5_cc_cache_get_first");

    ct = rtbl_create();
    rtbl_add_column(ct, COL_DEFCACHE, 0);
    rtbl_add_column(ct, COL_NAME, 0);
    rtbl_add_column(ct, COL_CACHENAME, 0);
    rtbl_add_column(ct, COL_EXPIRES, 0);
    rtbl_add_column(ct, COL_DEFCACHE, 0);
    rtbl_set_prefix(ct, "   ");
    rtbl_set_column_prefix(ct, COL_DEFCACHE, "");
    rtbl_set_column_prefix(ct, COL_NAME, " ");
    if (opt->json_flag)
	rtbl_set_flags(ct, RTBL_JSON);

    while (krb5_cccol_cursor_next(context, cursor, &id) == 0) {
	int expired = 0;
	char *name;
	time_t t;

	expired = check_expiration(context, id, &t);

	ret = krb5_cc_get_friendly_name(context, id, &name);
	if (ret == 0) {
	    const char *str;
	    char *fname;

	    rtbl_add_column_entry(ct, COL_NAME, name);
	    free(name);

	    if (expired)
		str = N_(">>> Expired <<<", "");
	    else
		str = printable_time(t);
	    rtbl_add_column_entry(ct, COL_EXPIRES, str);

	    ret = krb5_cc_get_full_name(context, id, &fname);
	    if (ret)
		krb5_err (context, 1, ret, "krb5_cc_get_full_name");

	    rtbl_add_column_entry(ct, COL_CACHENAME, fname);
	    if (opt->json_flag)
		;
	    else if (strcmp(fname, def_name) == 0)
		rtbl_add_column_entry(ct, COL_DEFCACHE, "*");
	    else
		rtbl_add_column_entry(ct, COL_DEFCACHE, "");

	    krb5_xfree(fname);
	}
	krb5_cc_close(context, id);
    }

    krb5_cccol_cursor_free(context, &cursor);

    free(def_name);
    rtbl_format(ct, stdout);
    rtbl_destroy(ct);

    if (opt->json_flag)
	printf("\n");

    return 0;
}
Example #11
0
static int
do_list(struct list_options *opt, const char *keytab_str)
{
    krb5_error_code ret;
    krb5_keytab keytab;
    krb5_keytab_entry entry;
    krb5_kt_cursor cursor;
    rtbl_t table;

    /* XXX specialcase the ANY type */
    if(strncasecmp(keytab_str, "ANY:", 4) == 0) {
	int flag = 0;
	char buf[1024];
	keytab_str += 4;
	ret = 0;
	while (strsep_copy((const char**)&keytab_str, ",", 
			   buf, sizeof(buf)) != -1) {
	    if(flag)
		printf("\n");
	    if(do_list(opt, buf))
		ret = 1;
	    flag = 1;
	}
	return ret;
    }

    ret = krb5_kt_resolve(context, keytab_str, &keytab);
    if (ret) {
	krb5_warn(context, ret, "resolving keytab %s", keytab_str);
	return ret;
    }

    ret = krb5_kt_start_seq_get(context, keytab, &cursor);
    if(ret) {
	krb5_warn(context, ret, "krb5_kt_start_seq_get %s", keytab_str);
	krb5_kt_close(context, keytab);
	return ret;
    }

    printf ("%s:\n\n", keytab_str);
	
    table = rtbl_create();
    rtbl_add_column_by_id(table, 0, "Vno", RTBL_ALIGN_RIGHT);
    rtbl_add_column_by_id(table, 1, "Type", 0);
    rtbl_add_column_by_id(table, 2, "Principal", 0);
    if (opt->timestamp_flag)
	rtbl_add_column_by_id(table, 3, "Date", 0);
    if(opt->keys_flag)
	rtbl_add_column_by_id(table, 4, "Key", 0);
    rtbl_set_separator(table, "  ");

    while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
	char buf[1024], *s;

	snprintf(buf, sizeof(buf), "%d", entry.vno);
	rtbl_add_column_entry_by_id(table, 0, buf);

	ret = krb5_enctype_to_string(context, 
				     entry.keyblock.keytype, &s);
	if (ret != 0) {
	    snprintf(buf, sizeof(buf), "unknown (%d)", entry.keyblock.keytype);
	    rtbl_add_column_entry_by_id(table, 1, buf);
	} else {
	    rtbl_add_column_entry_by_id(table, 1, s);
	    free(s);
	}

	krb5_unparse_name_fixed(context, entry.principal, buf, sizeof(buf));
	rtbl_add_column_entry_by_id(table, 2, buf);

	if (opt->timestamp_flag) {
	    krb5_format_time(context, entry.timestamp, buf, 
			     sizeof(buf), FALSE);
	    rtbl_add_column_entry_by_id(table, 3, buf);
	}
	if(opt->keys_flag) {
	    int i;
	    s = malloc(2 * entry.keyblock.keyvalue.length + 1);
	    if (s == NULL) {
		krb5_warnx(context, "malloc failed");
		ret = ENOMEM;
		goto out;
	    }
	    for(i = 0; i < entry.keyblock.keyvalue.length; i++)
		snprintf(s + 2 * i, 3, "%02x", 
			 ((unsigned char*)entry.keyblock.keyvalue.data)[i]);
	    rtbl_add_column_entry_by_id(table, 4, s);
	    free(s);
	}
	krb5_kt_free_entry(context, &entry);
    }
    ret = krb5_kt_end_seq_get(context, keytab, &cursor);
    rtbl_format(table, stdout);

out:
    rtbl_destroy(table);

    krb5_kt_close(context, keytab);
    return ret;
}