1 /* 2 auth_gss.c 3 4 RPCSEC_GSS client routines. 5 6 Copyright (c) 2000 The Regents of the University of Michigan. 7 All rights reserved. 8 9 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 10 All rights reserved, all wrongs reversed. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in the 20 documentation and/or other materials provided with the distribution. 21 3. Neither the name of the University nor the names of its 22 contributors may be used to endorse or promote products derived 23 from this software without specific prior written permission. 24 25 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 26 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 27 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 32 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 33 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 34 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 35 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 37 */ 38 39 #include <stdio.h> 40 #include <stdlib.h> 41 //#include <unistd.h> 42 #include <string.h> 43 #include <errno.h> 44 #include <rpc/types.h> 45 #include <rpc/xdr.h> 46 #include <rpc/auth.h> 47 #include <rpc/auth_gss.h> 48 #include <rpc/clnt.h> 49 #include <netinet/in.h> 50 #include <gssapi/gssapi.h> 51 52 static void authgss_nextverf(); 53 static bool_t authgss_marshal(); 54 static bool_t authgss_refresh(); 55 static bool_t authgss_validate(); 56 static void authgss_destroy(); 57 static void authgss_destroy_context(); 58 static bool_t authgss_wrap(); 59 static bool_t authgss_unwrap(); 60 61 62 /* 63 * from mit-krb5-1.2.1 mechglue/mglueP.h: 64 * Array of context IDs typed by mechanism OID 65 */ 66 typedef struct gss_union_ctx_id_t { 67 gss_OID mech_type; 68 gss_ctx_id_t internal_ctx_id; 69 } gss_union_ctx_id_desc, *gss_union_ctx_id_t; 70 71 static struct auth_ops authgss_ops = { 72 authgss_nextverf, 73 authgss_marshal, 74 authgss_validate, 75 authgss_refresh, 76 authgss_destroy, 77 authgss_wrap, 78 authgss_unwrap 79 }; 80 81 #ifdef DEBUG 82 83 /* useful as i add more mechanisms */ 84 void 85 print_rpc_gss_sec(struct rpc_gss_sec *ptr) 86 { 87 int i; 88 char *p; 89 90 log_debug("rpc_gss_sec:"); 91 if(ptr->mech == NULL) 92 log_debug("NULL gss_OID mech"); 93 else { 94 fprintf(stderr, " mechanism_OID: {"); 95 p = (char *)ptr->mech->elements; 96 for (i=0; i < ptr->mech->length; i++) 97 /* First byte of OIDs encoded to save a byte */ 98 if (i == 0) { 99 int first, second; 100 if (*p < 40) { 101 first = 0; 102 second = *p; 103 } 104 else if (40 <= *p && *p < 80) { 105 first = 1; 106 second = *p - 40; 107 } 108 else if (80 <= *p && *p < 127) { 109 first = 2; 110 second = *p - 80; 111 } 112 else { 113 /* Invalid value! */ 114 first = -1; 115 second = -1; 116 } 117 fprintf(stderr, " %u %u", first, second); 118 p++; 119 } 120 else { 121 fprintf(stderr, " %u", (unsigned char)*p++); 122 } 123 fprintf(stderr, " }\n"); 124 } 125 fprintf(stderr, " qop: %d\n", ptr->qop); 126 fprintf(stderr, " service: %d\n", ptr->svc); 127 fprintf(stderr, " cred: %p\n", ptr->cred); 128 } 129 #endif /*DEBUG*/ 130 131 struct rpc_gss_data { 132 bool_t established; /* context established */ 133 gss_buffer_desc gc_wire_verf; /* save GSS_S_COMPLETE NULL RPC verfier 134 * to process at end of context negotiation*/ 135 CLIENT *clnt; /* client handle */ 136 gss_name_t name; /* service name */ 137 struct rpc_gss_sec sec; /* security tuple */ 138 gss_ctx_id_t ctx; /* context id */ 139 struct rpc_gss_cred gc; /* client credentials */ 140 u_int win; /* sequence window */ 141 }; 142 143 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private) 144 145 static struct timeval AUTH_TIMEOUT = { 25, 0 }; 146 147 AUTH * 148 authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec) 149 { 150 AUTH *auth, *save_auth; 151 struct rpc_gss_data *gd; 152 OM_uint32 min_stat = 0; 153 154 log_debug("in authgss_create()"); 155 156 memset(&rpc_createerr, 0, sizeof(rpc_createerr)); 157 158 if ((auth = calloc(sizeof(*auth), 1)) == NULL) { 159 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 160 rpc_createerr.cf_error.re_errno = ENOMEM; 161 return (NULL); 162 } 163 if ((gd = calloc(sizeof(*gd), 1)) == NULL) { 164 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 165 rpc_createerr.cf_error.re_errno = ENOMEM; 166 free(auth); 167 return (NULL); 168 } 169 #ifdef DEBUG 170 fprintf(stderr, "authgss_create: name is %p\n", name); 171 #endif 172 if (name != GSS_C_NO_NAME) { 173 if (gss_duplicate_name(&min_stat, name, &gd->name) 174 != GSS_S_COMPLETE) { 175 rpc_createerr.cf_stat = RPC_SYSTEMERROR; 176 rpc_createerr.cf_error.re_errno = ENOMEM; 177 free(auth); 178 return (NULL); 179 } 180 } 181 else 182 gd->name = name; 183 184 #ifdef DEBUG 185 fprintf(stderr, "authgss_create: gd->name is %p\n", gd->name); 186 #endif 187 gd->clnt = clnt; 188 gd->ctx = GSS_C_NO_CONTEXT; 189 gd->sec = *sec; 190 191 gd->gc.gc_v = RPCSEC_GSS_VERSION; 192 gd->gc.gc_proc = RPCSEC_GSS_INIT; 193 gd->gc.gc_svc = gd->sec.svc; 194 195 auth->ah_ops = &authgss_ops; 196 auth->ah_private = (caddr_t)gd; 197 198 save_auth = clnt->cl_auth; 199 clnt->cl_auth = auth; 200 201 if (!authgss_refresh(auth)) 202 auth = NULL; 203 204 clnt->cl_auth = save_auth; 205 206 return (auth); 207 } 208 209 AUTH * 210 authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec) 211 { 212 AUTH *auth; 213 OM_uint32 maj_stat = 0, min_stat = 0; 214 gss_buffer_desc sname; 215 gss_name_t name = GSS_C_NO_NAME; 216 217 log_debug("in authgss_create_default()"); 218 219 220 sname.value = service; 221 sname.length = strlen(service); 222 223 maj_stat = gss_import_name(&min_stat, &sname, 224 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, 225 &name); 226 227 if (maj_stat != GSS_S_COMPLETE) { 228 log_status("gss_import_name", maj_stat, min_stat); 229 rpc_createerr.cf_stat = RPC_AUTHERROR; 230 return (NULL); 231 } 232 233 auth = authgss_create(clnt, name, sec); 234 235 if (name != GSS_C_NO_NAME) { 236 #ifdef DEBUG 237 fprintf(stderr, "authgss_create_default: freeing name %p\n", name); 238 #endif 239 gss_release_name(&min_stat, &name); 240 } 241 242 return (auth); 243 } 244 245 bool_t 246 authgss_get_private_data(AUTH *auth, struct authgss_private_data *pd) 247 { 248 struct rpc_gss_data *gd; 249 250 log_debug("in authgss_get_private_data()"); 251 252 if (!auth || !pd) 253 return (FALSE); 254 255 gd = AUTH_PRIVATE(auth); 256 257 if (!gd || !gd->established) 258 return (FALSE); 259 260 pd->pd_ctx = gd->ctx; 261 pd->pd_ctx_hndl = gd->gc.gc_ctx; 262 pd->pd_seq_win = gd->win; 263 264 return (TRUE); 265 } 266 267 static void 268 authgss_nextverf(AUTH *auth) 269 { 270 log_debug("in authgss_nextverf()"); 271 /* no action necessary */ 272 } 273 274 static bool_t 275 authgss_marshal(AUTH *auth, XDR *xdrs) 276 { 277 XDR tmpxdrs; 278 char tmp[MAX_AUTH_BYTES]; 279 struct rpc_gss_data *gd; 280 gss_buffer_desc rpcbuf, checksum; 281 OM_uint32 maj_stat, min_stat; 282 bool_t xdr_stat; 283 284 log_debug("in authgss_marshal()"); 285 286 gd = AUTH_PRIVATE(auth); 287 288 if (gd->established) 289 gd->gc.gc_seq++; 290 291 xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE); 292 293 if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) { 294 XDR_DESTROY(&tmpxdrs); 295 return (FALSE); 296 } 297 auth->ah_cred.oa_flavor = RPCSEC_GSS; 298 auth->ah_cred.oa_base = tmp; 299 auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs); 300 301 XDR_DESTROY(&tmpxdrs); 302 303 if (!xdr_opaque_auth(xdrs, &auth->ah_cred)) 304 return (FALSE); 305 306 if (gd->gc.gc_proc == RPCSEC_GSS_INIT || 307 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) { 308 return (xdr_opaque_auth(xdrs, &_null_auth)); 309 } 310 /* Checksum serialized RPC header, up to and including credential. */ 311 rpcbuf.length = XDR_GETPOS(xdrs); 312 XDR_SETPOS(xdrs, 0); 313 rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length); 314 315 maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop, 316 &rpcbuf, &checksum); 317 318 if (maj_stat != GSS_S_COMPLETE) { 319 log_status("gss_get_mic", maj_stat, min_stat); 320 if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 321 gd->established = FALSE; 322 authgss_destroy_context(auth); 323 } 324 return (FALSE); 325 } 326 auth->ah_verf.oa_flavor = RPCSEC_GSS; 327 auth->ah_verf.oa_base = checksum.value; 328 auth->ah_verf.oa_length = checksum.length; 329 330 xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf); 331 gss_release_buffer(&min_stat, &checksum); 332 333 return (xdr_stat); 334 } 335 336 static bool_t 337 authgss_validate(AUTH *auth, struct opaque_auth *verf) 338 { 339 struct rpc_gss_data *gd; 340 u_int num, qop_state; 341 gss_buffer_desc signbuf, checksum; 342 OM_uint32 maj_stat, min_stat; 343 344 log_debug("in authgss_validate()"); 345 346 gd = AUTH_PRIVATE(auth); 347 348 if (gd->established == FALSE) { 349 /* would like to do this only on NULL rpc -- 350 * gc->established is good enough. 351 * save the on the wire verifier to validate last 352 * INIT phase packet after decode if the major 353 * status is GSS_S_COMPLETE 354 */ 355 if ((gd->gc_wire_verf.value = 356 mem_alloc(verf->oa_length)) == NULL) { 357 fprintf(stderr, "gss_validate: out of memory\n"); 358 return (FALSE); 359 } 360 memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length); 361 gd->gc_wire_verf.length = verf->oa_length; 362 return (TRUE); 363 } 364 365 if (gd->gc.gc_proc == RPCSEC_GSS_INIT || 366 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) { 367 num = htonl(gd->win); 368 } 369 else num = htonl(gd->gc.gc_seq); 370 371 signbuf.value = # 372 signbuf.length = sizeof(num); 373 374 checksum.value = verf->oa_base; 375 checksum.length = verf->oa_length; 376 377 maj_stat = gss_verify_mic(&min_stat, gd->ctx, &signbuf, 378 &checksum, &qop_state); 379 if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) { 380 log_status("gss_verify_mic", maj_stat, min_stat); 381 if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 382 gd->established = FALSE; 383 authgss_destroy_context(auth); 384 } 385 return (FALSE); 386 } 387 return (TRUE); 388 } 389 390 static bool_t 391 authgss_refresh(AUTH *auth) 392 { 393 struct rpc_gss_data *gd; 394 struct rpc_gss_init_res gr; 395 gss_buffer_desc *recv_tokenp, send_token; 396 OM_uint32 maj_stat, min_stat, call_stat, ret_flags; 397 398 log_debug("in authgss_refresh()"); 399 400 gd = AUTH_PRIVATE(auth); 401 402 if (gd->established) 403 return (TRUE); 404 405 /* GSS context establishment loop. */ 406 memset(&gr, 0, sizeof(gr)); 407 recv_tokenp = GSS_C_NO_BUFFER; 408 409 #ifdef DEBUG 410 print_rpc_gss_sec(&gd->sec); 411 #endif /*DEBUG*/ 412 413 for (;;) { 414 #ifdef DEBUG 415 /* print the token we just received */ 416 if (recv_tokenp != GSS_C_NO_BUFFER) { 417 log_debug("The token we just received (length %d):", 418 recv_tokenp->length); 419 log_hexdump(recv_tokenp->value, recv_tokenp->length, 0); 420 } 421 #endif 422 maj_stat = gss_init_sec_context(&min_stat, 423 gd->sec.cred, 424 &gd->ctx, 425 gd->name, 426 gd->sec.mech, 427 gd->sec.req_flags, 428 0, /* time req */ 429 NULL, /* channel */ 430 recv_tokenp, 431 NULL, /* used mech */ 432 &send_token, 433 &ret_flags, 434 NULL); /* time rec */ 435 436 if (recv_tokenp != GSS_C_NO_BUFFER) { 437 gss_release_buffer(&min_stat, &gr.gr_token); 438 recv_tokenp = GSS_C_NO_BUFFER; 439 } 440 if (maj_stat != GSS_S_COMPLETE && 441 maj_stat != GSS_S_CONTINUE_NEEDED) { 442 log_status("gss_init_sec_context", maj_stat, min_stat); 443 break; 444 } 445 if (send_token.length != 0) { 446 memset(&gr, 0, sizeof(gr)); 447 448 #ifdef DEBUG 449 /* print the token we are about to send */ 450 log_debug("The token being sent (length %d):", 451 send_token.length); 452 log_hexdump(send_token.value, send_token.length, 0); 453 #endif 454 455 call_stat = clnt_call(gd->clnt, NULLPROC, 456 (xdrproc_t)xdr_rpc_gss_init_args, 457 &send_token, 458 (xdrproc_t)xdr_rpc_gss_init_res, 459 (caddr_t)&gr, AUTH_TIMEOUT); 460 461 gss_release_buffer(&min_stat, &send_token); 462 463 if (call_stat != RPC_SUCCESS || 464 (gr.gr_major != GSS_S_COMPLETE && 465 gr.gr_major != GSS_S_CONTINUE_NEEDED)) 466 return FALSE; 467 468 if (gr.gr_ctx.length != 0) { 469 if (gd->gc.gc_ctx.value) 470 gss_release_buffer(&min_stat, 471 &gd->gc.gc_ctx); 472 gd->gc.gc_ctx = gr.gr_ctx; 473 } 474 if (gr.gr_token.length != 0) { 475 if (maj_stat != GSS_S_CONTINUE_NEEDED) 476 break; 477 recv_tokenp = &gr.gr_token; 478 } 479 gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT; 480 } 481 482 /* GSS_S_COMPLETE => check gss header verifier, 483 * usually checked in gss_validate 484 */ 485 if (maj_stat == GSS_S_COMPLETE) { 486 gss_buffer_desc bufin; 487 gss_buffer_desc bufout; 488 u_int seq, qop_state = 0; 489 490 seq = htonl(gr.gr_win); 491 bufin.value = (unsigned char *)&seq; 492 bufin.length = sizeof(seq); 493 bufout.value = (unsigned char *)gd->gc_wire_verf.value; 494 bufout.length = gd->gc_wire_verf.length; 495 496 maj_stat = gss_verify_mic(&min_stat, gd->ctx, 497 &bufin, &bufout, &qop_state); 498 499 if (maj_stat != GSS_S_COMPLETE 500 || qop_state != gd->sec.qop) { 501 log_status("gss_verify_mic", maj_stat, min_stat); 502 if (maj_stat == GSS_S_CONTEXT_EXPIRED) { 503 gd->established = FALSE; 504 authgss_destroy_context(auth); 505 } 506 return (FALSE); 507 } 508 gd->established = TRUE; 509 gd->gc.gc_proc = RPCSEC_GSS_DATA; 510 gd->gc.gc_seq = 0; 511 gd->win = gr.gr_win; 512 break; 513 } 514 } 515 /* End context negotiation loop. */ 516 if (gd->gc.gc_proc != RPCSEC_GSS_DATA) { 517 if (gr.gr_token.length != 0) 518 gss_release_buffer(&min_stat, &gr.gr_token); 519 520 authgss_destroy(auth); 521 auth = NULL; 522 rpc_createerr.cf_stat = RPC_AUTHERROR; 523 524 return (FALSE); 525 } 526 return (TRUE); 527 } 528 529 bool_t 530 authgss_service(AUTH *auth, int svc) 531 { 532 struct rpc_gss_data *gd; 533 534 log_debug("in authgss_service()"); 535 536 if (!auth) 537 return(FALSE); 538 gd = AUTH_PRIVATE(auth); 539 if (!gd || !gd->established) 540 return (FALSE); 541 gd->sec.svc = svc; 542 gd->gc.gc_svc = svc; 543 return (TRUE); 544 } 545 546 static void 547 authgss_destroy_context(AUTH *auth) 548 { 549 struct rpc_gss_data *gd; 550 OM_uint32 min_stat; 551 552 log_debug("in authgss_destroy_context()"); 553 554 gd = AUTH_PRIVATE(auth); 555 556 if (gd->gc.gc_ctx.length != 0) { 557 if (gd->established) { 558 gd->gc.gc_proc = RPCSEC_GSS_DESTROY; 559 clnt_call(gd->clnt, NULLPROC, (xdrproc_t)xdr_void, NULL, 560 (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT); 561 } 562 gss_release_buffer(&min_stat, &gd->gc.gc_ctx); 563 /* XXX ANDROS check size of context - should be 8 */ 564 memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx)); 565 } 566 if (gd->ctx != GSS_C_NO_CONTEXT) { 567 gss_delete_sec_context(&min_stat, &gd->ctx, NULL); 568 gd->ctx = GSS_C_NO_CONTEXT; 569 } 570 571 /* free saved wire verifier (if any) */ 572 mem_free(gd->gc_wire_verf.value, gd->gc_wire_verf.length); 573 gd->gc_wire_verf.value = NULL; 574 gd->gc_wire_verf.length = 0; 575 576 gd->established = FALSE; 577 } 578 579 static void 580 authgss_destroy(AUTH *auth) 581 { 582 struct rpc_gss_data *gd; 583 OM_uint32 min_stat; 584 585 log_debug("in authgss_destroy()"); 586 587 gd = AUTH_PRIVATE(auth); 588 589 authgss_destroy_context(auth); 590 591 #ifdef DEBUG 592 fprintf(stderr, "authgss_destroy: freeing name %p\n", gd->name); 593 #endif 594 if (gd->name != GSS_C_NO_NAME) 595 gss_release_name(&min_stat, &gd->name); 596 597 free(gd); 598 free(auth); 599 } 600 601 bool_t 602 authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 603 { 604 struct rpc_gss_data *gd; 605 606 log_debug("in authgss_wrap()"); 607 608 gd = AUTH_PRIVATE(auth); 609 610 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) { 611 return ((*xdr_func)(xdrs, xdr_ptr)); 612 } 613 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr, 614 gd->ctx, gd->sec.qop, 615 gd->sec.svc, gd->gc.gc_seq)); 616 } 617 618 bool_t 619 authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 620 { 621 struct rpc_gss_data *gd; 622 623 log_debug("in authgss_unwrap()"); 624 625 gd = AUTH_PRIVATE(auth); 626 627 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) { 628 return ((*xdr_func)(xdrs, xdr_ptr)); 629 } 630 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr, 631 gd->ctx, gd->sec.qop, 632 gd->sec.svc, gd->gc.gc_seq)); 633 } 634