anastasis_api_policy_lookup.c (11012B)
1 /* 2 This file is part of ANASTASIS 3 Copyright (C) 2014-2019 Anastasis SARL 4 5 ANASTASIS is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 2.1, 8 or (at your option) any later version. 9 10 ANASTASIS 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 ANASTASIS; see the file COPYING.LGPL. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file restclient/anastasis_api_policy_lookup.c 22 * @brief Implementation of the /policy GET and POST 23 * @author Christian Grothoff 24 * @author Dennis Neufeld 25 * @author Dominik Meister 26 */ 27 #include "platform.h" 28 #include <curl/curl.h> 29 #include <jansson.h> 30 #include <microhttpd.h> /* just for HTTP status codes */ 31 #include "anastasis_service.h" 32 #include "anastasis_api_curl_defaults.h" 33 #include <taler/taler_signatures.h> 34 35 36 /** 37 * @brief A Contract Operation Handle 38 */ 39 struct ANASTASIS_PolicyLookupOperation 40 { 41 42 /** 43 * The url for this request, including parameters. 44 */ 45 char *url; 46 47 /** 48 * Handle for the request. 49 */ 50 struct GNUNET_CURL_Job *job; 51 52 /** 53 * Function to call with the result. 54 */ 55 ANASTASIS_PolicyLookupCallback cb; 56 57 /** 58 * Closure for @a cb. 59 */ 60 void *cb_cls; 61 62 /** 63 * Reference to the execution context. 64 */ 65 struct GNUNET_CURL_Context *ctx; 66 67 /** 68 * Public key of the account we are downloading from. 69 */ 70 struct ANASTASIS_CRYPTO_AccountPublicKeyP account_pub; 71 72 /** 73 * Signature returned in the #ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE 74 * header, or all zeros for none. 75 */ 76 struct ANASTASIS_AccountSignatureP account_sig; 77 78 /** 79 * Version of the policy, as claimed by the provider in the 80 * #ANASTASIS_HTTP_HEADER_POLICY_VERSION header. 81 */ 82 unsigned int version; 83 84 /** 85 * Version we asked for, or 0 if we asked for the latest one. 86 */ 87 unsigned int requested_version; 88 89 /** 90 * True if we asked for a specific version, so that @e requested_version 91 * must match what the provider returns. 92 */ 93 bool version_requested; 94 95 }; 96 97 98 void 99 ANASTASIS_policy_lookup_cancel (struct ANASTASIS_PolicyLookupOperation *plo) 100 { 101 if (NULL != plo->job) 102 { 103 GNUNET_CURL_job_cancel (plo->job); 104 plo->job = NULL; 105 } 106 GNUNET_free (plo->url); 107 GNUNET_free (plo); 108 } 109 110 111 /** 112 * Process GET /policy response 113 */ 114 static void 115 handle_policy_lookup_finished (void *cls, 116 long response_code, 117 const void *data, 118 size_t data_size) 119 { 120 struct ANASTASIS_PolicyLookupOperation *plo = cls; 121 struct ANASTASIS_DownloadDetails dd = { 122 .http_status = response_code 123 }; 124 125 plo->job = NULL; 126 switch (response_code) 127 { 128 case 0: 129 /* Hard error */ 130 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 131 "Backend didn't even return from GET /policy\n"); 132 break; 133 case MHD_HTTP_OK: 134 { 135 struct ANASTASIS_UploadSignaturePS usp = { 136 .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD), 137 .purpose.size = htonl (sizeof (usp)), 138 }; 139 140 GNUNET_CRYPTO_hash (data, 141 data_size, 142 &usp.new_recovery_data_hash); 143 if (GNUNET_OK != 144 GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD, 145 &usp, 146 &plo->account_sig.eddsa_sig, 147 &plo->account_pub.pub)) 148 { 149 GNUNET_break_op (0); 150 dd.http_status = 0; 151 dd.ec = -1; // FIXME: needs new code in Gana! 152 break; 153 } 154 if ( (plo->version_requested) && 155 (plo->version != plo->requested_version) ) 156 { 157 /* The version header is not covered by any signature, so a provider 158 can put whatever it likes there. We can at least insist that a 159 lookup for a specific version is answered with that version. */ 160 GNUNET_break_op (0); 161 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 162 "Provider claims version %u for a lookup of version %u\n", 163 plo->version, 164 plo->requested_version); 165 dd.http_status = 0; 166 dd.ec = -1; // FIXME: needs new code in Gana! 167 break; 168 } 169 /* Success, call callback with all details! */ 170 dd.details.ok.sig = plo->account_sig; 171 dd.details.ok.curr_policy_hash = usp.new_recovery_data_hash; 172 dd.details.ok.policy = data; 173 dd.details.ok.policy_size = data_size; 174 dd.details.ok.version = plo->version; 175 plo->cb (plo->cb_cls, 176 &dd); 177 plo->cb = NULL; 178 ANASTASIS_policy_lookup_cancel (plo); 179 return; 180 } 181 case MHD_HTTP_BAD_REQUEST: 182 /* This should never happen, either us or the anastasis server is buggy 183 (or API version conflict); just pass JSON reply to the application */ 184 break; 185 case MHD_HTTP_NOT_FOUND: 186 /* Nothing really to verify */ 187 break; 188 case MHD_HTTP_INTERNAL_SERVER_ERROR: 189 /* Server had an internal issue; we should retry, but this API 190 leaves this to the application */ 191 break; 192 default: 193 /* unexpected response code */ 194 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 195 "Unexpected response code %u\n", 196 (unsigned int) response_code); 197 GNUNET_break (0); 198 break; 199 } 200 plo->cb (plo->cb_cls, 201 &dd); 202 plo->cb = NULL; 203 ANASTASIS_policy_lookup_cancel (plo); 204 } 205 206 207 /** 208 * Handle HTTP header received by curl. 209 * 210 * @param buffer one line of HTTP header data 211 * @param size size of an item 212 * @param nitems number of items passed 213 * @param userdata our `struct ANASTASIS_PolicyLookupOperation *` 214 * @return `size * nitems` 215 */ 216 static size_t 217 handle_header (char *buffer, 218 size_t size, 219 size_t nitems, 220 void *userdata) 221 { 222 struct ANASTASIS_PolicyLookupOperation *plo = userdata; 223 size_t total = size * nitems; 224 char *ndup; 225 const char *hdr_type; 226 char *hdr_val; 227 char *sp; 228 229 ndup = GNUNET_strndup (buffer, 230 total); 231 hdr_type = strtok_r (ndup, 232 ":", 233 &sp); 234 if (NULL == hdr_type) 235 { 236 GNUNET_free (ndup); 237 return total; 238 } 239 hdr_val = strtok_r (NULL, 240 "\n\r", 241 &sp); 242 if (NULL == hdr_val) 243 { 244 GNUNET_free (ndup); 245 return total; 246 } 247 if (' ' == *hdr_val) 248 hdr_val++; 249 if (0 == strcasecmp (hdr_type, 250 ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE)) 251 { 252 if (GNUNET_OK != 253 GNUNET_STRINGS_string_to_data ( 254 hdr_val, 255 strlen (hdr_val), 256 &plo->account_sig, 257 sizeof (struct ANASTASIS_AccountSignatureP))) 258 { 259 GNUNET_break_op (0); 260 GNUNET_free (ndup); 261 return 0; 262 } 263 } 264 if (0 == strcasecmp (hdr_type, 265 ANASTASIS_HTTP_HEADER_POLICY_VERSION)) 266 { 267 char dummy; 268 269 if (1 != 270 sscanf (hdr_val, 271 "%u%c", 272 &plo->version, 273 &dummy)) 274 { 275 GNUNET_break_op (0); 276 GNUNET_free (ndup); 277 return 0; 278 } 279 } 280 GNUNET_free (ndup); 281 return total; 282 } 283 284 285 struct ANASTASIS_PolicyLookupOperation * 286 ANASTASIS_policy_lookup ( 287 struct GNUNET_CURL_Context *ctx, 288 const char *backend_url, 289 const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, 290 ANASTASIS_PolicyLookupCallback cb, 291 void *cb_cls) 292 { 293 struct ANASTASIS_PolicyLookupOperation *plo; 294 CURL *eh; 295 char *acc_pub_str; 296 char *path; 297 298 GNUNET_assert (NULL != cb); 299 plo = GNUNET_new (struct ANASTASIS_PolicyLookupOperation); 300 plo->account_pub = *anastasis_pub; 301 acc_pub_str = GNUNET_STRINGS_data_to_string_alloc (anastasis_pub, 302 sizeof (*anastasis_pub)); 303 GNUNET_asprintf (&path, 304 "policy/%s", 305 acc_pub_str); 306 GNUNET_free (acc_pub_str); 307 plo->url = TALER_url_join (backend_url, 308 path, 309 NULL); 310 GNUNET_free (path); 311 eh = ANASTASIS_curl_easy_get_ (plo->url); 312 if (NULL == eh) 313 { 314 GNUNET_break (0); 315 GNUNET_free (plo->url); 316 GNUNET_free (plo); 317 return NULL; 318 } 319 GNUNET_assert (CURLE_OK == 320 curl_easy_setopt (eh, 321 CURLOPT_HEADERFUNCTION, 322 &handle_header)); 323 GNUNET_assert (CURLE_OK == 324 curl_easy_setopt (eh, 325 CURLOPT_HEADERDATA, 326 plo)); 327 plo->cb = cb; 328 plo->cb_cls = cb_cls; 329 plo->job = GNUNET_CURL_job_add_raw (ctx, 330 eh, 331 NULL, 332 &handle_policy_lookup_finished, 333 plo); 334 return plo; 335 } 336 337 338 struct ANASTASIS_PolicyLookupOperation * 339 ANASTASIS_policy_lookup_version ( 340 struct GNUNET_CURL_Context *ctx, 341 const char *backend_url, 342 const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, 343 ANASTASIS_PolicyLookupCallback cb, 344 void *cb_cls, 345 unsigned int version) 346 { 347 struct ANASTASIS_PolicyLookupOperation *plo; 348 CURL *eh; 349 char *acc_pub_str; 350 char *path; 351 char version_s[14]; 352 353 GNUNET_assert (NULL != cb); 354 plo = GNUNET_new (struct ANASTASIS_PolicyLookupOperation); 355 plo->account_pub = *anastasis_pub; 356 acc_pub_str = GNUNET_STRINGS_data_to_string_alloc (anastasis_pub, 357 sizeof (*anastasis_pub)); 358 GNUNET_asprintf (&path, 359 "policy/%s", 360 acc_pub_str); 361 GNUNET_free (acc_pub_str); 362 plo->requested_version = version; 363 plo->version_requested = true; 364 GNUNET_snprintf (version_s, 365 sizeof (version_s), 366 "%u", 367 version); 368 plo->url = TALER_url_join (backend_url, 369 path, 370 "version", 371 version_s, 372 NULL); 373 GNUNET_free (path); 374 eh = ANASTASIS_curl_easy_get_ (plo->url); 375 GNUNET_assert (CURLE_OK == 376 curl_easy_setopt (eh, 377 CURLOPT_HEADERFUNCTION, 378 &handle_header)); 379 GNUNET_assert (CURLE_OK == 380 curl_easy_setopt (eh, 381 CURLOPT_HEADERDATA, 382 plo)); 383 plo->cb = cb; 384 plo->cb_cls = cb_cls; 385 plo->job = GNUNET_CURL_job_add_raw (ctx, 386 eh, 387 NULL, 388 &handle_policy_lookup_finished, 389 plo); 390 return plo; 391 }