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
report_error(gss_OID mech,OM_uint32 maj,OM_uint32 min)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
gsstest_1(struct thread * td)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
gsstest_2(struct thread * td,int step,const gss_buffer_t input_token,OM_uint32 * maj_stat_res,OM_uint32 * min_stat_res,gss_buffer_t output_token)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 *
gsstest_get_rpc(struct sockaddr * sa,rpcprog_t prog,rpcvers_t vers)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
gsstest_3(struct thread * td)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
gsstest_4(struct thread * td)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
server_program_1(struct svc_req * rqstp,register SVCXPRT * transp)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
print_principal(rpc_gss_principal_t principal)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
server_new_context(__unused struct svc_req * req,gss_cred_id_t deleg,__unused gss_ctx_id_t gss_context,rpc_gss_lock_t * lock,__unused void ** cookie)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
gsstest(struct thread * td,struct gsstest_args * uap)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
gsstest_load(struct module * module,int cmd,void * arg)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