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