challenger-httpd_setup.c (8576B)
1 /* 2 This file is part of Challenger 3 Copyright (C) 2023, 2024 Taler Systems SA 4 5 Challenger is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Challenger is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file challenger-httpd_setup.c 18 * @brief functions to handle incoming requests for setups 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include "challenger-httpd.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include <gnunet/gnunet_db_lib.h> 25 #include "challenger-httpd_setup.h" 26 #include "challenger-httpd_common.h" 27 #include "challenger-database/setup_nonce.h" 28 #include "challenger-database/client_check.h" 29 30 31 /** 32 * Maximum number of retries for the database interaction. 33 */ 34 #define MAX_RETRIES 3 35 36 /** 37 * Set to 1 to dump addresses into the log. 38 */ 39 #define DEBUG 0 40 41 struct SetupContext 42 { 43 44 /** 45 * Our handler context. 46 */ 47 struct CH_HandlerContext *hc; 48 49 /** 50 * Opaque parsing context. 51 */ 52 void *opaque_post_parsing_context; 53 54 /** 55 * Uploaded JSON data, NULL if upload is not yet complete. 56 */ 57 json_t *root; 58 59 }; 60 61 62 /** 63 * Callback to clean up the request context. 64 * 65 * @param[in,out] ctx a `struct SetupContext` to clean up 66 */ 67 static void 68 request_done (void *ctx) 69 { 70 struct SetupContext *sc = ctx; 71 72 if (NULL == ctx) 73 return; 74 if (NULL != sc->root) 75 { 76 json_decref (sc->root); 77 sc->root = NULL; 78 } 79 TALER_MHD_parse_post_cleanup_callback (sc->opaque_post_parsing_context); 80 GNUNET_free (sc); 81 } 82 83 84 enum MHD_Result 85 CH_handler_setup (struct CH_HandlerContext *hc, 86 const char *upload_data, 87 size_t *upload_data_size) 88 { 89 struct SetupContext *sc = hc->ctx; 90 unsigned long long client_id; 91 const char *client_secret; 92 const char *cl; 93 94 if (NULL == sc) 95 { 96 /* Fresh request, do initial setup */ 97 sc = GNUNET_new (struct SetupContext); 98 sc->hc = hc; 99 hc->cc = &request_done; 100 hc->ctx = sc; 101 return MHD_YES; 102 } 103 cl = MHD_lookup_connection_value (hc->connection, 104 MHD_HEADER_KIND, 105 MHD_HTTP_HEADER_CONTENT_LENGTH); 106 /* If we have a body (Content-length set and not zero) and we 107 are not yet done parsing it, try to parse it! */ 108 if ( (NULL == sc->root) && 109 (NULL != cl) && 110 (0 != strcmp ("0", 111 cl)) ) 112 { 113 /* parse byte stream upload into JSON */ 114 enum GNUNET_GenericReturnValue res; 115 116 #if DEBUG 117 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 118 "Handling upload of `%.*s' (%s)\n", 119 (int) *upload_data_size, 120 upload_data, 121 cl); 122 #endif 123 res = TALER_MHD_parse_post_json (hc->connection, 124 &sc->opaque_post_parsing_context, 125 upload_data, 126 upload_data_size, 127 &sc->root); 128 if (GNUNET_SYSERR == res) 129 { 130 GNUNET_assert (NULL == sc->root); 131 return MHD_NO; /* bad upload, could not even generate error */ 132 } 133 if ( (GNUNET_NO == res) || 134 (NULL == sc->root) ) 135 { 136 GNUNET_assert (NULL == sc->root); 137 return MHD_YES; /* so far incomplete upload or parser error */ 138 } 139 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 140 "Address data uploaded in /setup\n"); 141 #if DEBUG 142 json_dumpf (sc->root, 143 stderr, 144 JSON_INDENT (2)); 145 #endif 146 } 147 148 { 149 char dummy; 150 151 if (1 != sscanf (hc->path, 152 "%llu%c", 153 &client_id, 154 &dummy)) 155 { 156 GNUNET_break_op (0); 157 return TALER_MHD_reply_with_error (hc->connection, 158 MHD_HTTP_BAD_REQUEST, 159 TALER_EC_GENERIC_PARAMETER_MALFORMED, 160 "client_id"); 161 } 162 } 163 client_secret = CH_get_client_secret (hc->connection); 164 if (NULL == client_secret) 165 { 166 GNUNET_break_op (0); 167 return TALER_MHD_reply_with_error (hc->connection, 168 MHD_HTTP_BAD_REQUEST, 169 TALER_EC_GENERIC_PARAMETER_MISSING, 170 MHD_HTTP_HEADER_AUTHORIZATION); 171 } 172 { 173 const json_t *ro; 174 175 ro = json_object_get (sc->root, 176 "read_only"); 177 if ( (NULL != ro) && 178 (! json_is_boolean (ro)) ) 179 { 180 GNUNET_break_op (0); 181 return TALER_MHD_reply_with_error (hc->connection, 182 MHD_HTTP_BAD_REQUEST, 183 TALER_EC_GENERIC_PARAMETER_MALFORMED, 184 "read_only"); 185 } 186 } 187 for (unsigned int r = 0; r<MAX_RETRIES; r++) 188 { 189 enum GNUNET_DB_QueryStatus qs; 190 char *client_url = NULL; 191 192 qs = CHALLENGERDB_client_check (CH_context, 193 (uint64_t) client_id, 194 client_secret, 195 1, 196 &client_url); 197 switch (qs) 198 { 199 case GNUNET_DB_STATUS_HARD_ERROR: 200 GNUNET_break (0); 201 return TALER_MHD_reply_with_error (hc->connection, 202 MHD_HTTP_INTERNAL_SERVER_ERROR, 203 TALER_EC_GENERIC_DB_FETCH_FAILED, 204 NULL); 205 case GNUNET_DB_STATUS_SOFT_ERROR: 206 if (r < MAX_RETRIES - 1) 207 continue; 208 GNUNET_break (0); 209 return TALER_MHD_reply_with_error (hc->connection, 210 MHD_HTTP_INTERNAL_SERVER_ERROR, 211 TALER_EC_GENERIC_DB_FETCH_FAILED, 212 NULL); 213 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 214 return TALER_MHD_reply_with_error (hc->connection, 215 MHD_HTTP_NOT_FOUND, 216 TALER_EC_CHALLENGER_GENERIC_CLIENT_UNKNOWN, 217 NULL); 218 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 219 break; 220 } 221 GNUNET_free (client_url); 222 break; 223 } 224 225 { 226 struct CHALLENGER_ValidationNonceP nonce; 227 struct GNUNET_TIME_Absolute expiration_time; 228 enum GNUNET_DB_QueryStatus qs; 229 230 expiration_time = GNUNET_TIME_relative_to_absolute (CH_validation_duration); 231 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 232 &nonce, 233 sizeof (nonce)); 234 qs = CHALLENGERDB_setup_nonce (CH_context, 235 client_id, 236 &nonce, 237 expiration_time, 238 sc->root); 239 switch (qs) 240 { 241 case GNUNET_DB_STATUS_HARD_ERROR: 242 case GNUNET_DB_STATUS_SOFT_ERROR: 243 GNUNET_break (0); 244 return TALER_MHD_reply_with_error (hc->connection, 245 MHD_HTTP_INTERNAL_SERVER_ERROR, 246 TALER_EC_GENERIC_DB_STORE_FAILED, 247 "setup_nonce"); 248 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 249 GNUNET_break (0); 250 return TALER_MHD_reply_with_error (hc->connection, 251 MHD_HTTP_INTERNAL_SERVER_ERROR, 252 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 253 "no results from setup_nonce"); 254 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 255 break; 256 } 257 { 258 char *nstr; 259 260 nstr = GNUNET_STRINGS_data_to_string_alloc (&nonce, 261 sizeof (nonce)); 262 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 263 "/setup returns nonce `%s'\n", 264 nstr); 265 GNUNET_free (nstr); 266 } 267 return TALER_MHD_REPLY_JSON_PACK ( 268 hc->connection, 269 MHD_HTTP_OK, 270 GNUNET_JSON_pack_data_auto ("nonce", 271 &nonce)); 272 } 273 }