1184588Sdfr/*-
2184588Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3184588Sdfr * Authors: Doug Rabson <dfr@rabson.org>
4184588Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5184588Sdfr *
6184588Sdfr * Redistribution and use in source and binary forms, with or without
7184588Sdfr * modification, are permitted provided that the following conditions
8184588Sdfr * are met:
9184588Sdfr * 1. Redistributions of source code must retain the above copyright
10184588Sdfr *    notice, this list of conditions and the following disclaimer.
11184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright
12184588Sdfr *    notice, this list of conditions and the following disclaimer in the
13184588Sdfr *    documentation and/or other materials provided with the distribution.
14184588Sdfr *
15184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18184588Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25184588Sdfr * SUCH DAMAGE.
26184588Sdfr */
27184588Sdfr
28184588Sdfr#include <sys/cdefs.h>
29184588Sdfr__FBSDID("$FreeBSD$");
30184588Sdfr
31184588Sdfr#include <sys/ctype.h>
32184588Sdfr#include <sys/param.h>
33184588Sdfr#include <sys/kernel.h>
34184588Sdfr#include <sys/kobj.h>
35184588Sdfr#include <sys/malloc.h>
36184588Sdfr#include <sys/module.h>
37184588Sdfr#include <sys/proc.h>
38184588Sdfr#include <sys/socketvar.h>
39184588Sdfr#include <sys/sysent.h>
40184588Sdfr#include <sys/sysproto.h>
41184588Sdfr
42184588Sdfr#include <kgssapi/gssapi.h>
43184588Sdfr#include <kgssapi/gssapi_impl.h>
44184588Sdfr#include <rpc/rpc.h>
45184588Sdfr#include <rpc/rpc_com.h>
46184588Sdfr#include <rpc/rpcb_prot.h>
47184588Sdfr#include <rpc/rpcsec_gss.h>
48184588Sdfr
49184588Sdfrstatic void
50184588Sdfrreport_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
51184588Sdfr{
52184588Sdfr	OM_uint32 maj_stat, min_stat;
53184588Sdfr	OM_uint32 message_context;
54184588Sdfr	gss_buffer_desc buf;
55184588Sdfr
56184588Sdfr	uprintf("major_stat=%d, minor_stat=%d\n", maj, min);
57184588Sdfr	message_context = 0;
58184588Sdfr	do {
59184588Sdfr		maj_stat = gss_display_status(&min_stat, maj,
60184588Sdfr		    GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &buf);
61184588Sdfr		if (GSS_ERROR(maj_stat))
62184588Sdfr			break;
63184588Sdfr		uprintf("%.*s\n", (int)buf.length, (char *) buf.value);
64184588Sdfr		gss_release_buffer(&min_stat, &buf);
65184588Sdfr	} while (message_context);
66184588Sdfr	if (mech && min) {
67184588Sdfr		message_context = 0;
68184588Sdfr		do {
69184588Sdfr			maj_stat = gss_display_status(&min_stat, min,
70184588Sdfr			    GSS_C_MECH_CODE, mech, &message_context, &buf);
71184588Sdfr			if (GSS_ERROR(maj_stat))
72184588Sdfr				break;
73184588Sdfr			uprintf("%.*s\n", (int)buf.length, (char *) buf.value);
74184588Sdfr			gss_release_buffer(&min_stat, &buf);
75184588Sdfr		} while (message_context);
76184588Sdfr	}
77184588Sdfr}
78184588Sdfr
79184588Sdfr#if 0
80184588Sdfrstatic void
81184588Sdfrsend_token_to_peer(const gss_buffer_t token)
82184588Sdfr{
83184588Sdfr	const uint8_t *p;
84184588Sdfr	size_t i;
85184588Sdfr
86184588Sdfr	printf("send token:\n");
87184588Sdfr	printf("%d ", (int) token->length);
88184588Sdfr	p = (const uint8_t *) token->value;
89184588Sdfr	for (i = 0; i < token->length; i++)
90184588Sdfr		printf("%02x", *p++);
91184588Sdfr	printf("\n");
92184588Sdfr}
93184588Sdfr
94184588Sdfrstatic void
95184588Sdfrreceive_token_from_peer(gss_buffer_t token)
96184588Sdfr{
97184588Sdfr	char line[8192];
98184588Sdfr	char *p;
99184588Sdfr	uint8_t *q;
100184588Sdfr	int len, val;
101184588Sdfr
102184588Sdfr	printf("receive token:\n");
103184588Sdfr	fgets(line, sizeof(line), stdin);
104184588Sdfr	if (line[strlen(line) - 1] != '\n') {
105184588Sdfr		printf("token truncated\n");
106184588Sdfr		exit(1);
107184588Sdfr	}
108184588Sdfr	p = line;
109184588Sdfr	if (sscanf(line, "%d ", &len) != 1) {
110184588Sdfr		printf("bad token\n");
111184588Sdfr		exit(1);
112184588Sdfr	}
113184588Sdfr	p = strchr(p, ' ') + 1;
114184588Sdfr	token->length = len;
115184588Sdfr	token->value = malloc(len);
116184588Sdfr	q = (uint8_t *) token->value;
117184588Sdfr	while (len) {
118184588Sdfr		if (sscanf(p, "%02x", &val) != 1) {
119184588Sdfr			printf("bad token\n");
120184588Sdfr			exit(1);
121184588Sdfr		}
122184588Sdfr		*q++ = val;
123184588Sdfr		p += 2;
124184588Sdfr		len--;
125184588Sdfr	}
126184588Sdfr}
127184588Sdfr#endif
128184588Sdfr
129184588Sdfr#if 0
130184588Sdfrvoid
131184588Sdfrserver(int argc, char** argv)
132184588Sdfr{
133184588Sdfr	OM_uint32 maj_stat, min_stat;
134184588Sdfr	gss_buffer_desc input_token, output_token;
135184588Sdfr	gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;
136184588Sdfr	gss_name_t client_name;
137184588Sdfr	gss_OID mech_type;
138184588Sdfr
139184588Sdfr	if (argc != 1)
140184588Sdfr		usage();
141184588Sdfr
142184588Sdfr	do {
143184588Sdfr		receive_token_from_peer(&input_token);
144184588Sdfr		maj_stat = gss_accept_sec_context(&min_stat,
145184588Sdfr		    &context_hdl,
146184588Sdfr		    GSS_C_NO_CREDENTIAL,
147184588Sdfr		    &input_token,
148184588Sdfr		    GSS_C_NO_CHANNEL_BINDINGS,
149184588Sdfr		    &client_name,
150184588Sdfr		    &mech_type,
151184588Sdfr		    &output_token,
152184588Sdfr		    NULL,
153184588Sdfr		    NULL,
154184588Sdfr		    NULL);
155184588Sdfr		if (GSS_ERROR(maj_stat)) {
156184588Sdfr			report_error(mech_type, maj_stat, min_stat);
157184588Sdfr		}
158184588Sdfr		if (output_token.length != 0) {
159184588Sdfr			send_token_to_peer(&output_token);
160184588Sdfr			gss_release_buffer(&min_stat, &output_token);
161184588Sdfr		}
162184588Sdfr		if (GSS_ERROR(maj_stat)) {
163184588Sdfr			if (context_hdl != GSS_C_NO_CONTEXT)
164184588Sdfr				gss_delete_sec_context(&min_stat,
165184588Sdfr				    &context_hdl,
166184588Sdfr				    GSS_C_NO_BUFFER);
167184588Sdfr			break;
168184588Sdfr		}
169184588Sdfr	} while (maj_stat & GSS_S_CONTINUE_NEEDED);
170184588Sdfr
171184588Sdfr	if (client_name) {
172184588Sdfr		gss_buffer_desc name_desc;
173184588Sdfr		char buf[512];
174184588Sdfr
175184588Sdfr		gss_display_name(&min_stat, client_name, &name_desc, NULL);
176184588Sdfr		memcpy(buf, name_desc.value, name_desc.length);
177184588Sdfr		buf[name_desc.length] = 0;
178184588Sdfr		gss_release_buffer(&min_stat, &name_desc);
179184588Sdfr		printf("client name is %s\n", buf);
180184588Sdfr	}
181184588Sdfr
182184588Sdfr	receive_token_from_peer(&input_token);
183184588Sdfr	gss_unwrap(&min_stat, context_hdl, &input_token, &output_token,
184184588Sdfr	    NULL, NULL);
185184588Sdfr	printf("%.*s\n", (int)output_token.length, (char *) output_token.value);
186184588Sdfr	gss_release_buffer(&min_stat, &output_token);
187184588Sdfr}
188184588Sdfr#endif
189184588Sdfr
190184588Sdfr/* 1.2.752.43.13.14 */
191184588Sdfrstatic gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc =
192184588Sdfr{6, (void *) "\x2a\x85\x70\x2b\x0d\x0e"};
193184588Sdfr
194184588Sdfrgss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X = &gss_krb5_set_allowable_enctypes_x_desc;
195184588Sdfr#define ETYPE_DES_CBC_CRC	1
196184588Sdfr
197184588Sdfr/*
198184588Sdfr * Create an initiator context and acceptor context in the kernel and
199184588Sdfr * use them to exchange signed and sealed messages.
200184588Sdfr */
201184588Sdfrstatic int
202193066Sjamiegsstest_1(struct thread *td)
203184588Sdfr{
204184588Sdfr	OM_uint32 maj_stat, min_stat;
205184588Sdfr	OM_uint32 smaj_stat, smin_stat;
206184588Sdfr	int context_established = 0;
207184588Sdfr	gss_ctx_id_t client_context = GSS_C_NO_CONTEXT;
208184588Sdfr	gss_ctx_id_t server_context = GSS_C_NO_CONTEXT;
209184588Sdfr	gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL;
210184588Sdfr	gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
211184588Sdfr	gss_name_t name = GSS_C_NO_NAME;
212184588Sdfr	gss_name_t received_name = GSS_C_NO_NAME;
213184588Sdfr	gss_buffer_desc name_desc;
214184588Sdfr	gss_buffer_desc client_token, server_token, message_buf;
215184588Sdfr	gss_OID mech, actual_mech, mech_type;
216184588Sdfr	static gss_OID_desc krb5_desc =
217184588Sdfr		{9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
218184588Sdfr#if 0
219184588Sdfr	static gss_OID_desc spnego_desc =
220184588Sdfr		{6, (void *)"\x2b\x06\x01\x05\x05\x02"};
221184588Sdfr	static gss_OID_desc ntlm_desc =
222184588Sdfr		{10, (void *)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"};
223184588Sdfr#endif
224184588Sdfr	char enctype[sizeof(uint32_t)];
225184588Sdfr
226184588Sdfr	mech = GSS_C_NO_OID;
227184588Sdfr
228184588Sdfr	{
229184588Sdfr		static char sbuf[512];
230193066Sjamie		memcpy(sbuf, "nfs@", 4);
231193066Sjamie		getcredhostname(td->td_ucred, sbuf + 4, sizeof(sbuf) - 4);
232184588Sdfr		name_desc.value = sbuf;
233184588Sdfr	}
234184588Sdfr
235184588Sdfr	name_desc.length = strlen((const char *) name_desc.value);
236184588Sdfr	maj_stat = gss_import_name(&min_stat, &name_desc,
237184588Sdfr	    GSS_C_NT_HOSTBASED_SERVICE, &name);
238184588Sdfr	if (GSS_ERROR(maj_stat)) {
239184588Sdfr		printf("gss_import_name failed\n");
240184588Sdfr		report_error(mech, maj_stat, min_stat);
241184588Sdfr		goto out;
242184588Sdfr	}
243184588Sdfr
244184588Sdfr	maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME,
245184588Sdfr	    0, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client_cred,
246184588Sdfr	    NULL, NULL);
247184588Sdfr	if (GSS_ERROR(maj_stat)) {
248184588Sdfr		printf("gss_acquire_cred (client) failed\n");
249184588Sdfr		report_error(mech, maj_stat, min_stat);
250184588Sdfr		goto out;
251184588Sdfr	}
252184588Sdfr
253184588Sdfr	enctype[0] = (ETYPE_DES_CBC_CRC >> 24) & 0xff;
254184588Sdfr	enctype[1] = (ETYPE_DES_CBC_CRC >> 16) & 0xff;
255184588Sdfr	enctype[2] = (ETYPE_DES_CBC_CRC >> 8) & 0xff;
256184588Sdfr	enctype[3] = ETYPE_DES_CBC_CRC & 0xff;
257184588Sdfr	message_buf.length = sizeof(enctype);
258184588Sdfr	message_buf.value = enctype;
259184588Sdfr	maj_stat = gss_set_cred_option(&min_stat, &client_cred,
260184588Sdfr	    GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, &message_buf);
261184588Sdfr	if (GSS_ERROR(maj_stat)) {
262184588Sdfr		printf("gss_set_cred_option failed\n");
263184588Sdfr		report_error(mech, maj_stat, min_stat);
264184588Sdfr		goto out;
265184588Sdfr	}
266184588Sdfr
267184588Sdfr	server_token.length = 0;
268184588Sdfr	server_token.value = NULL;
269184588Sdfr	while (!context_established) {
270184588Sdfr		client_token.length = 0;
271184588Sdfr		client_token.value = NULL;
272184588Sdfr		maj_stat = gss_init_sec_context(&min_stat,
273184588Sdfr		    client_cred,
274184588Sdfr		    &client_context,
275184588Sdfr		    name,
276184588Sdfr		    mech,
277184588Sdfr		    GSS_C_MUTUAL_FLAG|GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG,
278184588Sdfr		    0,
279184588Sdfr		    GSS_C_NO_CHANNEL_BINDINGS,
280184588Sdfr		    &server_token,
281184588Sdfr		    &actual_mech,
282184588Sdfr		    &client_token,
283184588Sdfr		    NULL,
284184588Sdfr		    NULL);
285184588Sdfr		if (server_token.length)
286184588Sdfr			gss_release_buffer(&smin_stat, &server_token);
287184588Sdfr		if (GSS_ERROR(maj_stat)) {
288184588Sdfr			printf("gss_init_sec_context failed\n");
289184588Sdfr			report_error(mech, maj_stat, min_stat);
290184588Sdfr			goto out;
291184588Sdfr		}
292184588Sdfr
293184588Sdfr		if (client_token.length != 0) {
294184588Sdfr			if (!server_cred) {
295184588Sdfr				gss_OID_set_desc oid_set;
296184588Sdfr				oid_set.count = 1;
297184588Sdfr				oid_set.elements = &krb5_desc;
298184588Sdfr				smaj_stat = gss_acquire_cred(&smin_stat,
299184588Sdfr				    name, 0, &oid_set, GSS_C_ACCEPT, &server_cred,
300184588Sdfr				    NULL, NULL);
301184588Sdfr				if (GSS_ERROR(smaj_stat)) {
302184588Sdfr					printf("gss_acquire_cred (server) failed\n");
303184588Sdfr					report_error(mech_type, smaj_stat, smin_stat);
304184588Sdfr					goto out;
305184588Sdfr				}
306184588Sdfr			}
307184588Sdfr			smaj_stat = gss_accept_sec_context(&smin_stat,
308184588Sdfr			    &server_context,
309184588Sdfr			    server_cred,
310184588Sdfr			    &client_token,
311184588Sdfr			    GSS_C_NO_CHANNEL_BINDINGS,
312184588Sdfr			    &received_name,
313184588Sdfr			    &mech_type,
314184588Sdfr			    &server_token,
315184588Sdfr			    NULL,
316184588Sdfr			    NULL,
317184588Sdfr			    NULL);
318184588Sdfr			if (GSS_ERROR(smaj_stat)) {
319184588Sdfr				printf("gss_accept_sec_context failed\n");
320184588Sdfr				report_error(mech_type, smaj_stat, smin_stat);
321184588Sdfr				goto out;
322184588Sdfr			}
323184588Sdfr			gss_release_buffer(&min_stat, &client_token);
324184588Sdfr		}
325184588Sdfr		if (GSS_ERROR(maj_stat)) {
326184588Sdfr			if (client_context != GSS_C_NO_CONTEXT)
327184588Sdfr				gss_delete_sec_context(&min_stat,
328184588Sdfr				    &client_context,
329184588Sdfr				    GSS_C_NO_BUFFER);
330184588Sdfr			break;
331184588Sdfr		}
332184588Sdfr
333184588Sdfr		if (maj_stat == GSS_S_COMPLETE) {
334184588Sdfr			context_established = 1;
335184588Sdfr		}
336184588Sdfr	}
337184588Sdfr
338184588Sdfr	message_buf.length = strlen("Hello world");
339184588Sdfr	message_buf.value = (void *) "Hello world";
340184588Sdfr
341184588Sdfr	maj_stat = gss_get_mic(&min_stat, client_context,
342184588Sdfr	    GSS_C_QOP_DEFAULT, &message_buf, &client_token);
343184588Sdfr	if (GSS_ERROR(maj_stat)) {
344184588Sdfr		printf("gss_get_mic failed\n");
345184588Sdfr		report_error(mech_type, maj_stat, min_stat);
346184588Sdfr		goto out;
347184588Sdfr	}
348184588Sdfr	maj_stat = gss_verify_mic(&min_stat, server_context,
349184588Sdfr	    &message_buf, &client_token, NULL);
350184588Sdfr	if (GSS_ERROR(maj_stat)) {
351184588Sdfr		printf("gss_verify_mic failed\n");
352184588Sdfr		report_error(mech_type, maj_stat, min_stat);
353184588Sdfr		goto out;
354184588Sdfr	}
355184588Sdfr	gss_release_buffer(&min_stat, &client_token);
356184588Sdfr
357184588Sdfr	maj_stat = gss_wrap(&min_stat, client_context,
358184588Sdfr	    TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL, &client_token);
359184588Sdfr	if (GSS_ERROR(maj_stat)) {
360184588Sdfr		printf("gss_wrap failed\n");
361184588Sdfr		report_error(mech_type, maj_stat, min_stat);
362184588Sdfr		goto out;
363184588Sdfr	}
364184588Sdfr	maj_stat = gss_unwrap(&min_stat, server_context,
365184588Sdfr	    &client_token, &server_token, NULL, NULL);
366184588Sdfr	if (GSS_ERROR(maj_stat)) {
367184588Sdfr		printf("gss_unwrap failed\n");
368184588Sdfr		report_error(mech_type, maj_stat, min_stat);
369184588Sdfr		goto out;
370184588Sdfr	}
371184588Sdfr
372184588Sdfr 	if (message_buf.length != server_token.length
373184588Sdfr	    || memcmp(message_buf.value, server_token.value,
374184588Sdfr		message_buf.length))
375184588Sdfr		printf("unwrap result corrupt\n");
376184588Sdfr
377184588Sdfr	gss_release_buffer(&min_stat, &client_token);
378184588Sdfr	gss_release_buffer(&min_stat, &server_token);
379184588Sdfr
380184588Sdfrout:
381184588Sdfr	if (client_context)
382184588Sdfr		gss_delete_sec_context(&min_stat, &client_context,
383184588Sdfr		    GSS_C_NO_BUFFER);
384184588Sdfr	if (server_context)
385184588Sdfr		gss_delete_sec_context(&min_stat, &server_context,
386184588Sdfr		    GSS_C_NO_BUFFER);
387184588Sdfr	if (client_cred)
388184588Sdfr		gss_release_cred(&min_stat, &client_cred);
389184588Sdfr	if (server_cred)
390184588Sdfr		gss_release_cred(&min_stat, &server_cred);
391184588Sdfr	if (name)
392184588Sdfr		gss_release_name(&min_stat, &name);
393184588Sdfr	if (received_name)
394184588Sdfr		gss_release_name(&min_stat, &received_name);
395184588Sdfr
396184588Sdfr	return (0);
397184588Sdfr}
398184588Sdfr
399184588Sdfr/*
400184588Sdfr * Interoperability with userland. This takes several steps:
401184588Sdfr *
402184588Sdfr * 1. Accept an initiator token from userland, return acceptor
403184588Sdfr * token. Repeat this step until both userland and kernel return
404184588Sdfr * GSS_S_COMPLETE.
405184588Sdfr *
406184588Sdfr * 2. Receive a signed message from userland and verify the
407184588Sdfr * signature. Return a signed reply to userland for it to verify.
408184588Sdfr *
409184588Sdfr * 3. Receive a wrapped message from userland and unwrap it. Return a
410184588Sdfr * wrapped reply to userland.
411184588Sdfr */
412184588Sdfrstatic int
413193066Sjamiegsstest_2(struct thread *td, int step, const gss_buffer_t input_token,
414184588Sdfr    OM_uint32 *maj_stat_res, OM_uint32 *min_stat_res, gss_buffer_t output_token)
415184588Sdfr{
416184588Sdfr	OM_uint32 maj_stat, min_stat;
417184588Sdfr	static int context_established = 0;
418184588Sdfr	static gss_ctx_id_t server_context = GSS_C_NO_CONTEXT;
419184588Sdfr	static gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
420184588Sdfr	static gss_name_t name = GSS_C_NO_NAME;
421184588Sdfr	gss_buffer_desc name_desc;
422184588Sdfr	gss_buffer_desc message_buf;
423184588Sdfr	gss_OID mech_type = GSS_C_NO_OID;
424184588Sdfr	char enctype[sizeof(uint32_t)];
425184588Sdfr	int error = EINVAL;
426184588Sdfr
427184588Sdfr	maj_stat = GSS_S_FAILURE;
428184588Sdfr	min_stat = 0;
429184588Sdfr	switch (step) {
430184588Sdfr
431184588Sdfr	case 1:
432184588Sdfr		if (server_context == GSS_C_NO_CONTEXT) {
433184588Sdfr			static char sbuf[512];
434193066Sjamie			memcpy(sbuf, "nfs@", 4);
435193066Sjamie			getcredhostname(td->td_ucred, sbuf + 4,
436193066Sjamie			    sizeof(sbuf) - 4);
437184588Sdfr			name_desc.value = sbuf;
438184588Sdfr			name_desc.length = strlen((const char *)
439184588Sdfr			    name_desc.value);
440184588Sdfr			maj_stat = gss_import_name(&min_stat, &name_desc,
441184588Sdfr			    GSS_C_NT_HOSTBASED_SERVICE, &name);
442184588Sdfr			if (GSS_ERROR(maj_stat)) {
443184588Sdfr				printf("gss_import_name failed\n");
444184588Sdfr				report_error(mech_type, maj_stat, min_stat);
445184588Sdfr				goto out;
446184588Sdfr			}
447184588Sdfr
448184588Sdfr			maj_stat = gss_acquire_cred(&min_stat,
449184588Sdfr			    name, 0, GSS_C_NO_OID_SET, GSS_C_ACCEPT,
450184588Sdfr			    &server_cred, NULL, NULL);
451184588Sdfr			if (GSS_ERROR(maj_stat)) {
452184588Sdfr				printf("gss_acquire_cred (server) failed\n");
453184588Sdfr				report_error(mech_type, maj_stat, min_stat);
454184588Sdfr				goto out;
455184588Sdfr			}
456184588Sdfr
457184588Sdfr			enctype[0] = (ETYPE_DES_CBC_CRC >> 24) & 0xff;
458184588Sdfr			enctype[1] = (ETYPE_DES_CBC_CRC >> 16) & 0xff;
459184588Sdfr			enctype[2] = (ETYPE_DES_CBC_CRC >> 8) & 0xff;
460184588Sdfr			enctype[3] = ETYPE_DES_CBC_CRC & 0xff;
461184588Sdfr			message_buf.length = sizeof(enctype);
462184588Sdfr			message_buf.value = enctype;
463184588Sdfr			maj_stat = gss_set_cred_option(&min_stat, &server_cred,
464184588Sdfr			    GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, &message_buf);
465184588Sdfr			if (GSS_ERROR(maj_stat)) {
466184588Sdfr				printf("gss_set_cred_option failed\n");
467184588Sdfr				report_error(mech_type, maj_stat, min_stat);
468184588Sdfr				goto out;
469184588Sdfr			}
470184588Sdfr		}
471184588Sdfr
472184588Sdfr		maj_stat = gss_accept_sec_context(&min_stat,
473184588Sdfr		    &server_context,
474184588Sdfr		    server_cred,
475184588Sdfr		    input_token,
476184588Sdfr		    GSS_C_NO_CHANNEL_BINDINGS,
477184588Sdfr		    NULL,
478184588Sdfr		    &mech_type,
479184588Sdfr		    output_token,
480184588Sdfr		    NULL,
481184588Sdfr		    NULL,
482184588Sdfr		    NULL);
483184588Sdfr		if (GSS_ERROR(maj_stat)) {
484184588Sdfr			printf("gss_accept_sec_context failed\n");
485184588Sdfr			report_error(mech_type, maj_stat, min_stat);
486184588Sdfr			goto out;
487184588Sdfr		}
488184588Sdfr
489184588Sdfr		if (maj_stat == GSS_S_COMPLETE) {
490184588Sdfr			context_established = 1;
491184588Sdfr		}
492184588Sdfr		*maj_stat_res = maj_stat;
493184588Sdfr		*min_stat_res = min_stat;
494184588Sdfr		break;
495184588Sdfr
496184588Sdfr	case 2:
497184588Sdfr		message_buf.length = strlen("Hello world");
498184588Sdfr		message_buf.value = (void *) "Hello world";
499184588Sdfr
500184588Sdfr		maj_stat = gss_verify_mic(&min_stat, server_context,
501184588Sdfr		    &message_buf, input_token, NULL);
502184588Sdfr		if (GSS_ERROR(maj_stat)) {
503184588Sdfr			printf("gss_verify_mic failed\n");
504184588Sdfr			report_error(mech_type, maj_stat, min_stat);
505184588Sdfr			goto out;
506184588Sdfr		}
507184588Sdfr
508184588Sdfr		maj_stat = gss_get_mic(&min_stat, server_context,
509184588Sdfr		    GSS_C_QOP_DEFAULT, &message_buf, output_token);
510184588Sdfr		if (GSS_ERROR(maj_stat)) {
511184588Sdfr			printf("gss_get_mic failed\n");
512184588Sdfr			report_error(mech_type, maj_stat, min_stat);
513184588Sdfr			goto out;
514184588Sdfr		}
515184588Sdfr		break;
516184588Sdfr
517184588Sdfr	case 3:
518184588Sdfr		maj_stat = gss_unwrap(&min_stat, server_context,
519184588Sdfr		    input_token, &message_buf, NULL, NULL);
520184588Sdfr		if (GSS_ERROR(maj_stat)) {
521184588Sdfr			printf("gss_unwrap failed\n");
522184588Sdfr			report_error(mech_type, maj_stat, min_stat);
523184588Sdfr			goto out;
524184588Sdfr		}
525184588Sdfr		gss_release_buffer(&min_stat, &message_buf);
526184588Sdfr
527184588Sdfr		message_buf.length = strlen("Hello world");
528184588Sdfr		message_buf.value = (void *) "Hello world";
529184588Sdfr		maj_stat = gss_wrap(&min_stat, server_context,
530184588Sdfr		    TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL, output_token);
531184588Sdfr		if (GSS_ERROR(maj_stat)) {
532184588Sdfr			printf("gss_wrap failed\n");
533184588Sdfr			report_error(mech_type, maj_stat, min_stat);
534184588Sdfr			goto out;
535184588Sdfr		}
536184588Sdfr		break;
537184588Sdfr
538184588Sdfr	case 4:
539184588Sdfr		maj_stat = gss_unwrap(&min_stat, server_context,
540184588Sdfr		    input_token, &message_buf, NULL, NULL);
541184588Sdfr		if (GSS_ERROR(maj_stat)) {
542184588Sdfr			printf("gss_unwrap failed\n");
543184588Sdfr			report_error(mech_type, maj_stat, min_stat);
544184588Sdfr			goto out;
545184588Sdfr		}
546184588Sdfr		gss_release_buffer(&min_stat, &message_buf);
547184588Sdfr
548184588Sdfr		message_buf.length = strlen("Hello world");
549184588Sdfr		message_buf.value = (void *) "Hello world";
550184588Sdfr		maj_stat = gss_wrap(&min_stat, server_context,
551184588Sdfr		    FALSE, GSS_C_QOP_DEFAULT, &message_buf, NULL, output_token);
552184588Sdfr		if (GSS_ERROR(maj_stat)) {
553184588Sdfr			printf("gss_wrap failed\n");
554184588Sdfr			report_error(mech_type, maj_stat, min_stat);
555184588Sdfr			goto out;
556184588Sdfr		}
557184588Sdfr		break;
558184588Sdfr
559184588Sdfr	case 5:
560184588Sdfr		error = 0;
561184588Sdfr		goto out;
562184588Sdfr	}
563184588Sdfr	*maj_stat_res = maj_stat;
564184588Sdfr	*min_stat_res = min_stat;
565184588Sdfr	return (0);
566184588Sdfr
567184588Sdfrout:
568184588Sdfr	*maj_stat_res = maj_stat;
569184588Sdfr	*min_stat_res = min_stat;
570184588Sdfr	if (server_context)
571184588Sdfr		gss_delete_sec_context(&min_stat, &server_context,
572184588Sdfr		    GSS_C_NO_BUFFER);
573184588Sdfr	if (server_cred)
574184588Sdfr		gss_release_cred(&min_stat, &server_cred);
575184588Sdfr	if (name)
576184588Sdfr		gss_release_name(&min_stat, &name);
577184588Sdfr
578184588Sdfr	return (error);
579184588Sdfr}
580184588Sdfr
581184588Sdfr/*
582184588Sdfr * Create an RPC client handle for the given (address,prog,vers)
583184588Sdfr * triple using UDP.
584184588Sdfr */
585184588Sdfrstatic CLIENT *
586184588Sdfrgsstest_get_rpc(struct sockaddr *sa, rpcprog_t prog, rpcvers_t vers)
587184588Sdfr{
588184588Sdfr	struct thread *td = curthread;
589184588Sdfr	const char* protofmly;
590184588Sdfr	struct sockaddr_storage ss;
591184588Sdfr	struct socket *so;
592184588Sdfr	CLIENT *rpcb;
593184588Sdfr	struct timeval timo;
594184588Sdfr	RPCB parms;
595184588Sdfr	char *uaddr;
596184588Sdfr	enum clnt_stat stat = RPC_SUCCESS;
597184588Sdfr	int rpcvers = RPCBVERS4;
598184588Sdfr	bool_t do_tcp = FALSE;
599184588Sdfr	struct portmap mapping;
600184588Sdfr	u_short port = 0;
601184588Sdfr
602184588Sdfr	/*
603184588Sdfr	 * First we need to contact the remote RPCBIND service to find
604184588Sdfr	 * the right port.
605184588Sdfr	 */
606184588Sdfr	memcpy(&ss, sa, sa->sa_len);
607184588Sdfr	switch (ss.ss_family) {
608184588Sdfr	case AF_INET:
609184588Sdfr		((struct sockaddr_in *)&ss)->sin_port = htons(111);
610184588Sdfr		protofmly = "inet";
611184588Sdfr		socreate(AF_INET, &so, SOCK_DGRAM, 0, td->td_ucred, td);
612184588Sdfr		break;
613184588Sdfr
614184588Sdfr#ifdef INET6
615184588Sdfr	case AF_INET6:
616184588Sdfr		((struct sockaddr_in6 *)&ss)->sin6_port = htons(111);
617184588Sdfr		protofmly = "inet6";
618184588Sdfr		socreate(AF_INET6, &so, SOCK_DGRAM, 0, td->td_ucred, td);
619184588Sdfr		break;
620184588Sdfr#endif
621184588Sdfr
622184588Sdfr	default:
623184588Sdfr		/*
624184588Sdfr		 * Unsupported address family - fail.
625184588Sdfr		 */
626184588Sdfr		return (NULL);
627184588Sdfr	}
628184588Sdfr
629184588Sdfr	rpcb = clnt_dg_create(so, (struct sockaddr *)&ss,
630184588Sdfr	    RPCBPROG, rpcvers, 0, 0);
631184588Sdfr	if (!rpcb)
632184588Sdfr		return (NULL);
633184588Sdfr
634184588Sdfrtry_tcp:
635184588Sdfr	parms.r_prog = prog;
636184588Sdfr	parms.r_vers = vers;
637184588Sdfr	if (do_tcp)
638184588Sdfr		parms.r_netid = "tcp";
639184588Sdfr	else
640184588Sdfr		parms.r_netid = "udp";
641184588Sdfr	parms.r_addr = "";
642184588Sdfr	parms.r_owner = "";
643184588Sdfr
644184588Sdfr	/*
645184588Sdfr	 * Use the default timeout.
646184588Sdfr	 */
647184588Sdfr	timo.tv_sec = 25;
648184588Sdfr	timo.tv_usec = 0;
649184588Sdfragain:
650184588Sdfr	switch (rpcvers) {
651184588Sdfr	case RPCBVERS4:
652184588Sdfr	case RPCBVERS:
653184588Sdfr		/*
654184588Sdfr		 * Try RPCBIND 4 then 3.
655184588Sdfr		 */
656184588Sdfr		uaddr = NULL;
657184588Sdfr		stat = CLNT_CALL(rpcb, (rpcprog_t) RPCBPROC_GETADDR,
658184588Sdfr		    (xdrproc_t) xdr_rpcb, &parms,
659184588Sdfr		    (xdrproc_t) xdr_wrapstring, &uaddr, timo);
660184588Sdfr		if (stat == RPC_PROGVERSMISMATCH) {
661184588Sdfr			if (rpcvers == RPCBVERS4)
662184588Sdfr				rpcvers = RPCBVERS;
663184588Sdfr			else if (rpcvers == RPCBVERS)
664184588Sdfr				rpcvers = PMAPVERS;
665184588Sdfr			CLNT_CONTROL(rpcb, CLSET_VERS, &rpcvers);
666184588Sdfr			goto again;
667184588Sdfr		} else if (stat == RPC_SUCCESS) {
668184588Sdfr			/*
669184588Sdfr			 * We have a reply from the remote RPCBIND - turn it
670184588Sdfr			 * into an appropriate address and make a new client
671184588Sdfr			 * that can talk to the remote service.
672184588Sdfr			 *
673184588Sdfr			 * XXX fixup IPv6 scope ID.
674184588Sdfr			 */
675184588Sdfr			struct netbuf *a;
676184588Sdfr			a = __rpc_uaddr2taddr_af(ss.ss_family, uaddr);
677184588Sdfr			xdr_free((xdrproc_t) xdr_wrapstring, &uaddr);
678184588Sdfr			if (!a) {
679184588Sdfr				CLNT_DESTROY(rpcb);
680184588Sdfr				return (NULL);
681184588Sdfr			}
682184588Sdfr			memcpy(&ss, a->buf, a->len);
683184588Sdfr			free(a->buf, M_RPC);
684184588Sdfr			free(a, M_RPC);
685184588Sdfr		}
686184588Sdfr		break;
687184588Sdfr	case PMAPVERS:
688184588Sdfr		/*
689184588Sdfr		 * Try portmap.
690184588Sdfr		 */
691184588Sdfr		mapping.pm_prog = parms.r_prog;
692184588Sdfr		mapping.pm_vers = parms.r_vers;
693184588Sdfr		mapping.pm_prot = do_tcp ? IPPROTO_TCP : IPPROTO_UDP;
694184588Sdfr		mapping.pm_port = 0;
695184588Sdfr
696184588Sdfr		stat = CLNT_CALL(rpcb, (rpcprog_t) PMAPPROC_GETPORT,
697184588Sdfr		    (xdrproc_t) xdr_portmap, &mapping,
698184588Sdfr		    (xdrproc_t) xdr_u_short, &port, timo);
699184588Sdfr
700184588Sdfr		if (stat == RPC_SUCCESS) {
701184588Sdfr			switch (ss.ss_family) {
702184588Sdfr			case AF_INET:
703184588Sdfr				((struct sockaddr_in *)&ss)->sin_port =
704184588Sdfr					htons(port);
705184588Sdfr				break;
706184588Sdfr
707184588Sdfr#ifdef INET6
708184588Sdfr			case AF_INET6:
709184588Sdfr				((struct sockaddr_in6 *)&ss)->sin6_port =
710184588Sdfr					htons(port);
711184588Sdfr				break;
712184588Sdfr#endif
713184588Sdfr			}
714184588Sdfr		}
715184588Sdfr		break;
716184588Sdfr	default:
717184588Sdfr		panic("invalid rpcvers %d", rpcvers);
718184588Sdfr	}
719184588Sdfr	/*
720184588Sdfr	 * We may have a positive response from the portmapper, but
721184588Sdfr	 * the requested service was not found. Make sure we received
722184588Sdfr	 * a valid port.
723184588Sdfr	 */
724184588Sdfr	switch (ss.ss_family) {
725184588Sdfr	case AF_INET:
726184588Sdfr		port = ((struct sockaddr_in *)&ss)->sin_port;
727184588Sdfr		break;
728184588Sdfr#ifdef INET6
729184588Sdfr	case AF_INET6:
730184588Sdfr		port = ((struct sockaddr_in6 *)&ss)->sin6_port;
731184588Sdfr		break;
732184588Sdfr#endif
733184588Sdfr	}
734184588Sdfr	if (stat != RPC_SUCCESS || !port) {
735184588Sdfr		/*
736184588Sdfr		 * If we were able to talk to rpcbind or portmap, but the udp
737184588Sdfr		 * variant wasn't available, ask about tcp.
738184588Sdfr		 *
739184588Sdfr		 * XXX - We could also check for a TCP portmapper, but
740184588Sdfr		 * if the host is running a portmapper at all, we should be able
741184588Sdfr		 * to hail it over UDP.
742184588Sdfr		 */
743184588Sdfr		if (stat == RPC_SUCCESS && !do_tcp) {
744184588Sdfr			do_tcp = TRUE;
745184588Sdfr			goto try_tcp;
746184588Sdfr		}
747184588Sdfr
748184588Sdfr		/* Otherwise, bad news. */
749184588Sdfr		printf("gsstest_get_rpc: failed to contact remote rpcbind, "
750184588Sdfr		    "stat = %d, port = %d\n",
751184588Sdfr		    (int) stat, port);
752184588Sdfr		CLNT_DESTROY(rpcb);
753184588Sdfr		return (NULL);
754184588Sdfr	}
755184588Sdfr
756184588Sdfr	if (do_tcp) {
757184588Sdfr		/*
758184588Sdfr		 * Destroy the UDP client we used to speak to rpcbind and
759184588Sdfr		 * recreate as a TCP client.
760184588Sdfr		 */
761184588Sdfr		struct netconfig *nconf = NULL;
762184588Sdfr
763184588Sdfr		CLNT_DESTROY(rpcb);
764184588Sdfr
765184588Sdfr		switch (ss.ss_family) {
766184588Sdfr		case AF_INET:
767184588Sdfr			nconf = getnetconfigent("tcp");
768184588Sdfr			break;
769184588Sdfr#ifdef INET6
770184588Sdfr		case AF_INET6:
771184588Sdfr			nconf = getnetconfigent("tcp6");
772184588Sdfr			break;
773184588Sdfr#endif
774184588Sdfr		}
775184588Sdfr
776184588Sdfr		rpcb = clnt_reconnect_create(nconf, (struct sockaddr *)&ss,
777184588Sdfr		    prog, vers, 0, 0);
778184588Sdfr	} else {
779184588Sdfr		/*
780184588Sdfr		 * Re-use the client we used to speak to rpcbind.
781184588Sdfr		 */
782184588Sdfr		CLNT_CONTROL(rpcb, CLSET_SVC_ADDR, &ss);
783184588Sdfr		CLNT_CONTROL(rpcb, CLSET_PROG, &prog);
784184588Sdfr		CLNT_CONTROL(rpcb, CLSET_VERS, &vers);
785184588Sdfr	}
786184588Sdfr
787184588Sdfr	return (rpcb);
788184588Sdfr}
789184588Sdfr
790184588Sdfr/*
791184588Sdfr * RPCSEC_GSS client
792184588Sdfr */
793184588Sdfrstatic int
794193066Sjamiegsstest_3(struct thread *td)
795184588Sdfr{
796184588Sdfr	struct sockaddr_in sin;
797184588Sdfr	char service[128];
798184588Sdfr	CLIENT *client;
799184588Sdfr	AUTH *auth;
800184588Sdfr	rpc_gss_options_ret_t options_ret;
801184588Sdfr	enum clnt_stat stat;
802184588Sdfr	struct timeval tv;
803184588Sdfr	rpc_gss_service_t svc;
804184588Sdfr	int i;
805184588Sdfr
806184588Sdfr	sin.sin_len = sizeof(sin);
807184588Sdfr	sin.sin_family = AF_INET;
808184588Sdfr	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
809184588Sdfr	sin.sin_port = 0;
810184588Sdfr
811184588Sdfr	client = gsstest_get_rpc((struct sockaddr *) &sin, 123456, 1);
812184588Sdfr	if (!client) {
813184588Sdfr		uprintf("Can't connect to service\n");
814184588Sdfr		return(1);
815184588Sdfr	}
816184588Sdfr
817193066Sjamie	memcpy(service, "host@", 5);
818193066Sjamie	getcredhostname(td->td_ucred, service + 5, sizeof(service) - 5);
819184588Sdfr
820184588Sdfr	auth = rpc_gss_seccreate(client, curthread->td_ucred,
821184588Sdfr	    service, "kerberosv5", rpc_gss_svc_privacy,
822184588Sdfr	    NULL, NULL, &options_ret);
823184588Sdfr	if (!auth) {
824184588Sdfr		gss_OID oid;
825184588Sdfr		uprintf("Can't authorize to service (mech=%s)\n",
826184588Sdfr			options_ret.actual_mechanism);
827184588Sdfr		oid = GSS_C_NO_OID;
828184588Sdfr		rpc_gss_mech_to_oid(options_ret.actual_mechanism, &oid);
829184588Sdfr		report_error(oid, options_ret.major_status,
830184588Sdfr		    options_ret.minor_status);
831184588Sdfr		CLNT_DESTROY(client);
832184588Sdfr		return (1);
833184588Sdfr	}
834184588Sdfr
835184588Sdfr	for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) {
836184588Sdfr		const char *svc_names[] = {
837184588Sdfr			"rpc_gss_svc_default",
838184588Sdfr			"rpc_gss_svc_none",
839184588Sdfr			"rpc_gss_svc_integrity",
840184588Sdfr			"rpc_gss_svc_privacy"
841184588Sdfr		};
842184588Sdfr		int num;
843184588Sdfr
844184588Sdfr		rpc_gss_set_defaults(auth, svc, NULL);
845184588Sdfr
846184588Sdfr		client->cl_auth = auth;
847184588Sdfr		tv.tv_sec = 5;
848184588Sdfr		tv.tv_usec = 0;
849184588Sdfr		for (i = 42; i < 142; i++) {
850184588Sdfr			num = i;
851184588Sdfr			stat = CLNT_CALL(client, 1,
852184588Sdfr			    (xdrproc_t) xdr_int, (char *) &num,
853184588Sdfr			    (xdrproc_t) xdr_int, (char *) &num, tv);
854184588Sdfr			if (stat == RPC_SUCCESS) {
855184588Sdfr				if (num != i + 100)
856184588Sdfr					uprintf("unexpected reply %d\n", num);
857184588Sdfr			} else {
858184588Sdfr				uprintf("call failed, stat=%d\n", (int) stat);
859184588Sdfr				break;
860184588Sdfr			}
861184588Sdfr		}
862184588Sdfr		if (i == 142)
863184588Sdfr			uprintf("call succeeded with %s\n", svc_names[svc]);
864184588Sdfr	}
865184588Sdfr
866184588Sdfr	AUTH_DESTROY(auth);
867184588Sdfr	CLNT_RELEASE(client);
868184588Sdfr
869184588Sdfr	return (0);
870184588Sdfr}
871184588Sdfr
872184588Sdfr/*
873184588Sdfr * RPCSEC_GSS server
874184588Sdfr */
875184588Sdfrstatic rpc_gss_principal_t server_acl = NULL;
876184588Sdfrstatic bool_t server_new_context(struct svc_req *req, gss_cred_id_t deleg,
877184588Sdfr    gss_ctx_id_t gss_context, rpc_gss_lock_t *lock, void **cookie);
878184588Sdfrstatic void server_program_1(struct svc_req *rqstp, register SVCXPRT *transp);
879184588Sdfr
880184588Sdfrstatic int
881193066Sjamiegsstest_4(struct thread *td)
882184588Sdfr{
883184588Sdfr	SVCPOOL *pool;
884184588Sdfr	char principal[128 + 5];
885184588Sdfr	const char **mechs;
886184588Sdfr	static rpc_gss_callback_t cb;
887184588Sdfr
888193066Sjamie	memcpy(principal, "host@", 5);
889193066Sjamie	getcredhostname(td->td_ucred, principal + 5, sizeof(principal) - 5);
890184588Sdfr
891184588Sdfr	mechs = rpc_gss_get_mechanisms();
892184588Sdfr	while (*mechs) {
893184588Sdfr		if (!rpc_gss_set_svc_name(principal, *mechs, GSS_C_INDEFINITE,
894184588Sdfr			123456, 1)) {
895184588Sdfr			rpc_gss_error_t e;
896184588Sdfr
897184588Sdfr			rpc_gss_get_error(&e);
898184588Sdfr			printf("setting name for %s for %s failed: %d, %d\n",
899184588Sdfr			    principal, *mechs,
900184588Sdfr			    e.rpc_gss_error, e.system_error);
901184588Sdfr		}
902184588Sdfr		mechs++;
903184588Sdfr	}
904184588Sdfr
905184588Sdfr	cb.program = 123456;
906184588Sdfr	cb.version = 1;
907184588Sdfr	cb.callback = server_new_context;
908184588Sdfr	rpc_gss_set_callback(&cb);
909184588Sdfr
910184588Sdfr	pool = svcpool_create("gsstest", NULL);
911184588Sdfr
912184588Sdfr	svc_create(pool, server_program_1, 123456, 1, NULL);
913184588Sdfr	svc_run(pool);
914184588Sdfr
915184588Sdfr	rpc_gss_clear_svc_name(123456, 1);
916184588Sdfr	rpc_gss_clear_callback(&cb);
917184588Sdfr
918184588Sdfr	svcpool_destroy(pool);
919184588Sdfr
920184588Sdfr	return (0);
921184588Sdfr}
922184588Sdfr
923184588Sdfrstatic void
924184588Sdfrserver_program_1(struct svc_req *rqstp, register SVCXPRT *transp)
925184588Sdfr{
926184588Sdfr	rpc_gss_rawcred_t *rcred;
927184588Sdfr	rpc_gss_ucred_t *ucred;
928184588Sdfr	int		i, num;
929184588Sdfr
930184588Sdfr	if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) {
931184588Sdfr		svcerr_weakauth(rqstp);
932184588Sdfr		return;
933184588Sdfr	}
934184588Sdfr
935184588Sdfr	if (!rpc_gss_getcred(rqstp, &rcred, &ucred, NULL)) {
936184588Sdfr		svcerr_systemerr(rqstp);
937184588Sdfr		return;
938184588Sdfr	}
939184588Sdfr
940184588Sdfr	printf("svc=%d, mech=%s, uid=%d, gid=%d, gids={",
941184588Sdfr	    rcred->service, rcred->mechanism, ucred->uid, ucred->gid);
942184588Sdfr	for (i = 0; i < ucred->gidlen; i++) {
943184588Sdfr		if (i > 0) printf(",");
944184588Sdfr		printf("%d", ucred->gidlist[i]);
945184588Sdfr	}
946184588Sdfr	printf("}\n");
947184588Sdfr
948184588Sdfr	switch (rqstp->rq_proc) {
949184588Sdfr	case 0:
950184588Sdfr		if (!svc_getargs(rqstp, (xdrproc_t) xdr_void, 0)) {
951184588Sdfr			svcerr_decode(rqstp);
952184588Sdfr			goto out;
953184588Sdfr		}
954184588Sdfr		if (!svc_sendreply(rqstp, (xdrproc_t) xdr_void, 0)) {
955184588Sdfr			svcerr_systemerr(rqstp);
956184588Sdfr		}
957184588Sdfr		goto out;
958184588Sdfr
959184588Sdfr	case 1:
960184588Sdfr		if (!svc_getargs(rqstp, (xdrproc_t) xdr_int,
961184588Sdfr			(char *) &num)) {
962184588Sdfr			svcerr_decode(rqstp);
963184588Sdfr			goto out;
964184588Sdfr		}
965184588Sdfr		num += 100;
966184588Sdfr		if (!svc_sendreply(rqstp, (xdrproc_t) xdr_int,
967184588Sdfr			(char *) &num)) {
968184588Sdfr			svcerr_systemerr(rqstp);
969184588Sdfr		}
970184588Sdfr		goto out;
971184588Sdfr
972184588Sdfr	default:
973184588Sdfr		svcerr_noproc(rqstp);
974184588Sdfr		goto out;
975184588Sdfr	}
976184588Sdfr
977184588Sdfrout:
978184887Sdfr	svc_freereq(rqstp);
979184588Sdfr	return;
980184588Sdfr}
981184588Sdfr
982184588Sdfrstatic void
983184588Sdfrprint_principal(rpc_gss_principal_t principal)
984184588Sdfr{
985184588Sdfr	int i, len, n;
986184588Sdfr	uint8_t *p;
987184588Sdfr
988184588Sdfr	len = principal->len;
989184588Sdfr	p = (uint8_t *) principal->name;
990184588Sdfr	while (len > 0) {
991184588Sdfr		n = len;
992184588Sdfr		if (n > 16)
993184588Sdfr			n = 16;
994184588Sdfr		for (i = 0; i < n; i++)
995184588Sdfr			printf("%02x ", p[i]);
996184588Sdfr		for (; i < 16; i++)
997184588Sdfr			printf("   ");
998184588Sdfr		printf("|");
999184588Sdfr		for (i = 0; i < n; i++)
1000184588Sdfr			printf("%c", isprint(p[i]) ? p[i] : '.');
1001184588Sdfr		printf("|\n");
1002184588Sdfr		len -= n;
1003184588Sdfr		p += n;
1004184588Sdfr	}
1005184588Sdfr}
1006184588Sdfr
1007184588Sdfrstatic bool_t
1008184588Sdfrserver_new_context(__unused struct svc_req *req,
1009184588Sdfr    gss_cred_id_t deleg,
1010184588Sdfr    __unused gss_ctx_id_t gss_context,
1011184588Sdfr    rpc_gss_lock_t *lock,
1012184588Sdfr    __unused void **cookie)
1013184588Sdfr{
1014184588Sdfr	rpc_gss_rawcred_t *rcred = lock->raw_cred;
1015184588Sdfr	OM_uint32 junk;
1016184588Sdfr
1017184588Sdfr	printf("new security context version=%d, mech=%s, qop=%s:\n",
1018184588Sdfr	    rcred->version, rcred->mechanism, rcred->qop);
1019184588Sdfr	print_principal(rcred->client_principal);
1020184588Sdfr
1021184588Sdfr	if (server_acl) {
1022184588Sdfr		if (rcred->client_principal->len != server_acl->len
1023184588Sdfr		    || memcmp(rcred->client_principal->name, server_acl->name,
1024184588Sdfr			server_acl->len)) {
1025184588Sdfr			return (FALSE);
1026184588Sdfr		}
1027184588Sdfr	}
1028184588Sdfr	gss_release_cred(&junk, &deleg);
1029184588Sdfr
1030184588Sdfr	return (TRUE);
1031184588Sdfr}
1032184588Sdfr
1033184588Sdfr/*
1034184588Sdfr * Hook up a syscall for gssapi testing.
1035184588Sdfr */
1036184588Sdfr
1037184588Sdfrstruct gsstest_args {
1038184588Sdfr        int a_op;
1039184588Sdfr	void *a_args;
1040184588Sdfr	void *a_res;
1041184588Sdfr};
1042184588Sdfr
1043184588Sdfrstruct gsstest_2_args {
1044184588Sdfr	int step;		/* test step number */
1045184588Sdfr	gss_buffer_desc input_token; /* token from userland */
1046184588Sdfr	gss_buffer_desc output_token; /* buffer to receive reply token */
1047184588Sdfr};
1048184588Sdfrstruct gsstest_2_res {
1049184588Sdfr	OM_uint32 maj_stat;	/* maj_stat from kernel */
1050184588Sdfr	OM_uint32 min_stat;	/* min_stat from kernel */
1051184588Sdfr	gss_buffer_desc output_token; /* reply token (using space from gsstest_2_args.output) */
1052184588Sdfr};
1053184588Sdfr
1054184588Sdfrstatic int
1055184588Sdfrgsstest(struct thread *td, struct gsstest_args *uap)
1056184588Sdfr{
1057184588Sdfr	int error;
1058184588Sdfr
1059184588Sdfr	switch (uap->a_op) {
1060184588Sdfr	case 1:
1061193066Sjamie                return (gsstest_1(td));
1062184588Sdfr
1063184588Sdfr	case 2: {
1064184588Sdfr		struct gsstest_2_args args;
1065184588Sdfr		struct gsstest_2_res res;
1066184588Sdfr		gss_buffer_desc input_token, output_token;
1067184588Sdfr		OM_uint32 junk;
1068184588Sdfr
1069184588Sdfr		error = copyin(uap->a_args, &args, sizeof(args));
1070184588Sdfr		if (error)
1071184588Sdfr			return (error);
1072184588Sdfr		input_token.length = args.input_token.length;
1073184588Sdfr		input_token.value = malloc(input_token.length, M_GSSAPI,
1074184588Sdfr		    M_WAITOK);
1075184588Sdfr		error = copyin(args.input_token.value, input_token.value,
1076184588Sdfr		    input_token.length);
1077184588Sdfr		if (error) {
1078184588Sdfr			gss_release_buffer(&junk, &input_token);
1079184588Sdfr			return (error);
1080184588Sdfr		}
1081184588Sdfr		output_token.length = 0;
1082184588Sdfr		output_token.value = NULL;
1083193066Sjamie		gsstest_2(td, args.step, &input_token,
1084184588Sdfr		    &res.maj_stat, &res.min_stat, &output_token);
1085184588Sdfr		gss_release_buffer(&junk, &input_token);
1086184588Sdfr		if (output_token.length > args.output_token.length) {
1087184588Sdfr			gss_release_buffer(&junk, &output_token);
1088184588Sdfr			return (EOVERFLOW);
1089184588Sdfr		}
1090184588Sdfr		res.output_token.length = output_token.length;
1091184588Sdfr		res.output_token.value = args.output_token.value;
1092184588Sdfr		error = copyout(output_token.value, res.output_token.value,
1093184588Sdfr		    output_token.length);
1094184588Sdfr		gss_release_buffer(&junk, &output_token);
1095184588Sdfr		if (error)
1096184588Sdfr			return (error);
1097184588Sdfr
1098184588Sdfr		return (copyout(&res, uap->a_res, sizeof(res)));
1099184588Sdfr
1100184588Sdfr		break;
1101184588Sdfr	}
1102184588Sdfr	case 3:
1103193066Sjamie		return (gsstest_3(td));
1104184588Sdfr	case 4:
1105193066Sjamie		return (gsstest_4(td));
1106184588Sdfr	}
1107184588Sdfr
1108184588Sdfr        return (EINVAL);
1109184588Sdfr}
1110184588Sdfr
1111184588Sdfr/*
1112184588Sdfr * The `sysent' for the new syscall
1113184588Sdfr */
1114184588Sdfrstatic struct sysent gsstest_sysent = {
1115184588Sdfr        3,                      /* sy_narg */
1116184588Sdfr        (sy_call_t *) gsstest	/* sy_call */
1117184588Sdfr};
1118184588Sdfr
1119184588Sdfr/*
1120184588Sdfr * The offset in sysent where the syscall is allocated.
1121184588Sdfr */
1122184588Sdfrstatic int gsstest_offset = NO_SYSCALL;
1123184588Sdfr
1124184588Sdfr/*
1125184588Sdfr * The function called at load/unload.
1126184588Sdfr */
1127184588Sdfr
1128184588Sdfr
1129184588Sdfrstatic int
1130184588Sdfrgsstest_load(struct module *module, int cmd, void *arg)
1131184588Sdfr{
1132184588Sdfr        int error = 0;
1133184588Sdfr
1134184588Sdfr        switch (cmd) {
1135184588Sdfr        case MOD_LOAD :
1136184588Sdfr                break;
1137184588Sdfr        case MOD_UNLOAD :
1138184588Sdfr                break;
1139184588Sdfr        default :
1140184588Sdfr                error = EOPNOTSUPP;
1141184588Sdfr                break;
1142184588Sdfr        }
1143184588Sdfr        return error;
1144184588Sdfr}
1145184588Sdfr
1146184588SdfrSYSCALL_MODULE(gsstest_syscall, &gsstest_offset, &gsstest_sysent,
1147184588Sdfr    gsstest_load, NULL);
1148