anastasis-httpd_policy.c (9561B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2019, 2021 Anastasis SARL 4 5 Anastasis 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 Anastasis 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 Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file anastasis-httpd_policy.c 18 * @brief functions to handle incoming requests on /policy/ 19 * @author Dennis Neufeld 20 * @author Dominik Meister 21 * @author Christian Grothoff 22 */ 23 #include "platform.h" 24 #include "anastasis-httpd.h" 25 #include "anastasis-httpd_policy.h" 26 #include "anastasis_service.h" 27 #include <gnunet/gnunet_util_lib.h> 28 #include <gnunet/gnunet_rest_lib.h> 29 #include <taler/taler_json_lib.h> 30 #include <taler/taler_merchant_service.h> 31 #include <taler/taler_signatures.h> 32 33 34 /** 35 * Return the current recoverydocument of @a account on @a connection. 36 * 37 * @param connection MHD connection to use 38 * @param account_pub account to query 39 * @return MHD result code 40 */ 41 static MHD_RESULT 42 return_policy (struct MHD_Connection *connection, 43 const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub) 44 { 45 enum GNUNET_DB_QueryStatus qs; 46 struct MHD_Response *resp; 47 struct ANASTASIS_AccountSignatureP account_sig; 48 struct GNUNET_HashCode recovery_data_hash; 49 const char *version_s; 50 char version_b[14]; 51 uint32_t version; 52 void *res_recovery_data; 53 size_t res_recovery_data_size; 54 55 version_s = MHD_lookup_connection_value (connection, 56 MHD_GET_ARGUMENT_KIND, 57 "version"); 58 if (NULL != version_s) 59 { 60 char dummy; 61 62 if (1 != sscanf (version_s, 63 "%u%c", 64 &version, 65 &dummy)) 66 { 67 return TALER_MHD_reply_with_error (connection, 68 MHD_HTTP_BAD_REQUEST, 69 TALER_EC_GENERIC_PARAMETER_MALFORMED, 70 "version"); 71 } 72 qs = db->get_recovery_document (db->cls, 73 account_pub, 74 version, 75 &account_sig, 76 &recovery_data_hash, 77 &res_recovery_data_size, 78 &res_recovery_data); 79 } 80 else 81 { 82 qs = db->get_latest_recovery_document (db->cls, 83 account_pub, 84 &account_sig, 85 &recovery_data_hash, 86 &res_recovery_data_size, 87 &res_recovery_data, 88 &version); 89 GNUNET_snprintf (version_b, 90 sizeof (version_b), 91 "%u", 92 (unsigned int) version); 93 version_s = version_b; 94 } 95 switch (qs) 96 { 97 case GNUNET_DB_STATUS_HARD_ERROR: 98 GNUNET_break (0); 99 return TALER_MHD_reply_with_error (connection, 100 MHD_HTTP_INTERNAL_SERVER_ERROR, 101 TALER_EC_GENERIC_DB_FETCH_FAILED, 102 "get_recovery_document"); 103 case GNUNET_DB_STATUS_SOFT_ERROR: 104 GNUNET_break (0); 105 return TALER_MHD_reply_with_error (connection, 106 MHD_HTTP_INTERNAL_SERVER_ERROR, 107 TALER_EC_GENERIC_DB_SOFT_FAILURE, 108 "get_recovery_document"); 109 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 110 return TALER_MHD_reply_with_error (connection, 111 MHD_HTTP_NOT_FOUND, 112 TALER_EC_ANASTASIS_POLICY_NOT_FOUND, 113 NULL); 114 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 115 /* interesting case below */ 116 break; 117 } 118 resp = MHD_create_response_from_buffer (res_recovery_data_size, 119 res_recovery_data, 120 MHD_RESPMEM_MUST_FREE); 121 TALER_MHD_add_global_headers (resp, 122 false); 123 { 124 char *sig_s; 125 char *etag; 126 char *etagq; 127 128 sig_s = GNUNET_STRINGS_data_to_string_alloc (&account_sig, 129 sizeof (account_sig)); 130 GNUNET_break (MHD_YES == 131 MHD_add_response_header (resp, 132 ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE, 133 sig_s)); 134 GNUNET_free (sig_s); 135 GNUNET_break (MHD_YES == 136 MHD_add_response_header (resp, 137 ANASTASIS_HTTP_HEADER_POLICY_VERSION, 138 version_s)); 139 etag = GNUNET_STRINGS_data_to_string_alloc (&recovery_data_hash, 140 sizeof (recovery_data_hash)); 141 GNUNET_asprintf (&etagq, 142 "\"%s\"", 143 etag); 144 GNUNET_free (etag); 145 GNUNET_break (MHD_YES == 146 MHD_add_response_header (resp, 147 MHD_HTTP_HEADER_ETAG, 148 etagq)); 149 GNUNET_free (etagq); 150 } 151 { 152 MHD_RESULT ret; 153 154 ret = MHD_queue_response (connection, 155 MHD_HTTP_OK, 156 resp); 157 MHD_destroy_response (resp); 158 return ret; 159 } 160 } 161 162 163 MHD_RESULT 164 AH_policy_get (struct MHD_Connection *connection, 165 const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub) 166 { 167 struct GNUNET_HashCode recovery_data_hash; 168 enum ANASTASIS_DB_AccountStatus as; 169 MHD_RESULT ret; 170 uint32_t version; 171 struct GNUNET_TIME_Timestamp expiration; 172 173 as = db->lookup_account (db->cls, 174 account_pub, 175 &expiration, 176 &recovery_data_hash, 177 &version); 178 switch (as) 179 { 180 case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED: 181 return TALER_MHD_reply_with_error (connection, 182 MHD_HTTP_NOT_FOUND, 183 TALER_EC_SYNC_ACCOUNT_UNKNOWN, 184 NULL); 185 case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR: 186 GNUNET_break (0); 187 return TALER_MHD_reply_with_error (connection, 188 MHD_HTTP_INTERNAL_SERVER_ERROR, 189 TALER_EC_GENERIC_DB_FETCH_FAILED, 190 "lookup account"); 191 case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS: 192 { 193 struct MHD_Response *resp; 194 195 resp = MHD_create_response_from_buffer (0, 196 NULL, 197 MHD_RESPMEM_PERSISTENT); 198 TALER_MHD_add_global_headers (resp, 199 false); 200 ret = MHD_queue_response (connection, 201 MHD_HTTP_NO_CONTENT, 202 resp); 203 MHD_destroy_response (resp); 204 } 205 return ret; 206 case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED: 207 { 208 const char *inm; 209 210 inm = MHD_lookup_connection_value (connection, 211 MHD_HEADER_KIND, 212 MHD_HTTP_HEADER_IF_NONE_MATCH); 213 if ( (NULL != inm) && 214 (2 < strlen (inm)) && 215 ('"' == inm[0]) && 216 ('"' == inm[strlen (inm) - 1]) ) 217 { 218 struct GNUNET_HashCode inm_h; 219 220 if (GNUNET_OK != 221 GNUNET_STRINGS_string_to_data (inm + 1, 222 strlen (inm) - 2, 223 &inm_h, 224 sizeof (inm_h))) 225 { 226 GNUNET_break_op (0); 227 return TALER_MHD_reply_with_error (connection, 228 MHD_HTTP_BAD_REQUEST, 229 TALER_EC_ANASTASIS_POLICY_BAD_IF_NONE_MATCH, 230 "Etag must be a base32-encoded SHA-512 hash"); 231 } 232 if (0 == GNUNET_memcmp (&inm_h, 233 &recovery_data_hash)) 234 { 235 struct MHD_Response *resp; 236 237 resp = MHD_create_response_from_buffer (0, 238 NULL, 239 MHD_RESPMEM_PERSISTENT); 240 TALER_MHD_add_global_headers (resp, 241 false); 242 ret = MHD_queue_response (connection, 243 MHD_HTTP_NOT_MODIFIED, 244 resp); 245 MHD_destroy_response (resp); 246 return ret; 247 } 248 } 249 } 250 /* We have a result, should fetch and return it! */ 251 break; 252 } 253 return return_policy (connection, 254 account_pub); 255 }