aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/basicauth.c
blob: 07b124f1e7cfb9bf757c66290eb2cd67232d8933 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*
     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 = (params->token68.len / 4) * 3;
  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 */