paivana

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

paivana-httpd_cookie.c (6665B)


      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.abs_value_us = u * 1000LLU * 1000LLU;
    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 = strncasecmp (website,
    158 				"https://",
    159 				strlen ("https://"));
    160   struct GNUNET_TIME_Relative duration
    161     = GNUNET_TIME_absolute_get_remaining (cur_time.abs_time);
    162 
    163   if (! PH_global_cookie) 
    164   {
    165     const char *dslash = strstr (website,
    166 				 "//");
    167     if (NULL != dslash)
    168       url = strchr (dslash + 2,
    169 		    '/');
    170   }
    171   compute_cookie_hash (cur_time,
    172                        website,
    173                        ca_len,
    174                        ca,
    175                        &h);
    176   end = GNUNET_STRINGS_data_to_string (&h,
    177                                        sizeof (h),
    178                                        cstr,
    179                                        sizeof (cstr));
    180   *end = '\0';
    181   GNUNET_asprintf (
    182     &res,
    183     "Paivana-Cookie=%llu-%s; %sPath=%s; Max-Age=%llu;",
    184     (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU),
    185     cstr,
    186     use_https
    187     ? "Secure; "
    188     : "",
    189     url,
    190     (unsigned long long) (duration.rel_value_us / 1000 / 1000));
    191   return res;
    192 }
    193 
    194 
    195 char *
    196 PAIVANA_HTTPD_compute_paivana_id (struct GNUNET_TIME_Timestamp cur_time,
    197                                   const char *website,
    198                                   const struct PAIVANA_Nonce *nonce)
    199 {
    200   struct GNUNET_TIME_AbsoluteNBO e;
    201   char *res;
    202   gcry_md_hd_t hd;
    203   const void *sha256;
    204   char *cstr;
    205   size_t clen;
    206 
    207   e = GNUNET_TIME_absolute_hton (cur_time.abs_time);
    208   GNUNET_assert (0 ==
    209                  gcry_md_open (&hd,
    210                                GCRY_MD_SHA256,
    211                                0));
    212   gcry_md_write (hd,
    213                  nonce,
    214                  sizeof (*nonce));
    215   gcry_md_write (hd,
    216                  website,
    217                  strlen (website) + 1);
    218   gcry_md_write (hd,
    219                  &e,
    220                  sizeof (e));
    221   sha256 = gcry_md_read (hd,
    222                          0);
    223   cstr = NULL;
    224   clen = GNUNET_STRINGS_base64url_encode (sha256,
    225                                           256 / 8,
    226                                           &cstr);
    227   GNUNET_asprintf (
    228     &res,
    229     "%llu-%.*s",
    230     (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU),
    231     (int) clen,
    232     cstr);
    233   GNUNET_free (cstr);
    234   gcry_md_close (hd);
    235   return res;
    236 }