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