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 <gss-commands.h>
51#include <krb5.h>
52#include <parse_time.h>
53
54#include "crypto-headers.h"
55
56static int version_flag = 0;
57static int help_flag	= 0;
58
59static struct getargs args[] = {
60    {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
61    {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
62};
63
64static void
65usage (int ret)
66{
67    arg_printusage (args, sizeof(args)/sizeof(*args),
68		    NULL, "service@host");
69    exit (ret);
70}
71
72#define COL_DESC	"Description"
73#define COL_ENABLED	"Enabled"
74#define COL_EXPIRE	"Expire"
75#define COL_MECH	"Mech"
76#define COL_NAME	"Name"
77#define COL_OID		"OID"
78#define COL_OPTION	"Option"
79#define COL_SASL	"SASL"
80#define COL_VALUE	"Value"
81#define COL_UUID	"UUID"
82
83int
84supported_mechanisms(struct supported_mechanisms_options *opt, int argc, char **argv)
85{
86    OM_uint32 maj_stat, min_stat;
87    gss_OID_set mechs;
88    rtbl_t ct;
89    size_t i;
90
91    maj_stat = gss_indicate_mechs(&min_stat, &mechs);
92    if (maj_stat != GSS_S_COMPLETE)
93	errx(1, "gss_indicate_mechs failed");
94
95    printf("Supported mechanisms:\n");
96
97    ct = rtbl_create();
98    if (ct == NULL)
99	errx(1, "rtbl_create");
100
101    rtbl_set_separator(ct, "  ");
102    rtbl_add_column(ct, COL_OID, 0);
103    rtbl_add_column(ct, COL_NAME, 0);
104    if (opt->options_flag) {
105	rtbl_add_column(ct, COL_OPTION, 0);
106	rtbl_add_column(ct, COL_ENABLED, 0);
107    }
108
109    for (i = 0; i < mechs->count; i++) {
110	gss_buffer_desc str;
111	const char *name = NULL;
112
113	maj_stat = gss_oid_to_str(&min_stat, &mechs->elements[i], &str);
114	if (maj_stat != GSS_S_COMPLETE)
115	    errx(1, "gss_oid_to_str failed");
116
117	rtbl_add_column_entryv(ct, COL_OID, "%.*s",
118			       (int)str.length, (char *)str.value);
119	gss_release_buffer(&min_stat, &str);
120
121	name = gss_oid_to_name(&mechs->elements[i]);
122	if (name)
123	    rtbl_add_column_entry(ct, COL_NAME, name);
124	else
125	    rtbl_add_column_entry(ct, COL_NAME, "");
126
127	if (opt->options_flag) {
128	    gss_OID_set options = GSS_C_NO_OID_SET;
129	    gss_buffer_desc oname;
130	    size_t n;
131	    int ena;
132
133	    gss_mo_list(&mechs->elements[i], &options);
134
135	    if (options == NULL || options->count == 0) {
136		rtbl_add_column_entry(ct, COL_OPTION, "");
137	        rtbl_add_column_entry(ct, COL_ENABLED, "");
138	    }
139
140	    for (n = 0; options && n < options->count; n++) {
141		maj_stat = gss_mo_name(&mechs->elements[i], &options->elements[n], &oname);
142		if (maj_stat != GSS_S_COMPLETE)
143		    continue;
144
145		if (n != 0) {
146		    rtbl_add_column_entry(ct, COL_OID, "");
147		    rtbl_add_column_entry(ct, COL_NAME, "");
148		}
149		ena = gss_mo_get(&mechs->elements[i], &options->elements[n], NULL);
150
151		rtbl_add_column_entryv(ct, COL_OPTION, "%.*s", (int)oname.length, (char *)oname.value);
152		rtbl_add_column_entry(ct, COL_ENABLED, ena ? "yes" : "no");
153		gss_release_buffer(&min_stat, &oname);
154	    }
155	}
156    }
157    gss_release_oid_set(&min_stat, &mechs);
158
159    rtbl_format(ct, stdout);
160    rtbl_destroy(ct);
161
162    return 0;
163}
164
165int
166acquire_credential(struct acquire_credential_options *opt, int argc, char **argv)
167{
168    char password[512];
169    OM_uint32 maj_stat, min_stat;
170    gss_const_OID mech = NULL;
171    gss_OID nametype = GSS_C_NT_USER_NAME;
172    gss_name_t name = GSS_C_NO_NAME;
173    gss_buffer_desc buffer;
174    gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
175    CFMutableDictionaryRef attributes;
176    CFStringRef pw;
177    CFErrorRef error = NULL;
178
179    /*
180     * mech
181     */
182
183    attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
184					   &kCFTypeDictionaryKeyCallBacks,
185					   &kCFTypeDictionaryValueCallBacks);
186    if (attributes == NULL)
187	errx(1, "out of memory");
188
189    if (opt->mech_string) {
190	mech = gss_name_to_oid(opt->mech_string);
191	if (mech == NULL)
192	    errx(1, "No such mech: %s", opt->mech_string);
193    } else {
194	mech = GSS_KRB5_MECHANISM;
195    }
196
197    /*
198     * user
199     */
200
201    if (opt->user_string == NULL && argc < 1)
202	errx(1, "no user string");
203
204    if (opt->user_string) {
205	buffer.value = rk_UNCONST(opt->user_string);
206	buffer.length = strlen(opt->user_string);
207    } else {
208	buffer.value = argv[0];
209	buffer.length = strlen(argv[0]);
210    }
211
212    maj_stat = gss_import_name(&min_stat, &buffer, nametype, &name);
213    if (maj_stat)
214	errx(1, "failed to import name");
215
216    /*
217     * password
218     */
219
220    if (UI_UTIL_read_pw_string(password, sizeof(password),
221			       "Password: ", 0) != 0)
222       errx(1, "failed reading password");
223
224    pw = CFStringCreateWithCString(kCFAllocatorDefault, password, kCFStringEncodingUTF8);
225    CFDictionarySetValue(attributes, kGSSICPassword, pw);
226    CFRelease(pw);
227
228    if (opt->validate_flag)
229	CFDictionarySetValue(attributes, kGSSICVerifyCredential, kCFBooleanTrue);
230    if (opt->kdc_hostname_string) {
231	CFStringRef hn = CFStringCreateWithCString(NULL, opt->kdc_hostname_string, kCFStringEncodingUTF8);
232	if (hn == NULL)
233	    errx(1, "CFStringCreateWithCString");
234	CFDictionarySetValue(attributes, kGSSICLKDCHostname, hn);
235	CFRelease(hn);
236    }
237
238    maj_stat = gss_aapl_initial_cred(name,
239				     mech,
240				     attributes,
241				     &cred,
242				     &error);
243    if (maj_stat != GSS_S_COMPLETE) {
244	char *msg = NULL;
245	if (error) {
246	    CFStringRef m;
247	    m = CFErrorCopyDescription(error);
248	    if (m) {
249		msg = rk_cfstring2cstring(m);
250		CFRelease(m);
251	    }
252	}
253	errx(1, "gss_aapl_initial_cred: %s: %d",
254	     msg ? msg : "", (int)maj_stat);
255	free(msg);
256    }
257    gss_release_cred(&min_stat, &cred);
258    gss_release_name(&min_stat, &name);
259
260    CFRelease(attributes);
261
262    if (error)
263	CFRelease(error);
264
265    return 0;
266}
267
268
269struct print_cred {
270    rtbl_t t;
271};
272
273static void
274print_cred(void *ctx, gss_const_OID oid, gss_cred_id_t cred)
275{
276    struct print_cred *pc = ctx;
277    gss_buffer_set_t data_set;
278    OM_uint32 major, junk;
279    gss_buffer_desc buffer;
280    gss_name_t name;
281    const char *str;
282    OM_uint32 expire;
283
284    if (cred == NULL)
285	return;
286
287    major = gss_inquire_cred(&junk, cred, NULL, &expire, NULL, NULL);
288    if (major == GSS_S_CREDENTIALS_EXPIRED)
289	expire = 0;
290    else if (major)
291	goto out;
292    major = gss_inquire_cred_by_mech(&junk, cred, (gss_OID)oid, &name, NULL, NULL, NULL);
293    if (major) goto out;
294    major = gss_display_name(&junk, name, &buffer, NULL);
295    gss_release_name(&junk, &name);
296    if (major) goto out;
297
298    rtbl_add_column_entryv(pc->t, COL_NAME, "%.*s",
299			   (int)buffer.length, (char *)buffer.value);
300
301    gss_release_buffer(&junk, &buffer);
302
303    str = gss_oid_to_name(oid);
304    if (str)
305	rtbl_add_column_entry(pc->t, COL_MECH, str);
306
307    if (expire == GSS_C_INDEFINITE)
308	rtbl_add_column_entryv(pc->t, COL_EXPIRE, "never");
309    else if (expire == 0)
310	rtbl_add_column_entryv(pc->t, COL_EXPIRE, "expired");
311    else {
312	char life[80];
313	unparse_time_approx(expire, life, sizeof(life));
314	rtbl_add_column_entry(pc->t, COL_EXPIRE, life);
315    }
316
317    major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_NT_UUID, &data_set);
318    if (major == GSS_S_COMPLETE && data_set->count == 1)
319	rtbl_add_column_entryv(pc->t, COL_UUID, "%.*s\n", (int)data_set->elements[0].length, (const char *)data_set->elements[0].value);
320    else
321	rtbl_add_column_entry(pc->t, COL_UUID, "");
322
323    gss_release_buffer_set(&junk, &data_set);
324
325 out:
326    gss_release_cred(&junk, &cred);
327}
328
329static void
330diag_cred(void *ctx, gss_const_OID oid, gss_cred_id_t cred)
331{
332    const char *delim = "----------------";
333    gss_buffer_set_t data_set;
334    gss_buffer_desc buffer;
335    OM_uint32 major, junk;
336    gss_name_t name;
337    const char *mech;
338    size_t n;
339
340    if (cred == NULL)
341	return;
342
343    major = gss_inquire_cred_by_mech(&junk, cred, (gss_OID)oid, &name, NULL, NULL, NULL);
344    if (major)
345	return;
346
347    major = gss_display_name(&junk, name, &buffer, NULL);
348    gss_release_name(&junk, &name);
349    if (major)
350	return;
351
352    mech = gss_oid_to_name(oid);
353
354    printf("@GSSCred\n%s\n%s: %.*s\n",
355	   delim,
356	   mech ? mech : "<unknown-mech>",
357	   (int)buffer.length, buffer.value);
358    gss_release_buffer(&junk, &buffer);
359
360    major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_CRED_DIAG, &data_set);
361    if (major)
362	return;
363
364    for (n = 0; n < data_set->count; n++)
365	printf("%s\n%.*s\n", delim, (int)data_set->elements[n].length, data_set->elements[n].value);
366
367    printf("%s\n", delim);
368
369    gss_release_buffer_set(&junk, &data_set);
370
371    major = gss_inquire_cred_by_oid(&junk, cred, GSS_C_NT_UUID, &data_set);
372    if (major == GSS_S_COMPLETE || data_set->count == 1) {
373	printf("UUID: %.*s\n", (int)data_set->elements[0].length, (const char *)data_set->elements[0].value);
374	printf("%s\n", delim);
375    }
376    gss_release_buffer_set(&junk, &data_set);
377
378
379}
380
381
382
383int
384list_credentials(struct list_credentials_options *opt, int argc, char **argv)
385{
386    struct print_cred pc;
387    gss_const_OID mech = NULL;
388
389    if (opt->mech_string) {
390	mech = gss_name_to_oid(opt->mech_string);
391	if (mech == NULL)
392	    errx(1, "No such mech: %s", opt->mech_string);
393    }
394
395    if (opt->verbose_flag) {
396
397	gss_iter_creds_f(NULL, 0, mech, NULL, diag_cred);
398
399    } else {
400
401	pc.t = rtbl_create();
402	if (pc.t == NULL)
403	    errx(1, "rtbl_create");
404
405	rtbl_set_separator(pc.t, "  ");
406	rtbl_add_column(pc.t, COL_NAME, 0);
407	rtbl_add_column(pc.t, COL_EXPIRE, 0);
408	rtbl_add_column(pc.t, COL_MECH, 0);
409	rtbl_add_column(pc.t, COL_UUID, 0);
410
411	gss_iter_creds_f(NULL, 0, mech, &pc, print_cred);
412
413	rtbl_format(pc.t, stdout);
414	rtbl_destroy(pc.t);
415    }
416
417    return 0;
418}
419
420static gss_cred_id_t
421acquire_cred(const char *name_string, gss_const_OID mech, gss_const_OID nametype)
422{
423    OM_uint32 maj_stat, min_stat;
424    gss_OID_set mechset = NULL;
425    gss_cred_id_t cred = NULL;
426    gss_buffer_desc buffer;
427    gss_name_t name;
428
429    buffer.value = rk_UNCONST(name_string);
430    buffer.length = strlen(name_string);
431
432    maj_stat = gss_import_name(&min_stat, &buffer, nametype, &name);
433    if (maj_stat)
434	errx(1, "failed to import name");
435
436    if (mech) {
437	gss_create_empty_oid_set(&min_stat, &mechset);
438	gss_add_oid_set_member(&min_stat, mech, &mechset);
439    }
440
441    maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE,
442				mechset, GSS_C_INITIATE,
443				&cred, NULL, NULL);
444    gss_release_name(&min_stat, &name);
445    gss_release_oid_set(&min_stat, &mechset);
446    if (maj_stat || cred == NULL)
447	errx(1, "acquire_cred failed");
448
449    return cred;
450}
451
452static void
453destroy_cred(void *arg1, gss_const_OID oid, gss_cred_id_t cred)
454{
455    gss_destroy_cred(NULL, &cred);
456}
457
458int
459destroy(struct destroy_options *opt, int argc, char **argv)
460{
461    gss_const_OID mech = NULL;
462
463    if (opt->mech_string) {
464	mech = gss_name_to_oid(opt->mech_string);
465	if (mech == NULL)
466	    errx(1, "No such mech: %s", opt->mech_string);
467    }
468
469    if (opt->all_flag) {
470	gss_iter_creds_f(NULL, 0, mech, NULL, destroy_cred);
471    } else {
472	gss_cred_id_t cred;
473
474	if (argc < 1) {
475	    printf("%s: missing name\n", getprogname());
476	    return 1;
477	}
478
479	cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
480
481	gss_destroy_cred(NULL, &cred);
482
483    }
484
485    return 0;
486}
487
488/*
489 *
490 */
491
492static int
493common_hold(OM_uint32 (*func)(OM_uint32 *, gss_cred_id_t),
494	    const char *mech_string, int argc, char **argv)
495{
496    OM_uint32 min_stat, maj_stat;
497    gss_const_OID mech = GSS_C_NO_OID;
498    gss_cred_id_t cred;
499
500    if (argc < 1) {
501	printf("missing username to (un)hold\n");
502	return 1;
503    }
504
505    if (mech_string) {
506	mech = gss_name_to_oid(mech_string);
507	if (mech == NULL)
508	    errx(1, "No such mech: %s", mech_string);
509    }
510
511    cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
512
513    maj_stat = func(&min_stat, cred);
514    if (maj_stat != GSS_S_COMPLETE)
515	errx(1, "(un)hold failed");
516
517    gss_release_cred(&min_stat, &cred);
518
519    return 0;
520}
521
522int
523hold(struct hold_options *opt, int argc, char **argv)
524{
525    return common_hold(gss_cred_hold, opt->mech_string, argc, argv);
526}
527
528int
529unhold(struct unhold_options *opt, int argc, char **argv)
530{
531    return common_hold(gss_cred_unhold, opt->mech_string, argc, argv);
532}
533
534int
535get_label(struct get_label_options *opt, int argc, char **argv)
536{
537    OM_uint32 min_stat, maj_stat;
538    gss_const_OID mech = GSS_C_NO_OID;
539    gss_cred_id_t cred;
540    gss_buffer_desc buf;
541
542    if (opt->mech_string) {
543	mech = gss_name_to_oid(opt->mech_string);
544	if (mech == NULL)
545	    errx(1, "No such mech: %s", opt->mech_string);
546    }
547
548    cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
549
550    maj_stat = gss_cred_label_get(&min_stat, cred, argv[1], &buf);
551    if (maj_stat != GSS_S_COMPLETE)
552	errx(1, "label get failed");
553
554    printf("value: %.*s\n", (int)buf.length, (char *)buf.value);
555
556    gss_release_buffer(&min_stat, &buf);
557    gss_release_cred(&min_stat, &cred);
558
559    return 0;
560}
561
562int
563set_label(struct set_label_options *opt, int argc, char **argv)
564{
565    OM_uint32 min_stat, maj_stat;
566    gss_const_OID mech = GSS_C_NO_OID;
567    gss_cred_id_t cred;
568    gss_buffer_desc buf;
569    gss_buffer_t bufp = NULL;
570
571    if (opt->mech_string) {
572	mech = gss_name_to_oid(opt->mech_string);
573	if (mech == NULL)
574	    errx(1, "No such mech: %s", opt->mech_string);
575    }
576
577    cred = acquire_cred(argv[0], mech, GSS_C_NT_USER_NAME);
578
579    if (argc > 2) {
580	buf.value = argv[2];
581	buf.length = strlen(argv[2]);
582	bufp = &buf;
583    }
584
585    maj_stat = gss_cred_label_set(&min_stat, cred, argv[1], bufp);
586    if (maj_stat != GSS_S_COMPLETE)
587	errx(1, "label get failed");
588
589    gss_release_cred(&min_stat, &cred);
590
591    return 0;
592}
593
594/*
595 *
596 */
597
598static void
599print_mech_attr(const char *mechname, gss_const_OID mech, gss_OID_set set)
600{
601    gss_buffer_desc name, desc;
602    OM_uint32 major, minor;
603    rtbl_t ct;
604    size_t n;
605
606    ct = rtbl_create();
607    if (ct == NULL)
608	errx(1, "rtbl_create");
609
610    rtbl_set_separator(ct, "  ");
611    rtbl_add_column(ct, COL_OID, 0);
612    rtbl_add_column(ct, COL_DESC, 0);
613    if (mech)
614	rtbl_add_column(ct, COL_VALUE, 0);
615
616    for (n = 0; n < set->count; n++) {
617	major = gss_display_mech_attr(&minor, &set->elements[n], &name, &desc, NULL);
618	if (major)
619	    continue;
620
621	rtbl_add_column_entryv(ct, COL_OID, "%.*s",
622			       (int)name.length, (char *)name.value);
623	rtbl_add_column_entryv(ct, COL_DESC, "%.*s",
624			       (int)desc.length, (char *)desc.value);
625	if (mech) {
626	    gss_buffer_desc value;
627
628	    if (gss_mo_get(mech, &set->elements[n], &value) != 0)
629		value.length = 0;
630
631	    if (value.length)
632		rtbl_add_column_entryv(ct, COL_VALUE, "%.*s",
633				       (int)value.length, (char *)value.value);
634	    else
635		rtbl_add_column_entryv(ct, COL_VALUE, "<>");
636	    gss_release_buffer(&minor, &value);
637	}
638
639	gss_release_buffer(&minor, &name);
640	gss_release_buffer(&minor, &desc);
641    }
642
643    printf("attributes for: %s\n", mechname);
644    rtbl_format(ct, stdout);
645    rtbl_destroy(ct);
646}
647
648int
649attrs_for_mech(struct attrs_for_mech_options *opt, int argc, char **argv)
650{
651    gss_OID_set mech_attr = NULL, known_mech_attrs = NULL;
652    gss_const_OID mech = GSS_C_NO_OID;
653    OM_uint32 major, minor;
654
655    if (opt->mech_string) {
656	mech = gss_name_to_oid(opt->mech_string);
657	if (mech == NULL)
658	    errx(1, "mech %s is unknown", opt->mech_string);
659    }
660
661    major = gss_inquire_attrs_for_mech(&minor,
662				       mech,
663				       &mech_attr,
664				       &known_mech_attrs);
665    if (major)
666	errx(1, "gss_inquire_attrs_for_mech");
667
668    if (mech)
669	print_mech_attr(opt->mech_string, mech, mech_attr);
670
671    if (opt->all_flag)
672	print_mech_attr("all mechs", NULL, known_mech_attrs);
673
674    gss_release_oid_set(&minor, &mech_attr);
675    gss_release_oid_set(&minor, &known_mech_attrs);
676
677    return 0;
678}
679
680/*
681 *
682 */
683
684int
685help(void *opt, int argc, char **argv)
686{
687    sl_slc_help(commands, argc, argv);
688    return 0;
689}
690
691int
692main(int argc, char **argv)
693{
694    int optidx = 0;
695
696    setprogname(argv[0]);
697    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
698	usage(1);
699
700    if (help_flag)
701	usage (0);
702
703    if(version_flag){
704	print_version(NULL);
705	exit(0);
706    }
707
708    argc -= optidx;
709    argv += optidx;
710
711    if (argc == 0) {
712	help(NULL, argc, argv);
713	return 1;
714    }
715
716    return sl_command (commands, argc, argv);
717}
718