libmicrohttpd

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

test_digestauth.c (17039B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2010 Christian Grothoff
      4      Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file daemontest_digestauth.c
     24  * @brief  Testcase for libmicrohttpd Digest Auth
     25  * @author Amr Ali
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "mhd_options.h"
     30 #include "platform.h"
     31 #include <curl/curl.h>
     32 #include <microhttpd.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <time.h>
     36 #include <errno.h>
     37 
     38 #if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
     39   (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
     40 #define NEED_GCRYP_INIT 1
     41 #include <gcrypt.h>
     42 #endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
     43 
     44 #ifndef WINDOWS
     45 #include <sys/socket.h>
     46 #include <unistd.h>
     47 #else
     48 #include <wincrypt.h>
     49 #endif
     50 
     51 #ifndef CURL_VERSION_BITS
     52 #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
     53 #endif /* ! CURL_VERSION_BITS */
     54 #ifndef CURL_AT_LEAST_VERSION
     55 #define CURL_AT_LEAST_VERSION(x,y,z) \
     56   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
     57 #endif /* ! CURL_AT_LEAST_VERSION */
     58 
     59 #ifndef _MHD_INSTRMACRO
     60 /* Quoted macro parameter */
     61 #define _MHD_INSTRMACRO(a) #a
     62 #endif /* ! _MHD_INSTRMACRO */
     63 #ifndef _MHD_STRMACRO
     64 /* Quoted expanded macro parameter */
     65 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     66 #endif /* ! _MHD_STRMACRO */
     67 
     68 #if defined(HAVE___FUNC__)
     69 #define externalErrorExit(ignore) \
     70     _externalErrorExit_func(NULL, __func__, __LINE__)
     71 #define externalErrorExitDesc(errDesc) \
     72     _externalErrorExit_func(errDesc, __func__, __LINE__)
     73 #define libcurlErrorExit(ignore) \
     74     _libcurlErrorExit_func(NULL, __func__, __LINE__)
     75 #define libcurlErrorExitDesc(errDesc) \
     76     _libcurlErrorExit_func(errDesc, __func__, __LINE__)
     77 #define mhdErrorExit(ignore) \
     78     _mhdErrorExit_func(NULL, __func__, __LINE__)
     79 #define mhdErrorExitDesc(errDesc) \
     80     _mhdErrorExit_func(errDesc, __func__, __LINE__)
     81 #define checkCURLE_OK(libcurlcall) \
     82     _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
     83                         __func__, __LINE__)
     84 #elif defined(HAVE___FUNCTION__)
     85 #define externalErrorExit(ignore) \
     86     _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
     87 #define externalErrorExitDesc(errDesc) \
     88     _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     89 #define libcurlErrorExit(ignore) \
     90     _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
     91 #define libcurlErrorExitDesc(errDesc) \
     92     _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     93 #define mhdErrorExit(ignore) \
     94     _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
     95 #define mhdErrorExitDesc(errDesc) \
     96     _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     97 #define checkCURLE_OK(libcurlcall) \
     98     _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
     99                         __FUNCTION__, __LINE__)
    100 #else
    101 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
    102 #define externalErrorExitDesc(errDesc) \
    103   _externalErrorExit_func(errDesc, NULL, __LINE__)
    104 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
    105 #define libcurlErrorExitDesc(errDesc) \
    106   _libcurlErrorExit_func(errDesc, NULL, __LINE__)
    107 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
    108 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
    109 #define checkCURLE_OK(libcurlcall) \
    110   _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), NULL, __LINE__)
    111 #endif
    112 
    113 
    114 _MHD_NORETURN static void
    115 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    116 {
    117   fflush (stdout);
    118   if ((NULL != errDesc) && (0 != errDesc[0]))
    119     fprintf (stderr, "%s", errDesc);
    120   else
    121     fprintf (stderr, "System or external library call failed");
    122   if ((NULL != funcName) && (0 != funcName[0]))
    123     fprintf (stderr, " in %s", funcName);
    124   if (0 < lineNum)
    125     fprintf (stderr, " at line %d", lineNum);
    126 
    127   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    128            strerror (errno));
    129 #ifdef MHD_WINSOCK_SOCKETS
    130   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    131 #endif /* MHD_WINSOCK_SOCKETS */
    132   fflush (stderr);
    133   exit (99);
    134 }
    135 
    136 
    137 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    138 
    139 _MHD_NORETURN static void
    140 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    141 {
    142   fflush (stdout);
    143   if ((NULL != errDesc) && (0 != errDesc[0]))
    144     fprintf (stderr, "%s", errDesc);
    145   else
    146     fprintf (stderr, "CURL library call failed");
    147   if ((NULL != funcName) && (0 != funcName[0]))
    148     fprintf (stderr, " in %s", funcName);
    149   if (0 < lineNum)
    150     fprintf (stderr, " at line %d", lineNum);
    151 
    152   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    153            strerror (errno));
    154 #ifdef MHD_WINSOCK_SOCKETS
    155   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    156 #endif /* MHD_WINSOCK_SOCKETS */
    157   if (0 != libcurl_errbuf[0])
    158     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    159 
    160   fflush (stderr);
    161   exit (99);
    162 }
    163 
    164 
    165 _MHD_NORETURN static void
    166 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    167 {
    168   fflush (stdout);
    169   if ((NULL != errDesc) && (0 != errDesc[0]))
    170     fprintf (stderr, "%s", errDesc);
    171   else
    172     fprintf (stderr, "MHD unexpected error");
    173   if ((NULL != funcName) && (0 != funcName[0]))
    174     fprintf (stderr, " in %s", funcName);
    175   if (0 < lineNum)
    176     fprintf (stderr, " at line %d", lineNum);
    177 
    178   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    179            strerror (errno));
    180 #ifdef MHD_WINSOCK_SOCKETS
    181   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    182 #endif /* MHD_WINSOCK_SOCKETS */
    183 
    184   fflush (stderr);
    185   exit (8);
    186 }
    187 
    188 
    189 static void
    190 _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
    191                      const char *funcName, int lineNum)
    192 {
    193   if (CURLE_OK == code)
    194     return;
    195 
    196   fflush (stdout);
    197   if ((NULL != curlFunc) && (0 != curlFunc[0]))
    198     fprintf (stderr, "'%s' resulted in '%s'", curlFunc,
    199              curl_easy_strerror (code));
    200   else
    201     fprintf (stderr, "libcurl function call resulted in '%s'",
    202              curl_easy_strerror (code));
    203   if ((NULL != funcName) && (0 != funcName[0]))
    204     fprintf (stderr, " in %s", funcName);
    205   if (0 < lineNum)
    206     fprintf (stderr, " at line %d", lineNum);
    207 
    208   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    209            strerror (errno));
    210   if (0 != libcurl_errbuf[0])
    211     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    212 
    213   fflush (stderr);
    214   exit (9);
    215 }
    216 
    217 
    218 /* Could be increased to facilitate debugging */
    219 #define TIMEOUTS_VAL 5
    220 
    221 #define MHD_URI_BASE_PATH "/bar%20foo%20without%20args"
    222 
    223 #define PAGE \
    224   "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>"
    225 
    226 #define DENIED \
    227   "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>"
    228 
    229 #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
    230 
    231 struct CBC
    232 {
    233   char *buf;
    234   size_t pos;
    235   size_t size;
    236 };
    237 
    238 
    239 static size_t
    240 copyBuffer (void *ptr,
    241             size_t size,
    242             size_t nmemb,
    243             void *ctx)
    244 {
    245   struct CBC *cbc = ctx;
    246 
    247   if (cbc->pos + size * nmemb > cbc->size)
    248     mhdErrorExitDesc ("Wrong too large data");       /* overflow */
    249   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    250   cbc->pos += size * nmemb;
    251   return size * nmemb;
    252 }
    253 
    254 
    255 static enum MHD_Result
    256 ahc_echo (void *cls,
    257           struct MHD_Connection *connection,
    258           const char *url,
    259           const char *method,
    260           const char *version,
    261           const char *upload_data,
    262           size_t *upload_data_size,
    263           void **req_cls)
    264 {
    265   struct MHD_Response *response;
    266   char *username;
    267   const char *password = "testpass";
    268   const char *realm = "test@example.com";
    269   enum MHD_Result ret;
    270   int ret_i;
    271   static int already_called_marker;
    272   (void) cls; (void) url;                         /* Unused. Silent compiler warning. */
    273   (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */
    274   (void) upload_data_size; (void) req_cls;        /* Unused. Silent compiler warning. */
    275 
    276   if (&already_called_marker != *req_cls)
    277   { /* Called for the first time, request not fully read yet */
    278     *req_cls = &already_called_marker;
    279     /* Wait for complete request */
    280     return MHD_YES;
    281   }
    282 
    283   username = MHD_digest_auth_get_username (connection);
    284   if ( (username == NULL) ||
    285        (0 != strcmp (username, "testuser")) )
    286   {
    287     response = MHD_create_response_from_buffer_static (strlen (DENIED),
    288                                                        DENIED);
    289     if (NULL == response)
    290       mhdErrorExitDesc ("MHD_create_response_from_buffer failed");
    291     ret = MHD_queue_auth_fail_response2 (connection,
    292                                          realm,
    293                                          MY_OPAQUE,
    294                                          response,
    295                                          MHD_NO,
    296                                          MHD_DIGEST_ALG_MD5);
    297     if (MHD_YES != ret)
    298       mhdErrorExitDesc ("MHD_queue_auth_fail_response2 failed");
    299     MHD_destroy_response (response);
    300     return ret;
    301   }
    302   ret_i = MHD_digest_auth_check2 (connection,
    303                                   realm,
    304                                   username,
    305                                   password,
    306                                   300,
    307                                   MHD_DIGEST_ALG_MD5);
    308   MHD_free (username);
    309   if (ret_i != MHD_YES)
    310   {
    311     response = MHD_create_response_from_buffer_static (strlen (DENIED),
    312                                                        DENIED);
    313     if (NULL == response)
    314       mhdErrorExitDesc ("MHD_create_response_from_buffer() failed");
    315     ret = MHD_queue_auth_fail_response2 (connection,
    316                                          realm,
    317                                          MY_OPAQUE,
    318                                          response,
    319                                          (MHD_INVALID_NONCE == ret_i) ?
    320                                          MHD_YES : MHD_NO,
    321                                          MHD_DIGEST_ALG_MD5);
    322     if (MHD_YES != ret)
    323       mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed");
    324     MHD_destroy_response (response);
    325     return ret;
    326   }
    327   response = MHD_create_response_from_buffer_static (strlen (PAGE),
    328                                                      PAGE);
    329   if (NULL == response)
    330     mhdErrorExitDesc ("MHD_create_response_from_buffer() failed");
    331   ret = MHD_queue_response (connection,
    332                             MHD_HTTP_OK,
    333                             response);
    334   if (MHD_YES != ret)
    335     mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed");
    336   MHD_destroy_response (response);
    337   return ret;
    338 }
    339 
    340 
    341 static CURL *
    342 setupCURL (void *cbc, uint16_t port)
    343 {
    344   CURL *c;
    345   char url[512];
    346 
    347   if (1)
    348   {
    349     int res;
    350     /* A workaround for some old libcurl versions, which ignore the specified
    351      * port by CURLOPT_PORT when digest authorisation is used. */
    352     res = snprintf (url, (sizeof(url) / sizeof(url[0])),
    353                     "http://127.0.0.1:%u%s",
    354                     (unsigned int) port, MHD_URI_BASE_PATH);
    355     if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
    356       externalErrorExitDesc ("Cannot form request URL");
    357   }
    358 
    359   c = curl_easy_init ();
    360   if (NULL == c)
    361     libcurlErrorExitDesc ("curl_easy_init() failed");
    362 
    363   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    364       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    365                                      libcurl_errbuf)) ||
    366       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    367                                      &copyBuffer)) ||
    368       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    369       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    370                                      ((long) TIMEOUTS_VAL))) ||
    371       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    372                                      ((long) TIMEOUTS_VAL))) ||
    373       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    374                                      CURL_HTTP_VERSION_1_1)) ||
    375       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    376 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    377       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
    378 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    379       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
    380 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    381 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    382       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
    383 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    384       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) ||
    385       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url)))
    386     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    387   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST)) ||
    388       (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERPWD,
    389                                      "testuser:testpass")))
    390     libcurlErrorExitDesc ("curl_easy_setopt() authorization options failed");
    391   return c;
    392 }
    393 
    394 
    395 static unsigned int
    396 testDigestAuth (void)
    397 {
    398   CURL *c;
    399   struct MHD_Daemon *d;
    400   struct CBC cbc;
    401   char buf[2048];
    402   char rnd[8];
    403   uint16_t port;
    404 #ifndef WINDOWS
    405   int fd;
    406   size_t len;
    407   size_t off = 0;
    408 #endif /* ! WINDOWS */
    409 
    410   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    411     port = 0;
    412   else
    413     port = 1165;
    414 
    415   cbc.buf = buf;
    416   cbc.size = 2048;
    417   cbc.pos = 0;
    418 #ifndef WINDOWS
    419   fd = open ("/dev/urandom",
    420              O_RDONLY);
    421   if (-1 == fd)
    422     externalErrorExitDesc ("Failed to open '/dev/urandom'");
    423 
    424   while (off < 8)
    425   {
    426     len = (size_t) read (fd,
    427                          rnd + off,
    428                          8 - off);
    429     if (len == (size_t) -1)
    430       externalErrorExitDesc ("Failed to read '/dev/urandom'");
    431     off += len;
    432   }
    433   (void) close (fd);
    434 #else
    435   {
    436     HCRYPTPROV cc;
    437     BOOL b;
    438 
    439     b = CryptAcquireContext (&cc,
    440                              NULL,
    441                              NULL,
    442                              PROV_RSA_FULL,
    443                              CRYPT_VERIFYCONTEXT);
    444     if (b == 0)
    445       externalErrorExitDesc ("CryptAcquireContext() failed");
    446     b = CryptGenRandom (cc, 8, (BYTE *) rnd);
    447     if (b == 0)
    448       externalErrorExitDesc ("CryptGenRandom() failed");
    449     CryptReleaseContext (cc, 0);
    450   }
    451 #endif
    452   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    453                         port, NULL, NULL,
    454                         &ahc_echo, NULL,
    455                         MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
    456                         MHD_OPTION_NONCE_NC_SIZE, 300,
    457                         MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, (uint32_t) 999,
    458                         MHD_OPTION_END);
    459   if (d == NULL)
    460     return 1;
    461   if (0 == port)
    462   {
    463     const union MHD_DaemonInfo *dinfo;
    464 
    465     dinfo = MHD_get_daemon_info (d,
    466                                  MHD_DAEMON_INFO_BIND_PORT);
    467     if ( (NULL == dinfo) ||
    468          (0 == dinfo->port) )
    469       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
    470     port = dinfo->port;
    471   }
    472   c = setupCURL (&cbc, port);
    473 
    474   checkCURLE_OK (curl_easy_perform (c));
    475 
    476   curl_easy_cleanup (c);
    477   MHD_stop_daemon (d);
    478   if (cbc.pos != strlen (PAGE))
    479   {
    480     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    481              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    482              (unsigned) strlen (MHD_URI_BASE_PATH));
    483     mhdErrorExitDesc ("Wrong returned data length");
    484   }
    485   if (0 != strncmp (PAGE, cbc.buf, strlen (PAGE)))
    486   {
    487     fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
    488     mhdErrorExitDesc ("Wrong returned data");
    489   }
    490   return 0;
    491 }
    492 
    493 
    494 int
    495 main (int argc, char *const *argv)
    496 {
    497   unsigned int errorCount = 0;
    498   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
    499 
    500 #ifdef NEED_GCRYP_INIT
    501   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    502 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    503   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    504 #endif /* GCRYCTL_INITIALIZATION_FINISHED */
    505 #endif /* NEED_GCRYP_INIT */
    506   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    507     return 2;
    508   errorCount += testDigestAuth ();
    509   if (errorCount != 0)
    510     fprintf (stderr, "Error (code: %u)\n", errorCount);
    511   curl_global_cleanup ();
    512   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    513 }