xref: /freebsd/sys/kgssapi/gsstest.c (revision e57c2b130f2cd40967cf20698d376cc5ada95871)
1 /*-
2  * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3  * Authors: Doug Rabson <dfr@rabson.org>
4  * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/ctype.h>
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/kobj.h>
35 #include <sys/malloc.h>
36 #include <sys/module.h>
37 #include <sys/proc.h>
38 #include <sys/socketvar.h>
39 #include <sys/sysent.h>
40 #include <sys/sysproto.h>
41 
42 #include <kgssapi/gssapi.h>
43 #include <kgssapi/gssapi_impl.h>
44 #include <rpc/rpc.h>
45 #include <rpc/rpc_com.h>
46 #include <rpc/rpcb_prot.h>
47 #include <rpc/rpcsec_gss.h>
48 
49 static void
50 report_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
51 {
52 	OM_uint32 maj_stat, min_stat;
53 	OM_uint32 message_context;
54 	gss_buffer_desc buf;
55 
56 	uprintf("major_stat=%d, minor_stat=%d\n", maj, min);
57 	message_context = 0;
58 	do {
59 		maj_stat = gss_display_status(&min_stat, maj,
60 		    GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &buf);
61 		if (GSS_ERROR(maj_stat))
62 			break;
63 		uprintf("%.*s\n", (int)buf.length, (char *) buf.value);
64 		gss_release_buffer(&min_stat, &buf);
65 	} while (message_context);
66 	if (mech && min) {
67 		message_context = 0;
68 		do {
69 			maj_stat = gss_display_status(&min_stat, min,
70 			    GSS_C_MECH_CODE, mech, &message_context, &buf);
71 			if (GSS_ERROR(maj_stat))
72 				break;
73 			uprintf("%.*s\n", (int)buf.length, (char *) buf.value);
74 			gss_release_buffer(&min_stat, &buf);
75 		} while (message_context);
76 	}
77 }
78 
79 #if 0
80 static void
81 send_token_to_peer(const gss_buffer_t token)
82 {
83 	const uint8_t *p;
84 	size_t i;
85 
86 	printf("send token:\n");
87 	printf("%d ", (int) token->length);
88 	p = (const uint8_t *) token->value;
89 	for (i = 0; i < token->length; i++)
90 		printf("%02x", *p++);
91 	printf("\n");
92 }
93 
94 static void
95 receive_token_from_peer(gss_buffer_t token)
96 {
97 	char line[8192];
98 	char *p;
99 	uint8_t *q;
100 	int len, val;
101 
102 	printf("receive token:\n");
103 	fgets(line, sizeof(line), stdin);
104 	if (line[strlen(line) - 1] != '\n') {
105 		printf("token truncated\n");
106 		exit(1);
107 	}
108 	p = line;
109 	if (sscanf(line, "%d ", &len) != 1) {
110 		printf("bad token\n");
111 		exit(1);
112 	}
113 	p = strchr(p, ' ') + 1;
114 	token->length = len;
115 	token->value = malloc(len);
116 	q = (uint8_t *) token->value;
117 	while (len) {
118 		if (sscanf(p, "%02x", &val) != 1) {
119 			printf("bad token\n");
120 			exit(1);
121 		}
122 		*q++ = val;
123 		p += 2;
124 		len--;
125 	}
126 }
127 #endif
128 
129 #if 0
130 void
131 server(int argc, char** argv)
132 {
133 	OM_uint32 maj_stat, min_stat;
134 	gss_buffer_desc input_token, output_token;
135 	gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;
136 	gss_name_t client_name;
137 	gss_OID mech_type;
138 
139 	if (argc != 1)
140 		usage();
141 
142 	do {
143 		receive_token_from_peer(&input_token);
144 		maj_stat = gss_accept_sec_context(&min_stat,
145 		    &context_hdl,
146 		    GSS_C_NO_CREDENTIAL,
147 		    &input_token,
148 		    GSS_C_NO_CHANNEL_BINDINGS,
149 		    &client_name,
150 		    &mech_type,
151 		    &output_token,
152 		    NULL,
153 		    NULL,
154 		    NULL);
155 		if (GSS_ERROR(maj_stat)) {
156 			report_error(mech_type, maj_stat, min_stat);
157 		}
158 		if (output_token.length != 0) {
159 			send_token_to_peer(&output_token);
160 			gss_release_buffer(&min_stat, &output_token);
161 		}
162 		if (GSS_ERROR(maj_stat)) {
163 			if (context_hdl != GSS_C_NO_CONTEXT)
164 				gss_delete_sec_context(&min_stat,
165 				    &context_hdl,
166 				    GSS_C_NO_BUFFER);
167 			break;
168 		}
169 	} while (maj_stat & GSS_S_CONTINUE_NEEDED);
170 
171 	if (client_name) {
172 		gss_buffer_desc name_desc;
173 		char buf[512];
174 
175 		gss_display_name(&min_stat, client_name, &name_desc, NULL);
176 		memcpy(buf, name_desc.value, name_desc.length);
177 		buf[name_desc.length] = 0;
178 		gss_release_buffer(&min_stat, &name_desc);
179 		printf("client name is %s\n", buf);
180 	}
181 
182 	receive_token_from_peer(&input_token);
183 	gss_unwrap(&min_stat, context_hdl, &input_token, &output_token,
184 	    NULL, NULL);
185 	printf("%.*s\n", (int)output_token.length, (char *) output_token.value);
186 	gss_release_buffer(&min_stat, &output_token);
187 }
188 #endif
189 
190 /* 1.2.752.43.13.14 */
191 static gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc =
192 {6, (void *) "\x2a\x85\x70\x2b\x0d\x0e"};
193 
194 gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X = &gss_krb5_set_allowable_enctypes_x_desc;
195 #define ETYPE_DES_CBC_CRC	1
196 
197 /*
198  * Create an initiator context and acceptor context in the kernel and
199  * use them to exchange signed and sealed messages.
200  */
201 static int
202 gsstest_1(void)
203 {
204 	OM_uint32 maj_stat, min_stat;
205 	OM_uint32 smaj_stat, smin_stat;
206 	int context_established = 0;
207 	gss_ctx_id_t client_context = GSS_C_NO_CONTEXT;
208 	gss_ctx_id_t server_context = GSS_C_NO_CONTEXT;
209 	gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL;
210 	gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
211 	gss_name_t name = GSS_C_NO_NAME;
212 	gss_name_t received_name = GSS_C_NO_NAME;
213 	gss_buffer_desc name_desc;
214 	gss_buffer_desc client_token, server_token, message_buf;
215 	gss_OID mech, actual_mech, mech_type;
216 	static gss_OID_desc krb5_desc =
217 		{9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
218 #if 0
219 	static gss_OID_desc spnego_desc =
220 		{6, (void *)"\x2b\x06\x01\x05\x05\x02"};
221 	static gss_OID_desc ntlm_desc =
222 		{10, (void *)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"};
223 #endif
224 	char enctype[sizeof(uint32_t)];
225 
226 	mech = GSS_C_NO_OID;
227 
228 	{
229 		static char sbuf[512];
230 		snprintf(sbuf, sizeof(sbuf), "nfs@%s", hostname);
231 		name_desc.value = sbuf;
232 	}
233 
234 	name_desc.length = strlen((const char *) name_desc.value);
235 	maj_stat = gss_import_name(&min_stat, &name_desc,
236 	    GSS_C_NT_HOSTBASED_SERVICE, &name);
237 	if (GSS_ERROR(maj_stat)) {
238 		printf("gss_import_name failed\n");
239 		report_error(mech, maj_stat, min_stat);
240 		goto out;
241 	}
242 
243 	maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME,
244 	    0, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client_cred,
245 	    NULL, NULL);
246 	if (GSS_ERROR(maj_stat)) {
247 		printf("gss_acquire_cred (client) failed\n");
248 		report_error(mech, maj_stat, min_stat);
249 		goto out;
250 	}
251 
252 	enctype[0] = (ETYPE_DES_CBC_CRC >> 24) & 0xff;
253 	enctype[1] = (ETYPE_DES_CBC_CRC >> 16) & 0xff;
254 	enctype[2] = (ETYPE_DES_CBC_CRC >> 8) & 0xff;
255 	enctype[3] = ETYPE_DES_CBC_CRC & 0xff;
256 	message_buf.length = sizeof(enctype);
257 	message_buf.value = enctype;
258 	maj_stat = gss_set_cred_option(&min_stat, &client_cred,
259 	    GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, &message_buf);
260 	if (GSS_ERROR(maj_stat)) {
261 		printf("gss_set_cred_option failed\n");
262 		report_error(mech, maj_stat, min_stat);
263 		goto out;
264 	}
265 
266 	server_token.length = 0;
267 	server_token.value = NULL;
268 	while (!context_established) {
269 		client_token.length = 0;
270 		client_token.value = NULL;
271 		maj_stat = gss_init_sec_context(&min_stat,
272 		    client_cred,
273 		    &client_context,
274 		    name,
275 		    mech,
276 		    GSS_C_MUTUAL_FLAG|GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG,
277 		    0,
278 		    GSS_C_NO_CHANNEL_BINDINGS,
279 		    &server_token,
280 		    &actual_mech,
281 		    &client_token,
282 		    NULL,
283 		    NULL);
284 		if (server_token.length)
285 			gss_release_buffer(&smin_stat, &server_token);
286 		if (GSS_ERROR(maj_stat)) {
287 			printf("gss_init_sec_context failed\n");
288 			report_error(mech, maj_stat, min_stat);
289 			goto out;
290 		}
291 
292 		if (client_token.length != 0) {
293 			if (!server_cred) {
294 				gss_OID_set_desc oid_set;
295 				oid_set.count = 1;
296 				oid_set.elements = &krb5_desc;
297 				smaj_stat = gss_acquire_cred(&smin_stat,
298 				    name, 0, &oid_set, GSS_C_ACCEPT, &server_cred,
299 				    NULL, NULL);
300 				if (GSS_ERROR(smaj_stat)) {
301 					printf("gss_acquire_cred (server) failed\n");
302 					report_error(mech_type, smaj_stat, smin_stat);
303 					goto out;
304 				}
305 			}
306 			smaj_stat = gss_accept_sec_context(&smin_stat,
307 			    &server_context,
308 			    server_cred,
309 			    &client_token,
310 			    GSS_C_NO_CHANNEL_BINDINGS,
311 			    &received_name,
312 			    &mech_type,
313 			    &server_token,
314 			    NULL,
315 			    NULL,
316 			    NULL);
317 			if (GSS_ERROR(smaj_stat)) {
318 				printf("gss_accept_sec_context failed\n");
319 				report_error(mech_type, smaj_stat, smin_stat);
320 				goto out;
321 			}
322 			gss_release_buffer(&min_stat, &client_token);
323 		}
324 		if (GSS_ERROR(maj_stat)) {
325 			if (client_context != GSS_C_NO_CONTEXT)
326 				gss_delete_sec_context(&min_stat,
327 				    &client_context,
328 				    GSS_C_NO_BUFFER);
329 			break;
330 		}
331 
332 		if (maj_stat == GSS_S_COMPLETE) {
333 			context_established = 1;
334 		}
335 	}
336 
337 	message_buf.length = strlen("Hello world");
338 	message_buf.value = (void *) "Hello world";
339 
340 	maj_stat = gss_get_mic(&min_stat, client_context,
341 	    GSS_C_QOP_DEFAULT, &message_buf, &client_token);
342 	if (GSS_ERROR(maj_stat)) {
343 		printf("gss_get_mic failed\n");
344 		report_error(mech_type, maj_stat, min_stat);
345 		goto out;
346 	}
347 	maj_stat = gss_verify_mic(&min_stat, server_context,
348 	    &message_buf, &client_token, NULL);
349 	if (GSS_ERROR(maj_stat)) {
350 		printf("gss_verify_mic failed\n");
351 		report_error(mech_type, maj_stat, min_stat);
352 		goto out;
353 	}
354 	gss_release_buffer(&min_stat, &client_token);
355 
356 	maj_stat = gss_wrap(&min_stat, client_context,
357 	    TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL, &client_token);
358 	if (GSS_ERROR(maj_stat)) {
359 		printf("gss_wrap failed\n");
360 		report_error(mech_type, maj_stat, min_stat);
361 		goto out;
362 	}
363 	maj_stat = gss_unwrap(&min_stat, server_context,
364 	    &client_token, &server_token, NULL, NULL);
365 	if (GSS_ERROR(maj_stat)) {
366 		printf("gss_unwrap failed\n");
367 		report_error(mech_type, maj_stat, min_stat);
368 		goto out;
369 	}
370 
371  	if (message_buf.length != server_token.length
372 	    || memcmp(message_buf.value, server_token.value,
373 		message_buf.length))
374 		printf("unwrap result corrupt\n");
375 
376 	gss_release_buffer(&min_stat, &client_token);
377 	gss_release_buffer(&min_stat, &server_token);
378 
379 out:
380 	if (client_context)
381 		gss_delete_sec_context(&min_stat, &client_context,
382 		    GSS_C_NO_BUFFER);
383 	if (server_context)
384 		gss_delete_sec_context(&min_stat, &server_context,
385 		    GSS_C_NO_BUFFER);
386 	if (client_cred)
387 		gss_release_cred(&min_stat, &client_cred);
388 	if (server_cred)
389 		gss_release_cred(&min_stat, &server_cred);
390 	if (name)
391 		gss_release_name(&min_stat, &name);
392 	if (received_name)
393 		gss_release_name(&min_stat, &received_name);
394 
395 	return (0);
396 }
397 
398 /*
399  * Interoperability with userland. This takes several steps:
400  *
401  * 1. Accept an initiator token from userland, return acceptor
402  * token. Repeat this step until both userland and kernel return
403  * GSS_S_COMPLETE.
404  *
405  * 2. Receive a signed message from userland and verify the
406  * signature. Return a signed reply to userland for it to verify.
407  *
408  * 3. Receive a wrapped message from userland and unwrap it. Return a
409  * wrapped reply to userland.
410  */
411 static int
412 gsstest_2(int step, const gss_buffer_t input_token,
413     OM_uint32 *maj_stat_res, OM_uint32 *min_stat_res, gss_buffer_t output_token)
414 {
415 	OM_uint32 maj_stat, min_stat;
416 	static int context_established = 0;
417 	static gss_ctx_id_t server_context = GSS_C_NO_CONTEXT;
418 	static gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
419 	static gss_name_t name = GSS_C_NO_NAME;
420 	gss_buffer_desc name_desc;
421 	gss_buffer_desc message_buf;
422 	gss_OID mech_type = GSS_C_NO_OID;
423 	char enctype[sizeof(uint32_t)];
424 	int error = EINVAL;
425 
426 	maj_stat = GSS_S_FAILURE;
427 	min_stat = 0;
428 	switch (step) {
429 
430 	case 1:
431 		if (server_context == GSS_C_NO_CONTEXT) {
432 			static char sbuf[512];
433 			snprintf(sbuf, sizeof(sbuf), "nfs@%s", hostname);
434 			name_desc.value = sbuf;
435 			name_desc.length = strlen((const char *)
436 			    name_desc.value);
437 			maj_stat = gss_import_name(&min_stat, &name_desc,
438 			    GSS_C_NT_HOSTBASED_SERVICE, &name);
439 			if (GSS_ERROR(maj_stat)) {
440 				printf("gss_import_name failed\n");
441 				report_error(mech_type, maj_stat, min_stat);
442 				goto out;
443 			}
444 
445 			maj_stat = gss_acquire_cred(&min_stat,
446 			    name, 0, GSS_C_NO_OID_SET, GSS_C_ACCEPT,
447 			    &server_cred, NULL, NULL);
448 			if (GSS_ERROR(maj_stat)) {
449 				printf("gss_acquire_cred (server) failed\n");
450 				report_error(mech_type, maj_stat, min_stat);
451 				goto out;
452 			}
453 
454 			enctype[0] = (ETYPE_DES_CBC_CRC >> 24) & 0xff;
455 			enctype[1] = (ETYPE_DES_CBC_CRC >> 16) & 0xff;
456 			enctype[2] = (ETYPE_DES_CBC_CRC >> 8) & 0xff;
457 			enctype[3] = ETYPE_DES_CBC_CRC & 0xff;
458 			message_buf.length = sizeof(enctype);
459 			message_buf.value = enctype;
460 			maj_stat = gss_set_cred_option(&min_stat, &server_cred,
461 			    GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, &message_buf);
462 			if (GSS_ERROR(maj_stat)) {
463 				printf("gss_set_cred_option failed\n");
464 				report_error(mech_type, maj_stat, min_stat);
465 				goto out;
466 			}
467 		}
468 
469 		maj_stat = gss_accept_sec_context(&min_stat,
470 		    &server_context,
471 		    server_cred,
472 		    input_token,
473 		    GSS_C_NO_CHANNEL_BINDINGS,
474 		    NULL,
475 		    &mech_type,
476 		    output_token,
477 		    NULL,
478 		    NULL,
479 		    NULL);
480 		if (GSS_ERROR(maj_stat)) {
481 			printf("gss_accept_sec_context failed\n");
482 			report_error(mech_type, maj_stat, min_stat);
483 			goto out;
484 		}
485 
486 		if (maj_stat == GSS_S_COMPLETE) {
487 			context_established = 1;
488 		}
489 		*maj_stat_res = maj_stat;
490 		*min_stat_res = min_stat;
491 		break;
492 
493 	case 2:
494 		message_buf.length = strlen("Hello world");
495 		message_buf.value = (void *) "Hello world";
496 
497 		maj_stat = gss_verify_mic(&min_stat, server_context,
498 		    &message_buf, input_token, NULL);
499 		if (GSS_ERROR(maj_stat)) {
500 			printf("gss_verify_mic failed\n");
501 			report_error(mech_type, maj_stat, min_stat);
502 			goto out;
503 		}
504 
505 		maj_stat = gss_get_mic(&min_stat, server_context,
506 		    GSS_C_QOP_DEFAULT, &message_buf, output_token);
507 		if (GSS_ERROR(maj_stat)) {
508 			printf("gss_get_mic failed\n");
509 			report_error(mech_type, maj_stat, min_stat);
510 			goto out;
511 		}
512 		break;
513 
514 	case 3:
515 		maj_stat = gss_unwrap(&min_stat, server_context,
516 		    input_token, &message_buf, NULL, NULL);
517 		if (GSS_ERROR(maj_stat)) {
518 			printf("gss_unwrap failed\n");
519 			report_error(mech_type, maj_stat, min_stat);
520 			goto out;
521 		}
522 		gss_release_buffer(&min_stat, &message_buf);
523 
524 		message_buf.length = strlen("Hello world");
525 		message_buf.value = (void *) "Hello world";
526 		maj_stat = gss_wrap(&min_stat, server_context,
527 		    TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL, output_token);
528 		if (GSS_ERROR(maj_stat)) {
529 			printf("gss_wrap failed\n");
530 			report_error(mech_type, maj_stat, min_stat);
531 			goto out;
532 		}
533 		break;
534 
535 	case 4:
536 		maj_stat = gss_unwrap(&min_stat, server_context,
537 		    input_token, &message_buf, NULL, NULL);
538 		if (GSS_ERROR(maj_stat)) {
539 			printf("gss_unwrap failed\n");
540 			report_error(mech_type, maj_stat, min_stat);
541 			goto out;
542 		}
543 		gss_release_buffer(&min_stat, &message_buf);
544 
545 		message_buf.length = strlen("Hello world");
546 		message_buf.value = (void *) "Hello world";
547 		maj_stat = gss_wrap(&min_stat, server_context,
548 		    FALSE, GSS_C_QOP_DEFAULT, &message_buf, NULL, output_token);
549 		if (GSS_ERROR(maj_stat)) {
550 			printf("gss_wrap failed\n");
551 			report_error(mech_type, maj_stat, min_stat);
552 			goto out;
553 		}
554 		break;
555 
556 	case 5:
557 		error = 0;
558 		goto out;
559 	}
560 	*maj_stat_res = maj_stat;
561 	*min_stat_res = min_stat;
562 	return (0);
563 
564 out:
565 	*maj_stat_res = maj_stat;
566 	*min_stat_res = min_stat;
567 	if (server_context)
568 		gss_delete_sec_context(&min_stat, &server_context,
569 		    GSS_C_NO_BUFFER);
570 	if (server_cred)
571 		gss_release_cred(&min_stat, &server_cred);
572 	if (name)
573 		gss_release_name(&min_stat, &name);
574 
575 	return (error);
576 }
577 
578 /*
579  * Create an RPC client handle for the given (address,prog,vers)
580  * triple using UDP.
581  */
582 static CLIENT *
583 gsstest_get_rpc(struct sockaddr *sa, rpcprog_t prog, rpcvers_t vers)
584 {
585 	struct thread *td = curthread;
586 	const char* protofmly;
587 	struct sockaddr_storage ss;
588 	struct socket *so;
589 	CLIENT *rpcb;
590 	struct timeval timo;
591 	RPCB parms;
592 	char *uaddr;
593 	enum clnt_stat stat = RPC_SUCCESS;
594 	int rpcvers = RPCBVERS4;
595 	bool_t do_tcp = FALSE;
596 	struct portmap mapping;
597 	u_short port = 0;
598 
599 	/*
600 	 * First we need to contact the remote RPCBIND service to find
601 	 * the right port.
602 	 */
603 	memcpy(&ss, sa, sa->sa_len);
604 	switch (ss.ss_family) {
605 	case AF_INET:
606 		((struct sockaddr_in *)&ss)->sin_port = htons(111);
607 		protofmly = "inet";
608 		socreate(AF_INET, &so, SOCK_DGRAM, 0, td->td_ucred, td);
609 		break;
610 
611 #ifdef INET6
612 	case AF_INET6:
613 		((struct sockaddr_in6 *)&ss)->sin6_port = htons(111);
614 		protofmly = "inet6";
615 		socreate(AF_INET6, &so, SOCK_DGRAM, 0, td->td_ucred, td);
616 		break;
617 #endif
618 
619 	default:
620 		/*
621 		 * Unsupported address family - fail.
622 		 */
623 		return (NULL);
624 	}
625 
626 	rpcb = clnt_dg_create(so, (struct sockaddr *)&ss,
627 	    RPCBPROG, rpcvers, 0, 0);
628 	if (!rpcb)
629 		return (NULL);
630 
631 try_tcp:
632 	parms.r_prog = prog;
633 	parms.r_vers = vers;
634 	if (do_tcp)
635 		parms.r_netid = "tcp";
636 	else
637 		parms.r_netid = "udp";
638 	parms.r_addr = "";
639 	parms.r_owner = "";
640 
641 	/*
642 	 * Use the default timeout.
643 	 */
644 	timo.tv_sec = 25;
645 	timo.tv_usec = 0;
646 again:
647 	switch (rpcvers) {
648 	case RPCBVERS4:
649 	case RPCBVERS:
650 		/*
651 		 * Try RPCBIND 4 then 3.
652 		 */
653 		uaddr = NULL;
654 		stat = CLNT_CALL(rpcb, (rpcprog_t) RPCBPROC_GETADDR,
655 		    (xdrproc_t) xdr_rpcb, &parms,
656 		    (xdrproc_t) xdr_wrapstring, &uaddr, timo);
657 		if (stat == RPC_PROGVERSMISMATCH) {
658 			if (rpcvers == RPCBVERS4)
659 				rpcvers = RPCBVERS;
660 			else if (rpcvers == RPCBVERS)
661 				rpcvers = PMAPVERS;
662 			CLNT_CONTROL(rpcb, CLSET_VERS, &rpcvers);
663 			goto again;
664 		} else if (stat == RPC_SUCCESS) {
665 			/*
666 			 * We have a reply from the remote RPCBIND - turn it
667 			 * into an appropriate address and make a new client
668 			 * that can talk to the remote service.
669 			 *
670 			 * XXX fixup IPv6 scope ID.
671 			 */
672 			struct netbuf *a;
673 			a = __rpc_uaddr2taddr_af(ss.ss_family, uaddr);
674 			xdr_free((xdrproc_t) xdr_wrapstring, &uaddr);
675 			if (!a) {
676 				CLNT_DESTROY(rpcb);
677 				return (NULL);
678 			}
679 			memcpy(&ss, a->buf, a->len);
680 			free(a->buf, M_RPC);
681 			free(a, M_RPC);
682 		}
683 		break;
684 	case PMAPVERS:
685 		/*
686 		 * Try portmap.
687 		 */
688 		mapping.pm_prog = parms.r_prog;
689 		mapping.pm_vers = parms.r_vers;
690 		mapping.pm_prot = do_tcp ? IPPROTO_TCP : IPPROTO_UDP;
691 		mapping.pm_port = 0;
692 
693 		stat = CLNT_CALL(rpcb, (rpcprog_t) PMAPPROC_GETPORT,
694 		    (xdrproc_t) xdr_portmap, &mapping,
695 		    (xdrproc_t) xdr_u_short, &port, timo);
696 
697 		if (stat == RPC_SUCCESS) {
698 			switch (ss.ss_family) {
699 			case AF_INET:
700 				((struct sockaddr_in *)&ss)->sin_port =
701 					htons(port);
702 				break;
703 
704 #ifdef INET6
705 			case AF_INET6:
706 				((struct sockaddr_in6 *)&ss)->sin6_port =
707 					htons(port);
708 				break;
709 #endif
710 			}
711 		}
712 		break;
713 	default:
714 		panic("invalid rpcvers %d", rpcvers);
715 	}
716 	/*
717 	 * We may have a positive response from the portmapper, but
718 	 * the requested service was not found. Make sure we received
719 	 * a valid port.
720 	 */
721 	switch (ss.ss_family) {
722 	case AF_INET:
723 		port = ((struct sockaddr_in *)&ss)->sin_port;
724 		break;
725 #ifdef INET6
726 	case AF_INET6:
727 		port = ((struct sockaddr_in6 *)&ss)->sin6_port;
728 		break;
729 #endif
730 	}
731 	if (stat != RPC_SUCCESS || !port) {
732 		/*
733 		 * If we were able to talk to rpcbind or portmap, but the udp
734 		 * variant wasn't available, ask about tcp.
735 		 *
736 		 * XXX - We could also check for a TCP portmapper, but
737 		 * if the host is running a portmapper at all, we should be able
738 		 * to hail it over UDP.
739 		 */
740 		if (stat == RPC_SUCCESS && !do_tcp) {
741 			do_tcp = TRUE;
742 			goto try_tcp;
743 		}
744 
745 		/* Otherwise, bad news. */
746 		printf("gsstest_get_rpc: failed to contact remote rpcbind, "
747 		    "stat = %d, port = %d\n",
748 		    (int) stat, port);
749 		CLNT_DESTROY(rpcb);
750 		return (NULL);
751 	}
752 
753 	if (do_tcp) {
754 		/*
755 		 * Destroy the UDP client we used to speak to rpcbind and
756 		 * recreate as a TCP client.
757 		 */
758 		struct netconfig *nconf = NULL;
759 
760 		CLNT_DESTROY(rpcb);
761 
762 		switch (ss.ss_family) {
763 		case AF_INET:
764 			nconf = getnetconfigent("tcp");
765 			break;
766 #ifdef INET6
767 		case AF_INET6:
768 			nconf = getnetconfigent("tcp6");
769 			break;
770 #endif
771 		}
772 
773 		rpcb = clnt_reconnect_create(nconf, (struct sockaddr *)&ss,
774 		    prog, vers, 0, 0);
775 	} else {
776 		/*
777 		 * Re-use the client we used to speak to rpcbind.
778 		 */
779 		CLNT_CONTROL(rpcb, CLSET_SVC_ADDR, &ss);
780 		CLNT_CONTROL(rpcb, CLSET_PROG, &prog);
781 		CLNT_CONTROL(rpcb, CLSET_VERS, &vers);
782 	}
783 
784 	return (rpcb);
785 }
786 
787 /*
788  * RPCSEC_GSS client
789  */
790 static int
791 gsstest_3(void)
792 {
793 	struct sockaddr_in sin;
794 	char service[128];
795 	CLIENT *client;
796 	AUTH *auth;
797 	rpc_gss_options_ret_t options_ret;
798 	enum clnt_stat stat;
799 	struct timeval tv;
800 	rpc_gss_service_t svc;
801 	int i;
802 
803 	sin.sin_len = sizeof(sin);
804 	sin.sin_family = AF_INET;
805 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
806 	sin.sin_port = 0;
807 
808 	client = gsstest_get_rpc((struct sockaddr *) &sin, 123456, 1);
809 	if (!client) {
810 		uprintf("Can't connect to service\n");
811 		return(1);
812 	}
813 
814 	snprintf(service, sizeof(service), "host@%s", hostname);
815 
816 	auth = rpc_gss_seccreate(client, curthread->td_ucred,
817 	    service, "kerberosv5", rpc_gss_svc_privacy,
818 	    NULL, NULL, &options_ret);
819 	if (!auth) {
820 		gss_OID oid;
821 		uprintf("Can't authorize to service (mech=%s)\n",
822 			options_ret.actual_mechanism);
823 		oid = GSS_C_NO_OID;
824 		rpc_gss_mech_to_oid(options_ret.actual_mechanism, &oid);
825 		report_error(oid, options_ret.major_status,
826 		    options_ret.minor_status);
827 		CLNT_DESTROY(client);
828 		return (1);
829 	}
830 
831 	for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) {
832 		const char *svc_names[] = {
833 			"rpc_gss_svc_default",
834 			"rpc_gss_svc_none",
835 			"rpc_gss_svc_integrity",
836 			"rpc_gss_svc_privacy"
837 		};
838 		int num;
839 
840 		rpc_gss_set_defaults(auth, svc, NULL);
841 
842 		client->cl_auth = auth;
843 		tv.tv_sec = 5;
844 		tv.tv_usec = 0;
845 		for (i = 42; i < 142; i++) {
846 			num = i;
847 			stat = CLNT_CALL(client, 1,
848 			    (xdrproc_t) xdr_int, (char *) &num,
849 			    (xdrproc_t) xdr_int, (char *) &num, tv);
850 			if (stat == RPC_SUCCESS) {
851 				if (num != i + 100)
852 					uprintf("unexpected reply %d\n", num);
853 			} else {
854 				uprintf("call failed, stat=%d\n", (int) stat);
855 				break;
856 			}
857 		}
858 		if (i == 142)
859 			uprintf("call succeeded with %s\n", svc_names[svc]);
860 	}
861 
862 	AUTH_DESTROY(auth);
863 	CLNT_RELEASE(client);
864 
865 	return (0);
866 }
867 
868 /*
869  * RPCSEC_GSS server
870  */
871 static rpc_gss_principal_t server_acl = NULL;
872 static bool_t server_new_context(struct svc_req *req, gss_cred_id_t deleg,
873     gss_ctx_id_t gss_context, rpc_gss_lock_t *lock, void **cookie);
874 static void server_program_1(struct svc_req *rqstp, register SVCXPRT *transp);
875 
876 static int
877 gsstest_4(void)
878 {
879 	SVCPOOL *pool;
880 	char principal[128 + 5];
881 	const char **mechs;
882 	static rpc_gss_callback_t cb;
883 
884 	snprintf(principal, sizeof(principal), "host@%s", hostname);
885 
886 	mechs = rpc_gss_get_mechanisms();
887 	while (*mechs) {
888 		if (!rpc_gss_set_svc_name(principal, *mechs, GSS_C_INDEFINITE,
889 			123456, 1)) {
890 			rpc_gss_error_t e;
891 
892 			rpc_gss_get_error(&e);
893 			printf("setting name for %s for %s failed: %d, %d\n",
894 			    principal, *mechs,
895 			    e.rpc_gss_error, e.system_error);
896 		}
897 		mechs++;
898 	}
899 
900 	cb.program = 123456;
901 	cb.version = 1;
902 	cb.callback = server_new_context;
903 	rpc_gss_set_callback(&cb);
904 
905 	pool = svcpool_create("gsstest", NULL);
906 
907 	svc_create(pool, server_program_1, 123456, 1, NULL);
908 	svc_run(pool);
909 
910 	rpc_gss_clear_svc_name(123456, 1);
911 	rpc_gss_clear_callback(&cb);
912 
913 	svcpool_destroy(pool);
914 
915 	return (0);
916 }
917 
918 static void
919 server_program_1(struct svc_req *rqstp, register SVCXPRT *transp)
920 {
921 	rpc_gss_rawcred_t *rcred;
922 	rpc_gss_ucred_t *ucred;
923 	int		i, num;
924 
925 	if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) {
926 		svcerr_weakauth(rqstp);
927 		return;
928 	}
929 
930 	if (!rpc_gss_getcred(rqstp, &rcred, &ucred, NULL)) {
931 		svcerr_systemerr(rqstp);
932 		return;
933 	}
934 
935 	printf("svc=%d, mech=%s, uid=%d, gid=%d, gids={",
936 	    rcred->service, rcred->mechanism, ucred->uid, ucred->gid);
937 	for (i = 0; i < ucred->gidlen; i++) {
938 		if (i > 0) printf(",");
939 		printf("%d", ucred->gidlist[i]);
940 	}
941 	printf("}\n");
942 
943 	switch (rqstp->rq_proc) {
944 	case 0:
945 		if (!svc_getargs(rqstp, (xdrproc_t) xdr_void, 0)) {
946 			svcerr_decode(rqstp);
947 			goto out;
948 		}
949 		if (!svc_sendreply(rqstp, (xdrproc_t) xdr_void, 0)) {
950 			svcerr_systemerr(rqstp);
951 		}
952 		goto out;
953 
954 	case 1:
955 		if (!svc_getargs(rqstp, (xdrproc_t) xdr_int,
956 			(char *) &num)) {
957 			svcerr_decode(rqstp);
958 			goto out;
959 		}
960 		num += 100;
961 		if (!svc_sendreply(rqstp, (xdrproc_t) xdr_int,
962 			(char *) &num)) {
963 			svcerr_systemerr(rqstp);
964 		}
965 		goto out;
966 
967 	default:
968 		svcerr_noproc(rqstp);
969 		goto out;
970 	}
971 
972 out:
973 	svc_freereq(rqstp);
974 	return;
975 }
976 
977 static void
978 print_principal(rpc_gss_principal_t principal)
979 {
980 	int i, len, n;
981 	uint8_t *p;
982 
983 	len = principal->len;
984 	p = (uint8_t *) principal->name;
985 	while (len > 0) {
986 		n = len;
987 		if (n > 16)
988 			n = 16;
989 		for (i = 0; i < n; i++)
990 			printf("%02x ", p[i]);
991 		for (; i < 16; i++)
992 			printf("   ");
993 		printf("|");
994 		for (i = 0; i < n; i++)
995 			printf("%c", isprint(p[i]) ? p[i] : '.');
996 		printf("|\n");
997 		len -= n;
998 		p += n;
999 	}
1000 }
1001 
1002 static bool_t
1003 server_new_context(__unused struct svc_req *req,
1004     gss_cred_id_t deleg,
1005     __unused gss_ctx_id_t gss_context,
1006     rpc_gss_lock_t *lock,
1007     __unused void **cookie)
1008 {
1009 	rpc_gss_rawcred_t *rcred = lock->raw_cred;
1010 	OM_uint32 junk;
1011 
1012 	printf("new security context version=%d, mech=%s, qop=%s:\n",
1013 	    rcred->version, rcred->mechanism, rcred->qop);
1014 	print_principal(rcred->client_principal);
1015 
1016 	if (server_acl) {
1017 		if (rcred->client_principal->len != server_acl->len
1018 		    || memcmp(rcred->client_principal->name, server_acl->name,
1019 			server_acl->len)) {
1020 			return (FALSE);
1021 		}
1022 	}
1023 	gss_release_cred(&junk, &deleg);
1024 
1025 	return (TRUE);
1026 }
1027 
1028 /*
1029  * Hook up a syscall for gssapi testing.
1030  */
1031 
1032 struct gsstest_args {
1033         int a_op;
1034 	void *a_args;
1035 	void *a_res;
1036 };
1037 
1038 struct gsstest_2_args {
1039 	int step;		/* test step number */
1040 	gss_buffer_desc input_token; /* token from userland */
1041 	gss_buffer_desc output_token; /* buffer to receive reply token */
1042 };
1043 struct gsstest_2_res {
1044 	OM_uint32 maj_stat;	/* maj_stat from kernel */
1045 	OM_uint32 min_stat;	/* min_stat from kernel */
1046 	gss_buffer_desc output_token; /* reply token (using space from gsstest_2_args.output) */
1047 };
1048 
1049 static int
1050 gsstest(struct thread *td, struct gsstest_args *uap)
1051 {
1052 	int error;
1053 
1054 	switch (uap->a_op) {
1055 	case 1:
1056                 return (gsstest_1());
1057 
1058 	case 2: {
1059 		struct gsstest_2_args args;
1060 		struct gsstest_2_res res;
1061 		gss_buffer_desc input_token, output_token;
1062 		OM_uint32 junk;
1063 
1064 		error = copyin(uap->a_args, &args, sizeof(args));
1065 		if (error)
1066 			return (error);
1067 		input_token.length = args.input_token.length;
1068 		input_token.value = malloc(input_token.length, M_GSSAPI,
1069 		    M_WAITOK);
1070 		error = copyin(args.input_token.value, input_token.value,
1071 		    input_token.length);
1072 		if (error) {
1073 			gss_release_buffer(&junk, &input_token);
1074 			return (error);
1075 		}
1076 		output_token.length = 0;
1077 		output_token.value = NULL;
1078 		gsstest_2(args.step, &input_token,
1079 		    &res.maj_stat, &res.min_stat, &output_token);
1080 		gss_release_buffer(&junk, &input_token);
1081 		if (output_token.length > args.output_token.length) {
1082 			gss_release_buffer(&junk, &output_token);
1083 			return (EOVERFLOW);
1084 		}
1085 		res.output_token.length = output_token.length;
1086 		res.output_token.value = args.output_token.value;
1087 		error = copyout(output_token.value, res.output_token.value,
1088 		    output_token.length);
1089 		gss_release_buffer(&junk, &output_token);
1090 		if (error)
1091 			return (error);
1092 
1093 		return (copyout(&res, uap->a_res, sizeof(res)));
1094 
1095 		break;
1096 	}
1097 	case 3:
1098 		return (gsstest_3());
1099 	case 4:
1100 		return (gsstest_4());
1101 	}
1102 
1103         return (EINVAL);
1104 }
1105 
1106 /*
1107  * The `sysent' for the new syscall
1108  */
1109 static struct sysent gsstest_sysent = {
1110         3,                      /* sy_narg */
1111         (sy_call_t *) gsstest	/* sy_call */
1112 };
1113 
1114 /*
1115  * The offset in sysent where the syscall is allocated.
1116  */
1117 static int gsstest_offset = NO_SYSCALL;
1118 
1119 /*
1120  * The function called at load/unload.
1121  */
1122 
1123 
1124 static int
1125 gsstest_load(struct module *module, int cmd, void *arg)
1126 {
1127         int error = 0;
1128 
1129         switch (cmd) {
1130         case MOD_LOAD :
1131                 break;
1132         case MOD_UNLOAD :
1133                 break;
1134         default :
1135                 error = EOPNOTSUPP;
1136                 break;
1137         }
1138         return error;
1139 }
1140 
1141 SYSCALL_MODULE(gsstest_syscall, &gsstest_offset, &gsstest_sysent,
1142     gsstest_load, NULL);
1143