Blame src/microhttpd/digestauth.c

Packit 875988
/*
Packit 875988
     This file is part of libmicrohttpd
Packit 875988
     Copyright (C) 2010, 2011, 2012, 2015 Daniel Pittman and Christian Grothoff
Packit 875988
Packit 875988
     This library is free software; you can redistribute it and/or
Packit 875988
     modify it under the terms of the GNU Lesser General Public
Packit 875988
     License as published by the Free Software Foundation; either
Packit 875988
     version 2.1 of the License, or (at your option) any later version.
Packit 875988
Packit 875988
     This library is distributed in the hope that it will be useful,
Packit 875988
     but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 875988
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 875988
     Lesser General Public License for more details.
Packit 875988
Packit 875988
     You should have received a copy of the GNU Lesser General Public
Packit 875988
     License along with this library; if not, write to the Free Software
Packit 875988
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 875988
*/
Packit 875988
/**
Packit 875988
 * @file digestauth.c
Packit 875988
 * @brief Implements HTTP digest authentication
Packit 875988
 * @author Amr Ali
Packit 875988
 * @author Matthieu Speder
Packit 875988
 */
Packit 875988
#include "platform.h"
Packit 875988
#include "mhd_limits.h"
Packit 875988
#include "internal.h"
Packit 875988
#include "md5.h"
Packit 875988
#include "mhd_mono_clock.h"
Packit 875988
#include "mhd_str.h"
Packit 875988
#include "mhd_compat.h"
Packit 875988
Packit 875988
#if defined(MHD_W32_MUTEX_)
Packit 875988
#ifndef WIN32_LEAN_AND_MEAN
Packit 875988
#define WIN32_LEAN_AND_MEAN 1
Packit 875988
#endif /* !WIN32_LEAN_AND_MEAN */
Packit 875988
#include <windows.h>
Packit 875988
#endif /* MHD_W32_MUTEX_ */
Packit 875988
Packit 875988
#define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
Packit 875988
/* 32 bit value is 4 bytes */
Packit 875988
#define TIMESTAMP_BIN_SIZE 4
Packit 875988
#define TIMESTAMP_HEX_LEN (2 * TIMESTAMP_BIN_SIZE)
Packit 875988
Packit 875988
/* Standard server nonce length, not including terminating null */
Packit 875988
#define NONCE_STD_LEN (HASH_MD5_HEX_LEN + TIMESTAMP_HEX_LEN)
Packit 875988
Packit 875988
/**
Packit 875988
 * Beginning string for any valid Digest authentication header.
Packit 875988
 */
Packit 875988
#define _BASE		"Digest "
Packit 875988
Packit 875988
/**
Packit 875988
 * Maximum length of a username for digest authentication.
Packit 875988
 */
Packit 875988
#define MAX_USERNAME_LENGTH 128
Packit 875988
Packit 875988
/**
Packit 875988
 * Maximum length of a realm for digest authentication.
Packit 875988
 */
Packit 875988
#define MAX_REALM_LENGTH 256
Packit 875988
Packit 875988
/**
Packit 875988
 * Maximum length of the response in digest authentication.
Packit 875988
 */
Packit 875988
#define MAX_AUTH_RESPONSE_LENGTH 128
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * convert bin to hex
Packit 875988
 *
Packit 875988
 * @param bin binary data
Packit 875988
 * @param len number of bytes in bin
Packit 875988
 * @param hex pointer to len*2+1 bytes
Packit 875988
 */
Packit 875988
static void
Packit 875988
cvthex (const unsigned char *bin,
Packit 875988
	size_t len,
Packit 875988
	char *hex)
Packit 875988
{
Packit 875988
  size_t i;
Packit 875988
  unsigned int j;
Packit 875988
Packit 875988
  for (i = 0; i < len; ++i)
Packit 875988
    {
Packit 875988
      j = (bin[i] >> 4) & 0x0f;
Packit 875988
      hex[i * 2] = (char)((j <= 9) ? (j + '0') : (j - 10 + 'a'));
Packit 875988
      j = bin[i] & 0x0f;
Packit 875988
      hex[i * 2 + 1] = (char)((j <= 9) ? (j + '0') : (j - 10 + 'a'));
Packit 875988
    }
Packit 875988
  hex[len * 2] = '\0';
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * calculate H(A1) as per RFC2617 spec and store the
Packit 875988
 * result in 'sessionkey'.
Packit 875988
 *
Packit 875988
 * @param alg The hash algorithm used, can be "md5" or "md5-sess"
Packit 875988
 * @param username A `char *' pointer to the username value
Packit 875988
 * @param realm A `char *' pointer to the realm value
Packit 875988
 * @param password A `char *' pointer to the password value
Packit 875988
 * @param nonce A `char *' pointer to the nonce value
Packit 875988
 * @param cnonce A `char *' pointer to the cnonce value
Packit 875988
 * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
Packit 875988
 */
Packit 875988
static void
Packit 875988
digest_calc_ha1 (const char *alg,
Packit 875988
		 const char *username,
Packit 875988
		 const char *realm,
Packit 875988
		 const char *password,
Packit 875988
		 const char *nonce,
Packit 875988
		 const char *cnonce,
Packit 875988
		 char sessionkey[HASH_MD5_HEX_LEN + 1])
Packit 875988
{
Packit 875988
  struct MD5Context md5;
Packit 875988
  unsigned char ha1[MD5_DIGEST_SIZE];
Packit 875988
Packit 875988
  MD5Init (&md5;;
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) username,
Packit 875988
             strlen (username));
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ":",
Packit 875988
             1);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) realm,
Packit 875988
             strlen (realm));
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ":",
Packit 875988
             1);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) password,
Packit 875988
             strlen (password));
Packit 875988
  MD5Final (ha1,
Packit 875988
            &md5;;
Packit 875988
  if (MHD_str_equal_caseless_(alg,
Packit 875988
                              "md5-sess"))
Packit 875988
    {
Packit 875988
      MD5Init (&md5;;
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) ha1,
Packit 875988
                 sizeof (ha1));
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) ":",
Packit 875988
                 1);
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) nonce,
Packit 875988
                 strlen (nonce));
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) ":",
Packit 875988
                 1);
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) cnonce,
Packit 875988
                 strlen (cnonce));
Packit 875988
      MD5Final (ha1,
Packit 875988
                &md5;;
Packit 875988
    }
Packit 875988
  cvthex (ha1,
Packit 875988
          sizeof (ha1),
Packit 875988
          sessionkey);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Calculate request-digest/response-digest as per RFC2617 spec
Packit 875988
 *
Packit 875988
 * @param ha1 H(A1)
Packit 875988
 * @param nonce nonce from server
Packit 875988
 * @param noncecount 8 hex digits
Packit 875988
 * @param cnonce client nonce
Packit 875988
 * @param qop qop-value: "", "auth" or "auth-int"
Packit 875988
 * @param method method from request
Packit 875988
 * @param uri requested URL
Packit 875988
 * @param hentity H(entity body) if qop="auth-int"
Packit 875988
 * @param response request-digest or response-digest
Packit 875988
 */
Packit 875988
static void
Packit 875988
digest_calc_response (const char ha1[HASH_MD5_HEX_LEN + 1],
Packit 875988
		      const char *nonce,
Packit 875988
		      const char *noncecount,
Packit 875988
		      const char *cnonce,
Packit 875988
		      const char *qop,
Packit 875988
		      const char *method,
Packit 875988
		      const char *uri,
Packit 875988
		      const char *hentity,
Packit 875988
		      char response[HASH_MD5_HEX_LEN + 1])
Packit 875988
{
Packit 875988
  struct MD5Context md5;
Packit 875988
  unsigned char ha2[MD5_DIGEST_SIZE];
Packit 875988
  unsigned char resphash[MD5_DIGEST_SIZE];
Packit 875988
  char ha2hex[HASH_MD5_HEX_LEN + 1];
Packit 875988
  (void)hentity; /* Unused. Silent compiler warning. */
Packit 875988
Packit 875988
  MD5Init (&md5;;
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) method,
Packit 875988
             strlen (method));
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ":",
Packit 875988
             1);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) uri,
Packit 875988
             strlen (uri));
Packit 875988
#if 0
Packit 875988
  if (0 == strcasecmp(qop,
Packit 875988
                      "auth-int"))
Packit 875988
    {
Packit 875988
      /* This is dead code since the rest of this module does
Packit 875988
	 not support auth-int. */
Packit 875988
      MD5Update (&md5,
Packit 875988
                 ":",
Packit 875988
                 1);
Packit 875988
      if (NULL != hentity)
Packit 875988
	MD5Update (&md5,
Packit 875988
                   hentity,
Packit 875988
                   strlen (hentity));
Packit 875988
    }
Packit 875988
#endif
Packit 875988
  MD5Final (ha2,
Packit 875988
            &md5;;
Packit 875988
  cvthex (ha2,
Packit 875988
          MD5_DIGEST_SIZE,
Packit 875988
          ha2hex);
Packit 875988
  MD5Init (&md5;;
Packit 875988
  /* calculate response */
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ha1,
Packit 875988
             HASH_MD5_HEX_LEN);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ":",
Packit 875988
             1);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) nonce,
Packit 875988
             strlen (nonce));
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char*) ":",
Packit 875988
             1);
Packit 875988
  if ('\0' != *qop)
Packit 875988
    {
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) noncecount,
Packit 875988
                 strlen (noncecount));
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) ":",
Packit 875988
                 1);
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) cnonce,
Packit 875988
                 strlen (cnonce));
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) ":",
Packit 875988
                 1);
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) qop,
Packit 875988
                 strlen (qop));
Packit 875988
      MD5Update (&md5,
Packit 875988
                 (const unsigned char *) ":",
Packit 875988
                 1);
Packit 875988
    }
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ha2hex,
Packit 875988
             HASH_MD5_HEX_LEN);
Packit 875988
  MD5Final (resphash,
Packit 875988
            &md5;;
Packit 875988
  cvthex (resphash,
Packit 875988
          sizeof(resphash),
Packit 875988
          response);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Lookup subvalue off of the HTTP Authorization header.
Packit 875988
 *
Packit 875988
 * A description of the input format for 'data' is at
Packit 875988
 * http://en.wikipedia.org/wiki/Digest_access_authentication
Packit 875988
 *
Packit 875988
 *
Packit 875988
 * @param dest where to store the result (possibly truncated if
Packit 875988
 *             the buffer is not big enough).
Packit 875988
 * @param size size of dest
Packit 875988
 * @param data pointer to the Authorization header
Packit 875988
 * @param key key to look up in data
Packit 875988
 * @return size of the located value, 0 if otherwise
Packit 875988
 */
Packit 875988
static size_t
Packit 875988
lookup_sub_value (char *dest,
Packit 875988
		  size_t size,
Packit 875988
		  const char *data,
Packit 875988
		  const char *key)
Packit 875988
{
Packit 875988
  size_t keylen;
Packit 875988
  size_t len;
Packit 875988
  const char *ptr;
Packit 875988
  const char *eq;
Packit 875988
  const char *q1;
Packit 875988
  const char *q2;
Packit 875988
  const char *qn;
Packit 875988
Packit 875988
  if (0 == size)
Packit 875988
    return 0;
Packit 875988
  keylen = strlen (key);
Packit 875988
  ptr = data;
Packit 875988
  while ('\0' != *ptr)
Packit 875988
    {
Packit 875988
      if (NULL == (eq = strchr (ptr,
Packit 875988
                                '=')))
Packit 875988
	return 0;
Packit 875988
      q1 = eq + 1;
Packit 875988
      while (' ' == *q1)
Packit 875988
	q1++;
Packit 875988
      if ('\"' != *q1)
Packit 875988
	{
Packit 875988
	  q2 = strchr (q1,
Packit 875988
                       ',');
Packit 875988
	  qn = q2;
Packit 875988
	}
Packit 875988
      else
Packit 875988
	{
Packit 875988
	  q1++;
Packit 875988
	  q2 = strchr (q1,
Packit 875988
                       '\"');
Packit 875988
	  if (NULL == q2)
Packit 875988
	    return 0; /* end quote not found */
Packit 875988
	  qn = q2 + 1;
Packit 875988
	}
Packit 875988
      if ( (MHD_str_equal_caseless_n_(ptr,
Packit 875988
                                      key,
Packit 875988
                                      keylen)) &&
Packit 875988
	   (eq == &ptr[keylen]) )
Packit 875988
	{
Packit 875988
	  if (NULL == q2)
Packit 875988
	    {
Packit 875988
	      len = strlen (q1) + 1;
Packit 875988
	      if (size > len)
Packit 875988
		size = len;
Packit 875988
	      size--;
Packit 875988
	      strncpy (dest,
Packit 875988
		       q1,
Packit 875988
		       size);
Packit 875988
	      dest[size] = '\0';
Packit 875988
	      return size;
Packit 875988
	    }
Packit 875988
	  else
Packit 875988
	    {
Packit 875988
	      if (size > (size_t) ((q2 - q1) + 1))
Packit 875988
		size = (q2 - q1) + 1;
Packit 875988
	      size--;
Packit 875988
	      memcpy (dest,
Packit 875988
		      q1,
Packit 875988
		      size);
Packit 875988
	      dest[size] = '\0';
Packit 875988
	      return size;
Packit 875988
	    }
Packit 875988
	}
Packit 875988
      if (NULL == qn)
Packit 875988
	return 0;
Packit 875988
      ptr = strchr (qn,
Packit 875988
                    ',');
Packit 875988
      if (NULL == ptr)
Packit 875988
	return 0;
Packit 875988
      ptr++;
Packit 875988
      while (' ' == *ptr)
Packit 875988
	ptr++;
Packit 875988
    }
Packit 875988
  return 0;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Check nonce-nc map array with either new nonce counter
Packit 875988
 * or a whole new nonce.
Packit 875988
 *
Packit 875988
 * @param connection The MHD connection structure
Packit 875988
 * @param nonce A pointer that referenced a zero-terminated array of nonce
Packit 875988
 * @param nc The nonce counter, zero to add the nonce to the array
Packit 875988
 * @return #MHD_YES if successful, #MHD_NO if invalid (or we have no NC array)
Packit 875988
 */
Packit 875988
static int
Packit 875988
check_nonce_nc (struct MHD_Connection *connection,
Packit 875988
		const char *nonce,
Packit 875988
		uint64_t nc)
Packit 875988
{
Packit 875988
  struct MHD_Daemon *daemon = connection->daemon;
Packit 875988
  struct MHD_NonceNc *nn;
Packit 875988
  uint32_t off;
Packit 875988
  uint32_t mod;
Packit 875988
  const char *np;
Packit 875988
Packit 875988
  if (MAX_NONCE_LENGTH <= strlen (nonce))
Packit 875988
    return MHD_NO; /* This should be impossible, but static analysis
Packit 875988
                      tools have a hard time with it *and* this also
Packit 875988
                      protects against unsafe modifications that may
Packit 875988
                      happen in the future... */
Packit 875988
  mod = daemon->nonce_nc_size;
Packit 875988
  if (0 == mod)
Packit 875988
    return MHD_NO; /* no array! */
Packit 875988
  /* super-fast xor-based "hash" function for HT lookup in nonce array */
Packit 875988
  off = 0;
Packit 875988
  np = nonce;
Packit 875988
  while ('\0' != *np)
Packit 875988
    {
Packit 875988
      off = (off << 8) | (*np ^ (off >> 24));
Packit 875988
      np++;
Packit 875988
    }
Packit 875988
  off = off % mod;
Packit 875988
  /*
Packit 875988
   * Look for the nonce, if it does exist and its corresponding
Packit 875988
   * nonce counter is less than the current nonce counter by 1,
Packit 875988
   * then only increase the nonce counter by one.
Packit 875988
   */
Packit 875988
  nn = &daemon->nnc[off];
Packit 875988
  MHD_mutex_lock_chk_ (&daemon->nnc_lock);
Packit 875988
  if (0 == nc)
Packit 875988
    {
Packit 875988
      /* Fresh nonce, reinitialize array */
Packit 875988
      strcpy (nn->nonce,
Packit 875988
	      nonce);
Packit 875988
      nn->nc = 0;
Packit 875988
      nn->nmask = 0;
Packit 875988
      MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
Packit 875988
      return MHD_YES;
Packit 875988
    }
Packit 875988
  /* Note that we use 64 here, as we do not store the
Packit 875988
     bit for 'nn->nc' itself in 'nn->nmask' */
Packit 875988
  if ( (nc < nn->nc) &&
Packit 875988
       (nc + 64 > nc /* checking for overflow */) &&
Packit 875988
       (nc + 64 >= nn->nc) &&
Packit 875988
       (0 == ((1LLU << (nn->nc - nc - 1)) & nn->nmask)) )
Packit 875988
    {
Packit 875988
      /* Out-of-order nonce, but within 64-bit bitmask, set bit */
Packit 875988
      nn->nmask |= (1LLU << (nn->nc - nc - 1));
Packit 875988
      MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
Packit 875988
      return MHD_YES;
Packit 875988
    }
Packit 875988
Packit 875988
  if ( (nc <= nn->nc) ||
Packit 875988
       (0 != strcmp (nn->nonce,
Packit 875988
                     nonce)) )
Packit 875988
    {
Packit 875988
      /* Nonce does not match, fail */
Packit 875988
      MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (daemon,
Packit 875988
		_("Stale nonce received.  If this happens a lot, you should probably increase the size of the nonce array.\n"));
Packit 875988
#endif
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
  /* Nonce is larger, shift bitmask and bump limit */
Packit 875988
  if (64 > nc - nn->nc)
Packit 875988
    nn->nmask <<= (nc - nn->nc); /* small jump, less than mask width */
Packit 875988
  else
Packit 875988
    nn->nmask = 0; /* big jump, unset all bits in the mask */
Packit 875988
  nn->nc = nc;
Packit 875988
  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
Packit 875988
  return MHD_YES;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Get the username from the authorization header sent by the client
Packit 875988
 *
Packit 875988
 * @param connection The MHD connection structure
Packit 875988
 * @return NULL if no username could be found, a pointer
Packit 875988
 * 			to the username if found
Packit 875988
 * @warning Returned value must be freed by #MHD_free().
Packit 875988
 * @ingroup authentication
Packit 875988
 */
Packit 875988
char *
Packit 875988
MHD_digest_auth_get_username(struct MHD_Connection *connection)
Packit 875988
{
Packit 875988
  size_t len;
Packit 875988
  char user[MAX_USERNAME_LENGTH];
Packit 875988
  const char *header;
Packit 875988
Packit 875988
  if (NULL == (header =
Packit 875988
               MHD_lookup_connection_value (connection,
Packit 875988
                                            MHD_HEADER_KIND,
Packit 875988
                                            MHD_HTTP_HEADER_AUTHORIZATION)))
Packit 875988
    return NULL;
Packit 875988
  if (0 != strncmp (header,
Packit 875988
                    _BASE,
Packit 875988
                    MHD_STATICSTR_LEN_ (_BASE)))
Packit 875988
    return NULL;
Packit 875988
  header += MHD_STATICSTR_LEN_ (_BASE);
Packit 875988
  if (0 == (len = lookup_sub_value (user,
Packit 875988
				    sizeof (user),
Packit 875988
				    header,
Packit 875988
				    "username")))
Packit 875988
    return NULL;
Packit 875988
  return strdup (user);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Calculate the server nonce so that it mitigates replay attacks
Packit 875988
 * The current format of the nonce is ...
Packit 875988
 * H(timestamp ":" method ":" random ":" uri ":" realm) + Hex(timestamp)
Packit 875988
 *
Packit 875988
 * @param nonce_time The amount of time in seconds for a nonce to be invalid
Packit 875988
 * @param method HTTP method
Packit 875988
 * @param rnd A pointer to a character array for the random seed
Packit 875988
 * @param rnd_size The size of the random seed array @a rnd
Packit 875988
 * @param uri HTTP URI (in MHD, without the arguments ("?k=v")
Packit 875988
 * @param realm A string of characters that describes the realm of auth.
Packit 875988
 * @param nonce A pointer to a character array for the nonce to put in
Packit 875988
 */
Packit 875988
static void
Packit 875988
calculate_nonce (uint32_t nonce_time,
Packit 875988
		 const char *method,
Packit 875988
		 const char *rnd,
Packit 875988
		 size_t rnd_size,
Packit 875988
		 const char *uri,
Packit 875988
		 const char *realm,
Packit 875988
		 char nonce[NONCE_STD_LEN + 1])
Packit 875988
{
Packit 875988
  struct MD5Context md5;
Packit 875988
  unsigned char timestamp[TIMESTAMP_BIN_SIZE];
Packit 875988
  unsigned char tmpnonce[MD5_DIGEST_SIZE];
Packit 875988
  char timestamphex[TIMESTAMP_HEX_LEN + 1];
Packit 875988
Packit 875988
  MD5Init (&md5;;
Packit 875988
  timestamp[0] = (unsigned char)((nonce_time & 0xff000000) >> 0x18);
Packit 875988
  timestamp[1] = (unsigned char)((nonce_time & 0x00ff0000) >> 0x10);
Packit 875988
  timestamp[2] = (unsigned char)((nonce_time & 0x0000ff00) >> 0x08);
Packit 875988
  timestamp[3] = (unsigned char)((nonce_time & 0x000000ff));
Packit 875988
  MD5Update (&md5,
Packit 875988
             timestamp,
Packit 875988
             sizeof (timestamp));
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ":",
Packit 875988
             1);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) method,
Packit 875988
             strlen (method));
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ":",
Packit 875988
             1);
Packit 875988
  if (rnd_size > 0)
Packit 875988
    MD5Update (&md5,
Packit 875988
               (const unsigned char *) rnd,
Packit 875988
               rnd_size);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ":",
Packit 875988
             1);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) uri,
Packit 875988
             strlen (uri));
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) ":",
Packit 875988
             1);
Packit 875988
  MD5Update (&md5,
Packit 875988
             (const unsigned char *) realm,
Packit 875988
             strlen (realm));
Packit 875988
  MD5Final (tmpnonce,
Packit 875988
            &md5;;
Packit 875988
  cvthex (tmpnonce,
Packit 875988
          sizeof (tmpnonce),
Packit 875988
          nonce);
Packit 875988
  cvthex (timestamp,
Packit 875988
          sizeof (timestamp),
Packit 875988
          timestamphex);
Packit 875988
  strncat (nonce,
Packit 875988
           timestamphex,
Packit 875988
           8);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Test if the given key-value pair is in the headers for the
Packit 875988
 * given connection.
Packit 875988
 *
Packit 875988
 * @param connection the connection
Packit 875988
 * @param key the key
Packit 875988
 * @param value the value, can be NULL
Packit 875988
 * @param kind type of the header
Packit 875988
 * @return #MHD_YES if the key-value pair is in the headers,
Packit 875988
 *         #MHD_NO if not
Packit 875988
 */
Packit 875988
static int
Packit 875988
test_header (struct MHD_Connection *connection,
Packit 875988
	     const char *key,
Packit 875988
	     const char *value,
Packit 875988
	     enum MHD_ValueKind kind)
Packit 875988
{
Packit 875988
  struct MHD_HTTP_Header *pos;
Packit 875988
Packit 875988
  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
Packit 875988
    {
Packit 875988
      if (kind != pos->kind)
Packit 875988
	continue;
Packit 875988
      if (0 != strcmp (key,
Packit 875988
                       pos->header))
Packit 875988
	continue;
Packit 875988
      if ( (NULL == value) &&
Packit 875988
	   (NULL == pos->value) )
Packit 875988
	return MHD_YES;
Packit 875988
      if ( (NULL == value) ||
Packit 875988
	   (NULL == pos->value) ||
Packit 875988
	   (0 != strcmp (value,
Packit 875988
                         pos->value)) )
Packit 875988
	continue;
Packit 875988
      return MHD_YES;
Packit 875988
    }
Packit 875988
  return MHD_NO;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Check that the arguments given by the client as part
Packit 875988
 * of the authentication header match the arguments we
Packit 875988
 * got as part of the HTTP request URI.
Packit 875988
 *
Packit 875988
 * @param connection connections with headers to compare against
Packit 875988
 * @param args argument URI string (after "?" in URI)
Packit 875988
 * @return #MHD_YES if the arguments match,
Packit 875988
 *         #MHD_NO if not
Packit 875988
 */
Packit 875988
static int
Packit 875988
check_argument_match (struct MHD_Connection *connection,
Packit 875988
		      const char *args)
Packit 875988
{
Packit 875988
  struct MHD_HTTP_Header *pos;
Packit 875988
  char *argb;
Packit 875988
  unsigned int num_headers;
Packit 875988
  int ret;
Packit 875988
Packit 875988
  argb = strdup (args);
Packit 875988
  if (NULL == argb)
Packit 875988
    {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (connection->daemon,
Packit 875988
		_("Failed to allocate memory for copy of URI arguments\n"));
Packit 875988
#endif /* HAVE_MESSAGES */
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
  ret = MHD_parse_arguments_ (connection,
Packit 875988
			      MHD_GET_ARGUMENT_KIND,
Packit 875988
			      argb,
Packit 875988
			      &test_header,
Packit 875988
			      &num_headers);
Packit 875988
  free (argb);
Packit 875988
  if (MHD_YES != ret)
Packit 875988
    return MHD_NO;
Packit 875988
  /* also check that the number of headers matches */
Packit 875988
  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
Packit 875988
    {
Packit 875988
      if (MHD_GET_ARGUMENT_KIND != pos->kind)
Packit 875988
	continue;
Packit 875988
      num_headers--;
Packit 875988
    }
Packit 875988
  if (0 != num_headers)
Packit 875988
    {
Packit 875988
      /* argument count mismatch */
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
  return MHD_YES;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Authenticates the authorization header sent by the client
Packit 875988
 *
Packit 875988
 * @param connection The MHD connection structure
Packit 875988
 * @param realm The realm presented to the client
Packit 875988
 * @param username The username needs to be authenticated
Packit 875988
 * @param password The password used in the authentication
Packit 875988
 * @param nonce_timeout The amount of time for a nonce to be
Packit 875988
 * 			invalid in seconds
Packit 875988
 * @return #MHD_YES if authenticated, #MHD_NO if not,
Packit 875988
 * 			#MHD_INVALID_NONCE if nonce is invalid
Packit 875988
 * @ingroup authentication
Packit 875988
 */
Packit 875988
int
Packit 875988
MHD_digest_auth_check (struct MHD_Connection *connection,
Packit 875988
		       const char *realm,
Packit 875988
		       const char *username,
Packit 875988
		       const char *password,
Packit 875988
		       unsigned int nonce_timeout)
Packit 875988
{
Packit 875988
  struct MHD_Daemon *daemon = connection->daemon;
Packit 875988
  size_t len;
Packit 875988
  const char *header;
Packit 875988
  char nonce[MAX_NONCE_LENGTH];
Packit 875988
  char cnonce[MAX_NONCE_LENGTH];
Packit 875988
  char qop[15]; /* auth,auth-int */
Packit 875988
  char nc[20];
Packit 875988
  char response[MAX_AUTH_RESPONSE_LENGTH];
Packit 875988
  const char *hentity = NULL; /* "auth-int" is not supported */
Packit 875988
  char ha1[HASH_MD5_HEX_LEN + 1];
Packit 875988
  char respexp[HASH_MD5_HEX_LEN + 1];
Packit 875988
  char noncehashexp[NONCE_STD_LEN + 1];
Packit 875988
  uint32_t nonce_time;
Packit 875988
  uint32_t t;
Packit 875988
  size_t left; /* number of characters left in 'header' for 'uri' */
Packit 875988
  uint64_t nci;
Packit 875988
Packit 875988
  header = MHD_lookup_connection_value (connection,
Packit 875988
					MHD_HEADER_KIND,
Packit 875988
					MHD_HTTP_HEADER_AUTHORIZATION);
Packit 875988
  if (NULL == header)
Packit 875988
    return MHD_NO;
Packit 875988
  if (0 != strncmp (header,
Packit 875988
                    _BASE,
Packit 875988
                    MHD_STATICSTR_LEN_(_BASE)))
Packit 875988
    return MHD_NO;
Packit 875988
  header += MHD_STATICSTR_LEN_ (_BASE);
Packit 875988
  left = strlen (header);
Packit 875988
Packit 875988
  {
Packit 875988
    char un[MAX_USERNAME_LENGTH];
Packit 875988
Packit 875988
    len = lookup_sub_value (un,
Packit 875988
			    sizeof (un),
Packit 875988
			    header,
Packit 875988
                            "username");
Packit 875988
    if ( (0 == len) ||
Packit 875988
	 (0 != strcmp (username,
Packit 875988
                       un)) )
Packit 875988
      return MHD_NO;
Packit 875988
    left -= strlen ("username") + len;
Packit 875988
  }
Packit 875988
Packit 875988
  {
Packit 875988
    char r[MAX_REALM_LENGTH];
Packit 875988
Packit 875988
    len = lookup_sub_value (r,
Packit 875988
                            sizeof (r),
Packit 875988
                            header,
Packit 875988
                            "realm");
Packit 875988
    if ( (0 == len) ||
Packit 875988
	 (0 != strcmp (realm,
Packit 875988
                       r)) )
Packit 875988
      return MHD_NO;
Packit 875988
    left -= strlen ("realm") + len;
Packit 875988
  }
Packit 875988
Packit 875988
  if (0 == (len = lookup_sub_value (nonce,
Packit 875988
				    sizeof (nonce),
Packit 875988
				    header,
Packit 875988
                                    "nonce")))
Packit 875988
    return MHD_NO;
Packit 875988
  left -= strlen ("nonce") + len;
Packit 875988
  if (left > 32 * 1024)
Packit 875988
  {
Packit 875988
    /* we do not permit URIs longer than 32k, as we want to
Packit 875988
       make sure to not blow our stack (or per-connection
Packit 875988
       heap memory limit).  Besides, 32k is already insanely
Packit 875988
       large, but of course in theory the
Packit 875988
       #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
Packit 875988
       and would thus permit sending a >32k authorization
Packit 875988
       header value. */
Packit 875988
    return MHD_NO;
Packit 875988
  }
Packit 875988
  if (TIMESTAMP_HEX_LEN !=
Packit 875988
      MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_HEX_LEN,
Packit 875988
                             TIMESTAMP_HEX_LEN,
Packit 875988
                             &nonce_time))
Packit 875988
    {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (daemon,
Packit 875988
                _("Authentication failed, invalid timestamp format.\n"));
Packit 875988
#endif
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
  t = (uint32_t) MHD_monotonic_sec_counter();
Packit 875988
  /*
Packit 875988
   * First level vetting for the nonce validity: if the timestamp
Packit 875988
   * attached to the nonce exceeds `nonce_timeout', then the nonce is
Packit 875988
   * invalid.
Packit 875988
   */
Packit 875988
  if ( (t > nonce_time + nonce_timeout) ||
Packit 875988
       (nonce_time + nonce_timeout < nonce_time) )
Packit 875988
    {
Packit 875988
      /* too old */
Packit 875988
      return MHD_INVALID_NONCE;
Packit 875988
    }
Packit 875988
Packit 875988
  calculate_nonce (nonce_time,
Packit 875988
                   connection->method,
Packit 875988
                   daemon->digest_auth_random,
Packit 875988
                   daemon->digest_auth_rand_size,
Packit 875988
                   connection->url,
Packit 875988
                   realm,
Packit 875988
                   noncehashexp);
Packit 875988
  /*
Packit 875988
   * Second level vetting for the nonce validity
Packit 875988
   * if the timestamp attached to the nonce is valid
Packit 875988
   * and possibly fabricated (in case of an attack)
Packit 875988
   * the attacker must also know the random seed to be
Packit 875988
   * able to generate a "sane" nonce, which if he does
Packit 875988
   * not, the nonce fabrication process going to be
Packit 875988
   * very hard to achieve.
Packit 875988
   */
Packit 875988
Packit 875988
  if (0 != strcmp (nonce, noncehashexp))
Packit 875988
    {
Packit 875988
      return MHD_INVALID_NONCE;
Packit 875988
    }
Packit 875988
  if ( (0 == lookup_sub_value (cnonce,
Packit 875988
                               sizeof (cnonce),
Packit 875988
                               header,
Packit 875988
                               "cnonce")) ||
Packit 875988
       (0 == lookup_sub_value (qop,
Packit 875988
                               sizeof (qop),
Packit 875988
                               header,
Packit 875988
                               "qop")) ||
Packit 875988
       ( (0 != strcmp (qop,
Packit 875988
                       "auth")) &&
Packit 875988
         (0 != strcmp (qop,
Packit 875988
                       "")) ) ||
Packit 875988
       (0 == (len = lookup_sub_value (nc,
Packit 875988
                                      sizeof (nc),
Packit 875988
                                      header,
Packit 875988
                                      "nc")) )  ||
Packit 875988
       (0 == lookup_sub_value (response,
Packit 875988
                               sizeof (response),
Packit 875988
                               header,
Packit 875988
                               "response")) )
Packit 875988
    {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (daemon,
Packit 875988
		_("Authentication failed, invalid format.\n"));
Packit 875988
#endif
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
  if (len != MHD_strx_to_uint64_n_ (nc,
Packit 875988
                                    len,
Packit 875988
                                    &nci))
Packit 875988
    {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (daemon,
Packit 875988
		_("Authentication failed, invalid nc format.\n"));
Packit 875988
#endif
Packit 875988
      return MHD_NO; /* invalid nonce format */
Packit 875988
    }
Packit 875988
Packit 875988
  /*
Packit 875988
   * Checking if that combination of nonce and nc is sound
Packit 875988
   * and not a replay attack attempt. Also adds the nonce
Packit 875988
   * to the nonce-nc map if it does not exist there.
Packit 875988
   */
Packit 875988
  if (MHD_YES !=
Packit 875988
      check_nonce_nc (connection,
Packit 875988
                      nonce,
Packit 875988
                      nci))
Packit 875988
    {
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
Packit 875988
  {
Packit 875988
    char *uri;
Packit 875988
Packit 875988
    uri = malloc (left + 1);
Packit 875988
    if (NULL == uri)
Packit 875988
    {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG(daemon,
Packit 875988
               _("Failed to allocate memory for auth header processing\n"));
Packit 875988
#endif /* HAVE_MESSAGES */
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
    if (0 == lookup_sub_value (uri,
Packit 875988
                               left + 1,
Packit 875988
                               header,
Packit 875988
                               "uri"))
Packit 875988
    {
Packit 875988
      free (uri);
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
Packit 875988
    digest_calc_ha1 ("md5",
Packit 875988
                     username,
Packit 875988
                     realm,
Packit 875988
                     password,
Packit 875988
                     nonce,
Packit 875988
                     cnonce,
Packit 875988
                     ha1);
Packit 875988
    digest_calc_response (ha1,
Packit 875988
			  nonce,
Packit 875988
			  nc,
Packit 875988
			  cnonce,
Packit 875988
			  qop,
Packit 875988
			  connection->method,
Packit 875988
			  uri,
Packit 875988
			  hentity,
Packit 875988
			  respexp);
Packit 875988
Packit 875988
    /* Need to unescape URI before comparing with connection->url */
Packit 875988
    daemon->unescape_callback (daemon->unescape_callback_cls,
Packit 875988
                               connection,
Packit 875988
                               uri);
Packit 875988
    if (0 != strncmp (uri,
Packit 875988
		      connection->url,
Packit 875988
		      strlen (connection->url)))
Packit 875988
    {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (daemon,
Packit 875988
		_("Authentication failed, URI does not match.\n"));
Packit 875988
#endif
Packit 875988
      free (uri);
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
Packit 875988
    {
Packit 875988
      const char *args = strchr (uri,
Packit 875988
                                 '?');
Packit 875988
Packit 875988
      if (NULL == args)
Packit 875988
	args = "";
Packit 875988
      else
Packit 875988
	args++;
Packit 875988
      if (MHD_YES !=
Packit 875988
	  check_argument_match (connection,
Packit 875988
				args) )
Packit 875988
      {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
	MHD_DLOG (daemon,
Packit 875988
		  _("Authentication failed, arguments do not match.\n"));
Packit 875988
#endif
Packit 875988
       free (uri);
Packit 875988
       return MHD_NO;
Packit 875988
      }
Packit 875988
    }
Packit 875988
    free (uri);
Packit 875988
    return (0 == strcmp(response,
Packit 875988
                        respexp))
Packit 875988
      ? MHD_YES
Packit 875988
      : MHD_NO;
Packit 875988
  }
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Queues a response to request authentication from the client
Packit 875988
 *
Packit 875988
 * @param connection The MHD connection structure
Packit 875988
 * @param realm the realm presented to the client
Packit 875988
 * @param opaque string to user for opaque value
Packit 875988
 * @param response reply to send; should contain the "access denied"
Packit 875988
 *        body; note that this function will set the "WWW Authenticate"
Packit 875988
 *        header and that the caller should not do this
Packit 875988
 * @param signal_stale #MHD_YES if the nonce is invalid to add
Packit 875988
 * 			'stale=true' to the authentication header
Packit 875988
 * @return #MHD_YES on success, #MHD_NO otherwise
Packit 875988
 * @ingroup authentication
Packit 875988
 */
Packit 875988
int
Packit 875988
MHD_queue_auth_fail_response (struct MHD_Connection *connection,
Packit 875988
			      const char *realm,
Packit 875988
			      const char *opaque,
Packit 875988
			      struct MHD_Response *response,
Packit 875988
			      int signal_stale)
Packit 875988
{
Packit 875988
  int ret;
Packit 875988
  int hlen;
Packit 875988
  char nonce[NONCE_STD_LEN + 1];
Packit 875988
Packit 875988
  /* Generating the server nonce */
Packit 875988
  calculate_nonce ((uint32_t) MHD_monotonic_sec_counter(),
Packit 875988
		   connection->method,
Packit 875988
		   connection->daemon->digest_auth_random,
Packit 875988
		   connection->daemon->digest_auth_rand_size,
Packit 875988
		   connection->url,
Packit 875988
		   realm,
Packit 875988
		   nonce);
Packit 875988
  if (MHD_YES !=
Packit 875988
      check_nonce_nc (connection,
Packit 875988
                      nonce,
Packit 875988
                      0))
Packit 875988
    {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (connection->daemon,
Packit 875988
		_("Could not register nonce (is the nonce array size zero?).\n"));
Packit 875988
#endif
Packit 875988
      return MHD_NO;
Packit 875988
    }
Packit 875988
  /* Building the authentication header */
Packit 875988
  hlen = MHD_snprintf_ (NULL,
Packit 875988
                        0,
Packit 875988
                        "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
Packit 875988
                        realm,
Packit 875988
                        nonce,
Packit 875988
                        opaque,
Packit 875988
                        signal_stale
Packit 875988
                        ? ",stale=\"true\""
Packit 875988
                        : "");
Packit 875988
  if (hlen > 0)
Packit 875988
    {
Packit 875988
      char *header;
Packit 875988
Packit 875988
      header = MHD_calloc_ (1, hlen + 1);
Packit 875988
      if (NULL == header)
Packit 875988
        {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
          MHD_DLOG(connection->daemon,
Packit 875988
                   _("Failed to allocate memory for auth response header\n"));
Packit 875988
#endif /* HAVE_MESSAGES */
Packit 875988
          return MHD_NO;
Packit 875988
        }
Packit 875988
Packit 875988
      if (MHD_snprintf_ (header,
Packit 875988
                         hlen + 1,
Packit 875988
                         "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
Packit 875988
                         realm,
Packit 875988
                         nonce,
Packit 875988
                         opaque,
Packit 875988
                         signal_stale
Packit 875988
                         ? ",stale=\"true\""
Packit 875988
                         : "") == hlen)
Packit 875988
        ret = MHD_add_response_header(response,
Packit 875988
                                      MHD_HTTP_HEADER_WWW_AUTHENTICATE,
Packit 875988
                                      header);
Packit 875988
      else
Packit 875988
        ret = MHD_NO;
Packit 875988
      free (header);
Packit 875988
    }
Packit 875988
  else
Packit 875988
    ret = MHD_NO;
Packit 875988
Packit 875988
  if (MHD_YES == ret)
Packit 875988
    {
Packit 875988
      ret = MHD_queue_response (connection,
Packit 875988
                                MHD_HTTP_UNAUTHORIZED,
Packit 875988
                                response);
Packit 875988
    }
Packit 875988
  else
Packit 875988
    {
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (connection->daemon,
Packit 875988
                _("Failed to add Digest auth header\n"));
Packit 875988
#endif /* HAVE_MESSAGES */
Packit 875988
    }
Packit 875988
  return ret;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/* end of digestauth.c */