taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.c (7750B)
1 /* 2 This file is part of TALER 3 (C) 2022 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation; either version 3, 8 or (at your option) any later version. 9 10 TALER 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 TALER; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file src/backend/taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.c 22 * @brief implementing PATCH /otp-devices/$ID request handling 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include "taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.h" 27 #include "taler-merchant-httpd_helper.h" 28 #include <taler/taler_json_lib.h> 29 #include "merchant-database/update_otp_device.h" 30 #include "merchant-database/get_otp_device.h" 31 32 33 /** 34 * Does @a alg confirm payments by signing a challenge with a key pair 35 * generated by the backend? 36 * 37 * @param alg algorithm to check 38 * @return true for the challenge-signature algorithms 39 */ 40 static bool 41 is_challenge_alg (enum TALER_MerchantConfirmationAlgorithm alg) 42 { 43 return ( (TALER_MCA_ECDSA_CHALLENGE == alg) || 44 (TALER_MCA_EDDSA_CHALLENGE == alg) ); 45 } 46 47 48 enum MHD_Result 49 TMH_private_patch_otp_devices_ID (const struct TMH_RequestHandler *rh, 50 struct MHD_Connection *connection, 51 struct TMH_HandlerContext *hc) 52 { 53 struct TMH_MerchantInstance *mi = hc->instance; 54 const char *device_id = hc->infix; 55 struct TALER_MERCHANTDB_OtpDeviceDetails tp = {0}; 56 uint64_t expected_serial; 57 enum TALER_MerchantConfirmationAlgorithm expected_algorithm; 58 enum GNUNET_DB_QueryStatus qs; 59 struct GNUNET_JSON_Specification spec[] = { 60 GNUNET_JSON_spec_string ("otp_device_description", 61 (const char **) &tp.otp_description), 62 TALER_JSON_spec_otp_type ("otp_algorithm", 63 &tp.otp_algorithm), 64 GNUNET_JSON_spec_mark_optional ( 65 GNUNET_JSON_spec_uint64 ("otp_ctr", 66 &tp.otp_ctr), 67 NULL), 68 GNUNET_JSON_spec_mark_optional ( 69 70 TALER_JSON_spec_otp_key ("otp_key", 71 (const char **) &tp.otp_key), 72 NULL), 73 GNUNET_JSON_spec_end () 74 }; 75 76 GNUNET_assert (NULL != mi); 77 GNUNET_assert (NULL != device_id); 78 { 79 enum GNUNET_GenericReturnValue res; 80 81 res = TALER_MHD_parse_json_data (connection, 82 hc->request_body, 83 spec); 84 if (GNUNET_OK != res) 85 return (GNUNET_NO == res) 86 ? MHD_YES 87 : MHD_NO; 88 } 89 90 switch (tp.otp_algorithm) 91 { 92 case TALER_MCA_ECDSA_CHALLENGE: 93 case TALER_MCA_EDDSA_CHALLENGE: 94 /* the key pair belongs to the backend; there is nothing here for a 95 client to set, and replacing it would silently invalidate every 96 offline verifier already configured with the public key */ 97 if (NULL != tp.otp_key) 98 { 99 GNUNET_break_op (0); 100 return TALER_MHD_reply_with_error (connection, 101 MHD_HTTP_BAD_REQUEST, 102 TALER_EC_GENERIC_PARAMETER_MALFORMED, 103 "otp_key"); 104 } 105 break; 106 case TALER_MCA_NONE: 107 case TALER_MCA_WITHOUT_PRICE: 108 case TALER_MCA_WITH_PRICE: 109 break; 110 } 111 112 { 113 struct TALER_MERCHANTDB_OtpDeviceDetails etp; 114 bool incompatible = false; 115 116 qs = TALER_MERCHANTDB_get_otp_device (TMH_db, 117 mi->settings.id, 118 device_id, 119 &etp); 120 switch (qs) 121 { 122 case GNUNET_DB_STATUS_HARD_ERROR: 123 case GNUNET_DB_STATUS_SOFT_ERROR: 124 GNUNET_break (0); 125 GNUNET_JSON_parse_free (spec); 126 return TALER_MHD_reply_with_error (connection, 127 MHD_HTTP_INTERNAL_SERVER_ERROR, 128 TALER_EC_GENERIC_DB_FETCH_FAILED, 129 "get_otp_device"); 130 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 131 GNUNET_JSON_parse_free (spec); 132 return TALER_MHD_reply_with_error (connection, 133 MHD_HTTP_NOT_FOUND, 134 TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, 135 device_id); 136 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 137 expected_serial = etp.otp_serial; 138 expected_algorithm = etp.otp_algorithm; 139 incompatible = (etp.otp_algorithm != tp.otp_algorithm) && 140 (is_challenge_alg (etp.otp_algorithm) || 141 is_challenge_alg (tp.otp_algorithm)); 142 GNUNET_free (etp.otp_description); 143 GNUNET_free (etp.otp_key); 144 GNUNET_free (etp.otp_device_pub); 145 break; 146 } 147 if (incompatible) 148 { 149 /* Challenge keys belong to a specific algorithm and cannot be 150 reused with another challenge algorithm or with TOTP. Recreate 151 the device to change its algorithm. */ 152 GNUNET_break_op (0); 153 GNUNET_JSON_parse_free (spec); 154 return TALER_MHD_reply_with_error (connection, 155 MHD_HTTP_CONFLICT, 156 TALER_EC_MERCHANT_PRIVATE_PATCH_OTP_DEVICES_CONFLICT, 157 "otp_algorithm"); 158 } 159 } 160 161 qs = TALER_MERCHANTDB_update_otp_device (TMH_db, 162 mi->settings.id, 163 device_id, 164 expected_serial, 165 expected_algorithm, 166 &tp); 167 { 168 enum MHD_Result ret = MHD_NO; 169 170 switch (qs) 171 { 172 case GNUNET_DB_STATUS_HARD_ERROR: 173 GNUNET_break (0); 174 ret = TALER_MHD_reply_with_error (connection, 175 MHD_HTTP_INTERNAL_SERVER_ERROR, 176 TALER_EC_GENERIC_DB_STORE_FAILED, 177 "update_pos"); 178 break; 179 case GNUNET_DB_STATUS_SOFT_ERROR: 180 GNUNET_break (0); 181 ret = TALER_MHD_reply_with_error (connection, 182 MHD_HTTP_INTERNAL_SERVER_ERROR, 183 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 184 "unexpected serialization problem"); 185 break; 186 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 187 /* Validation no longer describes this device. The conditional 188 UPDATE left the concurrent writer's device untouched. */ 189 ret = TALER_MHD_reply_with_error (connection, 190 MHD_HTTP_CONFLICT, 191 TALER_EC_MERCHANT_PRIVATE_PATCH_OTP_DEVICES_CONFLICT, 192 "OTP device changed during update"); 193 break; 194 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 195 ret = TALER_MHD_reply_static (connection, 196 MHD_HTTP_NO_CONTENT, 197 NULL, 198 NULL, 199 0); 200 break; 201 } 202 GNUNET_JSON_parse_free (spec); 203 return ret; 204 } 205 } 206 207 208 /* end of taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.c */