taler-exchange-httpd_management_extensions.c (8903B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2021 Taler Systems SA 4 5 TALER 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 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 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 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-httpd_management_extensions.c 18 * @brief Handle request to POST /management/extensions 19 * @author Özgür Kesim 20 */ 21 #include "taler/platform.h" 22 #include <gnunet/gnunet_util_lib.h> 23 #include <gnunet/gnunet_json_lib.h> 24 #include <jansson.h> 25 #include <microhttpd.h> 26 #include "taler/taler_json_lib.h" 27 #include "taler/taler_mhd_lib.h" 28 #include "taler/taler_signatures.h" 29 #include "taler-exchange-httpd_management.h" 30 #include "taler-exchange-httpd_responses.h" 31 #include "taler/taler_extensions.h" 32 #include "taler/taler_dbevents.h" 33 34 /** 35 * Extension carries the necessary data for a particular extension. 36 * 37 */ 38 struct Extension 39 { 40 enum TALER_Extension_Type type; 41 json_t *manifest; 42 }; 43 44 /** 45 * Closure for the #set_extensions transaction 46 */ 47 struct SetExtensionsContext 48 { 49 uint32_t num_extensions; 50 struct Extension *extensions; 51 struct TALER_MasterSignatureP extensions_sig; 52 }; 53 54 /** 55 * Function implementing database transaction to set the manifests of 56 * extensions. It runs the transaction logic. 57 * - IF it returns a non-error code, the transaction logic MUST NOT queue a 58 * MHD response. 59 * - IF it returns an hard error, the transaction logic MUST queue a MHD 60 * response and set @a mhd_ret. 61 * - IF it returns the soft error code, the function MAY be called again to 62 * retry and MUST not queue a MHD response. 63 * 64 * @param cls closure with a `struct SetExtensionsContext` 65 * @param connection MHD request which triggered the transaction 66 * @param[out] mhd_ret set to MHD response status for @a connection, 67 * if transaction failed (!) 68 * @return transaction status 69 */ 70 static enum GNUNET_DB_QueryStatus 71 set_extensions (void *cls, 72 struct MHD_Connection *connection, 73 MHD_RESULT *mhd_ret) 74 { 75 struct SetExtensionsContext *sec = cls; 76 77 /* save the manifests of all extensions */ 78 for (uint32_t i = 0; i<sec->num_extensions; i++) 79 { 80 struct Extension *ext = &sec->extensions[i]; 81 const struct TALER_Extension *taler_ext; 82 enum GNUNET_DB_QueryStatus qs; 83 char *manifest; 84 85 taler_ext = TALER_extensions_get_by_type (ext->type); 86 if (NULL == taler_ext) 87 { 88 /* No such extension found */ 89 GNUNET_break (0); 90 return GNUNET_DB_STATUS_HARD_ERROR; 91 } 92 93 manifest = json_dumps (ext->manifest, JSON_COMPACT | JSON_SORT_KEYS); 94 if (NULL == manifest) 95 { 96 GNUNET_break (0); 97 *mhd_ret = TALER_MHD_reply_with_error (connection, 98 MHD_HTTP_INTERNAL_SERVER_ERROR, 99 TALER_EC_GENERIC_JSON_INVALID, 100 "convert configuration to string"); 101 return GNUNET_DB_STATUS_HARD_ERROR; 102 } 103 104 qs = TEH_plugin->set_extension_manifest ( 105 TEH_plugin->cls, 106 taler_ext->name, 107 manifest); 108 109 free (manifest); 110 111 if (qs < 0) 112 { 113 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 114 return qs; 115 GNUNET_break (0); 116 *mhd_ret = TALER_MHD_reply_with_error (connection, 117 MHD_HTTP_INTERNAL_SERVER_ERROR, 118 TALER_EC_GENERIC_DB_STORE_FAILED, 119 "save extension configuration"); 120 } 121 122 /* Success, trigger event */ 123 { 124 uint32_t nbo_type = htonl (sec->extensions[i].type); 125 struct GNUNET_DB_EventHeaderP ev = { 126 .size = htons (sizeof (ev)), 127 .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED) 128 }; 129 130 TEH_plugin->event_notify (TEH_plugin->cls, 131 &ev, 132 &nbo_type, 133 sizeof(nbo_type)); 134 } 135 136 } 137 138 /* All extensions configured, update the signature */ 139 TEH_extensions_sig = sec->extensions_sig; 140 TEH_extensions_signed = true; 141 142 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ 143 } 144 145 146 static enum GNUNET_GenericReturnValue 147 verify_extensions_from_json ( 148 const json_t *extensions, 149 struct SetExtensionsContext *sec) 150 { 151 const char*name; 152 const struct TALER_Extension *extension; 153 size_t i = 0; 154 json_t *manifest; 155 156 GNUNET_assert (NULL != extensions); 157 GNUNET_assert (json_is_object (extensions)); 158 159 sec->num_extensions = json_object_size (extensions); 160 sec->extensions = GNUNET_new_array (sec->num_extensions, 161 struct Extension); 162 163 json_object_foreach ((json_t *) extensions, name, manifest) 164 { 165 int critical = 0; 166 json_t *config; 167 const char *version = NULL; 168 169 /* load and verify criticality, version, etc. */ 170 extension = TALER_extensions_get_by_name (name); 171 if (NULL == extension) 172 { 173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 174 "no such extension: %s\n", name); 175 return GNUNET_SYSERR; 176 } 177 178 if (GNUNET_OK != 179 TALER_extensions_parse_manifest ( 180 manifest, &critical, &version, &config)) 181 return GNUNET_SYSERR; 182 183 if (critical != extension->critical 184 || 0 != strcmp (version, extension->version) // FIXME-oec: libtool compare 185 || NULL == config 186 || GNUNET_OK != extension->load_config (config, NULL)) 187 return GNUNET_SYSERR; 188 189 sec->extensions[i].type = extension->type; 190 sec->extensions[i].manifest = json_copy (manifest); 191 } 192 193 return GNUNET_OK; 194 } 195 196 197 MHD_RESULT 198 TEH_handler_management_post_extensions ( 199 struct MHD_Connection *connection, 200 const json_t *root) 201 { 202 MHD_RESULT ret; 203 const json_t *extensions; 204 struct SetExtensionsContext sec = {0}; 205 struct GNUNET_JSON_Specification top_spec[] = { 206 GNUNET_JSON_spec_object_const ("extensions", 207 &extensions), 208 GNUNET_JSON_spec_fixed_auto ("extensions_sig", 209 &sec.extensions_sig), 210 GNUNET_JSON_spec_end () 211 }; 212 213 /* Parse the top level json structure */ 214 { 215 enum GNUNET_GenericReturnValue res; 216 217 res = TALER_MHD_parse_json_data (connection, 218 root, 219 top_spec); 220 if (GNUNET_SYSERR == res) 221 return MHD_NO; /* hard failure */ 222 if (GNUNET_NO == res) 223 return MHD_YES; /* failure */ 224 } 225 226 /* Verify the signature */ 227 { 228 struct TALER_ExtensionManifestsHashP h_manifests; 229 230 if (GNUNET_OK != 231 TALER_JSON_extensions_manifests_hash (extensions, 232 &h_manifests) || 233 GNUNET_OK != 234 TALER_exchange_offline_extension_manifests_hash_verify ( 235 &h_manifests, 236 &TEH_master_public_key, 237 &sec.extensions_sig)) 238 { 239 return TALER_MHD_reply_with_error ( 240 connection, 241 MHD_HTTP_BAD_REQUEST, 242 TALER_EC_GENERIC_PARAMETER_MALFORMED, 243 "invalid signuture"); 244 } 245 } 246 247 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 248 "Received /management/extensions\n"); 249 250 /* Now parse individual extensions and signatures from those objects. */ 251 if (GNUNET_OK != 252 verify_extensions_from_json (extensions, &sec)) 253 { 254 return TALER_MHD_reply_with_error ( 255 connection, 256 MHD_HTTP_BAD_REQUEST, 257 TALER_EC_GENERIC_PARAMETER_MALFORMED, 258 "invalid object"); 259 } 260 261 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 262 "Received %u extensions\n", 263 sec.num_extensions); 264 265 /* now run the transaction to persist the configurations */ 266 { 267 enum GNUNET_GenericReturnValue res; 268 269 res = TEH_DB_run_transaction (connection, 270 "set extensions", 271 TEH_MT_REQUEST_OTHER, 272 &ret, 273 &set_extensions, 274 &sec); 275 276 if (GNUNET_SYSERR == res) 277 goto CLEANUP; 278 } 279 280 ret = TALER_MHD_reply_static ( 281 connection, 282 MHD_HTTP_NO_CONTENT, 283 NULL, 284 NULL, 285 0); 286 287 CLEANUP: 288 for (unsigned int i = 0; i < sec.num_extensions; i++) 289 { 290 if (NULL != sec.extensions[i].manifest) 291 { 292 json_decref (sec.extensions[i].manifest); 293 } 294 } 295 GNUNET_free (sec.extensions); 296 return ret; 297 } 298 299 300 /* end of taler-exchange-httpd_management_management_post_extensions.c */