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