#include <config.h>
#include <stdio.h>
#include <gssapi.h>
#include <gssapi_krb5.h>
#include <gssapi_spnego.h>
#include <gssapi_ntlm.h>
#include <gssapi_spi.h>
#include <heim-ipc.h>
#include <err.h>
#include <roken.h>
#include <getarg.h>
#include <rtbl.h>
#include <gss-commands.h>
#include <krb5.h>
#include <parse_time.h>
#include "crypto-headers.h"
static int version_flag = 0;
static int help_flag = 0;
static struct getargs args[] = {
{"version", 0, arg_flag, &version_flag, "print version", NULL },
{"help", 0, arg_flag, &help_flag, NULL, NULL }
};
static void
usage (int ret)
{
arg_printusage (args, sizeof(args)/sizeof(*args),
NULL, "service@host");
exit (ret);
}
#define COL_OID "OID"
#define COL_NAME "Name"
#define COL_MECH "Mech"
#define COL_OPTION "Option"
#define COL_ENABLED "Enabled"
#define COL_EXPIRE "Expire"
int
supported_mechanisms(struct supported_mechanisms_options *opt, 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);
if (opt->options_flag) {
rtbl_add_column(ct, COL_OPTION, 0);
rtbl_add_column(ct, COL_ENABLED, 0);
}
for (i = 0; i < mechs->count; i++) {
gss_buffer_desc str;
const char *name = NULL;
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);
name = gss_oid_to_name(&mechs->elements[i]);
if (name)
rtbl_add_column_entry(ct, COL_NAME, name);
else
rtbl_add_column_entry(ct, COL_NAME, "");
if (opt->options_flag) {
gss_OID_set options = GSS_C_NO_OID_SET;
gss_buffer_desc oname;
size_t n;
int ena;
gss_mo_list(&mechs->elements[i], &options);
if (options == NULL || options->count == 0) {
rtbl_add_column_entry(ct, COL_OPTION, "");
rtbl_add_column_entry(ct, COL_ENABLED, "");
}
for (n = 0; options && n < options->count; n++) {
maj_stat = gss_mo_name(&mechs->elements[i], &options->elements[n], &oname);
if (maj_stat != GSS_S_COMPLETE)
continue;
if (n != 0) {
rtbl_add_column_entry(ct, COL_OID, "");
rtbl_add_column_entry(ct, COL_NAME, "");
}
ena = gss_mo_get(&mechs->elements[i], &options->elements[n], NULL);
rtbl_add_column_entryv(ct, COL_OPTION, "%.*s", (int)oname.length, (char *)oname.value);
rtbl_add_column_entry(ct, COL_ENABLED, ena ? "yes" : "no");
gss_release_buffer(&min_stat, &oname);
}
}
}
gss_release_oid_set(&min_stat, &mechs);
rtbl_format(ct, stdout);
rtbl_destroy(ct);
return 0;
}
int
acquire_credential(struct acquire_credential_options *opt, int argc, char **argv)
{
char password[512];
OM_uint32 maj_stat, min_stat;
gss_OID mech = NULL;
gss_OID nametype = GSS_C_NT_USER_NAME;
gss_name_t name = GSS_C_NO_NAME;
gss_buffer_desc buffer;
gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
CFMutableDictionaryRef attributes;
CFStringRef pw;
attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (attributes == NULL)
errx(1, "out of memory");
if (opt->mech_string) {
mech = gss_name_to_oid(opt->mech_string);
if (mech == NULL)
errx(1, "No such mech: %s", opt->mech_string);
} else {
mech = GSS_KRB5_MECHANISM;
}
if (opt->user_string == NULL && argc < 1)
errx(1, "no user string");
if (opt->user_string) {
buffer.value = rk_UNCONST(opt->user_string);
buffer.length = strlen(opt->user_string);
} else {
buffer.value = argv[0];
buffer.length = strlen(argv[0]);
}
maj_stat = gss_import_name(&min_stat, &buffer, nametype, &name);
if (maj_stat)
errx(1, "failed to import name");
if (UI_UTIL_read_pw_string(password, sizeof(password),
"Password: ", 0) != 0)
errx(1, "failed reading password");
pw = CFStringCreateWithCString(kCFAllocatorDefault, password, kCFStringEncodingUTF8);
CFDictionaryAddValue(attributes, kGSSICPassword, pw);
CFRelease(pw);
maj_stat = gss_aapl_initial_cred(name,
mech,
attributes,
&cred,
NULL);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_acquire_cred_ex_f: %d", (int)maj_stat);
gss_release_cred(&min_stat, &cred);
gss_release_name(&min_stat, &name);
CFRelease(attributes);
return 0;
}
struct print_cred {
heim_isemaphore s;
rtbl_t t;
};
static void
print_cred(void *ctx, gss_OID oid, gss_cred_id_t cred)
{
struct print_cred *pc = ctx;
OM_uint32 major, junk;
gss_buffer_desc buffer;
gss_name_t name;
const char *str;
OM_uint32 expire;
if (cred == NULL) {
heim_ipc_semaphore_signal(pc->s);
return;
}
major = gss_inquire_cred(&junk, cred, &name, &expire, NULL, NULL);
if (major) goto out;
major = gss_display_name(&junk, name, &buffer, NULL);
gss_release_name(&junk, &name);
if (major) goto out;
rtbl_add_column_entryv(pc->t, COL_NAME, "%.*s",
(int)buffer.length, (char *)buffer.value);
gss_release_buffer(&junk, &buffer);
str = gss_oid_to_name(oid);
if (str)
rtbl_add_column_entry(pc->t, COL_MECH, str);
if (expire == GSS_C_INDEFINITE)
rtbl_add_column_entryv(pc->t, COL_EXPIRE, "never");
else if (expire == 0)
rtbl_add_column_entryv(pc->t, COL_EXPIRE, "expired");
else {
char life[80];
unparse_time_approx(expire, life, sizeof(life));
rtbl_add_column_entry(pc->t, COL_EXPIRE, life);
}
out:
gss_release_cred(&junk, &cred);
}
int
list_credentials(void *opt, int argc, char **argv)
{
struct print_cred pc;
pc.s = heim_ipc_semaphore_create(0);
pc.t = rtbl_create();
if (pc.t == NULL)
errx(1, "rtbl_create");
rtbl_set_separator(pc.t, " ");
rtbl_add_column(pc.t, COL_NAME, 0);
rtbl_add_column(pc.t, COL_EXPIRE, 0);
rtbl_add_column(pc.t, COL_MECH, 0);
gss_iter_creds_f(NULL, 0, NULL, &pc, print_cred);
heim_ipc_semaphore_wait(pc.s, HEIM_IPC_WAIT_FOREVER);
rtbl_format(pc.t, stdout);
rtbl_destroy(pc.t);
heim_ipc_semaphore_release(pc.s);
return 0;
}
static gss_cred_id_t
acquire_cred(const char *name_string, gss_OID mech, gss_OID nametype)
{
OM_uint32 maj_stat, min_stat;
gss_OID_set mechset = NULL;
gss_cred_id_t cred = NULL;
gss_buffer_desc buffer;
gss_name_t name;
buffer.value = rk_UNCONST(name_string);
buffer.length = strlen(name_string);
maj_stat = gss_import_name(&min_stat, &buffer, nametype, &name);
if (maj_stat)
errx(1, "failed to import name");
if (mech) {
gss_create_empty_oid_set(&min_stat, &mechset);
gss_add_oid_set_member(&min_stat, mech, &mechset);
}
maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE,
mechset, GSS_C_INITIATE,
&cred, NULL, NULL);
gss_release_name(&min_stat, &name);
gss_release_oid_set(&min_stat, &mechset);
if (maj_stat || cred == NULL)
errx(1, "acquire_cred failed");
return cred;
}
static void
destroy_cred(void *arg1, gss_OID oid, gss_cred_id_t cred)
{
gss_destroy_cred(NULL, &cred);
}
int
destroy(struct destroy_options *opt, int argc, char **argv)
{
gss_OID mech = NULL;
if (opt->mech_string) {
mech = gss_name_to_oid(opt->mech_string);
if (mech == NULL)
errx(1, "No such mech: %s", opt->mech_string);
}
if (opt->all_flag) {
gss_iter_creds_f(NULL, 0, mech, NULL, destroy_cred);
} else {
gss_cred_id_t cred;
if (argc < 1) {
printf("%s: missing name\n", getprogname());
return 1;
}
cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
gss_destroy_cred(NULL, &cred);
}
return 0;
}
static int
common_hold(OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t),
const char *mech_string, int argc, char **argv)
{
OM_uint32 min_stat, maj_stat;
gss_OID mech = GSS_C_NO_OID;
gss_cred_id_t cred;
if (argc < 1) {
printf("missing username to (un)hold\n");
return 1;
}
if (mech_string) {
mech = gss_name_to_oid(mech_string);
if (mech == NULL)
errx(1, "No such mech: %s", mech_string);
}
cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
maj_stat = func(&min_stat, cred);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "(un)hold failed");
gss_release_cred(&min_stat, &cred);
return 0;
}
int
hold(struct hold_options *opt, int argc, char **argv)
{
return common_hold(gss_cred_hold, opt->mech_string, argc, argv);
}
int
unhold(struct unhold_options *opt, int argc, char **argv)
{
return common_hold(gss_cred_unhold, opt->mech_string, argc, argv);
}
int
get_label(struct get_label_options *opt, int argc, char **argv)
{
OM_uint32 min_stat, maj_stat;
gss_OID mech = GSS_C_NO_OID;
gss_cred_id_t cred;
gss_buffer_desc buf;
if (opt->mech_string) {
mech = gss_name_to_oid(opt->mech_string);
if (mech == NULL)
errx(1, "No such mech: %s", opt->mech_string);
}
cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
maj_stat = gss_cred_label_get(&min_stat, cred, argv[1], &buf);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "label get failed");
printf("value: %.*s\n", (int)buf.length, (char *)buf.value);
gss_release_buffer(&min_stat, &buf);
gss_release_cred(&min_stat, &cred);
return 0;
}
int
set_label(struct set_label_options *opt, int argc, char **argv)
{
OM_uint32 min_stat, maj_stat;
gss_OID mech = GSS_C_NO_OID;
gss_cred_id_t cred;
gss_buffer_desc buf;
gss_buffer_t bufp = NULL;
if (opt->mech_string) {
mech = gss_name_to_oid(opt->mech_string);
if (mech == NULL)
errx(1, "No such mech: %s", opt->mech_string);
}
cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
if (argc > 2) {
buf.value = argv[2];
buf.length = strlen(argv[2]);
bufp = &buf;
}
maj_stat = gss_cred_label_set(&min_stat, cred, argv[1], bufp);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "label get failed");
gss_release_cred(&min_stat, &cred);
return 0;
}
int
help(void *opt, int argc, char **argv)
{
sl_slc_help(commands, argc, argv);
return 0;
}
int
main(int argc, char **argv)
{
int optidx = 0;
setprogname(argv[0]);
if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
usage(1);
if (help_flag)
usage (0);
if(version_flag){
print_version(NULL);
exit(0);
}
argc -= optidx;
argv += optidx;
if (argc == 0) {
help(NULL, argc, argv);
return 1;
}
return sl_command (commands, argc, argv);
}