1 /* 2 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. 3 * 4 * $Id$ 5 * 6 */ 7 8 /* 9 * svc_auth_gssapi.c 10 * Handles the GSS-API flavor authentication parameters on the service 11 * side of RPC. 12 */ 13 14 #include <stdio.h> 15 #include <errno.h> 16 #include <string.h> 17 #include <gssrpc/rpc.h> 18 #include <sys/stat.h> 19 20 #include <gssapi/gssapi_generic.h> 21 #include <gssrpc/auth_gssapi.h> 22 23 #ifdef GSS_BACKWARD_HACK 24 #include <gssapi/gssapi_krb5.h> 25 #endif 26 27 #include "gssrpcint.h" 28 29 #ifdef GSSAPI_KRB5 30 /* This is here for the krb5_error_code typedef and the 31 * KRB5KRB_AP_ERR_NOT_US #define.*/ 32 #include <krb5.h> 33 #endif 34 35 #include <sys/file.h> 36 #include <fcntl.h> 37 #include <time.h> 38 39 #define INITIATION_TIMEOUT 60*15 /* seconds until partially created */ 40 /* context is destroed */ 41 #define INDEF_EXPIRE 60*60*24 /* seconds until an context with no */ 42 /* expiration time is expired */ 43 44 #ifdef __CODECENTER__ 45 #define DEBUG_GSSAPI 1 46 #endif 47 48 #ifdef DEBUG_GSSAPI 49 int svc_debug_gssapi = DEBUG_GSSAPI; 50 void gssrpcint_printf(const char *format, ...) 51 { 52 va_list ap; 53 va_start(ap, format); 54 #if 1 55 vprintf(format, ap); 56 #else 57 { 58 static FILE *f; 59 if (f == NULL) 60 f = fopen("/dev/pts/4", "a"); 61 if (f) { 62 vfprintf(f, format, ap); 63 fflush(f); 64 } 65 } 66 #endif 67 va_end(ap); 68 } 69 #define L_PRINTF(l,args) if (svc_debug_gssapi >= l) gssrpcint_printf args 70 #define PRINTF(args) L_PRINTF(99, args) 71 #define AUTH_GSSAPI_DISPLAY_STATUS(args) \ 72 if (svc_debug_gssapi) auth_gssapi_display_status args 73 #else 74 #define PRINTF(args) 75 #define L_PRINTF(l, args) 76 #define AUTH_GSSAPI_DISPLAY_STATUS(args) 77 #endif 78 79 typedef struct _svc_auth_gssapi_data { 80 bool_t established; 81 82 gss_ctx_id_t context; 83 gss_name_t client_name, server_name; 84 gss_cred_id_t server_creds; 85 86 uint32_t expiration; 87 uint32_t seq_num; 88 uint32_t key; 89 90 SVCAUTH svcauth; 91 92 /* kludge to free verifiers on next call */ 93 gss_buffer_desc prev_verf; 94 } svc_auth_gssapi_data; 95 96 #define SVCAUTH_PRIVATE(auth) \ 97 ((svc_auth_gssapi_data *)(auth)->svc_ah_private) 98 99 static bool_t svc_auth_gssapi_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 100 static bool_t svc_auth_gssapi_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 101 static bool_t svc_auth_gssapi_destroy(SVCAUTH *); 102 103 static svc_auth_gssapi_data *create_client(void); 104 static svc_auth_gssapi_data *get_client 105 (gss_buffer_t client_handle); 106 static void destroy_client 107 (svc_auth_gssapi_data *client_data); 108 static void clean_client(void), cleanup(void); 109 static void client_expire 110 (svc_auth_gssapi_data *client_data, uint32_t exp); 111 static void dump_db (char *msg); 112 113 struct svc_auth_ops svc_auth_gssapi_ops = { 114 svc_auth_gssapi_wrap, 115 svc_auth_gssapi_unwrap, 116 svc_auth_gssapi_destroy 117 }; 118 119 /* 120 * Globals! Eeek! Run for the hills! 121 */ 122 static gss_cred_id_t *server_creds_list = NULL; 123 static gss_name_t *server_name_list = NULL; 124 static int server_creds_count = 0; 125 126 static auth_gssapi_log_badauth_func log_badauth = NULL; 127 static caddr_t log_badauth_data = NULL; 128 static auth_gssapi_log_badauth2_func log_badauth2 = NULL; 129 static caddr_t log_badauth2_data = NULL; 130 static auth_gssapi_log_badverf_func log_badverf = NULL; 131 static caddr_t log_badverf_data = NULL; 132 static auth_gssapi_log_miscerr_func log_miscerr = NULL; 133 static caddr_t log_miscerr_data = NULL; 134 135 #define LOG_MISCERR(arg) if (log_miscerr) \ 136 (*log_miscerr)(rqst, msg, arg, log_miscerr_data) 137 138 typedef struct _client_list { 139 svc_auth_gssapi_data *client; 140 struct _client_list *next; 141 } client_list; 142 143 static client_list *clients = NULL; 144 145 146 /* Invoke log_badauth callbacks for an authentication failure. */ 147 static void 148 badauth(OM_uint32 maj, OM_uint32 minor, SVCXPRT *xprt) 149 { 150 if (log_badauth != NULL) 151 (*log_badauth)(maj, minor, &xprt->xp_raddr, log_badauth_data); 152 if (log_badauth2 != NULL) 153 (*log_badauth2)(maj, minor, xprt, log_badauth2_data); 154 } 155 156 enum auth_stat gssrpc__svcauth_gssapi( 157 struct svc_req *rqst, 158 struct rpc_msg *msg, 159 bool_t *no_dispatch) 160 { 161 XDR xdrs; 162 auth_gssapi_creds creds; 163 auth_gssapi_init_arg call_arg; 164 auth_gssapi_init_res call_res; 165 gss_buffer_desc output_token, in_buf, out_buf; 166 gss_cred_id_t server_creds; 167 struct gss_channel_bindings_struct bindings, *bindp; 168 OM_uint32 gssstat, minor_stat, time_rec; 169 struct opaque_auth *cred, *verf; 170 svc_auth_gssapi_data *client_data; 171 int i; 172 enum auth_stat ret; 173 OM_uint32 ret_flags; 174 uint32_t seq_num; 175 176 PRINTF(("svcauth_gssapi: starting\n")); 177 178 /* clean up expired entries */ 179 clean_client(); 180 181 /* use AUTH_NONE until there is a client_handle */ 182 rqst->rq_xprt->xp_auth = &svc_auth_none; 183 184 memset(&call_res, 0, sizeof(call_res)); 185 creds.client_handle.length = 0; 186 creds.client_handle.value = NULL; 187 188 cred = &msg->rm_call.cb_cred; 189 verf = &msg->rm_call.cb_verf; 190 191 if (cred->oa_length == 0) { 192 PRINTF(("svcauth_gssapi: empty creds, failing\n")); 193 LOG_MISCERR("empty client credentials"); 194 ret = AUTH_BADCRED; 195 goto error; 196 } 197 198 PRINTF(("svcauth_gssapi: decoding credentials\n")); 199 xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE); 200 memset(&creds, 0, sizeof(creds)); 201 if (! xdr_authgssapi_creds(&xdrs, &creds)) { 202 PRINTF(("svcauth_gssapi: failed decoding creds\n")); 203 LOG_MISCERR("protocol error in client credentials"); 204 xdr_free((xdrproc_t)xdr_authgssapi_creds, &creds); 205 XDR_DESTROY(&xdrs); 206 ret = AUTH_BADCRED; 207 goto error; 208 } 209 XDR_DESTROY(&xdrs); 210 211 PRINTF(("svcauth_gssapi: got credentials, version %d, client_handle len %d\n", 212 creds.version, (int) creds.client_handle.length)); 213 214 if (creds.version != 2) { 215 PRINTF(("svcauth_gssapi: bad credential version\n")); 216 LOG_MISCERR("unsupported client credentials version"); 217 ret = AUTH_BADCRED; 218 goto error; 219 } 220 221 #ifdef DEBUG_GSSAPI 222 if (svc_debug_gssapi) { 223 if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_EXIT) { 224 PRINTF(("svcauth_gssapi: GSSAPI_EXIT, cleaning up\n")); 225 svc_sendreply(rqst->rq_xprt, xdr_void, NULL); 226 xdr_free((xdrproc_t)xdr_authgssapi_creds, &creds); 227 cleanup(); 228 exit(0); 229 } 230 } 231 #endif 232 233 /* 234 * If this is an auth_msg and proc is GSSAPI_INIT, then create a 235 * client handle for this client. Otherwise, look up the 236 * existing handle. 237 */ 238 if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_INIT) { 239 if (creds.client_handle.length != 0) { 240 PRINTF(("svcauth_gssapi: non-empty handle on GSSAPI_INIT\n")); 241 LOG_MISCERR("protocol error in client handle"); 242 ret = AUTH_FAILED; 243 goto error; 244 } 245 246 PRINTF(("svcauth_gssapi: GSSAPI_INIT, creating client.\n")); 247 248 client_data = create_client(); 249 if (client_data == NULL) { 250 PRINTF(("svcauth_gssapi: create_client failed\n")); 251 LOG_MISCERR("internal error creating client record"); 252 ret = AUTH_FAILED; 253 goto error; 254 } 255 } else { 256 if (creds.client_handle.length == 0) { 257 PRINTF(("svcauth_gssapi: expected non-empty creds\n")); 258 LOG_MISCERR("protocol error in client credentials"); 259 ret = AUTH_FAILED; 260 goto error; 261 } 262 263 PRINTF(("svcauth_gssapi: incoming client_handle %d, len %d\n", 264 *((uint32_t *) creds.client_handle.value), 265 (int) creds.client_handle.length)); 266 267 client_data = get_client(&creds.client_handle); 268 if (client_data == NULL) { 269 PRINTF(("svcauth_gssapi: client_handle lookup failed\n")); 270 LOG_MISCERR("invalid client handle received"); 271 ret = AUTH_BADCRED; 272 goto error; 273 } 274 PRINTF(("svcauth_gssapi: client_handle lookup succeeded\n")); 275 } 276 277 /* any response we send will use client_handle, so set it now */ 278 call_res.client_handle.length = sizeof(client_data->key); 279 call_res.client_handle.value = (char *) &client_data->key; 280 281 /* mark this call as using AUTH_GSSAPI via client_data's SVCAUTH */ 282 rqst->rq_xprt->xp_auth = &client_data->svcauth; 283 284 if (client_data->established == FALSE) { 285 PRINTF(("svcauth_gssapi: context is not established\n")); 286 287 if (creds.auth_msg == FALSE) { 288 PRINTF(("svcauth_gssapi: expected auth_msg TRUE\n")); 289 LOG_MISCERR("protocol error on incomplete connection"); 290 ret = AUTH_REJECTEDCRED; 291 goto error; 292 } 293 294 /* 295 * If the context is not established, then only GSSAPI_INIT 296 * and _CONTINUE requests are valid. 297 */ 298 if (rqst->rq_proc != AUTH_GSSAPI_INIT && rqst->rq_proc != 299 AUTH_GSSAPI_CONTINUE_INIT) { 300 PRINTF(("svcauth_gssapi: unacceptable procedure %d\n", 301 rqst->rq_proc)); 302 LOG_MISCERR("protocol error on incomplete connection"); 303 ret = AUTH_FAILED; 304 goto error; 305 } 306 307 /* call is for us, deserialize arguments */ 308 memset(&call_arg, 0, sizeof(call_arg)); 309 if (! svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_authgssapi_init_arg, 310 &call_arg)) { 311 PRINTF(("svcauth_gssapi: cannot decode args\n")); 312 LOG_MISCERR("protocol error in procedure arguments"); 313 ret = AUTH_BADCRED; 314 goto error; 315 } 316 317 /* 318 * Process the call arg version number. 319 * 320 * Set the krb5_gss backwards-compatibility mode based on client 321 * version. This controls whether the AP_REP message is 322 * encrypted with the session key (version 2+, correct) or the 323 * session subkey (version 1, incorrect). This function can 324 * never fail, so we don't bother checking its return value. 325 */ 326 switch (call_arg.version) { 327 case 1: 328 case 2: 329 LOG_MISCERR("Warning: Accepted old RPC protocol request"); 330 call_res.version = 1; 331 break; 332 case 3: 333 case 4: 334 /* 3 and 4 are essentially the same, don't bother warning */ 335 call_res.version = call_arg.version; 336 break; 337 default: 338 PRINTF(("svcauth_gssapi: bad GSSAPI_INIT version\n")); 339 LOG_MISCERR("unsupported GSSAPI_INIT version"); 340 ret = AUTH_BADCRED; 341 goto error; 342 } 343 344 #ifdef GSS_BACKWARD_HACK 345 krb5_gss_set_backward_mode(&minor_stat, call_arg.version == 1); 346 #endif 347 348 if (call_arg.version >= 3) { 349 memset(&bindings, 0, sizeof(bindings)); 350 bindings.application_data.length = 0; 351 bindings.initiator_addrtype = GSS_C_AF_INET; 352 bindings.initiator_address.length = 4; 353 bindings.initiator_address.value = 354 &svc_getcaller(rqst->rq_xprt)->sin_addr.s_addr; 355 356 if (rqst->rq_xprt->xp_laddrlen > 0) { 357 bindings.acceptor_addrtype = GSS_C_AF_INET; 358 bindings.acceptor_address.length = 4; 359 bindings.acceptor_address.value = 360 &rqst->rq_xprt->xp_laddr.sin_addr.s_addr; 361 } else { 362 LOG_MISCERR("cannot get local address"); 363 ret = AUTH_FAILED; 364 goto error; 365 } 366 367 368 bindp = &bindings; 369 } else { 370 bindp = GSS_C_NO_CHANNEL_BINDINGS; 371 } 372 373 /* 374 * If the client's server_creds is already set, use it. 375 * Otherwise, try each credential in server_creds_list until 376 * one of them succeeds, then set the client server_creds 377 * to that. If all fail, the client's server_creds isn't 378 * set (which is fine, because the client will be gc'ed 379 * anyway). 380 * 381 * If accept_sec_context returns something other than 382 * success and GSS_S_FAILURE, then assume different 383 * credentials won't help and stop looping. 384 * 385 * Note that there are really two cases here: (1) the client 386 * has a server_creds already, and (2) it does not. They 387 * are both written in the same loop so that there is only 388 * one textual call to gss_accept_sec_context; in fact, in 389 * case (1), the loop is executed exactly once. 390 */ 391 for (i = 0; i < server_creds_count; i++) { 392 if (client_data->server_creds != NULL) { 393 PRINTF(("svcauth_gssapi: using's clients server_creds\n")); 394 server_creds = client_data->server_creds; 395 } else { 396 PRINTF(("svcauth_gssapi: trying creds %d\n", i)); 397 server_creds = server_creds_list[i]; 398 } 399 400 /* Free previous output_token from loop */ 401 if(i != 0) gss_release_buffer(&minor_stat, &output_token); 402 403 call_res.gss_major = 404 gss_accept_sec_context(&call_res.gss_minor, 405 &client_data->context, 406 server_creds, 407 &call_arg.token, 408 bindp, 409 &client_data->client_name, 410 NULL, 411 &output_token, 412 &ret_flags, 413 &time_rec, 414 NULL); 415 416 if (server_creds == client_data->server_creds) 417 break; 418 419 PRINTF(("accept_sec_context returned 0x%x 0x%x not-us=%#x\n", 420 call_res.gss_major, call_res.gss_minor, 421 (int) KRB5KRB_AP_ERR_NOT_US)); 422 if (call_res.gss_major == GSS_S_COMPLETE || 423 call_res.gss_major == GSS_S_CONTINUE_NEEDED) { 424 /* server_creds was right, set it! */ 425 PRINTF(("svcauth_gssapi: creds are correct, storing\n")); 426 client_data->server_creds = server_creds; 427 client_data->server_name = server_name_list[i]; 428 break; 429 } else if (call_res.gss_major != GSS_S_FAILURE 430 #ifdef GSSAPI_KRB5 431 /* 432 * hard-coded because there is no other way 433 * to prevent all GSS_S_FAILURES from 434 * returning a "wrong principal in request" 435 * error 436 */ 437 || ((krb5_error_code) call_res.gss_minor != 438 (krb5_error_code) KRB5KRB_AP_ERR_NOT_US) 439 #endif 440 ) { 441 break; 442 } 443 } 444 445 gssstat = call_res.gss_major; 446 minor_stat = call_res.gss_minor; 447 448 /* done with call args */ 449 xdr_free((xdrproc_t)xdr_authgssapi_init_arg, &call_arg); 450 451 PRINTF(("svcauth_gssapi: accept_sec_context returned %#x %#x\n", 452 call_res.gss_major, call_res.gss_minor)); 453 if (call_res.gss_major != GSS_S_COMPLETE && 454 call_res.gss_major != GSS_S_CONTINUE_NEEDED) { 455 AUTH_GSSAPI_DISPLAY_STATUS(("accepting context", 456 call_res.gss_major, 457 call_res.gss_minor)); 458 459 badauth(call_res.gss_major, call_res.gss_minor, rqst->rq_xprt); 460 461 gss_release_buffer(&minor_stat, &output_token); 462 svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_authgssapi_init_res, 463 (caddr_t) &call_res); 464 *no_dispatch = TRUE; 465 ret = AUTH_OK; 466 goto error; 467 } 468 469 if (output_token.length != 0) { 470 PRINTF(("svcauth_gssapi: got new output token\n")); 471 GSS_COPY_BUFFER(call_res.token, output_token); 472 } 473 474 if (gssstat == GSS_S_COMPLETE) { 475 client_data->seq_num = rand(); 476 client_expire(client_data, 477 (time_rec == GSS_C_INDEFINITE ? 478 INDEF_EXPIRE : time_rec) + time(0)); 479 480 PRINTF(("svcauth_gssapi: context established, isn %d\n", 481 client_data->seq_num)); 482 483 if (auth_gssapi_seal_seq(client_data->context, 484 client_data->seq_num, 485 &call_res.signed_isn) == 486 FALSE) { 487 ret = AUTH_FAILED; 488 LOG_MISCERR("internal error sealing sequence number"); 489 gss_release_buffer(&minor_stat, &output_token); 490 goto error; 491 } 492 } 493 494 PRINTF(("svcauth_gssapi: sending reply\n")); 495 svc_sendreply(rqst->rq_xprt, (xdrproc_t)xdr_authgssapi_init_res, 496 (caddr_t) &call_res); 497 *no_dispatch = TRUE; 498 499 /* 500 * If appropriate, set established to TRUE *after* sending 501 * response (otherwise, the client will receive the final 502 * token encrypted) 503 */ 504 if (gssstat == GSS_S_COMPLETE) { 505 gss_release_buffer(&minor_stat, &call_res.signed_isn); 506 client_data->established = TRUE; 507 } 508 gss_release_buffer(&minor_stat, &output_token); 509 } else { 510 PRINTF(("svcauth_gssapi: context is established\n")); 511 512 /* check the verifier */ 513 PRINTF(("svcauth_gssapi: checking verifier, len %d\n", 514 verf->oa_length)); 515 516 in_buf.length = verf->oa_length; 517 in_buf.value = verf->oa_base; 518 519 if (auth_gssapi_unseal_seq(client_data->context, &in_buf, 520 &seq_num) == FALSE) { 521 ret = AUTH_BADVERF; 522 LOG_MISCERR("internal error unsealing sequence number"); 523 goto error; 524 } 525 526 if (seq_num != client_data->seq_num + 1) { 527 PRINTF(("svcauth_gssapi: expected isn %d, got %d\n", 528 client_data->seq_num + 1, seq_num)); 529 if (log_badverf != NULL) 530 (*log_badverf)(client_data->client_name, 531 client_data->server_name, 532 rqst, msg, log_badverf_data); 533 534 ret = AUTH_REJECTEDVERF; 535 goto error; 536 } 537 client_data->seq_num++; 538 539 PRINTF(("svcauth_gssapi: seq_num %d okay\n", seq_num)); 540 541 /* free previous response verifier, if any */ 542 if (client_data->prev_verf.length != 0) { 543 gss_release_buffer(&minor_stat, &client_data->prev_verf); 544 client_data->prev_verf.length = 0; 545 } 546 547 /* prepare response verifier */ 548 seq_num = client_data->seq_num + 1; 549 if (auth_gssapi_seal_seq(client_data->context, seq_num, 550 &out_buf) == FALSE) { 551 ret = AUTH_FAILED; 552 LOG_MISCERR("internal error sealing sequence number"); 553 goto error; 554 } 555 556 client_data->seq_num++; 557 558 PRINTF(("svcauth_gssapi; response seq_num %d\n", seq_num)); 559 560 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_GSSAPI; 561 rqst->rq_xprt->xp_verf.oa_base = out_buf.value; 562 rqst->rq_xprt->xp_verf.oa_length = out_buf.length; 563 564 /* save verifier so it can be freed next time */ 565 client_data->prev_verf.value = out_buf.value; 566 client_data->prev_verf.length = out_buf.length; 567 568 /* 569 * Message is authentic. If auth_msg if true, process the 570 * call; otherwise, return AUTH_OK so it will be dispatched 571 * to the application server. 572 */ 573 574 if (creds.auth_msg == TRUE) { 575 /* 576 * If process_token fails, then the token probably came 577 * from an attacker. No response (error or otherwise) 578 * should be returned to the client, since it won't be 579 * accepting one. 580 */ 581 582 switch (rqst->rq_proc) { 583 case AUTH_GSSAPI_MSG: 584 PRINTF(("svcauth_gssapi: GSSAPI_MSG, getting args\n")); 585 memset(&call_arg, 0, sizeof(call_arg)); 586 if (! svc_getargs(rqst->rq_xprt, 587 (xdrproc_t)xdr_authgssapi_init_arg, 588 &call_arg)) { 589 PRINTF(("svcauth_gssapi: cannot decode args\n")); 590 LOG_MISCERR("protocol error in call arguments"); 591 xdr_free((xdrproc_t)xdr_authgssapi_init_arg, 592 &call_arg); 593 ret = AUTH_BADCRED; 594 goto error; 595 } 596 597 PRINTF(("svcauth_gssapi: processing token\n")); 598 gssstat = gss_process_context_token(&minor_stat, 599 client_data->context, 600 &call_arg.token); 601 602 /* done with call args */ 603 xdr_free((xdrproc_t)xdr_authgssapi_init_arg, &call_arg); 604 605 if (gssstat != GSS_S_COMPLETE) { 606 AUTH_GSSAPI_DISPLAY_STATUS(("processing token", 607 gssstat, minor_stat)); 608 ret = AUTH_FAILED; 609 goto error; 610 } 611 612 svc_sendreply(rqst->rq_xprt, xdr_void, NULL); 613 *no_dispatch = TRUE; 614 break; 615 616 case AUTH_GSSAPI_DESTROY: 617 PRINTF(("svcauth_gssapi: GSSAPI_DESTROY\n")); 618 619 PRINTF(("svcauth_gssapi: sending reply\n")); 620 svc_sendreply(rqst->rq_xprt, xdr_void, NULL); 621 *no_dispatch = TRUE; 622 623 destroy_client(client_data); 624 rqst->rq_xprt->xp_auth = NULL; 625 break; 626 627 default: 628 PRINTF(("svcauth_gssapi: unacceptable procedure %d\n", 629 rqst->rq_proc)); 630 LOG_MISCERR("invalid call procedure number"); 631 ret = AUTH_FAILED; 632 goto error; 633 } 634 } else { 635 /* set credentials for app server; comment in svc.c */ 636 /* seems to imply this is incorrect, but I don't see */ 637 /* any problem with it... */ 638 rqst->rq_clntcred = (char *)client_data->client_name; 639 rqst->rq_svccred = (char *)client_data->context; 640 } 641 } 642 643 if (creds.client_handle.length != 0) { 644 PRINTF(("svcauth_gssapi: freeing client_handle len %d\n", 645 (int) creds.client_handle.length)); 646 xdr_free((xdrproc_t)xdr_authgssapi_creds, &creds); 647 } 648 649 PRINTF(("\n")); 650 return AUTH_OK; 651 652 error: 653 if (creds.client_handle.length != 0) { 654 PRINTF(("svcauth_gssapi: freeing client_handle len %d\n", 655 (int) creds.client_handle.length)); 656 xdr_free((xdrproc_t)xdr_authgssapi_creds, &creds); 657 } 658 659 PRINTF(("\n")); 660 return ret; 661 } 662 663 static void cleanup(void) 664 { 665 client_list *c, *c2; 666 667 PRINTF(("cleanup_and_exit: starting\n")); 668 669 c = clients; 670 while (c) { 671 c2 = c; 672 c = c->next; 673 destroy_client(c2->client); 674 free(c2); 675 } 676 677 exit(0); 678 } 679 680 /* 681 * Function: create_client 682 * 683 * Purpose: Creates an new client_data structure and stores it in the 684 * database. 685 * 686 * Returns: the new client_data structure, or NULL on failure. 687 * 688 * Effects: 689 * 690 * A new client_data is created and stored in the hash table and 691 * b-tree. A new key that is unique in the current database is 692 * chosen; this key should be used as the client's client_handle. 693 */ 694 static svc_auth_gssapi_data *create_client(void) 695 { 696 client_list *c; 697 svc_auth_gssapi_data *client_data; 698 static int client_key = 1; 699 700 PRINTF(("svcauth_gssapi: empty creds, creating\n")); 701 702 client_data = (svc_auth_gssapi_data *) malloc(sizeof(*client_data)); 703 if (client_data == NULL) 704 return NULL; 705 memset(client_data, 0, sizeof(*client_data)); 706 L_PRINTF(2, ("create_client: new client_data = %p\n", 707 (void *) client_data)); 708 709 /* set up client data structure */ 710 client_data->established = 0; 711 client_data->context = GSS_C_NO_CONTEXT; 712 client_data->expiration = time(0) + INITIATION_TIMEOUT; 713 714 /* set up psycho-recursive SVCAUTH hack */ 715 client_data->svcauth.svc_ah_ops = &svc_auth_gssapi_ops; 716 client_data->svcauth.svc_ah_private = (caddr_t) client_data; 717 718 client_data->key = client_key++; 719 720 c = (client_list *) malloc(sizeof(client_list)); 721 if (c == NULL) 722 return NULL; 723 c->client = client_data; 724 c->next = NULL; 725 726 727 if (clients == NULL) 728 clients = c; 729 else { 730 c->next = clients; 731 clients = c; 732 } 733 734 PRINTF(("svcauth_gssapi: new handle %d\n", client_data->key)); 735 L_PRINTF(2, ("create_client: done\n")); 736 737 return client_data; 738 } 739 740 /* 741 * Function: client_expire 742 * 743 * Purpose: change the expiration time of a client in the database 744 * 745 * Arguments: 746 * 747 * client_data (r) the client_data to expire 748 * exp (r) the new expiration time 749 * 750 * Effects: 751 * 752 * client_data->expiration = exp 753 * 754 * This function used to remove client_data from the database, change 755 * its expiration time, and re-add it, which was necessary because the 756 * database was sorted by expiration time so a simple modification 757 * would break the rep invariant. Now the database is an unsorted 758 * linked list, so it doesn't matter. 759 */ 760 static void client_expire( 761 svc_auth_gssapi_data *client_data, 762 uint32_t exp) 763 { 764 client_data->expiration = exp; 765 } 766 767 /* 768 * Function get_client 769 * 770 * Purpose: retrieve a client_data structure from the database based 771 * on its client handle (key) 772 * 773 * Arguments: 774 * 775 * client_handle (r) the handle (key) to retrieve 776 * 777 * Effects: 778 * 779 * Searches the list and returns the client_data whose key field 780 * matches the contents of client_handle, or returns NULL if none was 781 * found. 782 */ 783 static svc_auth_gssapi_data *get_client(gss_buffer_t client_handle) 784 { 785 client_list *c; 786 uint32_t handle; 787 788 memcpy(&handle, client_handle->value, 4); 789 790 L_PRINTF(2, ("get_client: looking for client %d\n", handle)); 791 792 c = clients; 793 while (c) { 794 if (c->client->key == handle) 795 return c->client; 796 c = c->next; 797 } 798 799 L_PRINTF(2, ("get_client: client_handle lookup failed\n")); 800 return NULL; 801 } 802 803 /* 804 * Function: destroy_client 805 * 806 * Purpose: destroys a client entry and removes it from the database 807 * 808 * Arguments: 809 * 810 * client_data (r) the client to be destroyed 811 * 812 * Effects: 813 * 814 * client_data->context is deleted with gss_delete_sec_context. 815 * client_data's entry in the database is destroyed. client_data is 816 * freed. 817 */ 818 static void destroy_client(svc_auth_gssapi_data *client_data) 819 { 820 OM_uint32 gssstat, minor_stat; 821 gss_buffer_desc out_buf; 822 client_list *c, *c2; 823 824 PRINTF(("destroy_client: destroying client_data\n")); 825 L_PRINTF(2, ("destroy_client: client_data = %p\n", (void *) client_data)); 826 827 #ifdef DEBUG_GSSAPI 828 if (svc_debug_gssapi >= 3) 829 dump_db("before frees"); 830 #endif 831 832 /* destroy client struct even if error occurs */ 833 834 gssstat = gss_delete_sec_context(&minor_stat, &client_data->context, 835 &out_buf); 836 if (gssstat != GSS_S_COMPLETE) 837 AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat, 838 minor_stat)); 839 840 gss_release_buffer(&minor_stat, &out_buf); 841 gss_release_name(&minor_stat, &client_data->client_name); 842 if (client_data->prev_verf.length != 0) 843 gss_release_buffer(&minor_stat, &client_data->prev_verf); 844 845 if (clients == NULL) { 846 PRINTF(("destroy_client: called on empty database\n")); 847 abort(); 848 } else if (clients->client == client_data) { 849 c = clients; 850 clients = clients->next; 851 free(c); 852 } else { 853 c2 = clients; 854 c = clients->next; 855 while (c) { 856 if (c->client == client_data) { 857 c2->next = c->next; 858 free(c); 859 goto done; 860 } else { 861 c2 = c; 862 c = c->next; 863 } 864 } 865 PRINTF(("destroy_client: client_handle delete failed\n")); 866 abort(); 867 } 868 869 done: 870 871 L_PRINTF(2, ("destroy_client: client %d destroyed\n", client_data->key)); 872 873 free(client_data); 874 } 875 876 static void dump_db(char *msg) 877 { 878 svc_auth_gssapi_data *client_data; 879 client_list *c; 880 881 L_PRINTF(3, ("dump_db: %s:\n", msg)); 882 883 c = clients; 884 while (c) { 885 client_data = c->client; 886 L_PRINTF(3, ("\tclient_data = %p, exp = %d\n", 887 (void *) client_data, client_data->expiration)); 888 c = c->next; 889 } 890 891 L_PRINTF(3, ("\n")); 892 } 893 894 static void clean_client(void) 895 { 896 svc_auth_gssapi_data *client_data; 897 client_list *c; 898 899 PRINTF(("clean_client: starting\n")); 900 901 c = clients; 902 while (c) { 903 client_data = c->client; 904 905 L_PRINTF(2, ("clean_client: client_data = %p\n", 906 (void *) client_data)); 907 908 if (client_data->expiration < time(0)) { 909 PRINTF(("clean_client: client %d expired\n", 910 client_data->key)); 911 destroy_client(client_data); 912 c = clients; /* start over, just to be safe */ 913 } else { 914 c = c->next; 915 } 916 } 917 918 PRINTF(("clean_client: done\n")); 919 } 920 921 /* 922 * Function: svcauth_gssapi_set_names 923 * 924 * Purpose: Sets the list of service names for which incoming 925 * authentication requests should be honored. 926 * 927 * See functional specifications. 928 */ 929 bool_t svcauth_gssapi_set_names( 930 auth_gssapi_name *names, 931 int num) 932 { 933 OM_uint32 gssstat, minor_stat; 934 gss_buffer_desc in_buf; 935 int i; 936 937 if (num == 0) 938 for (; names[num].name != NULL; num++) 939 ; 940 941 server_creds_list = NULL; 942 server_name_list = NULL; 943 944 server_creds_list = (gss_cred_id_t *) malloc(num*sizeof(gss_cred_id_t)); 945 if (server_creds_list == NULL) 946 goto fail; 947 server_name_list = (gss_name_t *) malloc(num*sizeof(gss_name_t)); 948 if (server_name_list == NULL) 949 goto fail; 950 951 for (i = 0; i < num; i++) { 952 server_name_list[i] = 0; 953 server_creds_list[i] = 0; 954 } 955 956 server_creds_count = num; 957 958 for (i = 0; i < num; i++) { 959 in_buf.value = names[i].name; 960 in_buf.length = strlen(in_buf.value) + 1; 961 962 PRINTF(("svcauth_gssapi_set_names: importing %s\n", names[i].name)); 963 964 gssstat = gss_import_name(&minor_stat, &in_buf, names[i].type, 965 &server_name_list[i]); 966 967 if (gssstat != GSS_S_COMPLETE) { 968 AUTH_GSSAPI_DISPLAY_STATUS(("importing name", gssstat, 969 minor_stat)); 970 goto fail; 971 } 972 973 gssstat = gss_acquire_cred(&minor_stat, server_name_list[i], 0, 974 GSS_C_NULL_OID_SET, GSS_C_ACCEPT, 975 &server_creds_list[i], NULL, NULL); 976 if (gssstat != GSS_S_COMPLETE) { 977 AUTH_GSSAPI_DISPLAY_STATUS(("acquiring credentials", 978 gssstat, minor_stat)); 979 goto fail; 980 } 981 } 982 983 return TRUE; 984 985 fail: 986 svcauth_gssapi_unset_names(); 987 988 return FALSE; 989 } 990 991 /* Function: svcauth_gssapi_unset_names 992 * 993 * Purpose: releases the names and credentials allocated by 994 * svcauth_gssapi_set_names 995 */ 996 997 void svcauth_gssapi_unset_names(void) 998 { 999 int i; 1000 OM_uint32 minor_stat; 1001 1002 if (server_creds_list) { 1003 for (i = 0; i < server_creds_count; i++) 1004 if (server_creds_list[i]) 1005 gss_release_cred(&minor_stat, &server_creds_list[i]); 1006 free(server_creds_list); 1007 server_creds_list = NULL; 1008 } 1009 1010 if (server_name_list) { 1011 for (i = 0; i < server_creds_count; i++) 1012 if (server_name_list[i]) 1013 gss_release_name(&minor_stat, &server_name_list[i]); 1014 free(server_name_list); 1015 server_name_list = NULL; 1016 } 1017 server_creds_count = 0; 1018 } 1019 1020 1021 /* 1022 * Function: svcauth_gssapi_set_log_badauth_func 1023 * 1024 * Purpose: sets the logging function called when an invalid RPC call 1025 * arrives 1026 * 1027 * See functional specifications. 1028 */ 1029 void svcauth_gssapi_set_log_badauth_func( 1030 auth_gssapi_log_badauth_func func, 1031 caddr_t data) 1032 { 1033 log_badauth = func; 1034 log_badauth_data = data; 1035 } 1036 1037 void 1038 svcauth_gssapi_set_log_badauth2_func(auth_gssapi_log_badauth2_func func, 1039 caddr_t data) 1040 { 1041 log_badauth2 = func; 1042 log_badauth2_data = data; 1043 } 1044 1045 /* 1046 * Function: svcauth_gssapi_set_log_badverf_func 1047 * 1048 * Purpose: sets the logging function called when an invalid RPC call 1049 * arrives 1050 * 1051 * See functional specifications. 1052 */ 1053 void svcauth_gssapi_set_log_badverf_func( 1054 auth_gssapi_log_badverf_func func, 1055 caddr_t data) 1056 { 1057 log_badverf = func; 1058 log_badverf_data = data; 1059 } 1060 1061 /* 1062 * Function: svcauth_gssapi_set_log_miscerr_func 1063 * 1064 * Purpose: sets the logging function called when a miscellaneous 1065 * AUTH_GSSAPI error occurs 1066 * 1067 * See functional specifications. 1068 */ 1069 void svcauth_gssapi_set_log_miscerr_func( 1070 auth_gssapi_log_miscerr_func func, 1071 caddr_t data) 1072 { 1073 log_miscerr = func; 1074 log_miscerr_data = data; 1075 } 1076 1077 /* 1078 * Encrypt the serialized arguments from xdr_func applied to xdr_ptr 1079 * and write the result to xdrs. 1080 */ 1081 static bool_t svc_auth_gssapi_wrap( 1082 SVCAUTH *auth, 1083 XDR *out_xdrs, 1084 xdrproc_t xdr_func, 1085 caddr_t xdr_ptr) 1086 { 1087 OM_uint32 gssstat, minor_stat; 1088 1089 if (! SVCAUTH_PRIVATE(auth)->established) { 1090 PRINTF(("svc_gssapi_wrap: not established, noop\n")); 1091 return (*xdr_func)(out_xdrs, xdr_ptr); 1092 } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat, 1093 SVCAUTH_PRIVATE(auth)->context, 1094 SVCAUTH_PRIVATE(auth)->seq_num, 1095 out_xdrs, xdr_func, xdr_ptr)) { 1096 if (gssstat != GSS_S_COMPLETE) 1097 AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments", 1098 gssstat, minor_stat)); 1099 return FALSE; 1100 } else 1101 return TRUE; 1102 } 1103 1104 static bool_t svc_auth_gssapi_unwrap( 1105 SVCAUTH *auth, 1106 XDR *in_xdrs, 1107 xdrproc_t xdr_func, 1108 caddr_t xdr_ptr) 1109 { 1110 svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth); 1111 OM_uint32 gssstat, minor_stat; 1112 1113 if (! client_data->established) { 1114 PRINTF(("svc_gssapi_unwrap: not established, noop\n")); 1115 return (*xdr_func)(in_xdrs, (auth_gssapi_init_arg *)(void *) xdr_ptr); 1116 } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat, 1117 client_data->context, 1118 client_data->seq_num-1, 1119 in_xdrs, xdr_func, xdr_ptr)) { 1120 if (gssstat != GSS_S_COMPLETE) 1121 AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments", 1122 gssstat, minor_stat)); 1123 return FALSE; 1124 } else 1125 return TRUE; 1126 } 1127 1128 static bool_t svc_auth_gssapi_destroy(SVCAUTH *auth) 1129 { 1130 svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth); 1131 1132 destroy_client(client_data); 1133 return TRUE; 1134 } 1135