auth_spnego.c (253895) | auth_spnego.c (262324) |
---|---|
1/* Copyright 2009 Justin Erenkrantz and Greg Stein 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * --- 167 unchanged lines hidden (view full) --- 176 const char *value; 177} gss_authn_info_t; 178 179/* On the initial 401 response of the server, request a session key from 180 the Kerberos KDC to pass to the server, proving that we are who we 181 claim to be. The session key can only be used with the HTTP service 182 on the target host. */ 183static apr_status_t | 1/* Copyright 2009 Justin Erenkrantz and Greg Stein 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * --- 167 unchanged lines hidden (view full) --- 176 const char *value; 177} gss_authn_info_t; 178 179/* On the initial 401 response of the server, request a session key from 180 the Kerberos KDC to pass to the server, proving that we are who we 181 claim to be. The session key can only be used with the HTTP service 182 on the target host. */ 183static apr_status_t |
184gss_api_get_credentials(char *token, apr_size_t token_len, | 184gss_api_get_credentials(serf_connection_t *conn, 185 char *token, apr_size_t token_len, |
185 const char *hostname, 186 const char **buf, apr_size_t *buf_len, 187 gss_authn_info_t *gss_info) 188{ 189 serf__spnego_buffer_t input_buf; 190 serf__spnego_buffer_t output_buf; 191 apr_status_t status = APR_SUCCESS; 192 --- 4 unchanged lines hidden (view full) --- 197 input_buf.length = token_len; 198 } else { 199 input_buf.value = 0; 200 input_buf.length = 0; 201 } 202 203 /* Establish a security context to the server. */ 204 status = serf__spnego_init_sec_context( | 186 const char *hostname, 187 const char **buf, apr_size_t *buf_len, 188 gss_authn_info_t *gss_info) 189{ 190 serf__spnego_buffer_t input_buf; 191 serf__spnego_buffer_t output_buf; 192 apr_status_t status = APR_SUCCESS; 193 --- 4 unchanged lines hidden (view full) --- 198 input_buf.length = token_len; 199 } else { 200 input_buf.value = 0; 201 input_buf.length = 0; 202 } 203 204 /* Establish a security context to the server. */ 205 status = serf__spnego_init_sec_context( |
206 conn, |
|
205 gss_info->gss_ctx, 206 KRB_HTTP_SERVICE, hostname, 207 &input_buf, 208 &output_buf, 209 gss_info->pool, 210 gss_info->pool 211 ); 212 213 switch(status) { 214 case APR_SUCCESS: | 207 gss_info->gss_ctx, 208 KRB_HTTP_SERVICE, hostname, 209 &input_buf, 210 &output_buf, 211 gss_info->pool, 212 gss_info->pool 213 ); 214 215 switch(status) { 216 case APR_SUCCESS: |
215 gss_info->state = gss_api_auth_completed; | 217 if (output_buf.length == 0) { 218 gss_info->state = gss_api_auth_completed; 219 } else { 220 gss_info->state = gss_api_auth_in_progress; 221 } |
216 break; 217 case APR_EAGAIN: 218 gss_info->state = gss_api_auth_in_progress; 219 status = APR_SUCCESS; 220 break; 221 default: 222 return status; 223 } --- 13 unchanged lines hidden (view full) --- 237 Read the header sent by the server (if any), invoke the gssapi authn 238 code and use the resulting Server Ticket on the next request to the 239 server. */ 240static apr_status_t 241do_auth(peer_t peer, 242 int code, 243 gss_authn_info_t *gss_info, 244 serf_connection_t *conn, | 222 break; 223 case APR_EAGAIN: 224 gss_info->state = gss_api_auth_in_progress; 225 status = APR_SUCCESS; 226 break; 227 default: 228 return status; 229 } --- 13 unchanged lines hidden (view full) --- 243 Read the header sent by the server (if any), invoke the gssapi authn 244 code and use the resulting Server Ticket on the next request to the 245 server. */ 246static apr_status_t 247do_auth(peer_t peer, 248 int code, 249 gss_authn_info_t *gss_info, 250 serf_connection_t *conn, |
251 serf_request_t *request, |
|
245 const char *auth_hdr, 246 apr_pool_t *pool) 247{ 248 serf_context_t *ctx = conn->ctx; 249 serf__authn_info_t *authn_info; 250 const char *tmp = NULL; 251 char *token = NULL; 252 apr_size_t tmp_len = 0, token_len = 0; --- 48 unchanged lines hidden (view full) --- 301 serf_connection_set_max_outstanding_requests(conn, 1); 302 break; 303 } 304 case pstate_stateless: 305 /* Nothing to do here */ 306 break; 307 } 308 | 252 const char *auth_hdr, 253 apr_pool_t *pool) 254{ 255 serf_context_t *ctx = conn->ctx; 256 serf__authn_info_t *authn_info; 257 const char *tmp = NULL; 258 char *token = NULL; 259 apr_size_t tmp_len = 0, token_len = 0; --- 48 unchanged lines hidden (view full) --- 308 serf_connection_set_max_outstanding_requests(conn, 1); 309 break; 310 } 311 case pstate_stateless: 312 /* Nothing to do here */ 313 break; 314 } 315 |
316 if (request->auth_baton && !token) { 317 /* We provided token with this request, but server responded with empty 318 authentication header. This means server rejected our credentials. 319 XXX: Probably we need separate error code for this case like 320 SERF_ERROR_AUTHN_CREDS_REJECTED? */ 321 return SERF_ERROR_AUTHN_FAILED; 322 } 323 |
|
309 /* If the server didn't provide us with a token, start with a new initial 310 step in the SPNEGO authentication. */ 311 if (!token) { 312 serf__spnego_reset_sec_context(gss_info->gss_ctx); 313 gss_info->state = gss_api_auth_not_started; 314 } 315 316 if (peer == HOST) { | 324 /* If the server didn't provide us with a token, start with a new initial 325 step in the SPNEGO authentication. */ 326 if (!token) { 327 serf__spnego_reset_sec_context(gss_info->gss_ctx); 328 gss_info->state = gss_api_auth_not_started; 329 } 330 331 if (peer == HOST) { |
317 status = gss_api_get_credentials(token, token_len, | 332 status = gss_api_get_credentials(conn, 333 token, token_len, |
318 conn->host_info.hostname, 319 &tmp, &tmp_len, 320 gss_info); 321 } else { 322 char *proxy_host; 323 apr_getnameinfo(&proxy_host, conn->ctx->proxy_address, 0); | 334 conn->host_info.hostname, 335 &tmp, &tmp_len, 336 gss_info); 337 } else { 338 char *proxy_host; 339 apr_getnameinfo(&proxy_host, conn->ctx->proxy_address, 0); |
324 status = gss_api_get_credentials(token, token_len, proxy_host, | 340 status = gss_api_get_credentials(conn, 341 token, token_len, proxy_host, |
325 &tmp, &tmp_len, 326 gss_info); 327 } 328 if (status) 329 return status; 330 331 /* On the next request, add an Authorization header. */ 332 if (tmp_len) { --- 19 unchanged lines hidden (view full) --- 352/* A new connection is created to a server that's known to use 353 Kerberos. */ 354apr_status_t 355serf__init_spnego_connection(const serf__authn_scheme_t *scheme, 356 int code, 357 serf_connection_t *conn, 358 apr_pool_t *pool) 359{ | 342 &tmp, &tmp_len, 343 gss_info); 344 } 345 if (status) 346 return status; 347 348 /* On the next request, add an Authorization header. */ 349 if (tmp_len) { --- 19 unchanged lines hidden (view full) --- 369/* A new connection is created to a server that's known to use 370 Kerberos. */ 371apr_status_t 372serf__init_spnego_connection(const serf__authn_scheme_t *scheme, 373 int code, 374 serf_connection_t *conn, 375 apr_pool_t *pool) 376{ |
360 gss_authn_info_t *gss_info; 361 apr_status_t status; | 377 serf_context_t *ctx = conn->ctx; 378 serf__authn_info_t *authn_info; 379 gss_authn_info_t *gss_info = NULL; |
362 | 380 |
363 gss_info = apr_pcalloc(conn->pool, sizeof(*gss_info)); 364 gss_info->pool = conn->pool; 365 gss_info->state = gss_api_auth_not_started; 366 gss_info->pstate = pstate_init; 367 status = serf__spnego_create_sec_context(&gss_info->gss_ctx, scheme, 368 gss_info->pool, pool); 369 370 if (status) { 371 return status; 372 } 373 | 381 /* For proxy authentication, reuse the gss context for all connections. 382 For server authentication, create a new gss context per connection. */ |
374 if (code == 401) { | 383 if (code == 401) { |
375 conn->authn_baton = gss_info; | 384 authn_info = &conn->authn_info; |
376 } else { | 385 } else { |
377 conn->proxy_authn_baton = gss_info; | 386 authn_info = &ctx->proxy_authn_info; |
378 } | 387 } |
388 gss_info = authn_info->baton; |
|
379 | 389 |
390 if (!gss_info) { 391 apr_status_t status; 392 393 gss_info = apr_pcalloc(conn->pool, sizeof(*gss_info)); 394 gss_info->pool = conn->pool; 395 gss_info->state = gss_api_auth_not_started; 396 gss_info->pstate = pstate_init; 397 status = serf__spnego_create_sec_context(&gss_info->gss_ctx, scheme, 398 gss_info->pool, pool); 399 if (status) { 400 return status; 401 } 402 authn_info->baton = gss_info; 403 } 404 |
|
380 /* Make serf send the initial requests one by one */ 381 serf_connection_set_max_outstanding_requests(conn, 1); 382 383 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 384 "Initialized Kerberos context for this connection.\n"); 385 386 return APR_SUCCESS; 387} --- 4 unchanged lines hidden (view full) --- 392 serf_request_t *request, 393 serf_bucket_t *response, 394 const char *auth_hdr, 395 const char *auth_attr, 396 void *baton, 397 apr_pool_t *pool) 398{ 399 serf_connection_t *conn = request->conn; | 405 /* Make serf send the initial requests one by one */ 406 serf_connection_set_max_outstanding_requests(conn, 1); 407 408 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 409 "Initialized Kerberos context for this connection.\n"); 410 411 return APR_SUCCESS; 412} --- 4 unchanged lines hidden (view full) --- 417 serf_request_t *request, 418 serf_bucket_t *response, 419 const char *auth_hdr, 420 const char *auth_attr, 421 void *baton, 422 apr_pool_t *pool) 423{ 424 serf_connection_t *conn = request->conn; |
400 gss_authn_info_t *gss_info = (code == 401) ? conn->authn_baton : 401 conn->proxy_authn_baton; | 425 serf_context_t *ctx = conn->ctx; 426 gss_authn_info_t *gss_info = (code == 401) ? conn->authn_info.baton : 427 ctx->proxy_authn_info.baton; |
402 403 return do_auth(code == 401 ? HOST : PROXY, 404 code, 405 gss_info, 406 request->conn, | 428 429 return do_auth(code == 401 ? HOST : PROXY, 430 code, 431 gss_info, 432 request->conn, |
433 request, |
|
407 auth_hdr, 408 pool); 409} 410 411/* Setup the authn headers on this request message. */ 412apr_status_t 413serf__setup_request_spnego_auth(peer_t peer, 414 int code, 415 serf_connection_t *conn, 416 serf_request_t *request, 417 const char *method, 418 const char *uri, 419 serf_bucket_t *hdrs_bkt) 420{ | 434 auth_hdr, 435 pool); 436} 437 438/* Setup the authn headers on this request message. */ 439apr_status_t 440serf__setup_request_spnego_auth(peer_t peer, 441 int code, 442 serf_connection_t *conn, 443 serf_request_t *request, 444 const char *method, 445 const char *uri, 446 serf_bucket_t *hdrs_bkt) 447{ |
421 gss_authn_info_t *gss_info = (peer == HOST) ? conn->authn_baton : 422 conn->proxy_authn_baton; | 448 serf_context_t *ctx = conn->ctx; 449 gss_authn_info_t *gss_info = (peer == HOST) ? conn->authn_info.baton : 450 ctx->proxy_authn_info.baton; |
423 424 /* If we have an ongoing authentication handshake, the handler of the 425 previous response will have created the authn headers for this request 426 already. */ 427 if (gss_info && gss_info->header && gss_info->value) { 428 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 429 "Set Negotiate authn header on retried request.\n"); 430 431 serf_bucket_headers_setn(hdrs_bkt, gss_info->header, 432 gss_info->value); 433 | 451 452 /* If we have an ongoing authentication handshake, the handler of the 453 previous response will have created the authn headers for this request 454 already. */ 455 if (gss_info && gss_info->header && gss_info->value) { 456 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 457 "Set Negotiate authn header on retried request.\n"); 458 459 serf_bucket_headers_setn(hdrs_bkt, gss_info->header, 460 gss_info->value); 461 |
462 /* Remember that we're using this request for authentication 463 handshake. */ 464 request->auth_baton = (void*) TRUE; 465 |
|
434 /* We should send each token only once. */ 435 gss_info->header = NULL; 436 gss_info->value = NULL; 437 438 return APR_SUCCESS; 439 } 440 441 switch (gss_info->pstate) { --- 22 unchanged lines hidden (view full) --- 464 (RFC 4559 section 4.2) */ 465 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 466 "Add initial Negotiate header to request.\n"); 467 468 status = do_auth(peer, 469 code, 470 gss_info, 471 conn, | 466 /* We should send each token only once. */ 467 gss_info->header = NULL; 468 gss_info->value = NULL; 469 470 return APR_SUCCESS; 471 } 472 473 switch (gss_info->pstate) { --- 22 unchanged lines hidden (view full) --- 496 (RFC 4559 section 4.2) */ 497 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 498 "Add initial Negotiate header to request.\n"); 499 500 status = do_auth(peer, 501 code, 502 gss_info, 503 conn, |
504 request, |
|
472 0l, /* no response authn header */ 473 conn->pool); 474 if (status) 475 return status; 476 477 serf_bucket_headers_setn(hdrs_bkt, gss_info->header, 478 gss_info->value); | 505 0l, /* no response authn header */ 506 conn->pool); 507 if (status) 508 return status; 509 510 serf_bucket_headers_setn(hdrs_bkt, gss_info->header, 511 gss_info->value); |
512 513 /* Remember that we're using this request for authentication 514 handshake. */ 515 request->auth_baton = (void*) TRUE; 516 |
|
479 /* We should send each token only once. */ 480 gss_info->header = NULL; 481 gss_info->value = NULL; 482 break; 483 } 484 } 485 486 return APR_SUCCESS; 487} 488 | 517 /* We should send each token only once. */ 518 gss_info->header = NULL; 519 gss_info->value = NULL; 520 break; 521 } 522 } 523 524 return APR_SUCCESS; 525} 526 |
527/** 528 * Baton passed to the get_auth_header callback function. 529 */ 530typedef struct { 531 const char *hdr_name; 532 const char *auth_name; 533 const char *hdr_value; 534 apr_pool_t *pool; 535} get_auth_header_baton_t; 536 537static int 538get_auth_header_cb(void *baton, 539 const char *key, 540 const char *header) 541{ 542 get_auth_header_baton_t *b = baton; 543 544 /* We're only interested in xxxx-Authenticate headers. */ 545 if (strcasecmp(key, b->hdr_name) != 0) 546 return 0; 547 548 /* Check if header value starts with interesting auth name. */ 549 if (strncmp(header, b->auth_name, strlen(b->auth_name)) == 0) { 550 /* Save interesting header value and stop iteration. */ 551 b->hdr_value = apr_pstrdup(b->pool, header); 552 return 1; 553 } 554 555 return 0; 556} 557 558static const char * 559get_auth_header(serf_bucket_t *hdrs, 560 const char *hdr_name, 561 const char *auth_name, 562 apr_pool_t *pool) 563{ 564 get_auth_header_baton_t b; 565 566 b.auth_name = hdr_name; 567 b.hdr_name = auth_name; 568 b.hdr_value = NULL; 569 b.pool = pool; 570 571 serf_bucket_headers_do(hdrs, get_auth_header_cb, &b); 572 573 return b.hdr_value; 574} 575 |
|
489/* Function is called when 2xx responses are received. Normally we don't 490 * have to do anything, except for the first response after the 491 * authentication handshake. This specific response includes authentication 492 * data which should be validated by the client (mutual authentication). 493 */ 494apr_status_t | 576/* Function is called when 2xx responses are received. Normally we don't 577 * have to do anything, except for the first response after the 578 * authentication handshake. This specific response includes authentication 579 * data which should be validated by the client (mutual authentication). 580 */ 581apr_status_t |
495serf__validate_response_spnego_auth(peer_t peer, | 582serf__validate_response_spnego_auth(const serf__authn_scheme_t *scheme, 583 peer_t peer, |
496 int code, 497 serf_connection_t *conn, 498 serf_request_t *request, 499 serf_bucket_t *response, 500 apr_pool_t *pool) 501{ | 584 int code, 585 serf_connection_t *conn, 586 serf_request_t *request, 587 serf_bucket_t *response, 588 apr_pool_t *pool) 589{ |
590 serf_context_t *ctx = conn->ctx; |
|
502 gss_authn_info_t *gss_info; 503 const char *auth_hdr_name; 504 505 /* TODO: currently this function is only called when a response includes 506 an Authenticate header. This header is optional. If the server does 507 not provide this header on the first 2xx response, we will not promote 508 the connection from undecided to stateful. This won't break anything, 509 but means we stay in non-pipelining mode. */ 510 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 511 "Validate Negotiate response header.\n"); 512 513 if (peer == HOST) { | 591 gss_authn_info_t *gss_info; 592 const char *auth_hdr_name; 593 594 /* TODO: currently this function is only called when a response includes 595 an Authenticate header. This header is optional. If the server does 596 not provide this header on the first 2xx response, we will not promote 597 the connection from undecided to stateful. This won't break anything, 598 but means we stay in non-pipelining mode. */ 599 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 600 "Validate Negotiate response header.\n"); 601 602 if (peer == HOST) { |
514 gss_info = conn->authn_baton; | 603 gss_info = conn->authn_info.baton; |
515 auth_hdr_name = "WWW-Authenticate"; 516 } else { | 604 auth_hdr_name = "WWW-Authenticate"; 605 } else { |
517 gss_info = conn->proxy_authn_baton; | 606 gss_info = ctx->proxy_authn_info.baton; |
518 auth_hdr_name = "Proxy-Authenticate"; 519 } 520 521 if (gss_info->state != gss_api_auth_completed) { 522 serf_bucket_t *hdrs; 523 const char *auth_hdr_val; 524 apr_status_t status; 525 526 hdrs = serf_bucket_response_get_headers(response); | 607 auth_hdr_name = "Proxy-Authenticate"; 608 } 609 610 if (gss_info->state != gss_api_auth_completed) { 611 serf_bucket_t *hdrs; 612 const char *auth_hdr_val; 613 apr_status_t status; 614 615 hdrs = serf_bucket_response_get_headers(response); |
527 auth_hdr_val = serf_bucket_headers_get(hdrs, auth_hdr_name); | 616 auth_hdr_val = get_auth_header(hdrs, auth_hdr_name, scheme->name, 617 pool); |
528 | 618 |
529 status = do_auth(peer, code, gss_info, conn, auth_hdr_val, pool); 530 if (status) 531 return status; | 619 if (auth_hdr_val) { 620 status = do_auth(peer, code, gss_info, conn, request, auth_hdr_val, 621 pool); 622 if (status) { 623 return status; 624 } 625 } else { 626 /* No Authenticate headers, nothing to validate: authentication 627 completed.*/ 628 gss_info->state = gss_api_auth_completed; 629 630 serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt, 631 "SPNEGO handshake completed.\n"); 632 } |
532 } 533 534 if (gss_info->state == gss_api_auth_completed) { 535 switch(gss_info->pstate) { 536 case pstate_init: 537 /* Authentication of the first request is done. */ 538 gss_info->pstate = pstate_undecided; 539 break; --- 17 unchanged lines hidden --- | 633 } 634 635 if (gss_info->state == gss_api_auth_completed) { 636 switch(gss_info->pstate) { 637 case pstate_init: 638 /* Authentication of the first request is done. */ 639 gss_info->pstate = pstate_undecided; 640 break; --- 17 unchanged lines hidden --- |