| /* |
| This file is part of libmicrohttpd |
| Copyright (C) 2010, 2011, 2012 Daniel Pittman and Christian Grothoff |
| Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| /** |
| * @file basicauth.c |
| * @brief Implements HTTP basic authentication methods |
| * @author Amr Ali |
| * @author Matthieu Speder |
| * @author Karlson2k (Evgeny Grin) |
| */ |
| #include "basicauth.h" |
| #include "gen_auth.h" |
| #include "platform.h" |
| #include "mhd_limits.h" |
| #include "internal.h" |
| #include "mhd_compat.h" |
| #include "mhd_str.h" |
| |
| |
| /** |
| * Get the username and password from the Basic Authorisation header |
| * sent by the client |
| * |
| * @param connection the MHD connection structure |
| * @return NULL if no valid Basic Authentication header is present in |
| * current request, or |
| * pointer to structure with username and password, which must be |
| * freed by #MHD_free(). |
| * @note Available since #MHD_VERSION 0x00097517 |
| * @ingroup authentication |
| */ |
| _MHD_EXTERN struct MHD_BasicAuthInfo * |
| MHD_basic_auth_get_username_password3 (struct MHD_Connection *connection) |
| { |
| const struct MHD_RqBAuth *params; |
| size_t decoded_max_len; |
| struct MHD_BasicAuthInfo *ret; |
| |
| params = MHD_get_rq_bauth_params_ (connection); |
| |
| if (NULL == params) |
| return NULL; |
| |
| if ((NULL == params->token68.str) || (0 == params->token68.len)) |
| return NULL; |
| |
| decoded_max_len = MHD_base64_max_dec_size_ (params->token68.len); |
| ret = (struct MHD_BasicAuthInfo *) malloc (sizeof(struct MHD_BasicAuthInfo) |
| + decoded_max_len + 1); |
| if (NULL != ret) |
| { |
| size_t decoded_len; |
| char *decoded; |
| |
| decoded = (char *) (ret + 1); |
| decoded_len = MHD_base64_to_bin_n (params->token68.str, params->token68.len, |
| decoded, decoded_max_len); |
| mhd_assert (decoded_max_len >= decoded_len); |
| if (0 != decoded_len) |
| { |
| size_t username_len; |
| char *colon; |
| |
| colon = memchr (decoded, ':', decoded_len); |
| if (NULL != colon) |
| { |
| size_t password_pos; |
| size_t password_len; |
| |
| username_len = (size_t) (colon - decoded); |
| password_pos = username_len + 1; |
| password_len = decoded_len - password_pos; |
| ret->password = decoded + password_pos; |
| ret->password[password_len] = 0; /* Zero-terminate the string */ |
| ret->password_len = password_len; |
| } |
| else |
| { |
| username_len = decoded_len; |
| ret->password = NULL; |
| ret->password_len = 0; |
| } |
| ret->username = decoded; |
| ret->username[username_len] = 0; /* Zero-terminate the string */ |
| ret->username_len = username_len; |
| |
| return ret; /* Success exit point */ |
| } |
| #ifdef HAVE_MESSAGES |
| else |
| MHD_DLOG (connection->daemon, |
| _ ("Error decoding Basic Authorization authentication.\n")); |
| #endif /* HAVE_MESSAGES */ |
| |
| free (ret); |
| } |
| #ifdef HAVE_MESSAGES |
| else |
| { |
| MHD_DLOG (connection->daemon, |
| _ ("Failed to allocate memory to process " \ |
| "Basic Authorization authentication.\n")); |
| } |
| #endif /* HAVE_MESSAGES */ |
| |
| return NULL; /* Failure exit point */ |
| } |
| |
| |
| /** |
| * Get the username and password from the basic authorization header sent by the client |
| * |
| * @param connection The MHD connection structure |
| * @param[out] password a pointer for the password, free using #MHD_free(). |
| * @return NULL if no username could be found, a pointer |
| * to the username if found, free using #MHD_free(). |
| * @deprecated use #MHD_basic_auth_get_username_password3() |
| * @ingroup authentication |
| */ |
| _MHD_EXTERN char * |
| MHD_basic_auth_get_username_password (struct MHD_Connection *connection, |
| char **password) |
| { |
| struct MHD_BasicAuthInfo *info; |
| |
| info = MHD_basic_auth_get_username_password3 (connection); |
| if (NULL == info) |
| return NULL; |
| |
| /* For backward compatibility this function must return NULL if |
| * no password is provided */ |
| if (NULL != info->password) |
| { |
| char *username; |
| |
| username = malloc (info->username_len + 1); |
| if (NULL != username) |
| { |
| memcpy (username, info->username, info->username_len + 1); |
| mhd_assert (0 == username[info->username_len]); |
| if (NULL != password) |
| { |
| *password = malloc (info->password_len + 1); |
| if (NULL != *password) |
| { |
| memcpy (*password, info->password, info->password_len + 1); |
| mhd_assert (0 == (*password)[info->password_len]); |
| |
| free (info); |
| return username; /* Success exit point */ |
| } |
| #ifdef HAVE_MESSAGES |
| else |
| MHD_DLOG (connection->daemon, |
| _ ("Failed to allocate memory.\n")); |
| #endif /* HAVE_MESSAGES */ |
| } |
| else |
| { |
| free (info); |
| return username; /* Success exit point */ |
| } |
| |
| free (username); |
| } |
| #ifdef HAVE_MESSAGES |
| else |
| MHD_DLOG (connection->daemon, |
| _ ("Failed to allocate memory.\n")); |
| #endif /* HAVE_MESSAGES */ |
| |
| } |
| free (info); |
| if (NULL != password) |
| *password = NULL; |
| return NULL; /* Failure exit point */ |
| } |
| |
| |
| /** |
| * Queues a response to request basic authentication from the client. |
| * |
| * The given response object is expected to include the payload for |
| * the response; the "WWW-Authenticate" header will be added and the |
| * response queued with the 'UNAUTHORIZED' status code. |
| * |
| * See RFC 7617#section-2 for details. |
| * |
| * The @a response is modified by this function. The modified response object |
| * can be used to respond subsequent requests by #MHD_queue_response() |
| * function with status code #MHD_HTTP_UNAUTHORIZED and must not be used again |
| * with MHD_queue_basic_auth_fail_response3() function. The response could |
| * be destroyed right after call of this function. |
| * |
| * @param connection the MHD connection structure |
| * @param realm the realm presented to the client |
| * @param prefer_utf8 if not set to #MHD_NO, parameter'charset="UTF-8"' will |
| * be added, indicating for client that UTF-8 encoding |
| * is preferred |
| * @param response the response object to modify and queue; the NULL |
| * is tolerated |
| * @return #MHD_YES on success, #MHD_NO otherwise |
| * @note Available since #MHD_VERSION 0x00097516 |
| * @ingroup authentication |
| */ |
| _MHD_EXTERN enum MHD_Result |
| MHD_queue_basic_auth_fail_response3 (struct MHD_Connection *connection, |
| const char *realm, |
| int prefer_utf8, |
| struct MHD_Response *response) |
| { |
| static const char prefix[] = "Basic realm=\""; |
| static const char suff_charset[] = "\", charset=\"UTF-8\""; |
| static const size_t prefix_len = MHD_STATICSTR_LEN_ (prefix); |
| static const size_t suff_simple_len = MHD_STATICSTR_LEN_ ("\""); |
| static const size_t suff_charset_len = |
| MHD_STATICSTR_LEN_ (suff_charset); |
| enum MHD_Result ret; |
| char *h_str; |
| size_t h_maxlen; |
| size_t suffix_len; |
| size_t realm_len; |
| size_t realm_quoted_len; |
| size_t pos; |
| |
| if (NULL == response) |
| return MHD_NO; |
| |
| suffix_len = (0 == prefer_utf8) ? suff_simple_len : suff_charset_len; |
| realm_len = strlen (realm); |
| h_maxlen = prefix_len + realm_len * 2 + suffix_len; |
| |
| h_str = (char *) malloc (h_maxlen + 1); |
| if (NULL == h_str) |
| { |
| #ifdef HAVE_MESSAGES |
| MHD_DLOG (connection->daemon, |
| "Failed to allocate memory for Basic Authentication header.\n"); |
| #endif /* HAVE_MESSAGES */ |
| return MHD_NO; |
| } |
| memcpy (h_str, prefix, prefix_len); |
| pos = prefix_len; |
| realm_quoted_len = MHD_str_quote (realm, realm_len, h_str + pos, |
| h_maxlen - prefix_len - suffix_len); |
| pos += realm_quoted_len; |
| mhd_assert (pos + suffix_len <= h_maxlen); |
| if (0 == prefer_utf8) |
| { |
| h_str[pos++] = '\"'; |
| h_str[pos++] = 0; /* Zero terminate the result */ |
| mhd_assert (pos <= h_maxlen + 1); |
| } |
| else |
| { |
| /* Copy with the final zero-termination */ |
| mhd_assert (pos + suff_charset_len <= h_maxlen); |
| memcpy (h_str + pos, suff_charset, suff_charset_len + 1); |
| mhd_assert (0 == h_str[pos + suff_charset_len]); |
| } |
| |
| ret = MHD_add_response_header (response, |
| MHD_HTTP_HEADER_WWW_AUTHENTICATE, |
| h_str); |
| free (h_str); |
| if (MHD_NO != ret) |
| { |
| ret = MHD_queue_response (connection, |
| MHD_HTTP_UNAUTHORIZED, |
| response); |
| } |
| else |
| { |
| #ifdef HAVE_MESSAGES |
| MHD_DLOG (connection->daemon, |
| _ ("Failed to add Basic Authentication header.\n")); |
| #endif /* HAVE_MESSAGES */ |
| } |
| return ret; |
| } |
| |
| |
| /** |
| * Queues a response to request basic authentication from the client |
| * The given response object is expected to include the payload for |
| * the response; the "WWW-Authenticate" header will be added and the |
| * response queued with the 'UNAUTHORIZED' status code. |
| * |
| * @param connection The MHD connection structure |
| * @param realm the realm presented to the client |
| * @param response response object to modify and queue; the NULL is tolerated |
| * @return #MHD_YES on success, #MHD_NO otherwise |
| * @deprecated use MHD_queue_basic_auth_fail_response3() |
| * @ingroup authentication |
| */ |
| _MHD_EXTERN enum MHD_Result |
| MHD_queue_basic_auth_fail_response (struct MHD_Connection *connection, |
| const char *realm, |
| struct MHD_Response *response) |
| { |
| return MHD_queue_basic_auth_fail_response3 (connection, realm, MHD_NO, |
| response); |
| } |
| |
| |
| /* end of basicauth.c */ |