auditor_api_get_config.c (7974B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2023 Taler Systems SA 4 5 TALER 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 TALER 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 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/auditor_api_get_config.c 19 * @brief Implementation of /config for the auditor's HTTP API 20 * @author Sree Harsha Totakura <sreeharsha@totakura.in> 21 * @author Christian Grothoff 22 */ 23 #include "taler/platform.h" 24 #include <microhttpd.h> 25 #include <gnunet/gnunet_curl_lib.h> 26 #include "taler/taler_json_lib.h" 27 #include "taler/taler_auditor_service.h" 28 #include "taler/taler_signatures.h" 29 #include "auditor_api_curl_defaults.h" 30 31 32 /** 33 * Which revision of the Taler auditor protocol is implemented 34 * by this library? Used to determine compatibility. 35 */ 36 #define TALER_PROTOCOL_CURRENT 1 37 38 /** 39 * How many revisions back are we compatible to? 40 */ 41 #define TALER_PROTOCOL_AGE 0 42 43 44 /** 45 * Log error related to CURL operations. 46 * 47 * @param type log level 48 * @param function which function failed to run 49 * @param code what was the curl error code 50 */ 51 #define CURL_STRERROR(type, function, code) \ 52 GNUNET_log (type, \ 53 "Curl function `%s' has failed at `%s:%d' with error: %s", \ 54 function, __FILE__, __LINE__, curl_easy_strerror (code)); 55 56 57 /** 58 * Handle for the get config request. 59 */ 60 struct TALER_AUDITOR_GetConfigHandle 61 { 62 /** 63 * The context of this handle 64 */ 65 struct GNUNET_CURL_Context *ctx; 66 67 /** 68 * Function to call with the auditor's certification data, 69 * NULL if this has already been done. 70 */ 71 TALER_AUDITOR_ConfigCallback config_cb; 72 73 /** 74 * Closure to pass to @e config_cb. 75 */ 76 void *config_cb_cls; 77 78 /** 79 * Data for the request to get the /config of a auditor, 80 * NULL once we are past stage #MHS_INIT. 81 */ 82 struct GNUNET_CURL_Job *vr; 83 84 /** 85 * The url for the @e vr job. 86 */ 87 char *vr_url; 88 89 }; 90 91 92 /* ***************** Internal /config fetching ************* */ 93 94 /** 95 * Decode the JSON in @a resp_obj from the /config response and store the data 96 * in the @a key_data. 97 * 98 * @param[in] resp_obj JSON object to parse 99 * @param[in,out] vi where to store the results we decoded 100 * @param[out] vc where to store config compatibility data 101 * @return #TALER_EC_NONE on success 102 */ 103 static enum TALER_ErrorCode 104 decode_config_json (const json_t *resp_obj, 105 struct TALER_AUDITOR_ConfigInformation *vi, 106 enum TALER_AUDITOR_VersionCompatibility *vc) 107 { 108 struct TALER_JSON_ProtocolVersion pv; 109 const char *ver; 110 struct GNUNET_JSON_Specification spec[] = { 111 TALER_JSON_spec_version ("version", 112 &pv), 113 GNUNET_JSON_spec_string ("version", 114 &ver), 115 GNUNET_JSON_spec_fixed_auto ("exchange_master_public_key", 116 &vi->exchange_master_public_key), 117 GNUNET_JSON_spec_fixed_auto ("auditor_public_key", 118 &vi->auditor_pub), 119 GNUNET_JSON_spec_end () 120 }; 121 122 if (JSON_OBJECT != json_typeof (resp_obj)) 123 { 124 GNUNET_break_op (0); 125 return TALER_EC_GENERIC_JSON_INVALID; 126 } 127 /* check the config */ 128 if (GNUNET_OK != 129 GNUNET_JSON_parse (resp_obj, 130 spec, 131 NULL, NULL)) 132 { 133 GNUNET_break_op (0); 134 return TALER_EC_GENERIC_JSON_INVALID; 135 } 136 vi->version = ver; 137 *vc = TALER_AUDITOR_VC_MATCH; 138 if (TALER_PROTOCOL_CURRENT < pv.current) 139 { 140 *vc |= TALER_AUDITOR_VC_NEWER; 141 if (TALER_PROTOCOL_CURRENT < pv.current - pv.age) 142 *vc |= TALER_AUDITOR_VC_INCOMPATIBLE; 143 } 144 if (TALER_PROTOCOL_CURRENT > pv.current) 145 { 146 *vc |= TALER_AUDITOR_VC_OLDER; 147 if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > pv.current) 148 *vc |= TALER_AUDITOR_VC_INCOMPATIBLE; 149 } 150 return TALER_EC_NONE; 151 } 152 153 154 /** 155 * Callback used when downloading the reply to a /config request 156 * is complete. 157 * 158 * @param cls the `struct TALER_AUDITOR_GetConfigHandle` 159 * @param response_code HTTP response code, 0 on error 160 * @param gresp_obj parsed JSON result, NULL on error, must be a `const json_t *` 161 */ 162 static void 163 config_completed_cb (void *cls, 164 long response_code, 165 const void *gresp_obj) 166 { 167 struct TALER_AUDITOR_GetConfigHandle *auditor = cls; 168 const json_t *resp_obj = gresp_obj; 169 struct TALER_AUDITOR_ConfigResponse vr = { 170 .hr.reply = resp_obj, 171 .hr.http_status = (unsigned int) response_code 172 }; 173 174 auditor->vr = NULL; 175 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 176 "Received config from URL `%s' with status %ld.\n", 177 auditor->vr_url, 178 response_code); 179 switch (response_code) 180 { 181 case 0: 182 GNUNET_break_op (0); 183 vr.hr.ec = TALER_EC_INVALID; 184 break; 185 case MHD_HTTP_OK: 186 if (NULL == resp_obj) 187 { 188 GNUNET_break_op (0); 189 vr.hr.http_status = 0; 190 vr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 191 break; 192 } 193 vr.hr.ec = decode_config_json (resp_obj, 194 &vr.details.ok.vi, 195 &vr.details.ok.compat); 196 if (TALER_EC_NONE != vr.hr.ec) 197 { 198 GNUNET_break_op (0); 199 vr.hr.http_status = 0; 200 break; 201 } 202 break; 203 case MHD_HTTP_INTERNAL_SERVER_ERROR: 204 vr.hr.ec = TALER_JSON_get_error_code (resp_obj); 205 vr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 206 break; 207 default: 208 vr.hr.ec = TALER_JSON_get_error_code (resp_obj); 209 vr.hr.hint = TALER_JSON_get_error_hint (resp_obj); 210 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 211 "Unexpected response code %u/%d\n", 212 (unsigned int) response_code, 213 (int) vr.hr.ec); 214 break; 215 } 216 auditor->config_cb (auditor->config_cb_cls, 217 &vr); 218 TALER_AUDITOR_get_config_cancel (auditor); 219 } 220 221 222 struct TALER_AUDITOR_GetConfigHandle * 223 TALER_AUDITOR_get_config (struct GNUNET_CURL_Context *ctx, 224 const char *url, 225 TALER_AUDITOR_ConfigCallback config_cb, 226 void *config_cb_cls) 227 { 228 struct TALER_AUDITOR_GetConfigHandle *auditor; 229 CURL *eh; 230 231 auditor = GNUNET_new (struct TALER_AUDITOR_GetConfigHandle); 232 auditor->config_cb = config_cb; 233 auditor->config_cb_cls = config_cb_cls; 234 auditor->ctx = ctx; 235 auditor->vr_url = TALER_url_join (url, 236 "config", 237 NULL); 238 if (NULL == auditor->vr_url) 239 { 240 GNUNET_break (0); 241 GNUNET_free (auditor); 242 return NULL; 243 } 244 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 245 "Requesting auditor config with URL `%s'.\n", 246 auditor->vr_url); 247 eh = TALER_AUDITOR_curl_easy_get_ (auditor->vr_url); 248 if (NULL == eh) 249 { 250 GNUNET_break (0); 251 TALER_AUDITOR_get_config_cancel (auditor); 252 return NULL; 253 } 254 GNUNET_break (CURLE_OK == 255 curl_easy_setopt (eh, 256 CURLOPT_TIMEOUT, 257 (long) 300)); 258 auditor->vr = GNUNET_CURL_job_add (auditor->ctx, 259 eh, 260 &config_completed_cb, 261 auditor); 262 return auditor; 263 } 264 265 266 void 267 TALER_AUDITOR_get_config_cancel (struct TALER_AUDITOR_GetConfigHandle *auditor) 268 { 269 if (NULL != auditor->vr) 270 { 271 GNUNET_CURL_job_cancel (auditor->vr); 272 auditor->vr = NULL; 273 } 274 GNUNET_free (auditor->vr_url); 275 GNUNET_free (auditor); 276 } 277 278 279 /* end of auditor_api_get_config.c */