Blame src/HttpAuth.cc

Packit Service a2489d
/*
Packit Service a2489d
 * lftp - file transfer program
Packit Service a2489d
 *
Packit Service a2489d
 * Copyright (c) 2016 by Alexander V. Lukyanov (lav@yars.free.net)
Packit Service a2489d
 *
Packit Service a2489d
 * This program is free software; you can redistribute it and/or modify
Packit Service a2489d
 * it under the terms of the GNU General Public License as published by
Packit Service a2489d
 * the Free Software Foundation; either version 3 of the License, or
Packit Service a2489d
 * (at your option) any later version.
Packit Service a2489d
 *
Packit Service a2489d
 * This program is distributed in the hope that it will be useful,
Packit Service a2489d
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2489d
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2489d
 * GNU General Public License for more details.
Packit Service a2489d
 *
Packit Service a2489d
 * You should have received a copy of the GNU General Public License
Packit Service a2489d
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service a2489d
 */
Packit Service a2489d
Packit Service a2489d
#include <config.h>
Packit Service a2489d
#include "HttpAuth.h"
Packit Service a2489d
#include "md5.h"
Packit Service a2489d
Packit Service a2489d
xarray_p<HttpAuth> HttpAuth::cache;
Packit Service a2489d
Packit Service a2489d
HttpAuth::Challenge::Challenge(const char *p_chal)
Packit Service a2489d
   : scheme_code(HttpAuth::NONE)
Packit Service a2489d
{
Packit Service a2489d
   const char *end=p_chal+strlen(p_chal);
Packit Service a2489d
Packit Service a2489d
   // challenge   = auth-scheme 1*SP 1#auth-param
Packit Service a2489d
   const char *space=strchr(p_chal,' ');
Packit Service a2489d
   if(!space || space==p_chal)
Packit Service a2489d
      return;
Packit Service a2489d
   scheme.nset(p_chal,space-p_chal).c_ucfirst();
Packit Service a2489d
Packit Service a2489d
   // auth-param     = token "=" ( token | quoted-string )
Packit Service a2489d
   const char *auth_param=space+1;
Packit Service a2489d
   while(auth_param
Packit Service a2489d
      const char *eq=strchr(auth_param,'=');
Packit Service a2489d
      xstring& key=xstring::get_tmp(auth_param,eq-auth_param).c_lc();
Packit Service a2489d
      SetParam(key,HttpHeader::extract_quoted_value(eq+1,&space));
Packit Service a2489d
      while(space
Packit Service a2489d
	 ++space;
Packit Service a2489d
      auth_param=space;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(scheme.eq("Basic"))
Packit Service a2489d
      scheme_code=HttpAuth::BASIC;
Packit Service a2489d
   else if(scheme.eq("Digest"))
Packit Service a2489d
      scheme_code=HttpAuth::DIGEST;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool HttpAuth::New(target_t t,const char *p_uri,Challenge *p_chal,const char *p_user,const char *p_pass)
Packit Service a2489d
{
Packit Service a2489d
   Ref<Challenge> chal(p_chal);
Packit Service a2489d
   Ref<HttpAuth> auth;
Packit Service a2489d
   switch(chal->GetSchemeCode()) {
Packit Service a2489d
   case BASIC:
Packit Service a2489d
      auth=new HttpAuthBasic(t,p_uri,chal.borrow(),p_user,p_pass);
Packit Service a2489d
      break;
Packit Service a2489d
   case DIGEST:
Packit Service a2489d
      auth=new HttpAuthDigest(t,p_uri,chal.borrow(),p_user,p_pass);
Packit Service a2489d
      break;
Packit Service a2489d
   case NONE:
Packit Service a2489d
      return false;
Packit Service a2489d
   }
Packit Service a2489d
   if(!auth->IsValid())
Packit Service a2489d
      return false;
Packit Service a2489d
   CleanCache(t,p_uri,p_user);
Packit Service a2489d
   cache.append(auth.borrow());
Packit Service a2489d
   return true;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool HttpAuth::Matches(target_t t,const char *p_uri,const char *p_user)
Packit Service a2489d
{
Packit Service a2489d
   if(this->target!=t)
Packit Service a2489d
      return false;
Packit Service a2489d
   if(this->user.ne(p_user))
Packit Service a2489d
      return false;
Packit Service a2489d
   if(!uri.prefixes(p_uri))
Packit Service a2489d
      return false;
Packit Service a2489d
   return true;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void HttpAuth::CleanCache(target_t t,const char *p_uri,const char *p_user)
Packit Service a2489d
{
Packit Service a2489d
   for(int i=cache.length()-1; i>=0; i--) {
Packit Service a2489d
      if(cache[i]->Matches(t,p_uri,p_user))
Packit Service a2489d
	 cache.remove(i);
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
HttpAuth *HttpAuth::Get(target_t t,const char *p_uri,const char *p_user)
Packit Service a2489d
{
Packit Service a2489d
   for(int i=cache.length()-1; i>=0; i--) {
Packit Service a2489d
      if(cache[i]->Matches(t,p_uri,p_user))
Packit Service a2489d
	 return cache[i];
Packit Service a2489d
   }
Packit Service a2489d
   return 0;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
xstring& HttpAuth::append_quoted(xstring& s,const char *n,const char *v)
Packit Service a2489d
{
Packit Service a2489d
   if(!v)
Packit Service a2489d
      return s;
Packit Service a2489d
   if(s.length()>0 && s.last_char()!=' ')
Packit Service a2489d
      s.append(',');
Packit Service a2489d
   s.append(n).append('=');
Packit Service a2489d
   return HttpHeader::append_quoted_value(s,v);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void HttpAuthBasic::MakeHeader()
Packit Service a2489d
{
Packit Service a2489d
   xstring& auth=xstring::get_tmp(user).append(':').append(pass);
Packit Service a2489d
   char *buf64=string_alloca(base64_length(auth.length())+1);
Packit Service a2489d
   base64_encode(auth,buf64,auth.length());
Packit Service a2489d
   header.SetValue(auth.set("Basic ").append(buf64));
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void HttpAuthDigest::MakeHA1()
Packit Service a2489d
{
Packit Service a2489d
   const xstring& realm=chal->GetParam("realm");
Packit Service a2489d
   const xstring& nonce=chal->GetParam("nonce");
Packit Service a2489d
   if(!realm || !nonce)
Packit Service a2489d
      return; // required
Packit Service a2489d
Packit Service a2489d
   // generate random client nonce
Packit Service a2489d
   cnonce.truncate();
Packit Service a2489d
   for(int i=0; i<8; i++)
Packit Service a2489d
      cnonce.appendf("%02x",unsigned(random()/13%256));
Packit Service a2489d
Packit Service a2489d
   struct md5_ctx ctx;
Packit Service a2489d
   md5_init_ctx (&ctx;;
Packit Service a2489d
   md5_process_bytes (user, user.length(), &ctx;;
Packit Service a2489d
   md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
   md5_process_bytes (realm, realm.length(), &ctx;;
Packit Service a2489d
   md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
   md5_process_bytes (pass, pass.length(), &ctx;;
Packit Service a2489d
Packit Service a2489d
   xstring buf;
Packit Service a2489d
   buf.get_space(MD5_DIGEST_SIZE);
Packit Service a2489d
   md5_finish_ctx (&ctx, buf.get_non_const());
Packit Service a2489d
   buf.set_length(MD5_DIGEST_SIZE);
Packit Service a2489d
Packit Service a2489d
   if(chal->GetParam("algorithm").eq("MD5-sess")) {
Packit Service a2489d
      md5_init_ctx (&ctx;;
Packit Service a2489d
      md5_process_bytes (buf, buf.length(), &ctx;;
Packit Service a2489d
      md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
      md5_process_bytes (nonce, nonce.length(), &ctx;;
Packit Service a2489d
      md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
      md5_process_bytes (cnonce, cnonce.length(), &ctx;;
Packit Service a2489d
      md5_finish_ctx (&ctx, buf.get_non_const());
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   // lower-case hex encoding
Packit Service a2489d
   HA1.truncate();
Packit Service a2489d
   buf.hexdump_to(HA1);
Packit Service a2489d
   HA1.c_lc();
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool HttpAuthDigest::Update(const char *p_method,const char *p_uri,const char *entity_hash)
Packit Service a2489d
{
Packit Service a2489d
   const xstring& qop_options=chal->GetParam("qop");
Packit Service a2489d
   xstring qop;
Packit Service a2489d
   if(qop_options) {
Packit Service a2489d
      // choose qop
Packit Service a2489d
      char *qop_options_split=alloca_strdup(qop_options);
Packit Service a2489d
      for(char *qop1=strtok(qop_options_split,","); qop1; qop1=strtok(NULL,",")) {
Packit Service a2489d
	 if(!strcmp(qop1,"auth-int") &&	entity_hash) {
Packit Service a2489d
	    qop.set(qop1);
Packit Service a2489d
	    break;
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!strcmp(qop1,"auth")) {
Packit Service a2489d
	    qop.set(qop1);
Packit Service a2489d
	    if(!entity_hash)
Packit Service a2489d
	       break;
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   if(qop_options && !qop)
Packit Service a2489d
      return false;  // no suitable qop found
Packit Service a2489d
Packit Service a2489d
   // calculate H(A2)
Packit Service a2489d
   struct md5_ctx ctx;
Packit Service a2489d
   md5_init_ctx (&ctx;;
Packit Service a2489d
   md5_process_bytes (p_method, strlen(p_method), &ctx;;
Packit Service a2489d
   md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
   md5_process_bytes (p_uri, strlen(p_uri), &ctx;;
Packit Service a2489d
Packit Service a2489d
   if(qop.eq("auth-int")) {
Packit Service a2489d
      md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
      md5_process_bytes (entity_hash, strlen(entity_hash), &ctx;;
Packit Service a2489d
   };
Packit Service a2489d
Packit Service a2489d
   xstring buf;
Packit Service a2489d
   buf.get_space(MD5_DIGEST_SIZE);
Packit Service a2489d
   md5_finish_ctx (&ctx, buf.get_non_const());
Packit Service a2489d
   buf.set_length(MD5_DIGEST_SIZE);
Packit Service a2489d
   xstring HA2;
Packit Service a2489d
   buf.hexdump_to(HA2);
Packit Service a2489d
   HA2.c_lc();
Packit Service a2489d
Packit Service a2489d
   // calculate response
Packit Service a2489d
   md5_init_ctx (&ctx;;
Packit Service a2489d
   md5_process_bytes (HA1, HA1.length(), &ctx;;
Packit Service a2489d
   md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
   const xstring& nonce=chal->GetParam("nonce");
Packit Service a2489d
   md5_process_bytes (nonce, nonce.length(), &ctx;;
Packit Service a2489d
   md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
   char nc_buf[9];
Packit Service a2489d
   if(qop) {
Packit Service a2489d
      sprintf(nc_buf,"%08x",++nc);
Packit Service a2489d
      md5_process_bytes (nc_buf, strlen(nc_buf), &ctx;;
Packit Service a2489d
      md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
      md5_process_bytes (cnonce, cnonce.length(), &ctx;;
Packit Service a2489d
      md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
      md5_process_bytes (qop, qop.length(), &ctx;;
Packit Service a2489d
      md5_process_bytes (":", 1, &ctx;;
Packit Service a2489d
   }
Packit Service a2489d
   md5_process_bytes (HA2, HA2.length(), &ctx;;
Packit Service a2489d
   md5_finish_ctx (&ctx, buf.get_non_const());
Packit Service a2489d
   xstring digest;
Packit Service a2489d
   buf.hexdump_to(digest);
Packit Service a2489d
   digest.c_lc();
Packit Service a2489d
Packit Service a2489d
   xstring auth("Digest ");
Packit Service a2489d
   append_quoted(auth,"username",user);
Packit Service a2489d
   append_quoted(auth,"realm",chal->GetParam("realm"));
Packit Service a2489d
   append_quoted(auth,"nonce",nonce);
Packit Service a2489d
   append_quoted(auth,"uri",p_uri);
Packit Service a2489d
   append_quoted(auth,"response",digest);
Packit Service a2489d
Packit Service a2489d
   append_quoted(auth,"algorithm",chal->GetParam("algorithm"));
Packit Service a2489d
   append_quoted(auth,"opaque",chal->GetParam("opaque"));
Packit Service a2489d
Packit Service a2489d
   if(qop) {
Packit Service a2489d
      auth.append(",qop=").append(qop);
Packit Service a2489d
      append_quoted(auth,"cnonce",cnonce);
Packit Service a2489d
      auth.append(",nc=").append(nc_buf);
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   header.SetValue(auth);
Packit Service a2489d
   return true;
Packit Service a2489d
}