?? httpauth.c
字號:
/* HTTP Authentication routines Copyright (C) 1999, Joe Orton <joe@orton.demon.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: httpauth.c,v 1.10.2.8 1999/07/26 10:49:12 joe Exp $*/#include <config.h>#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#include <stdio.h>#ifdef HAVE_STRING_H#include <string.h>#endif#include <time.h>#include "dates.h"#include "base64.h"#include "md5.h"#include "strsplit.h"#include "common.h"#include "httpdav.h"#include "httpauth.h"/* HTTP Authentication, as per RFC2617. *//* The challenge parameters */typedef struct { http_auth_scheme_t scheme; char *realm; char *domain; char *nonce; char *opaque; bool stale; http_auth_algorithm_t alg; bool got_qop; /* we were given a qop directive */ bool qop_auth; /* "auth" token in qop attrib */ bool qop_auth_int; /* "auth-int" token in qop attrib */} http_auth_chall_t;const char *http_auth_qop_values[] = { NULL, "auth", "auth-int"};const char *http_auth_alg_names[] = { "MD5", "MD5-sess", NULL};char *http_auth_get_cnonce(void);void http_auth_clean( http_auth_session_t *sess );bool http_auth_challenge_digest( http_auth_session_t *, http_auth_chall_t * );bool http_auth_challenge_basic( http_auth_session_t *, http_auth_chall_t * );char *http_auth_request_digest( http_auth_session_t * );char *http_auth_request_basic( http_auth_session_t * );/* Initialize an auth session */void http_auth_init( http_auth_session_t *sess, const char *username, const char *password ) { /* Initialize the session information */ memset( sess, 0, sizeof( http_auth_session_t ) ); /* Remember the username+password */ sess->username = username; sess->password = password;}/* Start a new request */void http_auth_new_request( http_auth_session_t *sess, const char *method, const char *uri, const char *body_buffer, FILE *body_stream ) { sess->uri = uri; sess->method = method; sess->got_body = (body_buffer!=NULL) || (body_stream!=NULL); sess->body_buffer = body_buffer; sess->body_stream = body_stream; md5_init_ctx( &sess->response_body );}void http_auth_clean( http_auth_session_t *sess ) {#define DOFREE( x ) \ if( x != NULL ) { \ DEBUG( DEBUG_HTTPAUTH, "Freeing " #x "\n" ); \ free( x ); x = NULL; \ } else { \ DEBUG( DEBUG_HTTPAUTH, "Not freeing " #x "\n" ); \ } DOFREE( sess->basic ); DOFREE( sess->unq_realm ); DOFREE( sess->unq_nonce ); DOFREE( sess->unq_cnonce ); DOFREE( sess->opaque );#undef DOFREE}void http_auth_finish( http_auth_session_t *sess ) { sess->scheme = http_auth_scheme_none; http_auth_clean( sess );}/* Returns cnonce-value. We just use base64( time ). * TODO: Could improve this? */char *http_auth_get_cnonce(void) { char *ret, *tmp; tmp = rfc1123_date( time(NULL) ); ret = base64( tmp ); free( tmp ); return ret;}/* Add authentication creditials to a request */char *http_auth_request( http_auth_session_t *sess) { switch( sess->scheme ) { case http_auth_scheme_basic: return http_auth_request_basic( sess ); break; case http_auth_scheme_digest: return http_auth_request_digest( sess ); break; default: break; } return NULL;}/* Examine a Basic auth challenge */bool http_auth_challenge_basic( http_auth_session_t *sess, http_auth_chall_t *parms ) { char *tmp; /* Verify challenge... must have realm, even though we ignore it. */ if( parms->realm == NULL ) return false; DEBUG( DEBUG_HTTPAUTH, "Got Basic challenge with realm [%s]\n", parms->realm ); sess->scheme = http_auth_scheme_basic; /* +2 = one for ':' + one for '\0' */ tmp = malloc( strlen(sess->username) + strlen(sess->password) + 2 ); strcpy( tmp, sess->username ); strcat( tmp, ":" ); strcat( tmp, sess->password ); sess->basic = base64( tmp ); free( tmp ); return true; }/* Add Basic authentication credentials to a request */char *http_auth_request_basic( http_auth_session_t *sess ) { char *buf; /* 6 for "Basic ", 2 for \r\n, 1 for \0 */ buf = malloc( 6 + strlen(sess->basic) + 2 + 1 ); strcpy( buf, "Basic " ); strcat( buf, sess->basic ); strcat( buf, "\r\n" ); return buf;}bool http_auth_challenge_digest( http_auth_session_t *sess, http_auth_chall_t *parms ) { struct md5_ctx a1, tmp; unsigned char a1_md5[16], tmp_md5[16]; char tmp_md5_ascii[33]; /* Do we understand this challenge? */ if( parms->alg == http_auth_alg_unknown ) { DEBUG( DEBUG_HTTPAUTH, "Unknown algorithm.\n" ); return false; } if( (parms->alg == http_auth_alg_md5_sess) && !( parms->qop_auth || parms->qop_auth_int ) ) { DEBUG( DEBUG_HTTPAUTH, "Server did not give qop with MD5-session alg.\n" ); return false; } if( (parms->realm==NULL) || (parms->nonce==NULL) ) { DEBUG( DEBUG_HTTPAUTH, "Challenge missing nonce or realm.\n" ); return false; } DEBUG( DEBUG_HTTPAUTH, "In digest challenge.\n" ); sess->alg = parms->alg; sess->scheme = http_auth_scheme_digest; sess->unq_realm = strstrip(parms->realm, '"' ); sess->unq_nonce = strstrip(parms->nonce, '"' ); sess->unq_cnonce = http_auth_get_cnonce(); if( parms->opaque != NULL ) { sess->opaque = strdup( parms->opaque ); /* don't strip the quotes */ } if( parms->got_qop ) { /* What type of qop are we to apply to the message? */ DEBUG( DEBUG_HTTPAUTH, "Got qop directive.\n" ); sess->nonce_count = 0; if( parms->qop_auth_int ) { sess->qop = http_auth_qop_auth_int; } else { sess->qop = http_auth_qop_auth; } } else { /* No qop at all/ */ sess->qop = http_auth_qop_none; } if( sess->alg == http_auth_alg_md5_sess ) { /* Calculate the session H(A1) * tmp = H( unq(username-value) ":" unq(realm-value) ":" passwd ) */ DEBUG( DEBUG_HTTPAUTH, "Calculating H(A1) for session.\n" ); md5_init_ctx( &a1 ); md5_init_ctx( &tmp ); md5_process_bytes( sess->username, strlen(sess->username), &tmp); md5_process_bytes( ":", 1, &tmp ); md5_process_bytes( sess->unq_realm, strlen(sess->unq_realm), &tmp ); md5_process_bytes( ":", 1, &tmp ); md5_process_bytes( sess->password, strlen(sess->password), &tmp); md5_finish_ctx( &tmp, tmp_md5 ); md5_hexify( tmp_md5, tmp_md5_ascii ); /* Now we calculate A1 proper: * A1 = H( ...above...) ":" unq(nonce-value) ":" unq(cnonce-value) */ md5_process_bytes( tmp_md5_ascii, 32, &a1 ); md5_process_bytes( ":", 1, &a1 ); md5_process_bytes( sess->unq_nonce, strlen(sess->unq_nonce), &a1 ); md5_process_bytes( ":", 1, &a1 ); md5_process_bytes( sess->unq_cnonce, strlen(sess->unq_cnonce), &a1 ); md5_finish_ctx( &a1, a1_md5 ); md5_hexify( a1_md5, sess->h_a1 ); DEBUG( DEBUG_HTTPAUTH, "Session H(A1) is [%s]\n", sess->h_a1 ); } DEBUG( DEBUG_HTTPAUTH, "I like this Digest challenge.\n" ); return true;}/* Return Digest authentication credentials header value for the given * session. */char *http_auth_request_digest( http_auth_session_t *sess ) { struct md5_ctx a1, a2, rdig; unsigned char a1_md5[16], a2_md5[16], rdig_md5[16]; char a1_md5_ascii[33], a2_md5_ascii[33], rdig_md5_ascii[33]; char nc_value[9] = {0}, *ret; const char *qop_value; /* qop-value */ size_t retlen; /* Increase the nonce-count */ if( sess->qop != http_auth_qop_none ) { sess->nonce_count++; sprintf( nc_value, "%08x", sess->nonce_count ); DEBUG( DEBUG_HTTPAUTH, "Nonce count is %d, nc is [%s]\n", sess->nonce_count, nc_value ); } qop_value = http_auth_qop_values[sess->qop]; /* If we are not using MD5-session, calculate H(A1) etc */ if( sess->alg == http_auth_alg_md5 ) { md5_init_ctx( &a1 ); md5_process_bytes( sess->username, strlen( sess->username ), &a1 ); md5_process_bytes( ":", 1, &a1 ); md5_process_bytes( sess->unq_realm, strlen( sess->unq_realm ), &a1 ); md5_process_bytes( ":", 1, &a1 ); md5_process_bytes( sess->password, strlen( sess->password ), &a1 ); md5_finish_ctx( &a1, a1_md5 ); md5_hexify( a1_md5, a1_md5_ascii ); DEBUG( DEBUG_HTTPAUTH, "New H(A1): %s\n", a1_md5_ascii ); } /* Otherwise, we reuse the session H(A1) */ /* Calculate H(A2). */ md5_init_ctx( &a2 ); md5_process_bytes( sess->method, strlen(sess->method), &a2 ); md5_process_bytes( ":", 1, &a2 ); md5_process_bytes( sess->uri, strlen(sess->uri), &a2 ); if( sess->qop == http_auth_qop_auth_int ) { /* Calculate H(entity-body) */ if( sess->got_body ) { char tmp_md5_ascii[33], tmp_md5[16]; if( sess->body_stream != NULL ) { DEBUG( DEBUG_HTTPAUTH, "Digesting body stream.\n" ); md5_stream( sess->body_stream, tmp_md5 ); rewind( sess->body_stream ); /* leave it at the beginning */ } else if( sess->body_buffer ) { DEBUG( DEBUG_HTTPAUTH, "Digesting body buffer.\n" ); md5_buffer( sess->body_buffer, strlen(sess->body_buffer), tmp_md5 ); } md5_hexify( tmp_md5, tmp_md5_ascii ); DEBUG( DEBUG_HTTPAUTH, "H(entity-body) is [%s]\n", tmp_md5_ascii ); /* Append to A2 */ md5_process_bytes( ":", 1, &a2 ); md5_process_bytes( tmp_md5_ascii, 32, &a2 ); } else { /* No entity-body. */ DEBUG( DEBUG_HTTPAUTH, "Digesting empty entity-body.\n" ); md5_process_bytes( ":d41d8cd98f00b204e9800998ecf8427e", 33, &a2 ); } } md5_finish_ctx( &a2, a2_md5 ); md5_hexify( a2_md5, a2_md5_ascii ); DEBUG( DEBUG_HTTPAUTH, "H(A2): %s\n", a2_md5_ascii ); DEBUG( DEBUG_HTTPAUTH, "Calculating Request-Digest.\n" ); /* Now, calculation of the Request-Digest. * The first section is the regardless of qop value * H(A1) ":" unq(nonce-value) ":" */ md5_init_ctx( &rdig ); if( sess->alg == http_auth_alg_md5 ) { /* Use the calculated H(A1) */ md5_process_bytes( a1_md5_ascii, 32, &rdig ); } else { /* Use the session H(A1) */ md5_process_bytes( sess->h_a1, 32, &rdig ); } md5_process_bytes( ":", 1, &rdig ); md5_process_bytes( sess->unq_nonce, strlen(sess->unq_nonce), &rdig ); md5_process_bytes( ":", 1, &rdig ); if( sess->qop != http_auth_qop_none ) { /* Add on: * nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" */ DEBUG( DEBUG_HTTPAUTH, "Have qop directive, digesting: [%s:%s:%s]\n", nc_value, sess->unq_cnonce, qop_value ); md5_process_bytes( nc_value, 8, &rdig ); md5_process_bytes( ":", 1, &rdig ); md5_process_bytes( sess->unq_cnonce, strlen(sess->unq_cnonce), &rdig ); md5_process_bytes( ":", 1, &rdig ); /* Store a copy of this structure */ sess->stored_rdig = rdig; md5_process_bytes( qop_value, strlen(qop_value), &rdig ); md5_process_bytes( ":", 1, &rdig ); } else { /* Store a copy of this structure */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -