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 433 case 1: 434 if (server_context == GSS_C_NO_CONTEXT) { 435 static char sbuf[512]; 436 memcpy(sbuf, "nfs@", 4); 437 getcredhostname(td->td_ucred, sbuf + 4, 438 sizeof(sbuf) - 4); 439 name_desc.value = sbuf; 440 name_desc.length = strlen((const char *) 441 name_desc.value); 442 maj_stat = gss_import_name(&min_stat, &name_desc, 443 GSS_C_NT_HOSTBASED_SERVICE, &name); 444 if (GSS_ERROR(maj_stat)) { 445 printf("gss_import_name failed\n"); 446 report_error(mech_type, maj_stat, min_stat); 447 goto out; 448 } 449 450 maj_stat = gss_acquire_cred(&min_stat, 451 name, 0, GSS_C_NO_OID_SET, GSS_C_ACCEPT, 452 &server_cred, NULL, NULL); 453 if (GSS_ERROR(maj_stat)) { 454 printf("gss_acquire_cred (server) failed\n"); 455 report_error(mech_type, maj_stat, min_stat); 456 goto out; 457 } 458 459 enctype[0] = (ETYPE_DES_CBC_CRC >> 24) & 0xff; 460 enctype[1] = (ETYPE_DES_CBC_CRC >> 16) & 0xff; 461 enctype[2] = (ETYPE_DES_CBC_CRC >> 8) & 0xff; 462 enctype[3] = ETYPE_DES_CBC_CRC & 0xff; 463 message_buf.length = sizeof(enctype); 464 message_buf.value = enctype; 465 maj_stat = gss_set_cred_option(&min_stat, &server_cred, 466 GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, &message_buf); 467 if (GSS_ERROR(maj_stat)) { 468 printf("gss_set_cred_option failed\n"); 469 report_error(mech_type, maj_stat, min_stat); 470 goto out; 471 } 472 } 473 474 maj_stat = gss_accept_sec_context(&min_stat, 475 &server_context, 476 server_cred, 477 input_token, 478 GSS_C_NO_CHANNEL_BINDINGS, 479 NULL, 480 &mech_type, 481 output_token, 482 NULL, 483 NULL, 484 NULL); 485 if (GSS_ERROR(maj_stat)) { 486 printf("gss_accept_sec_context failed\n"); 487 report_error(mech_type, maj_stat, min_stat); 488 goto out; 489 } 490 491 if (maj_stat == GSS_S_COMPLETE) { 492 context_established = 1; 493 } 494 *maj_stat_res = maj_stat; 495 *min_stat_res = min_stat; 496 break; 497 498 case 2: 499 message_buf.length = strlen("Hello world"); 500 message_buf.value = (void *) "Hello world"; 501 502 maj_stat = gss_verify_mic(&min_stat, server_context, 503 &message_buf, input_token, NULL); 504 if (GSS_ERROR(maj_stat)) { 505 printf("gss_verify_mic failed\n"); 506 report_error(mech_type, maj_stat, min_stat); 507 goto out; 508 } 509 510 maj_stat = gss_get_mic(&min_stat, server_context, 511 GSS_C_QOP_DEFAULT, &message_buf, output_token); 512 if (GSS_ERROR(maj_stat)) { 513 printf("gss_get_mic failed\n"); 514 report_error(mech_type, maj_stat, min_stat); 515 goto out; 516 } 517 break; 518 519 case 3: 520 maj_stat = gss_unwrap(&min_stat, server_context, 521 input_token, &message_buf, NULL, NULL); 522 if (GSS_ERROR(maj_stat)) { 523 printf("gss_unwrap failed\n"); 524 report_error(mech_type, maj_stat, min_stat); 525 goto out; 526 } 527 gss_release_buffer(&min_stat, &message_buf); 528 529 message_buf.length = strlen("Hello world"); 530 message_buf.value = (void *) "Hello world"; 531 maj_stat = gss_wrap(&min_stat, server_context, 532 TRUE, GSS_C_QOP_DEFAULT, &message_buf, NULL, output_token); 533 if (GSS_ERROR(maj_stat)) { 534 printf("gss_wrap failed\n"); 535 report_error(mech_type, maj_stat, min_stat); 536 goto out; 537 } 538 break; 539 540 case 4: 541 maj_stat = gss_unwrap(&min_stat, server_context, 542 input_token, &message_buf, NULL, NULL); 543 if (GSS_ERROR(maj_stat)) { 544 printf("gss_unwrap failed\n"); 545 report_error(mech_type, maj_stat, min_stat); 546 goto out; 547 } 548 gss_release_buffer(&min_stat, &message_buf); 549 550 message_buf.length = strlen("Hello world"); 551 message_buf.value = (void *) "Hello world"; 552 maj_stat = gss_wrap(&min_stat, server_context, 553 FALSE, GSS_C_QOP_DEFAULT, &message_buf, NULL, output_token); 554 if (GSS_ERROR(maj_stat)) { 555 printf("gss_wrap failed\n"); 556 report_error(mech_type, maj_stat, min_stat); 557 goto out; 558 } 559 break; 560 561 case 5: 562 error = 0; 563 goto out; 564 } 565 *maj_stat_res = maj_stat; 566 *min_stat_res = min_stat; 567 return (0); 568 569 out: 570 *maj_stat_res = maj_stat; 571 *min_stat_res = min_stat; 572 if (server_context) 573 gss_delete_sec_context(&min_stat, &server_context, 574 GSS_C_NO_BUFFER); 575 if (server_cred) 576 gss_release_cred(&min_stat, &server_cred); 577 if (name) 578 gss_release_name(&min_stat, &name); 579 580 return (error); 581 } 582 583 /* 584 * Create an RPC client handle for the given (address,prog,vers) 585 * triple using UDP. 586 */ 587 static CLIENT * 588 gsstest_get_rpc(struct sockaddr *sa, rpcprog_t prog, rpcvers_t vers) 589 { 590 struct thread *td = curthread; 591 const char* protofmly; 592 struct sockaddr_storage ss; 593 struct socket *so; 594 CLIENT *rpcb; 595 struct timeval timo; 596 RPCB parms; 597 char *uaddr; 598 enum clnt_stat stat = RPC_SUCCESS; 599 int rpcvers = RPCBVERS4; 600 bool_t do_tcp = FALSE; 601 struct portmap mapping; 602 u_short port = 0; 603 604 /* 605 * First we need to contact the remote RPCBIND service to find 606 * the right port. 607 */ 608 memcpy(&ss, sa, sa->sa_len); 609 switch (ss.ss_family) { 610 case AF_INET: 611 ((struct sockaddr_in *)&ss)->sin_port = htons(111); 612 protofmly = "inet"; 613 socreate(AF_INET, &so, SOCK_DGRAM, 0, td->td_ucred, td); 614 break; 615 616 #ifdef INET6 617 case AF_INET6: 618 ((struct sockaddr_in6 *)&ss)->sin6_port = htons(111); 619 protofmly = "inet6"; 620 socreate(AF_INET6, &so, SOCK_DGRAM, 0, td->td_ucred, td); 621 break; 622 #endif 623 624 default: 625 /* 626 * Unsupported address family - fail. 627 */ 628 return (NULL); 629 } 630 631 rpcb = clnt_dg_create(so, (struct sockaddr *)&ss, 632 RPCBPROG, rpcvers, 0, 0); 633 if (!rpcb) 634 return (NULL); 635 636 try_tcp: 637 parms.r_prog = prog; 638 parms.r_vers = vers; 639 if (do_tcp) 640 parms.r_netid = "tcp"; 641 else 642 parms.r_netid = "udp"; 643 parms.r_addr = ""; 644 parms.r_owner = ""; 645 646 /* 647 * Use the default timeout. 648 */ 649 timo.tv_sec = 25; 650 timo.tv_usec = 0; 651 again: 652 switch (rpcvers) { 653 case RPCBVERS4: 654 case RPCBVERS: 655 /* 656 * Try RPCBIND 4 then 3. 657 */ 658 uaddr = NULL; 659 stat = CLNT_CALL(rpcb, (rpcprog_t) RPCBPROC_GETADDR, 660 (xdrproc_t) xdr_rpcb, &parms, 661 (xdrproc_t) xdr_wrapstring, &uaddr, timo); 662 if (stat == RPC_PROGVERSMISMATCH) { 663 if (rpcvers == RPCBVERS4) 664 rpcvers = RPCBVERS; 665 else if (rpcvers == RPCBVERS) 666 rpcvers = PMAPVERS; 667 CLNT_CONTROL(rpcb, CLSET_VERS, &rpcvers); 668 goto again; 669 } else if (stat == RPC_SUCCESS) { 670 /* 671 * We have a reply from the remote RPCBIND - turn it 672 * into an appropriate address and make a new client 673 * that can talk to the remote service. 674 * 675 * XXX fixup IPv6 scope ID. 676 */ 677 struct netbuf *a; 678 a = __rpc_uaddr2taddr_af(ss.ss_family, uaddr); 679 xdr_free((xdrproc_t) xdr_wrapstring, &uaddr); 680 if (!a) { 681 CLNT_DESTROY(rpcb); 682 return (NULL); 683 } 684 memcpy(&ss, a->buf, a->len); 685 free(a->buf, M_RPC); 686 free(a, M_RPC); 687 } 688 break; 689 case PMAPVERS: 690 /* 691 * Try portmap. 692 */ 693 mapping.pm_prog = parms.r_prog; 694 mapping.pm_vers = parms.r_vers; 695 mapping.pm_prot = do_tcp ? IPPROTO_TCP : IPPROTO_UDP; 696 mapping.pm_port = 0; 697 698 stat = CLNT_CALL(rpcb, (rpcprog_t) PMAPPROC_GETPORT, 699 (xdrproc_t) xdr_portmap, &mapping, 700 (xdrproc_t) xdr_u_short, &port, timo); 701 702 if (stat == RPC_SUCCESS) { 703 switch (ss.ss_family) { 704 case AF_INET: 705 ((struct sockaddr_in *)&ss)->sin_port = 706 htons(port); 707 break; 708 709 #ifdef INET6 710 case AF_INET6: 711 ((struct sockaddr_in6 *)&ss)->sin6_port = 712 htons(port); 713 break; 714 #endif 715 } 716 } 717 break; 718 default: 719 panic("invalid rpcvers %d", rpcvers); 720 } 721 /* 722 * We may have a positive response from the portmapper, but 723 * the requested service was not found. Make sure we received 724 * a valid port. 725 */ 726 switch (ss.ss_family) { 727 case AF_INET: 728 port = ((struct sockaddr_in *)&ss)->sin_port; 729 break; 730 #ifdef INET6 731 case AF_INET6: 732 port = ((struct sockaddr_in6 *)&ss)->sin6_port; 733 break; 734 #endif 735 } 736 if (stat != RPC_SUCCESS || !port) { 737 /* 738 * If we were able to talk to rpcbind or portmap, but the udp 739 * variant wasn't available, ask about tcp. 740 * 741 * XXX - We could also check for a TCP portmapper, but 742 * if the host is running a portmapper at all, we should be able 743 * to hail it over UDP. 744 */ 745 if (stat == RPC_SUCCESS && !do_tcp) { 746 do_tcp = TRUE; 747 goto try_tcp; 748 } 749 750 /* Otherwise, bad news. */ 751 printf("gsstest_get_rpc: failed to contact remote rpcbind, " 752 "stat = %d, port = %d\n", 753 (int) stat, port); 754 CLNT_DESTROY(rpcb); 755 return (NULL); 756 } 757 758 if (do_tcp) { 759 /* 760 * Destroy the UDP client we used to speak to rpcbind and 761 * recreate as a TCP client. 762 */ 763 struct netconfig *nconf = NULL; 764 765 CLNT_DESTROY(rpcb); 766 767 switch (ss.ss_family) { 768 case AF_INET: 769 nconf = getnetconfigent("tcp"); 770 break; 771 #ifdef INET6 772 case AF_INET6: 773 nconf = getnetconfigent("tcp6"); 774 break; 775 #endif 776 } 777 778 rpcb = clnt_reconnect_create(nconf, (struct sockaddr *)&ss, 779 prog, vers, 0, 0); 780 } else { 781 /* 782 * Re-use the client we used to speak to rpcbind. 783 */ 784 CLNT_CONTROL(rpcb, CLSET_SVC_ADDR, &ss); 785 CLNT_CONTROL(rpcb, CLSET_PROG, &prog); 786 CLNT_CONTROL(rpcb, CLSET_VERS, &vers); 787 } 788 789 return (rpcb); 790 } 791 792 /* 793 * RPCSEC_GSS client 794 */ 795 static int 796 gsstest_3(struct thread *td) 797 { 798 struct sockaddr_in sin; 799 char service[128]; 800 CLIENT *client; 801 AUTH *auth; 802 rpc_gss_options_ret_t options_ret; 803 enum clnt_stat stat; 804 struct timeval tv; 805 rpc_gss_service_t svc; 806 int i; 807 808 sin.sin_len = sizeof(sin); 809 sin.sin_family = AF_INET; 810 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 811 sin.sin_port = 0; 812 813 client = gsstest_get_rpc((struct sockaddr *) &sin, 123456, 1); 814 if (!client) { 815 uprintf("Can't connect to service\n"); 816 return(1); 817 } 818 819 memcpy(service, "host@", 5); 820 getcredhostname(td->td_ucred, service + 5, sizeof(service) - 5); 821 822 auth = rpc_gss_seccreate(client, curthread->td_ucred, 823 service, "kerberosv5", rpc_gss_svc_privacy, 824 NULL, NULL, &options_ret); 825 if (!auth) { 826 gss_OID oid; 827 uprintf("Can't authorize to service (mech=%s)\n", 828 options_ret.actual_mechanism); 829 oid = GSS_C_NO_OID; 830 rpc_gss_mech_to_oid(options_ret.actual_mechanism, &oid); 831 report_error(oid, options_ret.major_status, 832 options_ret.minor_status); 833 CLNT_DESTROY(client); 834 return (1); 835 } 836 837 for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) { 838 const char *svc_names[] = { 839 "rpc_gss_svc_default", 840 "rpc_gss_svc_none", 841 "rpc_gss_svc_integrity", 842 "rpc_gss_svc_privacy" 843 }; 844 int num; 845 846 rpc_gss_set_defaults(auth, svc, NULL); 847 848 client->cl_auth = auth; 849 tv.tv_sec = 5; 850 tv.tv_usec = 0; 851 for (i = 42; i < 142; i++) { 852 num = i; 853 stat = CLNT_CALL(client, 1, 854 (xdrproc_t) xdr_int, (char *) &num, 855 (xdrproc_t) xdr_int, (char *) &num, tv); 856 if (stat == RPC_SUCCESS) { 857 if (num != i + 100) 858 uprintf("unexpected reply %d\n", num); 859 } else { 860 uprintf("call failed, stat=%d\n", (int) stat); 861 break; 862 } 863 } 864 if (i == 142) 865 uprintf("call succeeded with %s\n", svc_names[svc]); 866 } 867 868 AUTH_DESTROY(auth); 869 CLNT_RELEASE(client); 870 871 return (0); 872 } 873 874 /* 875 * RPCSEC_GSS server 876 */ 877 static rpc_gss_principal_t server_acl = NULL; 878 static bool_t server_new_context(struct svc_req *req, gss_cred_id_t deleg, 879 gss_ctx_id_t gss_context, rpc_gss_lock_t *lock, void **cookie); 880 static void server_program_1(struct svc_req *rqstp, register SVCXPRT *transp); 881 882 static int 883 gsstest_4(struct thread *td) 884 { 885 SVCPOOL *pool; 886 char principal[128 + 5]; 887 const char **mechs; 888 static rpc_gss_callback_t cb; 889 890 memcpy(principal, "host@", 5); 891 getcredhostname(td->td_ucred, principal + 5, sizeof(principal) - 5); 892 893 mechs = rpc_gss_get_mechanisms(); 894 while (*mechs) { 895 if (!rpc_gss_set_svc_name(principal, *mechs, GSS_C_INDEFINITE, 896 123456, 1)) { 897 rpc_gss_error_t e; 898 899 rpc_gss_get_error(&e); 900 printf("setting name for %s for %s failed: %d, %d\n", 901 principal, *mechs, 902 e.rpc_gss_error, e.system_error); 903 } 904 mechs++; 905 } 906 907 cb.program = 123456; 908 cb.version = 1; 909 cb.callback = server_new_context; 910 rpc_gss_set_callback(&cb); 911 912 pool = svcpool_create("gsstest", NULL); 913 914 svc_create(pool, server_program_1, 123456, 1, NULL); 915 svc_run(pool); 916 917 rpc_gss_clear_svc_name(123456, 1); 918 rpc_gss_clear_callback(&cb); 919 920 svcpool_destroy(pool); 921 922 return (0); 923 } 924 925 static void 926 server_program_1(struct svc_req *rqstp, register SVCXPRT *transp) 927 { 928 rpc_gss_rawcred_t *rcred; 929 rpc_gss_ucred_t *ucred; 930 int i, num; 931 932 if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) { 933 svcerr_weakauth(rqstp); 934 return; 935 } 936 937 if (!rpc_gss_getcred(rqstp, &rcred, &ucred, NULL)) { 938 svcerr_systemerr(rqstp); 939 return; 940 } 941 942 printf("svc=%d, mech=%s, uid=%d, gid=%d, gids={", 943 rcred->service, rcred->mechanism, ucred->uid, ucred->gid); 944 for (i = 0; i < ucred->gidlen; i++) { 945 if (i > 0) printf(","); 946 printf("%d", ucred->gidlist[i]); 947 } 948 printf("}\n"); 949 950 switch (rqstp->rq_proc) { 951 case 0: 952 if (!svc_getargs(rqstp, (xdrproc_t) xdr_void, 0)) { 953 svcerr_decode(rqstp); 954 goto out; 955 } 956 if (!svc_sendreply(rqstp, (xdrproc_t) xdr_void, 0)) { 957 svcerr_systemerr(rqstp); 958 } 959 goto out; 960 961 case 1: 962 if (!svc_getargs(rqstp, (xdrproc_t) xdr_int, 963 (char *) &num)) { 964 svcerr_decode(rqstp); 965 goto out; 966 } 967 num += 100; 968 if (!svc_sendreply(rqstp, (xdrproc_t) xdr_int, 969 (char *) &num)) { 970 svcerr_systemerr(rqstp); 971 } 972 goto out; 973 974 default: 975 svcerr_noproc(rqstp); 976 goto out; 977 } 978 979 out: 980 svc_freereq(rqstp); 981 return; 982 } 983 984 static void 985 print_principal(rpc_gss_principal_t principal) 986 { 987 int i, len, n; 988 uint8_t *p; 989 990 len = principal->len; 991 p = (uint8_t *) principal->name; 992 while (len > 0) { 993 n = len; 994 if (n > 16) 995 n = 16; 996 for (i = 0; i < n; i++) 997 printf("%02x ", p[i]); 998 for (; i < 16; i++) 999 printf(" "); 1000 printf("|"); 1001 for (i = 0; i < n; i++) 1002 printf("%c", isprint(p[i]) ? p[i] : '.'); 1003 printf("|\n"); 1004 len -= n; 1005 p += n; 1006 } 1007 } 1008 1009 static bool_t 1010 server_new_context(__unused struct svc_req *req, 1011 gss_cred_id_t deleg, 1012 __unused gss_ctx_id_t gss_context, 1013 rpc_gss_lock_t *lock, 1014 __unused void **cookie) 1015 { 1016 rpc_gss_rawcred_t *rcred = lock->raw_cred; 1017 OM_uint32 junk; 1018 1019 printf("new security context version=%d, mech=%s, qop=%s:\n", 1020 rcred->version, rcred->mechanism, rcred->qop); 1021 print_principal(rcred->client_principal); 1022 1023 if (server_acl) { 1024 if (rcred->client_principal->len != server_acl->len 1025 || memcmp(rcred->client_principal->name, server_acl->name, 1026 server_acl->len)) { 1027 return (FALSE); 1028 } 1029 } 1030 gss_release_cred(&junk, &deleg); 1031 1032 return (TRUE); 1033 } 1034 1035 /* 1036 * Hook up a syscall for gssapi testing. 1037 */ 1038 1039 struct gsstest_args { 1040 int a_op; 1041 void *a_args; 1042 void *a_res; 1043 }; 1044 1045 struct gsstest_2_args { 1046 int step; /* test step number */ 1047 gss_buffer_desc input_token; /* token from userland */ 1048 gss_buffer_desc output_token; /* buffer to receive reply token */ 1049 }; 1050 struct gsstest_2_res { 1051 OM_uint32 maj_stat; /* maj_stat from kernel */ 1052 OM_uint32 min_stat; /* min_stat from kernel */ 1053 gss_buffer_desc output_token; /* reply token (using space from gsstest_2_args.output) */ 1054 }; 1055 1056 static int 1057 gsstest(struct thread *td, struct gsstest_args *uap) 1058 { 1059 int error; 1060 1061 switch (uap->a_op) { 1062 case 1: 1063 return (gsstest_1(td)); 1064 1065 case 2: { 1066 struct gsstest_2_args args; 1067 struct gsstest_2_res res; 1068 gss_buffer_desc input_token, output_token; 1069 OM_uint32 junk; 1070 1071 error = copyin(uap->a_args, &args, sizeof(args)); 1072 if (error) 1073 return (error); 1074 input_token.length = args.input_token.length; 1075 input_token.value = malloc(input_token.length, M_GSSAPI, 1076 M_WAITOK); 1077 error = copyin(args.input_token.value, input_token.value, 1078 input_token.length); 1079 if (error) { 1080 gss_release_buffer(&junk, &input_token); 1081 return (error); 1082 } 1083 output_token.length = 0; 1084 output_token.value = NULL; 1085 gsstest_2(td, args.step, &input_token, 1086 &res.maj_stat, &res.min_stat, &output_token); 1087 gss_release_buffer(&junk, &input_token); 1088 if (output_token.length > args.output_token.length) { 1089 gss_release_buffer(&junk, &output_token); 1090 return (EOVERFLOW); 1091 } 1092 res.output_token.length = output_token.length; 1093 res.output_token.value = args.output_token.value; 1094 error = copyout(output_token.value, res.output_token.value, 1095 output_token.length); 1096 gss_release_buffer(&junk, &output_token); 1097 if (error) 1098 return (error); 1099 1100 return (copyout(&res, uap->a_res, sizeof(res))); 1101 1102 break; 1103 } 1104 case 3: 1105 return (gsstest_3(td)); 1106 case 4: 1107 return (gsstest_4(td)); 1108 } 1109 1110 return (EINVAL); 1111 } 1112 1113 /* 1114 * The `sysent' for the new syscall 1115 */ 1116 static struct sysent gsstest_sysent = { 1117 3, /* sy_narg */ 1118 (sy_call_t *) gsstest /* sy_call */ 1119 }; 1120 1121 /* 1122 * The offset in sysent where the syscall is allocated. 1123 */ 1124 static int gsstest_offset = NO_SYSCALL; 1125 1126 /* 1127 * The function called at load/unload. 1128 */ 1129 1130 1131 static int 1132 gsstest_load(struct module *module, int cmd, void *arg) 1133 { 1134 int error = 0; 1135 1136 switch (cmd) { 1137 case MOD_LOAD : 1138 break; 1139 case MOD_UNLOAD : 1140 break; 1141 default : 1142 error = EOPNOTSUPP; 1143 break; 1144 } 1145 return error; 1146 } 1147 1148 SYSCALL_MODULE(gsstest_syscall, &gsstest_offset, &gsstest_sysent, 1149 gsstest_load, NULL); 1150