1/*
2 * Copyright (c) 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of KTH nor the names of its contributors may be
20 *    used to endorse or promote products derived from this software without
21 *    specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#include <config.h>
37
38#include <stdio.h>
39#include <gssapi.h>
40#include <gssapi_krb5.h>
41#include <gssapi_spnego.h>
42#include <gssapi_ntlm.h>
43#include <gssapi_oid.h>
44#include <gssapi_spi.h>
45#include <heim-ipc.h>
46#include <err.h>
47#include <roken.h>
48#include <getarg.h>
49#include <rtbl.h>
50#include <hex.h>
51#include <gss-commands.h>
52#include <krb5.h>
53#ifdef __APPLE__
54#include <heimcred.h>
55#endif
56#include <parse_time.h>
57
58#include "crypto-headers.h"
59
60static void usage (int ret) __attribute__((noreturn));
61
62
63static int version_flag = 0;
64static int help_flag	= 0;
65
66static struct getargs args[] = {
67    {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
68    {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
69};
70
71static void
72usage (int ret)
73{
74    arg_printusage (args, sizeof(args)/sizeof(*args),
75		    NULL, "service@host");
76    exit (ret);
77}
78
79#define COL_DESC	"Description"
80#define COL_ENABLED	"Enabled"
81#define COL_EXPIRE	"Expire"
82#define COL_MECH	"Mech"
83#define COL_NAME	"Name"
84#define COL_OID		"OID"
85#define COL_OPTION	"Option"
86#define COL_SASL	"SASL"
87#define COL_VALUE	"Value"
88#define COL_UUID	"UUID"
89
90int
91supported_mechanisms(struct supported_mechanisms_options *opt, int argc, char **argv)
92{
93    OM_uint32 maj_stat, min_stat;
94    gss_OID_set mechs;
95    rtbl_t ct;
96    size_t i;
97
98    maj_stat = gss_indicate_mechs(&min_stat, &mechs);
99    if (maj_stat != GSS_S_COMPLETE)
100	errx(1, "gss_indicate_mechs failed");
101
102    printf("Supported mechanisms:\n");
103
104    ct = rtbl_create();
105    if (ct == NULL)
106	errx(1, "rtbl_create");
107
108    rtbl_set_separator(ct, "  ");
109    rtbl_add_column(ct, COL_OID, 0);
110    rtbl_add_column(ct, COL_NAME, 0);
111    rtbl_add_column(ct, COL_SASL, 0);
112    if (opt->options_flag) {
113	rtbl_add_column(ct, COL_OPTION, 0);
114	rtbl_add_column(ct, COL_ENABLED, 0);
115    }
116
117    for (i = 0; i < mechs->count; i++) {
118	gss_buffer_desc str;
119	const char *name = NULL;
120
121	maj_stat = gss_oid_to_str(&min_stat, &mechs->elements[i], &str);
122	if (maj_stat != GSS_S_COMPLETE)
123	    errx(1, "gss_oid_to_str failed");
124
125	rtbl_add_column_entryv(ct, COL_OID, "%.*s",
126			       (int)str.length, (char *)str.value);
127	gss_release_buffer(&min_stat, &str);
128
129	name = gss_oid_to_name(&mechs->elements[i]);
130	if (name)
131	    rtbl_add_column_entry(ct, COL_NAME, name);
132	else
133	    rtbl_add_column_entry(ct, COL_NAME, "");
134
135	maj_stat = gss_inquire_saslname_for_mech(&min_stat, &mechs->elements[i], &str, NULL, NULL);
136	if (maj_stat != GSS_S_COMPLETE)
137	    errx(1, "gss_inquire_saslname_for_mech failed");
138	rtbl_add_column_entryv(ct, COL_SASL, "%.*s",
139			       (int)str.length, (char *)str.value);
140	gss_release_buffer(&min_stat, &str);
141
142	if (opt->options_flag) {
143	    gss_OID_set options = GSS_C_NO_OID_SET;
144	    gss_buffer_desc oname;
145	    size_t n;
146	    int ena;
147
148	    gss_mo_list(&mechs->elements[i], &options);
149
150	    rtbl_add_column_entry(ct, COL_OPTION, "");
151	    rtbl_add_column_entry(ct, COL_ENABLED, "");
152
153	    for (n = 0; options && n < options->count; n++) {
154		maj_stat = gss_mo_name(&mechs->elements[i], &options->elements[n], &oname);
155		if (maj_stat != GSS_S_COMPLETE)
156		    continue;
157
158		rtbl_add_column_entry(ct, COL_OID, "");
159		rtbl_add_column_entry(ct, COL_NAME, "");
160		rtbl_add_column_entry(ct, COL_SASL, "");
161
162		ena = gss_mo_get(&mechs->elements[i], &options->elements[n], NULL);
163
164		rtbl_add_column_entryv(ct, COL_OPTION, "%.*s", (int)oname.length, (char *)oname.value);
165		rtbl_add_column_entry(ct, COL_ENABLED, ena ? "yes" : "no");
166		gss_release_buffer(&min_stat, &oname);
167	    }
168	}
169    }
170    gss_release_oid_set(&min_stat, &mechs);
171
172    rtbl_format(ct, stdout);
173    rtbl_destroy(ct);
174
175    return 0;
176}
177
178int
179acquire_credential(struct acquire_credential_options *opt, int argc, char **argv)
180{
181    char password[512];
182    OM_uint32 maj_stat, min_stat;
183    gss_const_OID mech = NULL;
184    gss_OID nametype = GSS_C_NT_USER_NAME;
185    gss_name_t name = GSS_C_NO_NAME;
186    gss_buffer_desc buffer;
187    gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
188    CFMutableDictionaryRef attributes;
189    CFStringRef pw;
190    CFErrorRef error = NULL;
191
192    /*
193     * mech
194     */
195
196    attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
197					   &kCFTypeDictionaryKeyCallBacks,
198					   &kCFTypeDictionaryValueCallBacks);
199    if (attributes == NULL)
200	errx(1, "out of memory");
201
202    if (opt->mech_string) {
203	mech = gss_name_to_oid(opt->mech_string);
204	if (mech == NULL)
205	    errx(1, "No such mech: %s", opt->mech_string);
206    } else {
207	mech = GSS_KRB5_MECHANISM;
208    }
209
210    /*
211     * user
212     */
213
214    if (opt->user_string == NULL && argc < 1)
215	errx(1, "no user string");
216
217    if (opt->user_string) {
218	buffer.value = rk_UNCONST(opt->user_string);
219	buffer.length = strlen(opt->user_string);
220    } else {
221	buffer.value = argv[0];
222	buffer.length = strlen(argv[0]);
223    }
224
225    maj_stat = gss_import_name(&min_stat, &buffer, nametype, &name);
226    if (maj_stat)
227	errx(1, "failed to import name");
228
229
230    if (opt->certificate_persistant_string) {
231	size_t slen = strlen(opt->certificate_persistant_string);
232	CFMutableDataRef data = CFDataCreateMutable(NULL, slen);
233	ssize_t dlen;
234
235	CFDataSetLength(data, strlen);
236
237	dlen = hex_decode(opt->certificate_persistant_string,
238			  CFDataGetMutableBytePtr(data),
239			  slen);
240	if (dlen < 0)
241	    errx(1, "failed to hex decode reference");
242
243	CFDataSetLength(data, dlen);
244
245	CFDictionarySetValue(attributes, kGSSICCertificate, data);
246	CFRelease(data);
247
248    } else {
249
250	/*
251	 * password
252	 */
253
254	if (UI_UTIL_read_pw_string(password, sizeof(password),
255				   "Password: ", 0) != 0)
256	    errx(1, "failed reading password");
257
258	pw = CFStringCreateWithCString(kCFAllocatorDefault, password, kCFStringEncodingUTF8);
259	CFDictionarySetValue(attributes, kGSSICPassword, pw);
260	CFRelease(pw);
261    }
262
263    if (opt->validate_flag)
264	CFDictionarySetValue(attributes, kGSSICVerifyCredential, kCFBooleanTrue);
265    if (opt->kdc_hostname_string) {
266	CFStringRef hn = CFStringCreateWithCString(NULL, opt->kdc_hostname_string, kCFStringEncodingUTF8);
267	if (hn == NULL)
268	    errx(1, "CFStringCreateWithCString");
269	CFDictionarySetValue(attributes, kGSSICLKDCHostname, hn);
270	CFRelease(hn);
271    }
272
273    maj_stat = gss_aapl_initial_cred(name,
274				     mech,
275				     attributes,
276				     &cred,
277				     &error);
278    if (maj_stat != GSS_S_COMPLETE) {
279	char *msg = NULL;
280	if (error) {
281	    CFStringRef m;
282	    m = CFErrorCopyDescription(error);
283	    if (m) {
284		msg = rk_cfstring2cstring(m);
285		CFRelease(m);
286	    }
287	}
288	errx(1, "gss_aapl_initial_cred: %s: %d",
289	     msg ? msg : "", (int)maj_stat);
290    }
291    gss_release_cred(&min_stat, &cred);
292    gss_release_name(&min_stat, &name);
293
294    CFRelease(attributes);
295
296    if (error)
297	CFRelease(error);
298
299    return 0;
300}
301
302
303struct print_cred {
304    rtbl_t t;
305};
306
307static void
308print_cred(void *ctx, gss_const_OID oid, gss_cred_id_t cred)
309{
310    struct print_cred *pc = ctx;
311    gss_buffer_set_t data_set;
312    OM_uint32 major, junk;
313    gss_buffer_desc buffer;
314    gss_name_t name;
315    const char *str;
316    OM_uint32 expire;
317
318    if (cred == NULL)
319	return;
320
321    major = gss_inquire_cred(&junk, cred, NULL, &expire, NULL, NULL);
322    if (major == GSS_S_CREDENTIALS_EXPIRED)
323	expire = 0;
324    else if (major)
325	goto out;
326    major = gss_inquire_cred_by_mech(&junk, cred, (gss_OID)oid, &name, NULL, NULL, NULL);
327    if (major) goto out;
328    major = gss_display_name(&junk, name, &buffer, NULL);
329    gss_release_name(&junk, &name);
330    if (major) goto out;
331
332    rtbl_add_column_entryv(pc->t, COL_NAME, "%.*s",
333			   (int)buffer.length, (char *)buffer.value);
334
335    gss_release_buffer(&junk, &buffer);
336
337    str = gss_oid_to_name(oid);
338    if (str)
339	rtbl_add_column_entry(pc->t, COL_MECH, str);
340
341    if (expire == GSS_C_INDEFINITE)
342	rtbl_add_column_entryv(pc->t, COL_EXPIRE, "never");
343    else if (expire == 0)
344	rtbl_add_column_entryv(pc->t, COL_EXPIRE, "expired");
345    else {
346	char life[80];
347	unparse_time_approx(expire, life, sizeof(life));
348	rtbl_add_column_entry(pc->t, COL_EXPIRE, life);
349    }
350
351    major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_NT_UUID, &data_set);
352    if (major == GSS_S_COMPLETE && data_set->count == 1)
353	rtbl_add_column_entryv(pc->t, COL_UUID, "%.*s\n", (int)data_set->elements[0].length, (const char *)data_set->elements[0].value);
354    else
355	rtbl_add_column_entry(pc->t, COL_UUID, "");
356
357    gss_release_buffer_set(&junk, &data_set);
358
359 out:
360    gss_release_cred(&junk, &cred);
361}
362
363static void
364diag_cred(void *ctx, gss_const_OID oid, gss_cred_id_t cred)
365{
366    const char *delim = "----------------";
367    gss_buffer_set_t data_set;
368    gss_buffer_desc buffer;
369    OM_uint32 major, junk;
370    gss_name_t name;
371    const char *mech;
372    size_t n;
373
374    if (cred == NULL)
375	return;
376
377    major = gss_inquire_cred_by_mech(&junk, cred, (gss_OID)oid, &name, NULL, NULL, NULL);
378    if (major)
379	return;
380
381    major = gss_display_name(&junk, name, &buffer, NULL);
382    gss_release_name(&junk, &name);
383    if (major)
384	return;
385
386    mech = gss_oid_to_name(oid);
387
388    printf("@GSSCred\n%s\n%s: %.*s\n",
389	   delim,
390	   mech ? mech : "<unknown-mech>",
391	   (int)buffer.length, buffer.value);
392    gss_release_buffer(&junk, &buffer);
393
394    major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_CRED_DIAG, &data_set);
395    if (major)
396	return;
397
398    for (n = 0; n < data_set->count; n++)
399	printf("%s\n%.*s\n", delim, (int)data_set->elements[n].length, data_set->elements[n].value);
400
401    printf("%s\n", delim);
402
403    gss_release_buffer_set(&junk, &data_set);
404
405    major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_NT_UUID, &data_set);
406    if (major == GSS_S_COMPLETE || data_set->count == 1) {
407	printf("UUID: %.*s\n", (int)data_set->elements[0].length, (const char *)data_set->elements[0].value);
408	printf("%s\n", delim);
409    }
410    gss_release_buffer_set(&junk, &data_set);
411
412
413}
414
415
416
417int
418list_credentials(struct list_credentials_options *opt, int argc, char **argv)
419{
420    struct print_cred pc;
421    gss_const_OID mech = NULL;
422
423    if (opt->mech_string) {
424	mech = gss_name_to_oid(opt->mech_string);
425	if (mech == NULL)
426	    errx(1, "No such mech: %s", opt->mech_string);
427    }
428
429    if (opt->verbose_flag) {
430
431	gss_iter_creds_f(NULL, 0, mech, NULL, diag_cred);
432
433    } else {
434
435	pc.t = rtbl_create();
436	if (pc.t == NULL)
437	    errx(1, "rtbl_create");
438
439	rtbl_set_separator(pc.t, "  ");
440	rtbl_add_column(pc.t, COL_NAME, 0);
441	rtbl_add_column(pc.t, COL_EXPIRE, 0);
442	rtbl_add_column(pc.t, COL_MECH, 0);
443	rtbl_add_column(pc.t, COL_UUID, 0);
444
445	gss_iter_creds_f(NULL, 0, mech, &pc, print_cred);
446
447	rtbl_format(pc.t, stdout);
448	rtbl_destroy(pc.t);
449    }
450
451    return 0;
452}
453
454static gss_cred_id_t
455acquire_cred(const char *name_string, gss_const_OID mech, gss_const_OID nametype)
456{
457    OM_uint32 maj_stat, min_stat;
458    gss_OID_set mechset = NULL;
459    gss_cred_id_t cred = NULL;
460    gss_buffer_desc buffer;
461    gss_name_t name;
462
463    buffer.value = rk_UNCONST(name_string);
464    buffer.length = strlen(name_string);
465
466    maj_stat = gss_import_name(&min_stat, &buffer, nametype, &name);
467    if (maj_stat)
468	errx(1, "failed to import name");
469
470    if (mech) {
471	gss_create_empty_oid_set(&min_stat, &mechset);
472	gss_add_oid_set_member(&min_stat, mech, &mechset);
473    }
474
475    maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE,
476				mechset, GSS_C_INITIATE,
477				&cred, NULL, NULL);
478    gss_release_name(&min_stat, &name);
479    gss_release_oid_set(&min_stat, &mechset);
480    if (maj_stat || cred == NULL)
481	errx(1, "acquire_cred failed");
482
483    return cred;
484}
485
486static void
487destroy_cred(void *arg1, gss_const_OID oid, gss_cred_id_t cred)
488{
489    gss_destroy_cred(NULL, &cred);
490}
491
492int
493destroy(struct destroy_options *opt, int argc, char **argv)
494{
495    gss_const_OID mech = NULL;
496
497    if (opt->mech_string) {
498	mech = gss_name_to_oid(opt->mech_string);
499	if (mech == NULL)
500	    errx(1, "No such mech: %s", opt->mech_string);
501    }
502
503    if (opt->all_flag) {
504	gss_iter_creds_f(NULL, 0, mech, NULL, destroy_cred);
505    } else {
506	gss_cred_id_t cred;
507
508	if (argc < 1) {
509	    printf("%s: missing name\n", getprogname());
510	    return 1;
511	}
512
513	cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
514
515	gss_destroy_cred(NULL, &cred);
516
517    }
518
519    return 0;
520}
521
522/*
523 *
524 */
525
526static int
527common_hold(OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t),
528	    const char *mech_string, int argc, char **argv)
529{
530    OM_uint32 min_stat, maj_stat;
531    gss_const_OID mech = GSS_C_NO_OID;
532    gss_cred_id_t cred;
533
534    if (argc < 1) {
535	printf("missing username to (un)hold\n");
536	return 1;
537    }
538
539    if (mech_string) {
540	mech = gss_name_to_oid(mech_string);
541	if (mech == NULL)
542	    errx(1, "No such mech: %s", mech_string);
543    }
544
545    cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
546
547    maj_stat = func(&min_stat, cred);
548    if (maj_stat != GSS_S_COMPLETE)
549	errx(1, "(un)hold failed");
550
551    gss_release_cred(&min_stat, &cred);
552
553    return 0;
554}
555
556int
557hold(struct hold_options *opt, int argc, char **argv)
558{
559    return common_hold(gss_cred_hold, opt->mech_string, argc, argv);
560}
561
562int
563unhold(struct unhold_options *opt, int argc, char **argv)
564{
565    return common_hold(gss_cred_unhold, opt->mech_string, argc, argv);
566}
567
568int
569get_label(struct get_label_options *opt, int argc, char **argv)
570{
571    OM_uint32 min_stat, maj_stat;
572    gss_const_OID mech = GSS_C_NO_OID;
573    gss_cred_id_t cred;
574    gss_buffer_desc buf;
575
576    if (opt->mech_string) {
577	mech = gss_name_to_oid(opt->mech_string);
578	if (mech == NULL)
579	    errx(1, "No such mech: %s", opt->mech_string);
580    }
581
582    cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
583
584    maj_stat = gss_cred_label_get(&min_stat, cred, argv[1], &buf);
585    if (maj_stat != GSS_S_COMPLETE)
586	errx(1, "label get failed");
587
588    printf("value: %.*s\n", (int)buf.length, (char *)buf.value);
589
590    gss_release_buffer(&min_stat, &buf);
591    gss_release_cred(&min_stat, &cred);
592
593    return 0;
594}
595
596int
597set_label(struct set_label_options *opt, int argc, char **argv)
598{
599    OM_uint32 min_stat, maj_stat;
600    gss_const_OID mech = GSS_C_NO_OID;
601    gss_cred_id_t cred;
602    gss_buffer_desc buf;
603    gss_buffer_t bufp = NULL;
604
605    if (opt->mech_string) {
606	mech = gss_name_to_oid(opt->mech_string);
607	if (mech == NULL)
608	    errx(1, "No such mech: %s", opt->mech_string);
609    }
610
611    cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
612
613    if (argc > 2) {
614	buf.value = argv[2];
615	buf.length = strlen(argv[2]);
616	bufp = &buf;
617    }
618
619    maj_stat = gss_cred_label_set(&min_stat, cred, argv[1], bufp);
620    if (maj_stat != GSS_S_COMPLETE)
621	errx(1, "label get failed");
622
623    gss_release_cred(&min_stat, &cred);
624
625    return 0;
626}
627
628/*
629 *
630 */
631
632static void
633print_mech_attr(const char *mechname, gss_const_OID mech, gss_OID_set set)
634{
635    gss_buffer_desc name, desc;
636    OM_uint32 major, minor;
637    rtbl_t ct;
638    size_t n;
639
640    ct = rtbl_create();
641    if (ct == NULL)
642	errx(1, "rtbl_create");
643
644    rtbl_set_separator(ct, "  ");
645    rtbl_add_column(ct, COL_OID, 0);
646    rtbl_add_column(ct, COL_DESC, 0);
647    if (mech)
648	rtbl_add_column(ct, COL_VALUE, 0);
649
650    for (n = 0; n < set->count; n++) {
651	major = gss_display_mech_attr(&minor, &set->elements[n], &name, &desc, NULL);
652	if (major)
653	    continue;
654
655	rtbl_add_column_entryv(ct, COL_OID, "%.*s",
656			       (int)name.length, (char *)name.value);
657	rtbl_add_column_entryv(ct, COL_DESC, "%.*s",
658			       (int)desc.length, (char *)desc.value);
659	if (mech) {
660	    gss_buffer_desc value;
661
662	    if (gss_mo_get(mech, &set->elements[n], &value) != 0)
663		value.length = 0;
664
665	    if (value.length)
666		rtbl_add_column_entryv(ct, COL_VALUE, "%.*s",
667				       (int)value.length, (char *)value.value);
668	    else
669		rtbl_add_column_entryv(ct, COL_VALUE, "<>");
670	    gss_release_buffer(&minor, &value);
671	}
672
673	gss_release_buffer(&minor, &name);
674	gss_release_buffer(&minor, &desc);
675    }
676
677    printf("attributes for: %s\n", mechname);
678    rtbl_format(ct, stdout);
679    rtbl_destroy(ct);
680}
681
682int
683attrs_for_mech(struct attrs_for_mech_options *opt, int argc, char **argv)
684{
685    gss_OID_set mech_attr = NULL, known_mech_attrs = NULL;
686    gss_const_OID mech = GSS_C_NO_OID;
687    OM_uint32 major, minor;
688
689    if (opt->mech_string) {
690	mech = gss_name_to_oid(opt->mech_string);
691	if (mech == NULL)
692	    errx(1, "mech %s is unknown", opt->mech_string);
693    }
694
695    major = gss_inquire_attrs_for_mech(&minor,
696				       mech,
697				       &mech_attr,
698				       &known_mech_attrs);
699    if (major)
700	errx(1, "gss_inquire_attrs_for_mech");
701
702    if (mech)
703	print_mech_attr(opt->mech_string, mech, mech_attr);
704
705    if (opt->all_flag)
706	print_mech_attr("all mechs", NULL, known_mech_attrs);
707
708    gss_release_oid_set(&minor, &mech_attr);
709    gss_release_oid_set(&minor, &known_mech_attrs);
710
711    return 0;
712}
713
714int
715display_status(struct display_status_options *opt, int argc, char **argv)
716{
717    int status_type = GSS_C_GSS_CODE;
718    gss_const_OID mech = GSS_C_NO_OID;
719
720    if (opt->minor_status_flag)
721	status_type = GSS_C_MECH_CODE;
722
723    if (opt->mech_string) {
724	mech = gss_name_to_oid(opt->mech_string);
725	if (mech == NULL)
726	    errx(1, "mech %s is unknown", opt->mech_string);
727    }
728
729    for (;argc; argc--, argv++) {
730	OM_uint32 ret, new_stat, msg_ctx;
731	gss_buffer_desc status_string;
732	long num;
733
734	num = atol(argv[0]);
735	if (num == 0) {
736	    warnx("failed to parse %s as a number", argv[0]);
737	    continue;
738	}
739
740	msg_ctx = 0;
741	do {
742	    ret = gss_display_status(&new_stat,
743				     (OM_uint32)num,
744				     status_type,
745				     (gss_OID)mech,
746				     &msg_ctx,
747				     &status_string);
748	    if (!GSS_ERROR(ret)) {
749		printf("%s: %.*s\n", argv[0],
750		       (int)status_string.length,
751		       (char *)status_string.value);
752		gss_release_buffer(&new_stat, &status_string);
753	    }
754	} while (!GSS_ERROR(ret) && msg_ctx != 0);
755
756    }
757    return 0;
758}
759
760/*
761 *
762 */
763
764int
765credentials_status(struct credentials_status_options *opt, int argc, char **argv)
766{
767#ifdef __APPLE__
768    CFDictionaryRef status = HeimCredCopyStatus(NULL);
769    if (status) {
770	CFDataRef data = CFPropertyListCreateData(NULL, status, kCFPropertyListXMLFormat_v1_0, 0, NULL);
771	CFRelease(status);
772	if (data == NULL)
773	    return 1;
774	printf("%.*s\n", (int)CFDataGetLength(data), CFDataGetBytePtr(data));
775	CFRelease(data);
776    } else {
777	printf("no credentials to dump\n");
778	return 1;
779    }
780#endif
781    return 0;
782}
783
784/*
785 *
786 */
787
788int
789help(void *opt, int argc, char **argv)
790{
791    sl_slc_help(commands, argc, argv);
792    return 0;
793}
794
795int
796main(int argc, char **argv)
797{
798    int optidx = 0;
799
800    setprogname(argv[0]);
801    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
802	usage(1);
803
804    if (help_flag)
805	usage (0);
806
807    if(version_flag){
808	print_version(NULL);
809	exit(0);
810    }
811
812    argc -= optidx;
813    argv += optidx;
814
815    if (argc == 0) {
816	help(NULL, argc, argv);
817	return 1;
818    }
819
820    return sl_command (commands, argc, argv);
821}
822