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