libmicrohttpd

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

basicauth.c (10404B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2010, 2011, 2012 Daniel Pittman and Christian Grothoff
      4      Copyright (C) 2014-2023 Evgeny Grin (Karlson2k)
      5 
      6      This library 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      This library 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      You should have received a copy of the GNU Lesser General Public
     17      License along with this library; if not, write to the Free Software
     18      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     19 */
     20 /**
     21  * @file basicauth.c
     22  * @brief Implements HTTP basic authentication methods
     23  * @author Amr Ali
     24  * @author Matthieu Speder
     25  * @author Karlson2k (Evgeny Grin)
     26  */
     27 #include "basicauth.h"
     28 #include "gen_auth.h"
     29 #include "platform.h"
     30 #include "mhd_limits.h"
     31 #include "internal.h"
     32 #include "mhd_compat.h"
     33 #include "mhd_str.h"
     34 
     35 
     36 /**
     37  * Get the username and password from the Basic Authorisation header
     38  * sent by the client
     39  *
     40  * @param connection the MHD connection structure
     41  * @return NULL if no valid Basic Authentication header is present in
     42  *         current request, or
     43  *         pointer to structure with username and password, which must be
     44  *         freed by #MHD_free().
     45  * @note Available since #MHD_VERSION 0x00097701
     46  * @ingroup authentication
     47  */
     48 _MHD_EXTERN struct MHD_BasicAuthInfo *
     49 MHD_basic_auth_get_username_password3 (struct MHD_Connection *connection)
     50 {
     51   const struct MHD_RqBAuth *params;
     52   size_t decoded_max_len;
     53   struct MHD_BasicAuthInfo *ret;
     54 
     55   params = MHD_get_rq_bauth_params_ (connection);
     56 
     57   if (NULL == params)
     58     return NULL;
     59 
     60   if ((NULL == params->token68.str) || (0 == params->token68.len))
     61     return NULL;
     62 
     63   decoded_max_len = MHD_base64_max_dec_size_ (params->token68.len);
     64   ret = (struct MHD_BasicAuthInfo *) malloc (sizeof(struct MHD_BasicAuthInfo)
     65                                              + decoded_max_len + 1);
     66   if (NULL != ret)
     67   {
     68     size_t decoded_len;
     69     char *decoded;
     70 
     71     decoded = (char *) (ret + 1);
     72     decoded_len = MHD_base64_to_bin_n (params->token68.str, params->token68.len,
     73                                        decoded, decoded_max_len);
     74     mhd_assert (decoded_max_len >= decoded_len);
     75     if (0 != decoded_len)
     76     {
     77       size_t username_len;
     78       char *colon;
     79 
     80       colon = memchr (decoded, ':', decoded_len);
     81       if (NULL != colon)
     82       {
     83         size_t password_pos;
     84         size_t password_len;
     85 
     86         username_len = (size_t) (colon - decoded);
     87         password_pos = username_len + 1;
     88         password_len = decoded_len - password_pos;
     89         ret->password = decoded + password_pos;
     90         ret->password[password_len] = 0;  /* Zero-terminate the string */
     91         ret->password_len = password_len;
     92       }
     93       else
     94       {
     95         username_len = decoded_len;
     96         ret->password = NULL;
     97         ret->password_len = 0;
     98       }
     99       ret->username = decoded;
    100       ret->username[username_len] = 0;  /* Zero-terminate the string */
    101       ret->username_len = username_len;
    102 
    103       return ret; /* Success exit point */
    104     }
    105 #ifdef HAVE_MESSAGES
    106     else
    107       MHD_DLOG (connection->daemon,
    108                 _ ("Error decoding Basic Authorization authentication.\n"));
    109 #endif /* HAVE_MESSAGES */
    110 
    111     free (ret);
    112   }
    113 #ifdef HAVE_MESSAGES
    114   else
    115   {
    116     MHD_DLOG (connection->daemon,
    117               _ ("Failed to allocate memory to process " \
    118                  "Basic Authorization authentication.\n"));
    119   }
    120 #endif /* HAVE_MESSAGES */
    121 
    122   return NULL; /* Failure exit point */
    123 }
    124 
    125 
    126 /**
    127  * Get the username and password from the basic authorization header sent by the client
    128  *
    129  * @param connection The MHD connection structure
    130  * @param[out] password a pointer for the password, free using #MHD_free().
    131  * @return NULL if no username could be found, a pointer
    132  *      to the username if found, free using #MHD_free().
    133  * @deprecated use #MHD_basic_auth_get_username_password3()
    134  * @ingroup authentication
    135  */
    136 _MHD_EXTERN char *
    137 MHD_basic_auth_get_username_password (struct MHD_Connection *connection,
    138                                       char **password)
    139 {
    140   struct MHD_BasicAuthInfo *info;
    141 
    142   info = MHD_basic_auth_get_username_password3 (connection);
    143   if (NULL == info)
    144     return NULL;
    145 
    146   /* For backward compatibility this function must return NULL if
    147    * no password is provided */
    148   if (NULL != info->password)
    149   {
    150     char *username;
    151 
    152     username = malloc (info->username_len + 1);
    153     if (NULL != username)
    154     {
    155       memcpy (username, info->username, info->username_len + 1);
    156       mhd_assert (0 == username[info->username_len]);
    157       if (NULL != password)
    158       {
    159         *password = malloc (info->password_len + 1);
    160         if (NULL != *password)
    161         {
    162           memcpy (*password, info->password, info->password_len + 1);
    163           mhd_assert (0 == (*password)[info->password_len]);
    164 
    165           free (info);
    166           return username; /* Success exit point */
    167         }
    168 #ifdef HAVE_MESSAGES
    169         else
    170           MHD_DLOG (connection->daemon,
    171                     _ ("Failed to allocate memory.\n"));
    172 #endif /* HAVE_MESSAGES */
    173       }
    174       else
    175       {
    176         free (info);
    177         return username; /* Success exit point */
    178       }
    179 
    180       free (username);
    181     }
    182 #ifdef HAVE_MESSAGES
    183     else
    184       MHD_DLOG (connection->daemon,
    185                 _ ("Failed to allocate memory.\n"));
    186 #endif /* HAVE_MESSAGES */
    187 
    188   }
    189   free (info);
    190   if (NULL != password)
    191     *password = NULL;
    192   return NULL;  /* Failure exit point */
    193 }
    194 
    195 
    196 /**
    197  * Queues a response to request basic authentication from the client.
    198  *
    199  * The given response object is expected to include the payload for
    200  * the response; the "WWW-Authenticate" header will be added and the
    201  * response queued with the 'UNAUTHORIZED' status code.
    202  *
    203  * See RFC 7617#section-2 for details.
    204  *
    205  * The @a response is modified by this function. The modified response object
    206  * can be used to respond subsequent requests by #MHD_queue_response()
    207  * function with status code #MHD_HTTP_UNAUTHORIZED and must not be used again
    208  * with MHD_queue_basic_auth_required_response3() function. The response could
    209  * be destroyed right after call of this function.
    210  *
    211  * @param connection the MHD connection structure
    212  * @param realm the realm presented to the client
    213  * @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will
    214  *                    be added, indicating for client that UTF-8 encoding
    215  *                    is preferred
    216  * @param response the response object to modify and queue; the NULL
    217  *                 is tolerated
    218  * @return #MHD_YES on success, #MHD_NO otherwise
    219  * @note Available since #MHD_VERSION 0x00097704
    220  * @ingroup authentication
    221  */
    222 _MHD_EXTERN enum MHD_Result
    223 MHD_queue_basic_auth_required_response3 (struct MHD_Connection *connection,
    224                                          const char *realm,
    225                                          int prefer_utf8,
    226                                          struct MHD_Response *response)
    227 {
    228   static const char prefix[] = "Basic realm=\"";
    229   static const char suff_charset[] = "\", charset=\"UTF-8\"";
    230   static const size_t prefix_len = MHD_STATICSTR_LEN_ (prefix);
    231   static const size_t suff_simple_len = MHD_STATICSTR_LEN_ ("\"");
    232   static const size_t suff_charset_len =
    233     MHD_STATICSTR_LEN_ (suff_charset);
    234   enum MHD_Result ret;
    235   char *h_str;
    236   size_t h_maxlen;
    237   size_t suffix_len;
    238   size_t realm_len;
    239   size_t realm_quoted_len;
    240   size_t pos;
    241 
    242   if (NULL == response)
    243     return MHD_NO;
    244 
    245   suffix_len = (0 == prefer_utf8) ? suff_simple_len : suff_charset_len;
    246   realm_len = strlen (realm);
    247   h_maxlen = prefix_len + realm_len * 2 + suffix_len;
    248 
    249   h_str = (char *) malloc (h_maxlen + 1);
    250   if (NULL == h_str)
    251   {
    252 #ifdef HAVE_MESSAGES
    253     MHD_DLOG (connection->daemon,
    254               "Failed to allocate memory for Basic Authentication header.\n");
    255 #endif /* HAVE_MESSAGES */
    256     return MHD_NO;
    257   }
    258   memcpy (h_str, prefix, prefix_len);
    259   pos = prefix_len;
    260   realm_quoted_len = MHD_str_quote (realm, realm_len, h_str + pos,
    261                                     h_maxlen - prefix_len - suffix_len);
    262   pos += realm_quoted_len;
    263   mhd_assert (pos + suffix_len <= h_maxlen);
    264   if (0 == prefer_utf8)
    265   {
    266     h_str[pos++] = '\"';
    267     h_str[pos++] = 0; /* Zero terminate the result */
    268     mhd_assert (pos <= h_maxlen + 1);
    269   }
    270   else
    271   {
    272     /* Copy with the final zero-termination */
    273     mhd_assert (pos + suff_charset_len <= h_maxlen);
    274     memcpy (h_str + pos, suff_charset, suff_charset_len + 1);
    275     mhd_assert (0 == h_str[pos + suff_charset_len]);
    276   }
    277 
    278   ret = MHD_add_response_header (response,
    279                                  MHD_HTTP_HEADER_WWW_AUTHENTICATE,
    280                                  h_str);
    281   free (h_str);
    282   if (MHD_NO != ret)
    283   {
    284     ret = MHD_queue_response (connection,
    285                               MHD_HTTP_UNAUTHORIZED,
    286                               response);
    287   }
    288   else
    289   {
    290 #ifdef HAVE_MESSAGES
    291     MHD_DLOG (connection->daemon,
    292               _ ("Failed to add Basic Authentication header.\n"));
    293 #endif /* HAVE_MESSAGES */
    294   }
    295   return ret;
    296 }
    297 
    298 
    299 /**
    300  * Queues a response to request basic authentication from the client
    301  * The given response object is expected to include the payload for
    302  * the response; the "WWW-Authenticate" header will be added and the
    303  * response queued with the 'UNAUTHORIZED' status code.
    304  *
    305  * @param connection The MHD connection structure
    306  * @param realm the realm presented to the client
    307  * @param response response object to modify and queue; the NULL is tolerated
    308  * @return #MHD_YES on success, #MHD_NO otherwise
    309  * @deprecated use MHD_queue_basic_auth_required_response3()
    310  * @ingroup authentication
    311  */
    312 _MHD_EXTERN enum MHD_Result
    313 MHD_queue_basic_auth_fail_response (struct MHD_Connection *connection,
    314                                     const char *realm,
    315                                     struct MHD_Response *response)
    316 {
    317   return MHD_queue_basic_auth_required_response3 (connection, realm, MHD_NO,
    318                                                   response);
    319 }
    320 
    321 
    322 /* end of basicauth.c */