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