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 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#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39
40#include <roken.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <stdarg.h>
45#include <gssapi.h>
46#include <gssapi_krb5.h>
47#include <gssapi_spnego.h>
48#include <gssapi_ntlm.h>
49#include <gssapi_spi.h>
50#include <err.h>
51#include <getarg.h>
52
53static void
54gss_err(int exitval, int status, const char *fmt, ...)
55    __attribute__((format (printf, 3, 4)));
56
57
58static void
59gss_print_errors (int min_stat)
60{
61    OM_uint32 new_stat;
62    OM_uint32 msg_ctx = 0;
63    gss_buffer_desc status_string;
64    OM_uint32 ret;
65
66    do {
67	ret = gss_display_status (&new_stat,
68				  min_stat,
69				  GSS_C_MECH_CODE,
70				  GSS_C_NO_OID,
71				  &msg_ctx,
72				  &status_string);
73	if (!GSS_ERROR(ret)) {
74	    fprintf (stderr, "%.*s\n", (int)status_string.length,
75					(char *)status_string.value);
76	    gss_release_buffer (&new_stat, &status_string);
77	}
78    } while (!GSS_ERROR(ret) && msg_ctx != 0);
79}
80
81static void
82gss_err(int exitval, int status, const char *fmt, ...)
83{
84    va_list args;
85
86    va_start(args, fmt);
87    vwarnx (fmt, args);
88    gss_print_errors (status);
89    va_end(args);
90    exit (exitval);
91}
92
93static int verbose_flag = 0;
94static int version_flag = 0;
95static int help_flag	= 0;
96
97static struct getargs args[] = {
98    {"verbose",	0,	arg_counter,	&verbose_flag, "verbose output", NULL },
99    {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
100    {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
101};
102
103static void
104usage (int ret)
105{
106    arg_printusage (args, sizeof(args)/sizeof(*args),
107		    NULL, "service@host");
108    exit (ret);
109}
110
111static void
112test_import_export(void)
113{
114    gss_buffer_desc name_buffer;
115    OM_uint32 maj_stat, min_stat;
116    gss_name_t name, MNname, MNname2;
117    int len, equal;
118    char *str;
119
120    /*
121     * test import/export
122     */
123
124    len = asprintf(&str, "ftp@freeze-arrow.mit.edu");
125    if (len == -1)
126	errx(1, "asprintf");
127
128    name_buffer.value = str;
129    name_buffer.length = len;
130
131    maj_stat = gss_import_name(&min_stat, &name_buffer,
132			       GSS_C_NT_HOSTBASED_SERVICE,
133			       &name);
134    if (maj_stat != GSS_S_COMPLETE)
135	gss_err(1, min_stat, "import name error");
136    free(str);
137
138    maj_stat = gss_canonicalize_name (&min_stat,
139				      name,
140				      GSS_KRB5_MECHANISM,
141				      &MNname);
142    if (maj_stat != GSS_S_COMPLETE)
143	gss_err(1, min_stat, "canonicalize name error");
144
145    maj_stat = gss_export_name(&min_stat,
146			       MNname,
147			       &name_buffer);
148    if (maj_stat != GSS_S_COMPLETE)
149	gss_err(1, min_stat, "export name error (KRB5)");
150
151
152    /*
153     * test import/export
154     */
155
156    str = NULL;
157    len = asprintf(&str, "ftp@freeze-arrow.mit.edu");
158    if (len < 0 || str == NULL)
159	errx(1, "asprintf");
160
161    name_buffer.value = str;
162    name_buffer.length = len;
163
164    maj_stat = gss_import_name(&min_stat, &name_buffer,
165			       GSS_C_NT_HOSTBASED_SERVICE,
166			       &name);
167    if (maj_stat != GSS_S_COMPLETE)
168	gss_err(1, min_stat, "import name error");
169    free(str);
170
171    maj_stat = gss_canonicalize_name (&min_stat,
172				      name,
173				      GSS_KRB5_MECHANISM,
174				      &MNname);
175    if (maj_stat != GSS_S_COMPLETE)
176	gss_err(1, min_stat, "canonicalize name error");
177
178    maj_stat = gss_export_name(&min_stat,
179			       MNname,
180			       &name_buffer);
181    if (maj_stat != GSS_S_COMPLETE)
182	gss_err(1, min_stat, "export name error (KRB5)");
183
184    /*
185     * Import the exported name and compare
186     */
187
188    maj_stat = gss_import_name(&min_stat, &name_buffer,
189			       GSS_C_NT_EXPORT_NAME,
190			       &MNname2);
191    if (maj_stat != GSS_S_COMPLETE)
192	gss_err(1, min_stat, "import name error (exported KRB5 name)");
193
194
195    maj_stat = gss_compare_name(&min_stat, MNname, MNname2, &equal);
196    if (maj_stat != GSS_S_COMPLETE)
197	errx(1, "gss_compare_name");
198    if (!equal)
199	errx(1, "names not equal");
200
201    gss_release_name(&min_stat, &MNname2);
202    gss_release_buffer(&min_stat, &name_buffer);
203    gss_release_name(&min_stat, &MNname);
204    gss_release_name(&min_stat, &name);
205
206    /*
207     * Import oid less name and compare to mech name.
208     * Dovecot SASL lib does this.
209     */
210
211    str = NULL;
212    len = asprintf(&str, "lha");
213    if (len < 0 || str == NULL)
214	errx(1, "asprintf");
215
216    name_buffer.value = str;
217    name_buffer.length = len;
218
219    maj_stat = gss_import_name(&min_stat, &name_buffer,
220			       GSS_C_NO_OID,
221			       &name);
222    if (maj_stat != GSS_S_COMPLETE)
223	gss_err(1, min_stat, "import (no oid) name error");
224
225    maj_stat = gss_import_name(&min_stat, &name_buffer,
226			       GSS_KRB5_NT_USER_NAME,
227			       &MNname);
228    if (maj_stat != GSS_S_COMPLETE)
229	gss_err(1, min_stat, "import (krb5 mn) name error");
230
231    free(str);
232
233    maj_stat = gss_compare_name(&min_stat, name, MNname, &equal);
234    if (maj_stat != GSS_S_COMPLETE)
235	errx(1, "gss_compare_name");
236    if (!equal)
237	errx(1, "names not equal");
238
239    gss_release_name(&min_stat, &MNname);
240    gss_release_name(&min_stat, &name);
241
242#if 0
243    maj_stat = gss_canonicalize_name (&min_stat,
244				      name,
245				      GSS_SPNEGO_MECHANISM,
246				      &MNname);
247    if (maj_stat != GSS_S_COMPLETE)
248	gss_err(1, min_stat, "canonicalize name error");
249
250
251    maj_stat = gss_export_name(&maj_stat,
252			       MNname,
253			       &name_buffer);
254    if (maj_stat != GSS_S_COMPLETE)
255	gss_err(1, min_stat, "export name error (SPNEGO)");
256
257    gss_release_name(&min_stat, &MNname);
258    gss_release_buffer(&min_stat, &name_buffer);
259#endif
260}
261
262struct {
263    gss_OID type;
264    const char *name;
265    OM_uint32 e;
266} names[] = {
267    { GSS_C_NT_USER_NAME, "DOMAIN\\user", GSS_S_COMPLETE },
268    { GSS_C_NT_USER_NAME, "user", GSS_S_COMPLETE },
269    { GSS_C_NT_USER_NAME, "DOMAIN@user", GSS_S_COMPLETE },
270    { GSS_C_NT_NTLM, "user", GSS_S_FAILURE },
271    { GSS_C_NT_NTLM, "DOMAIN\\user", GSS_S_COMPLETE },
272    { GSS_C_NT_HOSTBASED_SERVICE, "service@host.domain", GSS_S_COMPLETE },
273    { GSS_C_NT_HOSTBASED_SERVICE, "host.domain", GSS_S_BAD_NAME }
274};
275
276
277static void
278test_names(void)
279{
280    gss_buffer_desc buffer;
281    OM_uint32 maj_stat, min_stat;
282    gss_name_t name;
283    unsigned i;
284
285    for (i = 0; i < sizeof(names)/sizeof(names[0]); i++) {
286	buffer.value = rk_UNCONST(names[i].name);
287	buffer.length = strlen(names[i].name);
288
289	if (verbose_flag)
290	    printf("running name test: %s\n", names[i].name);
291
292	maj_stat = gss_import_name(&min_stat, &buffer, names[i].type, &name);
293	if (maj_stat != names[i].e)
294	    errx(1, "gss_import_name unexpected: %u:%s", i, names[i].name);
295
296	gss_release_name(&min_stat, &name);
297    }
298}
299
300
301int
302main(int argc, char **argv)
303{
304    int optidx = 0;
305
306    setprogname(argv[0]);
307    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
308	usage(1);
309
310    if (help_flag)
311	usage (0);
312
313    if(version_flag){
314	print_version(NULL);
315	exit(0);
316    }
317
318    gsskrb5_set_default_realm("MIT.EDU");
319
320    test_names();
321
322    test_import_export();
323
324    return 0;
325}
326