paivana

HTTP paywall reverse proxy
Log | Files | Refs | Submodules | README | LICENSE

paivana-httpd_cookie.c (6885B)


      1 /*
      2   This file is part of GNU Taler
      3   Copyright (C) 2026 Taler Systems SA
      4 
      5   GNU Taler is free software; you can redistribute it and/or
      6   modify it under the terms of the GNU General Public License
      7   as published by the Free Software Foundation; either version
      8   3, or (at your option) any later version.
      9 
     10   GNU Taler is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with GNU Taler; see the file COPYING.  If not,
     17   write to the Free Software Foundation, Inc., 51 Franklin
     18   Street, Fifth Floor, Boston, MA 02110-1301, USA.
     19 */
     20 
     21 /**
     22  * @author Christian Grothoff
     23  * @file src/backend/paivana-httpd_cookie.c
     24  * @brief Cookie computation logic for paivana
     25  */
     26 #include "platform.h"
     27 #include <curl/curl.h>
     28 #include <gcrypt.h>
     29 #include <gnunet/gnunet_util_lib.h>
     30 #include <taler/taler_mhd_lib.h>
     31 #include "paivana-httpd_cookie.h"
     32 #include "paivana-httpd.h"
     33 
     34 
     35 /**
     36  * Secret for the cookie generation.
     37  */
     38 struct GNUNET_HashCode paivana_secret;
     39 
     40 
     41 /**
     42  * Compute access cookie hash for the given @a cur_time, the
     43  * @a website and @a ca.
     44  *
     45  * @param cur_time current time used in the cookie
     46  * @param website URL the cookie is valid for
     47  * @param ca_len number of bytes in @a ca
     48  * @param ca client (IP) address
     49  * @param[out] c set to the cookie hash
     50  */
     51 static void
     52 compute_cookie_hash (struct GNUNET_TIME_Timestamp cur_time,
     53                      const char *website,
     54                      size_t ca_len,
     55                      const void *ca,
     56                      struct GNUNET_HashCode *c)
     57 {
     58   struct GNUNET_TIME_AbsoluteNBO e;
     59 
     60   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     61               "Computing cookie for %s at %llu and client %s\n",
     62               website,
     63               (unsigned long long) cur_time.abs_time.abs_value_us,
     64               TALER_b2s (ca,
     65                          ca_len));
     66   e = GNUNET_TIME_absolute_hton (cur_time.abs_time);
     67   if (PH_global_cookie)
     68     website = "";
     69   GNUNET_assert (GNUNET_YES ==
     70                  GNUNET_CRYPTO_hkdf_gnunet (
     71                    c,            /* result */
     72                    sizeof (*c),
     73                    &e,          /* salt */
     74                    sizeof (e),
     75                    &paivana_secret, /* source key material */
     76                    sizeof (paivana_secret),
     77                    GNUNET_CRYPTO_kdf_arg (website,
     78                                           strlen (website) + 1),
     79                    GNUNET_CRYPTO_kdf_arg (ca,
     80                                           ca_len)));
     81 }
     82 
     83 
     84 bool
     85 PAIVANA_HTTPD_check_cookie (const char *cookie,
     86                             const char *website,
     87                             size_t ca_len,
     88                             const void *ca)
     89 {
     90   const char *dash;
     91   unsigned long long u;
     92   struct GNUNET_HashCode h;
     93   struct GNUNET_HashCode c;
     94   struct GNUNET_TIME_Timestamp a;
     95 
     96   dash = strchr (cookie,
     97                  '-');
     98   if (NULL == dash)
     99   {
    100     GNUNET_break_op (0);
    101     return false;
    102   }
    103   dash++;
    104   if (1 !=
    105       sscanf (cookie,
    106               "%llu-",
    107               &u))
    108   {
    109     GNUNET_break_op (0);
    110     return false;
    111   }
    112   a.abs_time = GNUNET_TIME_absolute_from_s (u);
    113   if (GNUNET_TIME_absolute_is_past (a.abs_time))
    114   {
    115     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    116                 "Cookie expired %s ago\n",
    117                 GNUNET_TIME_relative2s (
    118                   GNUNET_TIME_absolute_get_duration (a.abs_time),
    119                   true));
    120     return false;
    121   }
    122   if (GNUNET_OK !=
    123       GNUNET_STRINGS_string_to_data (dash,
    124                                      strlen (dash),
    125                                      &c,
    126                                      sizeof (c)))
    127   {
    128     GNUNET_break_op (0);
    129     return false;
    130   }
    131   compute_cookie_hash (a,
    132                        website,
    133                        ca_len,
    134                        ca,
    135                        &h);
    136   if (0 ==
    137       GNUNET_memcmp (&c,
    138                      &h))
    139     return true;
    140   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    141               "Cookie hash does not match!\n");
    142   return false;
    143 }
    144 
    145 
    146 char *
    147 PAIVANA_HTTPD_compute_cookie (struct GNUNET_TIME_Timestamp cur_time,
    148                               const char *website,
    149                               size_t ca_len,
    150                               const void *ca)
    151 {
    152   struct GNUNET_HashCode h;
    153   char *end;
    154   char cstr[128];
    155   char *res;
    156   const char *url = "/";
    157   bool use_https = (0 ==
    158                     strncasecmp (website,
    159                                  "https://",
    160                                  strlen ("https://")));
    161   struct GNUNET_TIME_Relative duration
    162     = GNUNET_TIME_absolute_get_remaining (cur_time.abs_time);
    163 
    164   if (! PH_global_cookie)
    165   {
    166     const char *dslash = strstr (website,
    167                                  "//");
    168     if (NULL != dslash)
    169       url = strchr (dslash + 2,
    170                     '/');
    171     if (NULL == url)
    172       url = "/";
    173   }
    174   compute_cookie_hash (cur_time,
    175                        website,
    176                        ca_len,
    177                        ca,
    178                        &h);
    179   end = GNUNET_STRINGS_data_to_string (&h,
    180                                        sizeof (h),
    181                                        cstr,
    182                                        sizeof (cstr));
    183   *end = '\0';
    184   GNUNET_asprintf (
    185     &res,
    186     "Paivana-Cookie=%llu-%s; %sPath=%s; Max-Age=%llu; HttpOnly;",
    187     (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU),
    188     cstr,
    189     use_https
    190     ? "Secure; "
    191     : "",
    192     url,
    193     (unsigned long long) (duration.rel_value_us / 1000 / 1000));
    194   return res;
    195 }
    196 
    197 
    198 char *
    199 PAIVANA_HTTPD_compute_paivana_id (struct GNUNET_TIME_Timestamp cur_time,
    200                                   const char *website,
    201                                   const struct PAIVANA_Nonce *nonce)
    202 {
    203   struct GNUNET_TIME_AbsoluteNBO e;
    204   char *res;
    205   gcry_md_hd_t hd;
    206   const void *sha256;
    207   char *cstr;
    208   size_t clen;
    209 
    210   e = GNUNET_TIME_absolute_hton (cur_time.abs_time);
    211   GNUNET_assert (0 ==
    212                  gcry_md_open (&hd,
    213                                GCRY_MD_SHA256,
    214                                0));
    215   gcry_md_write (hd,
    216                  nonce,
    217                  sizeof (*nonce));
    218   gcry_md_write (hd,
    219                  website,
    220                  strlen (website) + 1);
    221   gcry_md_write (hd,
    222                  &e,
    223                  sizeof (e));
    224   sha256 = gcry_md_read (hd,
    225                          0);
    226   cstr = NULL;
    227   clen = GNUNET_STRINGS_base64url_encode (sha256,
    228                                           256 / 8,
    229                                           &cstr);
    230   GNUNET_asprintf (
    231     &res,
    232     "%llu-%.*s",
    233     (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU),
    234     (int) clen,
    235     cstr);
    236   GNUNET_free (cstr);
    237   gcry_md_close (hd);
    238   return res;
    239 }