libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

response_auth_basic.c (6714B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
      5 
      6   GNU libmicrohttpd is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU Lesser General Public
      8   License as published by the Free Software Foundation; either
      9   version 2.1 of the License, or (at your option) any later version.
     10 
     11   GNU libmicrohttpd is distributed in the hope that it will be useful,
     12   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14   Lesser General Public License for more details.
     15 
     16   Alternatively, you can redistribute GNU libmicrohttpd and/or
     17   modify it under the terms of the GNU General Public License as
     18   published by the Free Software Foundation; either version 2 of
     19   the License, or (at your option) any later version, together
     20   with the eCos exception, as follows:
     21 
     22     As a special exception, if other files instantiate templates or
     23     use macros or inline functions from this file, or you compile this
     24     file and link it with other works to produce a work based on this
     25     file, this file does not by itself cause the resulting work to be
     26     covered by the GNU General Public License. However the source code
     27     for this file must still be made available in accordance with
     28     section (3) of the GNU General Public License v2.
     29 
     30     This exception does not invalidate any other reasons why a work
     31     based on this file might be covered by the GNU General Public
     32     License.
     33 
     34   You should have received copies of the GNU Lesser General Public
     35   License and the GNU General Public License along with this library;
     36   if not, see <https://www.gnu.org/licenses/>.
     37 */
     38 
     39 /**
     40  * @file src/mhd2/response_auth_basic.c
     41  * @brief  The definitions of MHD_response_add_auth_basic_challenge() function
     42  * @author Karlson2k (Evgeny Grin)
     43  */
     44 
     45 #include "mhd_sys_options.h"
     46 
     47 #include "mhd_response.h"
     48 #include "mhd_locks.h"
     49 #include "mhd_str_types.h"
     50 
     51 #include <string.h>
     52 #include "sys_malloc.h"
     53 
     54 #include "mhd_str_macros.h"
     55 #include "mhd_str.h"
     56 
     57 #include "mhd_public_api.h"
     58 
     59 
     60 static MHD_FN_PAR_NONNULL_ALL_
     61 MHD_FN_PAR_CSTR_ (2) enum MHD_StatusCode
     62 response_add_auth_basic_challenge_int (struct MHD_Response *restrict response,
     63                                        const char *restrict realm,
     64                                        enum MHD_Bool prefer_utf8)
     65 {
     66   static const struct MHD_String hdr_name =
     67     mhd_MSTR_INIT (MHD_HTTP_HEADER_WWW_AUTHENTICATE);
     68   static const struct MHD_String prefix =
     69     mhd_MSTR_INIT ("Basic realm=\"");
     70   static const struct MHD_String add_charset =
     71     mhd_MSTR_INIT (", charset=\"UTF-8\"");
     72   const size_t realm_len = strlen (realm);
     73   char *val_str;
     74   size_t hval_maxlen;
     75   size_t suffix_len;
     76   size_t realm_quoted_len;
     77   size_t pos;
     78   struct mhd_ResponseHeader *new_hdr;
     79 
     80   if ((NULL != memchr (realm, '\n', realm_len)) ||
     81       (NULL != memchr (realm, '\r', realm_len)))
     82     return MHD_SC_RESP_HEADER_VALUE_INVALID;
     83   if (0 == realm_len)
     84     return MHD_SC_RESP_HEADER_VALUE_INVALID;
     85 
     86   suffix_len = 1; /* for (closing) quote char */
     87   if (MHD_NO != prefer_utf8)
     88     suffix_len += add_charset.len;
     89   hval_maxlen = prefix.len + realm_len * 2 + suffix_len;
     90 
     91   new_hdr = (struct mhd_ResponseHeader *)
     92             malloc (sizeof(struct mhd_ResponseHeader)
     93                     + hdr_name.len + 1 + hval_maxlen + 1);
     94 
     95   if (NULL == new_hdr)
     96     return MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED;
     97 
     98   /* Set the name of the header */
     99   memcpy ((char *) (new_hdr + 1),
    100           hdr_name.cstr,
    101           hdr_name.len + 1);
    102 
    103   /* Set the value of the header */
    104   val_str = ((char *) (new_hdr + 1)) + hdr_name.len + 1;
    105   memcpy (val_str, prefix.cstr, prefix.len);
    106   pos = prefix.len;
    107   realm_quoted_len = mhd_str_quote (realm,
    108                                     realm_len,
    109                                     val_str + pos,
    110                                     hval_maxlen - prefix.len - suffix_len);
    111   mhd_assert (0 != realm_quoted_len);
    112   pos += realm_quoted_len;
    113   val_str[pos++] = '\"';
    114   mhd_assert (pos + suffix_len <= hval_maxlen);
    115 
    116   if (MHD_NO != prefer_utf8)
    117   {
    118     mhd_assert (pos + add_charset.len <= hval_maxlen);
    119     memcpy (val_str + pos, add_charset.cstr, add_charset.len);
    120     pos += add_charset.len;
    121   }
    122   val_str[pos] = 0; /* Zero terminate the result */
    123   mhd_assert (pos <= hval_maxlen);
    124 
    125   if (1)
    126   { /* Try to shrink malloc'ed area */
    127     void *new_ptr;
    128     new_ptr = realloc (new_hdr,
    129                        sizeof(struct mhd_ResponseHeader)
    130                        + hdr_name.len + 1 + pos + 1);
    131     /* Just use the old pointer if realloc() failed */
    132     if (NULL != new_ptr)
    133       new_hdr = (struct mhd_ResponseHeader *) new_ptr;
    134   }
    135 
    136   new_hdr->name.cstr = (char *) (new_hdr + 1);
    137   new_hdr->name.len = hdr_name.len;
    138   mhd_assert (0 == memcmp (hdr_name.cstr, \
    139                            new_hdr->name.cstr, \
    140                            new_hdr->name.len + 1));
    141 
    142   new_hdr->value.cstr = ((char *) (new_hdr + 1)) + hdr_name.len + 1;
    143   new_hdr->value.len = pos;
    144   mhd_assert (0 == memcmp (prefix.cstr, \
    145                            new_hdr->value.cstr, \
    146                            prefix.len));
    147   mhd_assert (0 == new_hdr->value.cstr[new_hdr->value.len]);
    148 
    149   mhd_DLINKEDL_INIT_LINKS (new_hdr, headers);
    150   mhd_DLINKEDL_INS_LAST (response, new_hdr, headers);
    151 
    152   response->cfg.has_bauth = true;
    153 
    154   return MHD_SC_OK;
    155 }
    156 
    157 
    158 MHD_EXTERN_
    159 MHD_FN_PAR_NONNULL_ (2)
    160 MHD_FN_PAR_CSTR_ (2) enum MHD_StatusCode
    161 MHD_response_add_auth_basic_challenge (
    162   struct MHD_Response *MHD_RESTRICT response,
    163   const char *MHD_RESTRICT realm,
    164   enum MHD_Bool prefer_utf8)
    165 {
    166   bool need_unlock;
    167   enum MHD_StatusCode res;
    168 
    169   if (NULL == response)
    170     return MHD_SC_RESP_POINTER_NULL;
    171   if (response->frozen)
    172     return MHD_SC_TOO_LATE;
    173   if (MHD_HTTP_STATUS_UNAUTHORIZED != response->sc)
    174     return MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE;
    175 
    176   if (response->reuse.reusable)
    177   {
    178     need_unlock = true;
    179     if (! mhd_mutex_lock (&(response->reuse.settings_lock)))
    180       return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
    181     mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter)));
    182   }
    183   else
    184     need_unlock = false;
    185 
    186   if (response->frozen) /* Re-check with the lock held */
    187     res = MHD_SC_TOO_LATE;
    188   else if (response->cfg.has_bauth)
    189     res = MHD_SC_RESP_HEADERS_CONFLICT;
    190   else
    191     res = response_add_auth_basic_challenge_int (response,
    192                                                  realm,
    193                                                  prefer_utf8);
    194 
    195   if (need_unlock)
    196     mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
    197 
    198   return res;
    199 }