libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

gen_auth.c (23764B)


      1 /*
      2   This file is part of libmicrohttpd
      3   Copyright (C) 2022-2023 Evgeny Grin (Karlson2k)
      4 
      5   This library is free software; you can redistribute it and/or
      6   modify it under the terms of the GNU Lesser General Public
      7   License as published by the Free Software Foundation; either
      8   version 2.1 of the License, or (at your option) any later version.
      9 
     10   This library is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13   Lesser General Public License for more details.
     14 
     15   You should have received a copy of the GNU Lesser General Public
     16   License along with this library.
     17   If not, see <http://www.gnu.org/licenses/>.
     18 */
     19 
     20 /**
     21  * @file microhttpd/gen_auth.c
     22  * @brief  HTTP authorisation general functions
     23  * @author Karlson2k (Evgeny Grin)
     24  */
     25 
     26 #include "gen_auth.h"
     27 #include "internal.h"
     28 #include "connection.h"
     29 #include "mhd_str.h"
     30 #include "mhd_assert.h"
     31 
     32 #ifdef BAUTH_SUPPORT
     33 #include "basicauth.h"
     34 #endif /* BAUTH_SUPPORT */
     35 #ifdef DAUTH_SUPPORT
     36 #include "digestauth.h"
     37 #endif /* DAUTH_SUPPORT */
     38 
     39 #if ! defined(BAUTH_SUPPORT) && ! defined(DAUTH_SUPPORT)
     40 #error This file requires Basic or Digest authentication support
     41 #endif
     42 
     43 /**
     44  * Type of authorisation
     45  */
     46 enum MHD_AuthType
     47 {
     48   MHD_AUTHTYPE_NONE = 0,/**< No authorisation, unused */
     49   MHD_AUTHTYPE_BASIC,   /**< Basic Authorisation, RFC 7617  */
     50   MHD_AUTHTYPE_DIGEST,  /**< Digest Authorisation, RFC 7616 */
     51   MHD_AUTHTYPE_UNKNOWN, /**< Unknown/Unsupported authorisation type, unused */
     52   MHD_AUTHTYPE_INVALID  /**< Wrong/broken authorisation header, unused */
     53 };
     54 
     55 /**
     56  * Find required "Authorization" request header
     57  * @param c the connection with request
     58  * @param type the type of the authorisation: basic or digest
     59  * @param[out] auth_value will be set to the remaining of header value after
     60  *                        authorisation token (after "Basic " or "Digest ")
     61  * @return true if requested header is found,
     62  *         false otherwise
     63  */
     64 static bool
     65 find_auth_rq_header_ (const struct MHD_Connection *c, enum MHD_AuthType type,
     66                       struct _MHD_str_w_len *auth_value)
     67 {
     68   const struct MHD_HTTP_Req_Header *h;
     69   const char *token;
     70   size_t token_len;
     71 
     72   mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= c->state);
     73   if (MHD_CONNECTION_HEADERS_PROCESSED > c->state)
     74     return false;
     75 
     76 #ifdef DAUTH_SUPPORT
     77   if (MHD_AUTHTYPE_DIGEST == type)
     78   {
     79     token = _MHD_AUTH_DIGEST_BASE;
     80     token_len = MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE);
     81   }
     82   else /* combined with the next line */
     83 #endif /* DAUTH_SUPPORT */
     84 #ifdef BAUTH_SUPPORT
     85   if (MHD_AUTHTYPE_BASIC == type)
     86   {
     87     token = _MHD_AUTH_BASIC_BASE;
     88     token_len = MHD_STATICSTR_LEN_ (_MHD_AUTH_BASIC_BASE);
     89   }
     90   else /* combined with the next line */
     91 #endif /* BAUTH_SUPPORT */
     92   {
     93     mhd_assert (0);
     94     return false;
     95   }
     96 
     97   for (h = c->rq.headers_received; NULL != h; h = h->next)
     98   {
     99     if (MHD_HEADER_KIND != h->kind)
    100       continue;
    101     if (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_AUTHORIZATION) != h->header_size)
    102       continue;
    103     if (token_len > h->value_size)
    104       continue;
    105     if (! MHD_str_equal_caseless_bin_n_ (MHD_HTTP_HEADER_AUTHORIZATION,
    106                                          h->header,
    107                                          MHD_STATICSTR_LEN_ ( \
    108                                            MHD_HTTP_HEADER_AUTHORIZATION)))
    109       continue;
    110     if (! MHD_str_equal_caseless_bin_n_ (h->value, token, token_len))
    111       continue;
    112     /* Match only if token string is full header value or token is
    113      * followed by space or tab
    114      * Note: RFC 9110 (and RFC 7234) allows only space character, but
    115      * tab is supported here as well for additional flexibility and uniformity
    116      * as tabs are supported as separators between parameters.
    117      */
    118     if ((token_len == h->value_size) ||
    119         (' ' == h->value[token_len]) || ('\t'  == h->value[token_len]))
    120     {
    121       if (token_len != h->value_size)
    122       { /* Skip whitespace */
    123         auth_value->str = h->value + token_len + 1;
    124         auth_value->len = h->value_size - (token_len + 1);
    125       }
    126       else
    127       { /* No whitespace to skip */
    128         auth_value->str = h->value + token_len;
    129         auth_value->len = h->value_size - token_len;
    130       }
    131       return true; /* Found a match */
    132     }
    133   }
    134   return false; /* No matching header has been found */
    135 }
    136 
    137 
    138 #ifdef BAUTH_SUPPORT
    139 
    140 
    141 /**
    142  * Parse request Authorization header parameters for Basic Authentication
    143  * @param str the header string, everything after "Basic " substring
    144  * @param str_len the length of @a str in characters
    145  * @param[out] pbauth the pointer to the structure with Basic Authentication
    146  *               parameters
    147  * @return true if parameters has been successfully parsed,
    148  *         false if format of the @a str is invalid
    149  */
    150 static bool
    151 parse_bauth_params (const char *str,
    152                     size_t str_len,
    153                     struct MHD_RqBAuth *pbauth)
    154 {
    155   size_t i;
    156 
    157   i = 0;
    158 
    159   /* Skip all whitespaces at start */
    160   while (i < str_len && (' ' == str[i] || '\t' == str[i]))
    161     i++;
    162 
    163   if (str_len > i)
    164   {
    165     size_t token68_start;
    166     size_t token68_len;
    167 
    168     /* 'i' points to the first non-whitespace char after scheme token */
    169     token68_start = i;
    170     /* Find end of the token. Token cannot contain whitespace. */
    171     while (i < str_len && ' ' != str[i] && '\t' != str[i])
    172     {
    173       if (0 == str[i])
    174         return false;  /* Binary zero is not allowed */
    175       if ((',' == str[i]) || (';' == str[i]))
    176         return false;  /* Only single token68 is allowed */
    177       i++;
    178     }
    179     token68_len = i - token68_start;
    180     mhd_assert (0 != token68_len);
    181 
    182     /* Skip all whitespaces */
    183     while (i < str_len && (' ' == str[i] || '\t' == str[i]))
    184       i++;
    185     /* Check whether any garbage is present at the end of the string */
    186     if (str_len != i)
    187       return false;
    188     else
    189     {
    190       /* No more data in the string, only single token68. */
    191       pbauth->token68.str = str + token68_start;
    192       pbauth->token68.len = token68_len;
    193     }
    194   }
    195   return true;
    196 }
    197 
    198 
    199 /**
    200  * Return request's Basic Authorisation parameters.
    201  *
    202  * Function return result of parsing of the request's "Authorization" header or
    203  * returns cached parsing result if the header was already parsed for
    204  * the current request.
    205  * @param connection the connection to process
    206  * @return the pointer to structure with Authentication parameters,
    207  *         NULL if no memory in memory pool, if called too early (before
    208  *         header has been received) or if no valid Basic Authorisation header
    209  *         found.
    210  */
    211 const struct MHD_RqBAuth *
    212 MHD_get_rq_bauth_params_ (struct MHD_Connection *connection)
    213 {
    214   struct _MHD_str_w_len h_auth_value;
    215   struct MHD_RqBAuth *bauth;
    216 
    217   mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= connection->state);
    218 
    219   if (connection->rq.bauth_tried)
    220     return connection->rq.bauth;
    221 
    222   if (MHD_CONNECTION_HEADERS_PROCESSED > connection->state)
    223     return NULL;
    224 
    225   if (! find_auth_rq_header_ (connection, MHD_AUTHTYPE_BASIC, &h_auth_value))
    226   {
    227     connection->rq.bauth_tried = true;
    228     connection->rq.bauth = NULL;
    229     return NULL;
    230   }
    231 
    232   bauth =
    233     (struct MHD_RqBAuth *)
    234     MHD_connection_alloc_memory_ (connection, sizeof (struct MHD_RqBAuth));
    235 
    236   if (NULL == bauth)
    237   {
    238 #ifdef HAVE_MESSAGES
    239     MHD_DLOG (connection->daemon,
    240               _ ("Not enough memory in the connection's pool to allocate " \
    241                  "for Basic Authorization header parsing.\n"));
    242 #endif /* HAVE_MESSAGES */
    243     return NULL;
    244   }
    245 
    246   memset (bauth, 0, sizeof(struct MHD_RqBAuth));
    247   if (parse_bauth_params (h_auth_value.str, h_auth_value.len, bauth))
    248     connection->rq.bauth = bauth;
    249   else
    250   {
    251 #ifdef HAVE_MESSAGES
    252     MHD_DLOG (connection->daemon,
    253               _ ("The Basic Authorization client's header has "
    254                  "incorrect format.\n"));
    255 #endif /* HAVE_MESSAGES */
    256     connection->rq.bauth = NULL;
    257     /* Memory in the pool remains allocated until next request */
    258   }
    259   connection->rq.bauth_tried = true;
    260   return connection->rq.bauth;
    261 }
    262 
    263 
    264 #endif /* BAUTH_SUPPORT */
    265 
    266 #ifdef DAUTH_SUPPORT
    267 
    268 
    269 /**
    270  * Get client's Digest Authorization algorithm type.
    271  * If no algorithm is specified by client, MD5 is assumed.
    272  * @param params the Digest Authorization 'algorithm' parameter
    273  * @return the algorithm type
    274  */
    275 static enum MHD_DigestAuthAlgo3
    276 get_rq_dauth_algo (const struct MHD_RqDAuthParam *const algo_param)
    277 {
    278   if (NULL == algo_param->value.str)
    279     return MHD_DIGEST_AUTH_ALGO3_MD5; /* Assume MD5 by default */
    280 
    281   if (algo_param->quoted)
    282   {
    283     if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
    284                                                algo_param->value.len, \
    285                                                _MHD_MD5_TOKEN))
    286       return MHD_DIGEST_AUTH_ALGO3_MD5;
    287     if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
    288                                                algo_param->value.len, \
    289                                                _MHD_SHA256_TOKEN))
    290       return MHD_DIGEST_AUTH_ALGO3_SHA256;
    291     if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
    292                                                algo_param->value.len, \
    293                                                _MHD_MD5_TOKEN _MHD_SESS_TOKEN))
    294       return MHD_DIGEST_AUTH_ALGO3_SHA512_256;
    295     if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
    296                                                algo_param->value.len, \
    297                                                _MHD_SHA512_256_TOKEN \
    298                                                _MHD_SESS_TOKEN))
    299 
    300       /* Algorithms below are not supported by MHD for authentication */
    301 
    302       return MHD_DIGEST_AUTH_ALGO3_MD5_SESSION;
    303     if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
    304                                                algo_param->value.len, \
    305                                                _MHD_SHA256_TOKEN \
    306                                                _MHD_SESS_TOKEN))
    307       return MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION;
    308     if (MHD_str_equal_caseless_quoted_s_bin_n (algo_param->value.str, \
    309                                                algo_param->value.len, \
    310                                                _MHD_SHA512_256_TOKEN))
    311       return MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION;
    312 
    313     /* No known algorithm has been detected */
    314     return MHD_DIGEST_AUTH_ALGO3_INVALID;
    315   }
    316   /* The algorithm value is not quoted */
    317   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_MD5_TOKEN, \
    318                                        algo_param->value.str, \
    319                                        algo_param->value.len))
    320     return MHD_DIGEST_AUTH_ALGO3_MD5;
    321   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA256_TOKEN, \
    322                                        algo_param->value.str, \
    323                                        algo_param->value.len))
    324     return MHD_DIGEST_AUTH_ALGO3_SHA256;
    325   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA512_256_TOKEN, \
    326                                        algo_param->value.str, \
    327                                        algo_param->value.len))
    328     return MHD_DIGEST_AUTH_ALGO3_SHA512_256;
    329 
    330   /* Algorithms below are not supported by MHD for authentication */
    331 
    332   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_MD5_TOKEN _MHD_SESS_TOKEN, \
    333                                        algo_param->value.str, \
    334                                        algo_param->value.len))
    335     return MHD_DIGEST_AUTH_ALGO3_MD5_SESSION;
    336   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA256_TOKEN _MHD_SESS_TOKEN, \
    337                                        algo_param->value.str, \
    338                                        algo_param->value.len))
    339     return MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION;
    340   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA512_256_TOKEN _MHD_SESS_TOKEN, \
    341                                        algo_param->value.str, \
    342                                        algo_param->value.len))
    343     return MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION;
    344 
    345   /* No known algorithm has been detected */
    346   return MHD_DIGEST_AUTH_ALGO3_INVALID;
    347 }
    348 
    349 
    350 /**
    351  * Get QOP ('quality of protection') type.
    352  * @param qop_param the Digest Authorization 'QOP' parameter
    353  * @return detected QOP ('quality of protection') type.
    354  */
    355 static enum MHD_DigestAuthQOP
    356 get_rq_dauth_qop (const struct MHD_RqDAuthParam *const qop_param)
    357 {
    358   if (NULL == qop_param->value.str)
    359     return MHD_DIGEST_AUTH_QOP_NONE;
    360   if (qop_param->quoted)
    361   {
    362     if (MHD_str_equal_caseless_quoted_s_bin_n (qop_param->value.str, \
    363                                                qop_param->value.len, \
    364                                                MHD_TOKEN_AUTH_))
    365       return MHD_DIGEST_AUTH_QOP_AUTH;
    366     if (MHD_str_equal_caseless_quoted_s_bin_n (qop_param->value.str, \
    367                                                qop_param->value.len, \
    368                                                MHD_TOKEN_AUTH_INT_))
    369       return MHD_DIGEST_AUTH_QOP_AUTH_INT;
    370   }
    371   else
    372   {
    373     if (MHD_str_equal_caseless_s_bin_n_ (MHD_TOKEN_AUTH_, \
    374                                          qop_param->value.str, \
    375                                          qop_param->value.len))
    376       return MHD_DIGEST_AUTH_QOP_AUTH;
    377     if (MHD_str_equal_caseless_s_bin_n_ (MHD_TOKEN_AUTH_INT_, \
    378                                          qop_param->value.str, \
    379                                          qop_param->value.len))
    380       return MHD_DIGEST_AUTH_QOP_AUTH_INT;
    381   }
    382   /* No know QOP has been detected */
    383   return MHD_DIGEST_AUTH_QOP_INVALID;
    384 }
    385 
    386 
    387 /**
    388  * Parse request Authorization header parameters for Digest Authentication
    389  * @param str the header string, everything after "Digest " substring
    390  * @param str_len the length of @a str in characters
    391  * @param[out] pdauth the pointer to the structure with Digest Authentication
    392  *               parameters
    393  * @return true if parameters has been successfully parsed,
    394  *         false if format of the @a str is invalid
    395  */
    396 static bool
    397 parse_dauth_params (const char *str,
    398                     const size_t str_len,
    399                     struct MHD_RqDAuth *pdauth)
    400 {
    401   /* The tokens */
    402   static const struct _MHD_cstr_w_len nonce_tk = _MHD_S_STR_W_LEN ("nonce");
    403   static const struct _MHD_cstr_w_len opaque_tk = _MHD_S_STR_W_LEN ("opaque");
    404   static const struct _MHD_cstr_w_len algorithm_tk =
    405     _MHD_S_STR_W_LEN ("algorithm");
    406   static const struct _MHD_cstr_w_len response_tk =
    407     _MHD_S_STR_W_LEN ("response");
    408   static const struct _MHD_cstr_w_len username_tk =
    409     _MHD_S_STR_W_LEN ("username");
    410   static const struct _MHD_cstr_w_len username_ext_tk =
    411     _MHD_S_STR_W_LEN ("username*");
    412   static const struct _MHD_cstr_w_len realm_tk = _MHD_S_STR_W_LEN ("realm");
    413   static const struct _MHD_cstr_w_len uri_tk = _MHD_S_STR_W_LEN ("uri");
    414   static const struct _MHD_cstr_w_len qop_tk = _MHD_S_STR_W_LEN ("qop");
    415   static const struct _MHD_cstr_w_len cnonce_tk = _MHD_S_STR_W_LEN ("cnonce");
    416   static const struct _MHD_cstr_w_len nc_tk = _MHD_S_STR_W_LEN ("nc");
    417   static const struct _MHD_cstr_w_len userhash_tk =
    418     _MHD_S_STR_W_LEN ("userhash");
    419   /* The locally processed parameters */
    420   struct MHD_RqDAuthParam userhash;
    421   struct MHD_RqDAuthParam algorithm;
    422   /* Indexes */
    423   size_t i;
    424   size_t p;
    425   /* The list of the tokens.
    426      The order of the elements matches the next array. */
    427   static const struct _MHD_cstr_w_len *const tk_names[] = {
    428     &nonce_tk,          /* 0 */
    429     &opaque_tk,         /* 1 */
    430     &algorithm_tk,      /* 2 */
    431     &response_tk,       /* 3 */
    432     &username_tk,       /* 4 */
    433     &username_ext_tk,   /* 5 */
    434     &realm_tk,          /* 6 */
    435     &uri_tk,            /* 7 */
    436     &qop_tk,            /* 8 */
    437     &cnonce_tk,         /* 9 */
    438     &nc_tk,             /* 10 */
    439     &userhash_tk        /* 11 */
    440   };
    441   /* The list of the parameters.
    442      The order of the elements matches the previous array. */
    443   struct MHD_RqDAuthParam *params[sizeof(tk_names) / sizeof(tk_names[0])];
    444 
    445   params[0 ] = &(pdauth->nonce);           /* 0 */
    446   params[1 ] = &(pdauth->opaque);          /* 1 */
    447   params[2 ] = &algorithm;                 /* 2 */
    448   params[3 ] = &(pdauth->response);        /* 3 */
    449   params[4 ] = &(pdauth->username);        /* 4 */
    450   params[5 ] = &(pdauth->username_ext);    /* 5 */
    451   params[6 ] = &(pdauth->realm);           /* 6 */
    452   params[7 ] = &(pdauth->uri);             /* 7 */
    453   params[8 ] = &(pdauth->qop_raw);         /* 8 */
    454   params[9 ] = &(pdauth->cnonce);          /* 9 */
    455   params[10] = &(pdauth->nc);              /* 10 */
    456   params[11] = &userhash;                  /* 11 */
    457 
    458   mhd_assert ((sizeof(tk_names) / sizeof(tk_names[0])) == \
    459               (sizeof(params) / sizeof(params[0])));
    460   memset (&userhash, 0, sizeof(userhash));
    461   memset (&algorithm, 0, sizeof(algorithm));
    462   i = 0;
    463 
    464   /* Skip all whitespaces at start */
    465   while (i < str_len && (' ' == str[i] || '\t' == str[i]))
    466     i++;
    467 
    468   while (str_len > i)
    469   {
    470     size_t left;
    471     mhd_assert (' ' != str[i]);
    472     mhd_assert ('\t' != str[i]);
    473 
    474     left = str_len - i;
    475     if ('=' == str[i])
    476       return false; /* The equal sign is not allowed as the first character */
    477     for (p = 0; p < (sizeof(tk_names) / sizeof(tk_names[0])); ++p)
    478     {
    479       const struct _MHD_cstr_w_len *const tk_name = tk_names[p];
    480       struct MHD_RqDAuthParam *const param = params[p];
    481       if ( (tk_name->len <= left) &&
    482            MHD_str_equal_caseless_bin_n_ (str + i, tk_name->str,
    483                                           tk_name->len) &&
    484            ((tk_name->len == left) ||
    485             ('=' == str[i + tk_name->len]) ||
    486             (' ' == str[i + tk_name->len]) ||
    487             ('\t' == str[i + tk_name->len]) ||
    488             (',' == str[i + tk_name->len]) ||
    489             (';' == str[i + tk_name->len])) )
    490       {
    491         size_t value_start;
    492         size_t value_len;
    493         bool quoted; /* Only mark as "quoted" if backslash-escape used */
    494 
    495         if (tk_name->len == left)
    496           return false; /* No equal sign after parameter name, broken data */
    497 
    498         quoted = false;
    499         i += tk_name->len;
    500         /* Skip all whitespaces before '=' */
    501         while (str_len > i && (' ' == str[i] || '\t' == str[i]))
    502           i++;
    503         if ((i == str_len) || ('=' != str[i]))
    504           return false; /* No equal sign, broken data */
    505         i++;
    506         /* Skip all whitespaces after '=' */
    507         while (str_len > i && (' ' == str[i] || '\t' == str[i]))
    508           i++;
    509         if ((str_len > i) && ('"' == str[i]))
    510         { /* Value is in quotation marks */
    511           i++; /* Advance after the opening quote */
    512           value_start = i;
    513           while (str_len > i && '"' != str[i])
    514           {
    515             if ('\\' == str[i])
    516             {
    517               i++;
    518               quoted = true; /* Have escaped chars */
    519             }
    520             if (0 == str[i])
    521               return false; /* Binary zero in parameter value */
    522             i++;
    523           }
    524           if (str_len <= i)
    525             return false; /* No closing quote */
    526           mhd_assert ('"' == str[i]);
    527           value_len = i - value_start;
    528           i++; /* Advance after the closing quote */
    529         }
    530         else
    531         {
    532           value_start = i;
    533           while (str_len > i && ',' != str[i] &&
    534                  ' ' != str[i] && '\t' != str[i] && ';' != str[i])
    535           {
    536             if (0 == str[i])
    537               return false;  /* Binary zero in parameter value */
    538             i++;
    539           }
    540           if (';' == str[i])
    541             return false;  /* Semicolon in parameter value */
    542           value_len = i - value_start;
    543         }
    544         /* Skip all whitespaces after parameter value */
    545         while (str_len > i && (' ' == str[i] || '\t' == str[i]))
    546           i++;
    547         if ((str_len > i) && (',' != str[i]))
    548           return false; /* Garbage after parameter value */
    549 
    550         /* Have valid parameter name and value */
    551         mhd_assert (! quoted || 0 != value_len);
    552         param->value.str = str + value_start;
    553         param->value.len = value_len;
    554         param->quoted = quoted;
    555 
    556         break; /* Found matching parameter name */
    557       }
    558     }
    559     if (p == (sizeof(tk_names) / sizeof(tk_names[0])))
    560     {
    561       /* No matching parameter name */
    562       while (str_len > i && ',' != str[i])
    563       {
    564         if ((0 == str[i]) || (';' == str[i]))
    565           return false; /* Not allowed characters */
    566         if ('"' == str[i])
    567         { /* Skip quoted part */
    568           i++; /* Advance after the opening quote */
    569           while (str_len > i && '"' != str[i])
    570           {
    571             if (0 == str[i])
    572               return false;  /* Binary zero is not allowed */
    573             if ('\\' == str[i])
    574               i++;           /* Skip escaped char */
    575             i++;
    576           }
    577           if (str_len <= i)
    578             return false; /* No closing quote */
    579           mhd_assert ('"' == str[i]);
    580         }
    581         i++;
    582       }
    583     }
    584     mhd_assert (str_len == i || ',' == str[i]);
    585     if (str_len > i)
    586       i++; /* Advance after ',' */
    587     /* Skip all whitespaces before next parameter name */
    588     while (i < str_len && (' ' == str[i] || '\t' == str[i]))
    589       i++;
    590   }
    591 
    592   /* Postprocess values */
    593 
    594   if (NULL != userhash.value.str)
    595   {
    596     if (userhash.quoted)
    597       pdauth->userhash =
    598         MHD_str_equal_caseless_quoted_s_bin_n (userhash.value.str, \
    599                                                userhash.value.len, \
    600                                                "true");
    601     else
    602       pdauth->userhash =
    603         MHD_str_equal_caseless_s_bin_n_ ("true", userhash.value.str, \
    604                                          userhash.value.len);
    605 
    606   }
    607   else
    608     pdauth->userhash = false;
    609 
    610   pdauth->algo3 = get_rq_dauth_algo (&algorithm);
    611   pdauth->qop = get_rq_dauth_qop (&pdauth->qop_raw);
    612 
    613   return true;
    614 }
    615 
    616 
    617 /**
    618  * Return request's Digest Authorisation parameters.
    619  *
    620  * Function return result of parsing of the request's "Authorization" header or
    621  * returns cached parsing result if the header was already parsed for
    622  * the current request.
    623  * @param connection the connection to process
    624  * @return the pointer to structure with Authentication parameters,
    625  *         NULL if no memory in memory pool, if called too early (before
    626  *         header has been received) or if no valid Basic Authorisation header
    627  *         found.
    628  */
    629 const struct MHD_RqDAuth *
    630 MHD_get_rq_dauth_params_ (struct MHD_Connection *connection)
    631 {
    632   struct _MHD_str_w_len h_auth_value;
    633   struct MHD_RqDAuth *dauth;
    634 
    635   mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= connection->state);
    636 
    637   if (connection->rq.dauth_tried)
    638     return connection->rq.dauth;
    639 
    640   if (MHD_CONNECTION_HEADERS_PROCESSED > connection->state)
    641     return NULL;
    642 
    643   if (! find_auth_rq_header_ (connection, MHD_AUTHTYPE_DIGEST, &h_auth_value))
    644   {
    645     connection->rq.dauth_tried = true;
    646     connection->rq.dauth = NULL;
    647     return NULL;
    648   }
    649 
    650   dauth =
    651     (struct MHD_RqDAuth *)
    652     MHD_connection_alloc_memory_ (connection, sizeof (struct MHD_RqDAuth));
    653 
    654   if (NULL == dauth)
    655   {
    656 #ifdef HAVE_MESSAGES
    657     MHD_DLOG (connection->daemon,
    658               _ ("Not enough memory in the connection's pool to allocate " \
    659                  "for Digest Authorization header parsing.\n"));
    660 #endif /* HAVE_MESSAGES */
    661     return NULL;
    662   }
    663 
    664   memset (dauth, 0, sizeof(struct MHD_RqDAuth));
    665   if (parse_dauth_params (h_auth_value.str, h_auth_value.len, dauth))
    666     connection->rq.dauth = dauth;
    667   else
    668   {
    669 #ifdef HAVE_MESSAGES
    670     MHD_DLOG (connection->daemon,
    671               _ ("The Digest Authorization client's header has "
    672                  "incorrect format.\n"));
    673 #endif /* HAVE_MESSAGES */
    674     connection->rq.dauth = NULL;
    675     /* Memory in the pool remains allocated until next request */
    676   }
    677   connection->rq.dauth_tried = true;
    678   return connection->rq.dauth;
    679 }
    680 
    681 
    682 #endif /* DAUTH_SUPPORT */