anastasis_api_config.c (9241B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2020, 2021 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Anastasis 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file restclient/anastasis_api_config.c 18 * @brief Implementation of the /config GET 19 * @author Christian Grothoff 20 * @author Dennis Neufeld 21 * @author Dominik Meister 22 */ 23 #include "platform.h" 24 #include <curl/curl.h> 25 #include <microhttpd.h> /* just for HTTP status codes */ 26 #include "anastasis_service.h" 27 #include "anastasis_api_curl_defaults.h" 28 #include <gnunet/gnunet_json_lib.h> 29 #include <taler/taler_json_lib.h> 30 31 32 /** 33 * Which version of the Taler protocol is implemented 34 * by this library? Used to determine compatibility. 35 * 36 * Raised to 1 when the key derivation was fixed to use the full context 37 * string: every key derived by a 0:x:x client differs, so backups written by 38 * one side cannot be recovered by the other. Hence 0 must stay outside of 39 * the compatibility range. 40 */ 41 #define ANASTASIS_PROTOCOL_CURRENT 2 42 43 /** 44 * How many versions are we backwards compatible with? 45 */ 46 #define ANASTASIS_PROTOCOL_AGE 1 47 48 49 struct ANASTASIS_ConfigOperation 50 { 51 /** 52 * The url for this request. 53 */ 54 char *url; 55 56 /** 57 * Handle for the request. 58 */ 59 struct GNUNET_CURL_Job *job; 60 61 /** 62 * Reference to the execution context. 63 */ 64 struct GNUNET_CURL_Context *ctx; 65 66 /** 67 * The callback to pass the backend response to 68 */ 69 ANASTASIS_ConfigCallback cb; 70 71 /** 72 * Closure for @a cb. 73 */ 74 void *cb_cls; 75 76 }; 77 78 79 /** 80 * Function called when we're done processing the 81 * HTTP /config request. 82 * 83 * @param cls the `struct ANASTASIS_ConfigOperation` 84 * @param response_code HTTP response code, 0 on error 85 * @param response parsed JSON result, NULL on error 86 */ 87 static void 88 handle_config_finished (void *cls, 89 long response_code, 90 const void *response) 91 { 92 struct ANASTASIS_ConfigOperation *co = cls; 93 const json_t *json = response; 94 struct ANASTASIS_Config acfg = { 95 .http_status = response_code, 96 .response = json 97 }; 98 99 co->job = NULL; 100 switch (response_code) 101 { 102 case 0: 103 /* No reply received */ 104 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 105 "Backend `%s' failed to respond to GET /config\n", 106 co->url); 107 break; 108 case MHD_HTTP_OK: 109 { 110 const char *name; 111 const json_t *methods; 112 struct TALER_JSON_ProtocolVersion pv; 113 struct GNUNET_JSON_Specification spec[] = { 114 GNUNET_JSON_spec_string ("name", 115 &name), 116 GNUNET_JSON_spec_string ("business_name", 117 &acfg.details.ok.business_name), 118 GNUNET_JSON_spec_string ("version", 119 &acfg.details.ok.version), 120 TALER_JSON_spec_version ("version", 121 &pv), 122 GNUNET_JSON_spec_array_const ("methods", 123 &methods), 124 GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes", 125 &acfg.details.ok.storage_limit_in_megabytes), 126 TALER_JSON_spec_amount_any ("annual_fee", 127 &acfg.details.ok.annual_fee), 128 TALER_JSON_spec_amount_any ("truth_upload_fee", 129 &acfg.details.ok.truth_upload_fee), 130 TALER_JSON_spec_amount_any ("liability_limit", 131 &acfg.details.ok.liability_limit), 132 GNUNET_JSON_spec_fixed_auto ("provider_salt", 133 &acfg.details.ok.provider_salt), 134 GNUNET_JSON_spec_end () 135 }; 136 137 if (GNUNET_OK != 138 GNUNET_JSON_parse (json, 139 spec, 140 NULL, NULL)) 141 { 142 GNUNET_break_op (0); 143 json_dumpf (json, 144 stderr, 145 JSON_INDENT (2)); 146 acfg.http_status = 0; 147 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 148 break; 149 } 150 if (0 != strcmp (name, 151 "anastasis")) 152 { 153 GNUNET_JSON_parse_free (spec); 154 acfg.http_status = 0; 155 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 156 break; 157 } 158 if ( (ANASTASIS_PROTOCOL_CURRENT < pv.current) && 159 (ANASTASIS_PROTOCOL_CURRENT < pv.current - pv.age) ) 160 { 161 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 162 "Provider protocol version too new\n"); 163 acfg.http_status = 0; 164 acfg.ec = TALER_EC_GENERIC_VERSION_MALFORMED; 165 break; 166 } 167 if ( (ANASTASIS_PROTOCOL_CURRENT > pv.current) && 168 (ANASTASIS_PROTOCOL_CURRENT - ANASTASIS_PROTOCOL_AGE > pv.current) ) 169 { 170 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 171 "Provider protocol version too old\n"); 172 GNUNET_break_op (0); 173 acfg.http_status = 0; 174 acfg.ec = TALER_EC_GENERIC_VERSION_MALFORMED; 175 break; 176 } 177 acfg.details.ok.methods_length = (unsigned int) json_array_size (methods); 178 if (((size_t) acfg.details.ok.methods_length) != 179 json_array_size (methods)) 180 { 181 GNUNET_break_op (0); 182 acfg.http_status = 0; 183 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 184 break; 185 } 186 /* Bound the array so a malicious provider cannot force an unbounded 187 stack VLA (a ~40 MB response could otherwise yield ~10^6 elements). */ 188 if (acfg.details.ok.methods_length > 1024) 189 { 190 GNUNET_break_op (0); 191 acfg.http_status = 0; 192 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 193 break; 194 } 195 { 196 struct ANASTASIS_AuthorizationMethodConfig mcfg[ 197 GNUNET_NZL (acfg.details.ok.methods_length)]; 198 199 for (unsigned int i = 0; i<acfg.details.ok.methods_length; i++) 200 { 201 struct ANASTASIS_AuthorizationMethodConfig *m = &mcfg[i]; 202 struct GNUNET_JSON_Specification ispec[] = { 203 GNUNET_JSON_spec_string ("type", 204 &m->type), 205 TALER_JSON_spec_amount_any ("cost", 206 &m->usage_fee), 207 GNUNET_JSON_spec_end () 208 }; 209 210 if ( (GNUNET_OK != 211 GNUNET_JSON_parse (json_array_get (methods, 212 i), 213 ispec, 214 NULL, NULL)) ) 215 { 216 GNUNET_break_op (0); 217 acfg.http_status = 0; 218 acfg.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 219 goto end; 220 } 221 } 222 acfg.details.ok.methods = mcfg; 223 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 224 "Good backend found at `%s'\n", 225 co->url); 226 co->cb (co->cb_cls, 227 &acfg); 228 ANASTASIS_config_cancel (co); 229 return; 230 } 231 } 232 case MHD_HTTP_BAD_REQUEST: 233 /* This should never happen, either us or the anastasis server is buggy 234 (or API version conflict); just pass JSON reply to the application */ 235 break; 236 case MHD_HTTP_NOT_FOUND: 237 /* Nothing really to verify */ 238 break; 239 case MHD_HTTP_INTERNAL_SERVER_ERROR: 240 /* Server had an internal issue; we should retry, but this API 241 leaves this to the application */ 242 break; 243 default: 244 /* unexpected response code */ 245 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 246 "Unexpected response code %u\n", 247 (unsigned int) response_code); 248 GNUNET_break_op (0); 249 break; 250 } 251 end: 252 co->cb (co->cb_cls, 253 &acfg); 254 ANASTASIS_config_cancel (co); 255 } 256 257 258 struct ANASTASIS_ConfigOperation * 259 ANASTASIS_get_config (struct GNUNET_CURL_Context *ctx, 260 const char *base_url, 261 ANASTASIS_ConfigCallback cb, 262 void *cb_cls) 263 { 264 struct ANASTASIS_ConfigOperation *co; 265 266 co = GNUNET_new (struct ANASTASIS_ConfigOperation); 267 co->url = TALER_url_join (base_url, 268 "config", 269 NULL); 270 co->ctx = ctx; 271 co->cb = cb; 272 co->cb_cls = cb_cls; 273 { 274 CURL *eh; 275 276 eh = ANASTASIS_curl_easy_get_ (co->url); 277 co->job = GNUNET_CURL_job_add (ctx, 278 eh, 279 &handle_config_finished, 280 co); 281 } 282 if (NULL == co->job) 283 { 284 GNUNET_free (co->url); 285 GNUNET_free (co); 286 return NULL; 287 } 288 return co; 289 } 290 291 292 void 293 ANASTASIS_config_cancel (struct ANASTASIS_ConfigOperation *co) 294 { 295 if (NULL != co->job) 296 { 297 GNUNET_CURL_job_cancel (co->job); 298 co->job = NULL; 299 } 300 GNUNET_free (co->url); 301 GNUNET_free (co); 302 } 303 304 305 /* end of anastasis_api_config.c */