#include <CoreFoundation/CoreFoundation.h>
#include <Kerberos/Kerberos.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
const char *program = NULL;
char *ccacheString = NULL;
char *etypeString = NULL;
char *keytabString = NULL;
char * const *serviceNames = NULL;
int serviceNameCount = 0;
int quiet = 0;
static int options (int argc, char * const * argv);
static int usage (void);
static void printiferr (errcode_t err, const char *format, ...);
static void printerr (const char *format, ...);
static void vprinterr (const char *format, va_list args);
int main (int argc, char * const * argv)
{
int err = 0;
int errCount = 0;
int i;
krb5_context context = NULL;
krb5_principal client = NULL;
krb5_ccache ccache = NULL;
krb5_enctype etype = 0;
krb5_keytab keytab = NULL;
program = strrchr (argv[0], '/') ? strrchr (argv[0], '/') + 1 : argv[0];
if (!err) {
err = options (argc, argv);
}
if (!err) {
err = krb5_init_context (&context);
printiferr (err, "while initializing Kerberos 5");
}
if (!err && etypeString) {
err = krb5_string_to_enctype (etypeString, &etype);
printiferr (err, "while converting etype");
}
if (!err) {
if (ccacheString) {
err = krb5_cc_resolve (context, ccacheString, &ccache);
printiferr (err, "while opening credentials cache '%s'",
ccacheString);
} else {
err = krb5_cc_default (context, &ccache);
printiferr (err, "while opening credentials cache");
}
}
if (!err && keytabString) {
err = krb5_kt_resolve(context, keytabString, &keytab);
printiferr (err, "resolving keytab %s", keytabString);
}
if (!err) {
err = krb5_cc_get_principal (context, ccache, &client);
printiferr (err, "while getting client principal name");
}
for (i = 0; i < serviceNameCount; i++) {
krb5_creds inCreds;
krb5_creds *outCreds = NULL;
krb5_principal server = NULL;
krb5_ticket *ticket = NULL;
char *principalString = NULL;
if (!err) {
err = krb5_parse_name (context, serviceNames[i], &server);
printiferr (err, "while parsing principal name '%s'", serviceNames[i]);
}
if (!err) {
err = krb5_unparse_name (context, server, &principalString);
printiferr (err, "while getting string for principal name '%s'", serviceNames[i]);
}
if (!err) {
memset (&inCreds, 0, sizeof (inCreds));
inCreds.client = client;
inCreds.server = server;
inCreds.keyblock.enctype = etype;
}
if (!err) {
krb5_creds oldCreds;
int freeOldCreds = 0;
int removedOldCreds = 0;
if (krb5_cc_retrieve_cred (context, ccache, 0, &inCreds, &oldCreds) == 0) {
freeOldCreds = 1;
if (krb5_cc_remove_cred (context, ccache, 0, &oldCreds) == 0) {
removedOldCreds = 1;
}
}
err = krb5_get_credentials (context, 0, ccache, &inCreds, &outCreds);
printiferr (err, "while getting credentials for '%s'", principalString);
if (err && removedOldCreds) {
krb5_cc_store_cred (context, ccache, &oldCreds);
}
if (freeOldCreds) { krb5_free_cred_contents (context, &oldCreds); }
}
if (!err) {
err = krb5_decode_ticket (&outCreds->ticket, &ticket);
printiferr (err, "while decoding ticket for '%s'", principalString);
}
if (!err) {
if (keytab) {
err = krb5_server_decrypt_ticket_keytab (context, keytab, ticket);
if (!quiet) {
printf ("%s: kvno = %d, keytab entry %s\n", principalString,
ticket->enc_part.kvno, (err ? "invalid" : "valid"));
}
printiferr (err, "while decrypting ticket for '%s'", principalString);
} else if (!quiet) {
printf ("%s: kvno = %d\n", principalString, ticket->enc_part.kvno);
}
}
if (err) {
err = 0;
errCount++;
}
if (server ) { krb5_free_principal (context, server); }
if (ticket ) { krb5_free_ticket (context, ticket); }
if (outCreds ) { krb5_free_creds (context, outCreds); }
if (principalString) { krb5_free_unparsed_name (context, principalString); }
}
if (client ) { krb5_free_principal (context, client); }
if (keytab ) { krb5_kt_close (context, keytab); }
if (ccache ) { krb5_cc_close (context, ccache); }
if (context) { krb5_free_context (context); }
return (err || errCount > 0) ? 1 : 0;
}
static int options (int argc, char * const * argv)
{
int option;
while ((option = getopt (argc, argv, "c:e:k:hq4")) != -1) {
switch (option) {
case 'c':
if (ccacheString) {
printerr ("Only one -c option allowed\n");
return usage ();
}
ccacheString = optarg;
break;
case 'e':
if (etypeString) {
printerr ("Only one -e option allowed\n");
return usage ();
}
etypeString = optarg;
break;
case 'k':
if (keytabString) {
printerr ("Only one -k option allowed\n");
return usage ();
}
keytabString = optarg;
break;
case 'q':
quiet = 1;
break;
case '4':
printerr ("Warning: -4 is no longer supported\n");
return usage ();
break;
case 'h':
return usage ();
default:
return usage ();
}
}
if (argc - optind <= 0) {
printerr ("Please specify a service name\n");
return usage ();
}
serviceNameCount = argc - optind;
serviceNames = &argv[optind];
return 0;
}
static int usage (void)
{
fprintf (stderr, "Usage: %s [-h] | [-q] [-c ccache] [-e etype] [-k keytab]]"
" service1 service2 ...\n", program);
fprintf (stderr, "\t-h print usage message\n");
fprintf (stderr, "\t-q quiet mode\n");
fprintf (stderr, "\t-c <ccache> use credentials cache \"ccache\"\n");
fprintf (stderr, "\t-e <etype> get tickets with enctype \"etype\"\n");
fprintf (stderr, "\t-k <keytab> validate service tickets with \"keytab\"\n");
return 2;
}
static void printiferr (errcode_t err, const char *format, ...)
{
if (err && (err != ccIteratorEnd) && (err != KRB5_CC_END)) {
va_list pvar;
va_start (pvar, format);
com_err_va (program, err, format, pvar);
va_end (pvar);
}
}
static void printerr (const char *format, ...)
{
va_list pvar;
va_start (pvar, format);
vprinterr (format, pvar);
va_end (pvar);
}
static void vprinterr (const char *format, va_list args)
{
if (!quiet) {
fprintf (stderr, "%s: ", program);
vfprintf (stderr, format, args);
}
}