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