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