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(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(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, 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(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, 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, 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, xdr_authgssapi_init_arg, 587 &call_arg)) { 588 PRINTF(("svcauth_gssapi: cannot decode args\n")); 589 LOG_MISCERR("protocol error in call arguments"); 590 xdr_free(xdr_authgssapi_init_arg, &call_arg); 591 ret = AUTH_BADCRED; 592 goto error; 593 } 594 595 PRINTF(("svcauth_gssapi: processing token\n")); 596 gssstat = gss_process_context_token(&minor_stat, 597 client_data->context, 598 &call_arg.token); 599 600 /* done with call args */ 601 xdr_free(xdr_authgssapi_init_arg, &call_arg); 602 603 if (gssstat != GSS_S_COMPLETE) { 604 AUTH_GSSAPI_DISPLAY_STATUS(("processing token", 605 gssstat, minor_stat)); 606 ret = AUTH_FAILED; 607 goto error; 608 } 609 610 svc_sendreply(rqst->rq_xprt, xdr_void, NULL); 611 *no_dispatch = TRUE; 612 break; 613 614 case AUTH_GSSAPI_DESTROY: 615 PRINTF(("svcauth_gssapi: GSSAPI_DESTROY\n")); 616 617 PRINTF(("svcauth_gssapi: sending reply\n")); 618 svc_sendreply(rqst->rq_xprt, xdr_void, NULL); 619 *no_dispatch = TRUE; 620 621 destroy_client(client_data); 622 rqst->rq_xprt->xp_auth = NULL; 623 break; 624 625 default: 626 PRINTF(("svcauth_gssapi: unacceptable procedure %d\n", 627 rqst->rq_proc)); 628 LOG_MISCERR("invalid call procedure number"); 629 ret = AUTH_FAILED; 630 goto error; 631 } 632 } else { 633 /* set credentials for app server; comment in svc.c */ 634 /* seems to imply this is incorrect, but I don't see */ 635 /* any problem with it... */ 636 rqst->rq_clntcred = (char *)client_data->client_name; 637 rqst->rq_svccred = (char *)client_data->context; 638 } 639 } 640 641 if (creds.client_handle.length != 0) { 642 PRINTF(("svcauth_gssapi: freeing client_handle len %d\n", 643 (int) creds.client_handle.length)); 644 xdr_free(xdr_authgssapi_creds, &creds); 645 } 646 647 PRINTF(("\n")); 648 return AUTH_OK; 649 650 error: 651 if (creds.client_handle.length != 0) { 652 PRINTF(("svcauth_gssapi: freeing client_handle len %d\n", 653 (int) creds.client_handle.length)); 654 xdr_free(xdr_authgssapi_creds, &creds); 655 } 656 657 PRINTF(("\n")); 658 return ret; 659 } 660 661 static void cleanup(void) 662 { 663 client_list *c, *c2; 664 665 PRINTF(("cleanup_and_exit: starting\n")); 666 667 c = clients; 668 while (c) { 669 c2 = c; 670 c = c->next; 671 destroy_client(c2->client); 672 free(c2); 673 } 674 675 exit(0); 676 } 677 678 /* 679 * Function: create_client 680 * 681 * Purpose: Creates an new client_data structure and stores it in the 682 * database. 683 * 684 * Returns: the new client_data structure, or NULL on failure. 685 * 686 * Effects: 687 * 688 * A new client_data is created and stored in the hash table and 689 * b-tree. A new key that is unique in the current database is 690 * chosen; this key should be used as the client's client_handle. 691 */ 692 static svc_auth_gssapi_data *create_client(void) 693 { 694 client_list *c; 695 svc_auth_gssapi_data *client_data; 696 static int client_key = 1; 697 698 PRINTF(("svcauth_gssapi: empty creds, creating\n")); 699 700 client_data = (svc_auth_gssapi_data *) malloc(sizeof(*client_data)); 701 if (client_data == NULL) 702 return NULL; 703 memset(client_data, 0, sizeof(*client_data)); 704 L_PRINTF(2, ("create_client: new client_data = %p\n", 705 (void *) client_data)); 706 707 /* set up client data structure */ 708 client_data->established = 0; 709 client_data->context = GSS_C_NO_CONTEXT; 710 client_data->expiration = time(0) + INITIATION_TIMEOUT; 711 712 /* set up psycho-recursive SVCAUTH hack */ 713 client_data->svcauth.svc_ah_ops = &svc_auth_gssapi_ops; 714 client_data->svcauth.svc_ah_private = (caddr_t) client_data; 715 716 client_data->key = client_key++; 717 718 c = (client_list *) malloc(sizeof(client_list)); 719 if (c == NULL) 720 return NULL; 721 c->client = client_data; 722 c->next = NULL; 723 724 725 if (clients == NULL) 726 clients = c; 727 else { 728 c->next = clients; 729 clients = c; 730 } 731 732 PRINTF(("svcauth_gssapi: new handle %d\n", client_data->key)); 733 L_PRINTF(2, ("create_client: done\n")); 734 735 return client_data; 736 } 737 738 /* 739 * Function: client_expire 740 * 741 * Purpose: change the expiration time of a client in the database 742 * 743 * Arguments: 744 * 745 * client_data (r) the client_data to expire 746 * exp (r) the new expiration time 747 * 748 * Effects: 749 * 750 * client_data->expiration = exp 751 * 752 * This function used to remove client_data from the database, change 753 * its expiration time, and re-add it, which was necessary because the 754 * database was sorted by expiration time so a simple modification 755 * would break the rep invariant. Now the database is an unsorted 756 * linked list, so it doesn't matter. 757 */ 758 static void client_expire( 759 svc_auth_gssapi_data *client_data, 760 uint32_t exp) 761 { 762 client_data->expiration = exp; 763 } 764 765 /* 766 * Function get_client 767 * 768 * Purpose: retrieve a client_data structure from the database based 769 * on its client handle (key) 770 * 771 * Arguments: 772 * 773 * client_handle (r) the handle (key) to retrieve 774 * 775 * Effects: 776 * 777 * Searches the list and returns the client_data whose key field 778 * matches the contents of client_handle, or returns NULL if none was 779 * found. 780 */ 781 static svc_auth_gssapi_data *get_client(gss_buffer_t client_handle) 782 { 783 client_list *c; 784 uint32_t handle; 785 786 memcpy(&handle, client_handle->value, 4); 787 788 L_PRINTF(2, ("get_client: looking for client %d\n", handle)); 789 790 c = clients; 791 while (c) { 792 if (c->client->key == handle) 793 return c->client; 794 c = c->next; 795 } 796 797 L_PRINTF(2, ("get_client: client_handle lookup failed\n")); 798 return NULL; 799 } 800 801 /* 802 * Function: destroy_client 803 * 804 * Purpose: destroys a client entry and removes it from the database 805 * 806 * Arguments: 807 * 808 * client_data (r) the client to be destroyed 809 * 810 * Effects: 811 * 812 * client_data->context is deleted with gss_delete_sec_context. 813 * client_data's entry in the database is destroyed. client_data is 814 * freed. 815 */ 816 static void destroy_client(svc_auth_gssapi_data *client_data) 817 { 818 OM_uint32 gssstat, minor_stat; 819 gss_buffer_desc out_buf; 820 client_list *c, *c2; 821 822 PRINTF(("destroy_client: destroying client_data\n")); 823 L_PRINTF(2, ("destroy_client: client_data = %p\n", (void *) client_data)); 824 825 #ifdef DEBUG_GSSAPI 826 if (svc_debug_gssapi >= 3) 827 dump_db("before frees"); 828 #endif 829 830 /* destroy client struct even if error occurs */ 831 832 gssstat = gss_delete_sec_context(&minor_stat, &client_data->context, 833 &out_buf); 834 if (gssstat != GSS_S_COMPLETE) 835 AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat, 836 minor_stat)); 837 838 gss_release_buffer(&minor_stat, &out_buf); 839 gss_release_name(&minor_stat, &client_data->client_name); 840 if (client_data->prev_verf.length != 0) 841 gss_release_buffer(&minor_stat, &client_data->prev_verf); 842 843 if (clients == NULL) { 844 PRINTF(("destroy_client: called on empty database\n")); 845 abort(); 846 } else if (clients->client == client_data) { 847 c = clients; 848 clients = clients->next; 849 free(c); 850 } else { 851 c2 = clients; 852 c = clients->next; 853 while (c) { 854 if (c->client == client_data) { 855 c2->next = c->next; 856 free(c); 857 goto done; 858 } else { 859 c2 = c; 860 c = c->next; 861 } 862 } 863 PRINTF(("destroy_client: client_handle delete failed\n")); 864 abort(); 865 } 866 867 done: 868 869 L_PRINTF(2, ("destroy_client: client %d destroyed\n", client_data->key)); 870 871 free(client_data); 872 } 873 874 static void dump_db(char *msg) 875 { 876 svc_auth_gssapi_data *client_data; 877 client_list *c; 878 879 L_PRINTF(3, ("dump_db: %s:\n", msg)); 880 881 c = clients; 882 while (c) { 883 client_data = c->client; 884 L_PRINTF(3, ("\tclient_data = %p, exp = %d\n", 885 (void *) client_data, client_data->expiration)); 886 c = c->next; 887 } 888 889 L_PRINTF(3, ("\n")); 890 } 891 892 static void clean_client(void) 893 { 894 svc_auth_gssapi_data *client_data; 895 client_list *c; 896 897 PRINTF(("clean_client: starting\n")); 898 899 c = clients; 900 while (c) { 901 client_data = c->client; 902 903 L_PRINTF(2, ("clean_client: client_data = %p\n", 904 (void *) client_data)); 905 906 if (client_data->expiration < time(0)) { 907 PRINTF(("clean_client: client %d expired\n", 908 client_data->key)); 909 destroy_client(client_data); 910 c = clients; /* start over, just to be safe */ 911 } else { 912 c = c->next; 913 } 914 } 915 916 PRINTF(("clean_client: done\n")); 917 } 918 919 /* 920 * Function: svcauth_gssapi_set_names 921 * 922 * Purpose: Sets the list of service names for which incoming 923 * authentication requests should be honored. 924 * 925 * See functional specifications. 926 */ 927 bool_t svcauth_gssapi_set_names( 928 auth_gssapi_name *names, 929 int num) 930 { 931 OM_uint32 gssstat, minor_stat; 932 gss_buffer_desc in_buf; 933 int i; 934 935 if (num == 0) 936 for (; names[num].name != NULL; num++) 937 ; 938 939 server_creds_list = NULL; 940 server_name_list = NULL; 941 942 server_creds_list = (gss_cred_id_t *) malloc(num*sizeof(gss_cred_id_t)); 943 if (server_creds_list == NULL) 944 goto fail; 945 server_name_list = (gss_name_t *) malloc(num*sizeof(gss_name_t)); 946 if (server_name_list == NULL) 947 goto fail; 948 949 for (i = 0; i < num; i++) { 950 server_name_list[i] = 0; 951 server_creds_list[i] = 0; 952 } 953 954 server_creds_count = num; 955 956 for (i = 0; i < num; i++) { 957 in_buf.value = names[i].name; 958 in_buf.length = strlen(in_buf.value) + 1; 959 960 PRINTF(("svcauth_gssapi_set_names: importing %s\n", names[i].name)); 961 962 gssstat = gss_import_name(&minor_stat, &in_buf, names[i].type, 963 &server_name_list[i]); 964 965 if (gssstat != GSS_S_COMPLETE) { 966 AUTH_GSSAPI_DISPLAY_STATUS(("importing name", gssstat, 967 minor_stat)); 968 goto fail; 969 } 970 971 gssstat = gss_acquire_cred(&minor_stat, server_name_list[i], 0, 972 GSS_C_NULL_OID_SET, GSS_C_ACCEPT, 973 &server_creds_list[i], NULL, NULL); 974 if (gssstat != GSS_S_COMPLETE) { 975 AUTH_GSSAPI_DISPLAY_STATUS(("acquiring credentials", 976 gssstat, minor_stat)); 977 goto fail; 978 } 979 } 980 981 return TRUE; 982 983 fail: 984 svcauth_gssapi_unset_names(); 985 986 return FALSE; 987 } 988 989 /* Function: svcauth_gssapi_unset_names 990 * 991 * Purpose: releases the names and credentials allocated by 992 * svcauth_gssapi_set_names 993 */ 994 995 void svcauth_gssapi_unset_names(void) 996 { 997 int i; 998 OM_uint32 minor_stat; 999 1000 if (server_creds_list) { 1001 for (i = 0; i < server_creds_count; i++) 1002 if (server_creds_list[i]) 1003 gss_release_cred(&minor_stat, &server_creds_list[i]); 1004 free(server_creds_list); 1005 server_creds_list = NULL; 1006 } 1007 1008 if (server_name_list) { 1009 for (i = 0; i < server_creds_count; i++) 1010 if (server_name_list[i]) 1011 gss_release_name(&minor_stat, &server_name_list[i]); 1012 free(server_name_list); 1013 server_name_list = NULL; 1014 } 1015 server_creds_count = 0; 1016 } 1017 1018 1019 /* 1020 * Function: svcauth_gssapi_set_log_badauth_func 1021 * 1022 * Purpose: sets the logging function called when an invalid RPC call 1023 * arrives 1024 * 1025 * See functional specifications. 1026 */ 1027 void svcauth_gssapi_set_log_badauth_func( 1028 auth_gssapi_log_badauth_func func, 1029 caddr_t data) 1030 { 1031 log_badauth = func; 1032 log_badauth_data = data; 1033 } 1034 1035 void 1036 svcauth_gssapi_set_log_badauth2_func(auth_gssapi_log_badauth2_func func, 1037 caddr_t data) 1038 { 1039 log_badauth2 = func; 1040 log_badauth2_data = data; 1041 } 1042 1043 /* 1044 * Function: svcauth_gssapi_set_log_badverf_func 1045 * 1046 * Purpose: sets the logging function called when an invalid RPC call 1047 * arrives 1048 * 1049 * See functional specifications. 1050 */ 1051 void svcauth_gssapi_set_log_badverf_func( 1052 auth_gssapi_log_badverf_func func, 1053 caddr_t data) 1054 { 1055 log_badverf = func; 1056 log_badverf_data = data; 1057 } 1058 1059 /* 1060 * Function: svcauth_gssapi_set_log_miscerr_func 1061 * 1062 * Purpose: sets the logging function called when a miscellaneous 1063 * AUTH_GSSAPI error occurs 1064 * 1065 * See functional specifications. 1066 */ 1067 void svcauth_gssapi_set_log_miscerr_func( 1068 auth_gssapi_log_miscerr_func func, 1069 caddr_t data) 1070 { 1071 log_miscerr = func; 1072 log_miscerr_data = data; 1073 } 1074 1075 /* 1076 * Encrypt the serialized arguments from xdr_func applied to xdr_ptr 1077 * and write the result to xdrs. 1078 */ 1079 static bool_t svc_auth_gssapi_wrap( 1080 SVCAUTH *auth, 1081 XDR *out_xdrs, 1082 bool_t (*xdr_func)(), 1083 caddr_t xdr_ptr) 1084 { 1085 OM_uint32 gssstat, minor_stat; 1086 1087 if (! SVCAUTH_PRIVATE(auth)->established) { 1088 PRINTF(("svc_gssapi_wrap: not established, noop\n")); 1089 return (*xdr_func)(out_xdrs, xdr_ptr); 1090 } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat, 1091 SVCAUTH_PRIVATE(auth)->context, 1092 SVCAUTH_PRIVATE(auth)->seq_num, 1093 out_xdrs, xdr_func, xdr_ptr)) { 1094 if (gssstat != GSS_S_COMPLETE) 1095 AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments", 1096 gssstat, minor_stat)); 1097 return FALSE; 1098 } else 1099 return TRUE; 1100 } 1101 1102 static bool_t svc_auth_gssapi_unwrap( 1103 SVCAUTH *auth, 1104 XDR *in_xdrs, 1105 bool_t (*xdr_func)(), 1106 caddr_t xdr_ptr) 1107 { 1108 svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth); 1109 OM_uint32 gssstat, minor_stat; 1110 1111 if (! client_data->established) { 1112 PRINTF(("svc_gssapi_unwrap: not established, noop\n")); 1113 return (*xdr_func)(in_xdrs, (auth_gssapi_init_arg *)(void *) xdr_ptr); 1114 } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat, 1115 client_data->context, 1116 client_data->seq_num-1, 1117 in_xdrs, xdr_func, xdr_ptr)) { 1118 if (gssstat != GSS_S_COMPLETE) 1119 AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments", 1120 gssstat, minor_stat)); 1121 return FALSE; 1122 } else 1123 return TRUE; 1124 } 1125 1126 static bool_t svc_auth_gssapi_destroy(SVCAUTH *auth) 1127 { 1128 svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth); 1129 1130 destroy_client(client_data); 1131 return TRUE; 1132 } 1133