anastasis_authorization_plugin_file.c (10371B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2019 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.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file anastasis_authorization_plugin_file.c 18 * @brief authorization plugin file based for testing 19 * @author Dominik Meister 20 */ 21 #include "platform.h" 22 #include "anastasis_authorization_plugin.h" 23 #include <taler/taler_mhd_lib.h> 24 #include <gnunet/gnunet_db_lib.h> 25 #include "anastasis_database_lib.h" 26 27 /** 28 * How many retries do we allow per code? 29 */ 30 #define INITIAL_RETRY_COUNTER 3 31 32 33 /** 34 * Saves the state of a authorization process 35 */ 36 struct ANASTASIS_AUTHORIZATION_State 37 { 38 /** 39 * UUID of the challenge which is authorised 40 */ 41 struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; 42 43 /** 44 * Code which is sent to the user (here saved into a file) 45 */ 46 uint64_t code; 47 48 /** 49 * holds the truth information 50 */ 51 char *filename; 52 53 /** 54 * closure 55 */ 56 void *cls; 57 }; 58 59 60 /** 61 * Validate @a data is a well-formed input into the challenge method, 62 * i.e. @a data is a well-formed phone number for sending an SMS, or 63 * a well-formed e-mail address for sending an e-mail. Not expected to 64 * check that the phone number or e-mail account actually exists. 65 * 66 * To be possibly used before issuing a 402 payment required to the client. 67 * 68 * @param cls closure with a `const struct ANASTASIS_AuthorizationContext *` 69 * @param connection HTTP client request (for queuing response) 70 * @param truth_mime mime type of @e data 71 * @param data input to validate (i.e. is it a valid phone number, etc.) 72 * @param data_length number of bytes in @a data 73 * @return #GNUNET_OK if @a data is valid, 74 * #GNUNET_NO if @a data is invalid and a reply was successfully queued on @a connection 75 * #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on @a connection 76 */ 77 static enum GNUNET_GenericReturnValue 78 file_validate (void *cls, 79 struct MHD_Connection *connection, 80 const char *truth_mime, 81 const char *data, 82 size_t data_length) 83 { 84 char *filename; 85 bool flag; 86 87 (void) cls; 88 if (NULL == data) 89 return GNUNET_SYSERR; 90 filename = GNUNET_STRINGS_data_to_string_alloc (data, 91 data_length); 92 flag = false; 93 for (size_t i = 0; i<strlen (filename); i++) 94 { 95 if ( (filename[i] == ' ') || 96 (filename[i] == '/') ) 97 { 98 flag = true; 99 break; 100 } 101 } 102 if (flag) 103 return GNUNET_SYSERR; 104 GNUNET_free (filename); 105 return GNUNET_OK; 106 } 107 108 109 /** 110 * Begin issuing authentication challenge to user based on @a data. 111 * I.e. start to send SMS or e-mail or launch video identification. 112 * 113 * @param cls closure 114 * @param trigger function to call when we made progress 115 * @param trigger_cls closure for @a trigger 116 * @param truth_uuid Identifier of the challenge, to be (if possible) included in the 117 * interaction with the user 118 * @param code secret code that the user has to provide back to satisfy the challenge in 119 * the main anastasis protocol 120 * @param data input to validate (i.e. is it a valid phone number, etc.) 121 * @param data_length number of bytes in @a data 122 * @return state to track progress on the authorization operation, NULL on failure 123 */ 124 static struct ANASTASIS_AUTHORIZATION_State * 125 file_start (void *cls, 126 GNUNET_SCHEDULER_TaskCallback trigger, 127 void *trigger_cls, 128 const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, 129 uint64_t code, 130 const void *data, 131 size_t data_length) 132 { 133 const struct ANASTASIS_AuthorizationContext *ac = cls; 134 struct ANASTASIS_AUTHORIZATION_State *as; 135 enum GNUNET_DB_QueryStatus qs; 136 137 /* If the user can show this challenge code, this 138 plugin is already happy (no additional 139 requirements), so mark this challenge as 140 already satisfied from the start. */ 141 qs = ac->db->mark_challenge_code_satisfied (ac->db->cls, 142 truth_uuid, 143 code); 144 if (qs <= 0) 145 { 146 GNUNET_break (0); 147 return NULL; 148 } 149 as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State); 150 as->cls = cls; 151 as->truth_uuid = *truth_uuid; 152 as->code = code; 153 as->filename = GNUNET_strndup (data, 154 data_length); 155 return as; 156 } 157 158 159 /** 160 * Begin issuing authentication challenge to user based on @a data. 161 * I.e. start to send SMS or e-mail or launch video identification. 162 * 163 * @param as authorization state 164 * @param connection HTTP client request (for queuing response, such as redirection to video portal) 165 * @return state of the request 166 */ 167 static enum ANASTASIS_AUTHORIZATION_ChallengeResult 168 file_challenge (struct ANASTASIS_AUTHORIZATION_State *as, 169 struct MHD_Connection *connection) 170 { 171 const char *mime; 172 const char *lang; 173 174 mime = MHD_lookup_connection_value (connection, 175 MHD_HEADER_KIND, 176 MHD_HTTP_HEADER_ACCEPT); 177 if (NULL == mime) 178 mime = "text/plain"; 179 lang = MHD_lookup_connection_value (connection, 180 MHD_HEADER_KIND, 181 MHD_HTTP_HEADER_ACCEPT_LANGUAGE); 182 if (NULL == lang) 183 lang = "en"; 184 { 185 FILE *f = fopen (as->filename, "w"); 186 187 if (NULL == f) 188 { 189 struct MHD_Response *resp; 190 MHD_RESULT mres; 191 192 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 193 "open", 194 as->filename); 195 resp = TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 196 "open"); 197 mres = MHD_queue_response (connection, 198 MHD_HTTP_INTERNAL_SERVER_ERROR, 199 resp); 200 MHD_destroy_response (resp); 201 if (MHD_YES != mres) 202 return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 203 return ANASTASIS_AUTHORIZATION_CRES_FAILED; 204 } 205 206 /* print challenge code to file */ 207 if (0 >= fprintf (f, 208 "%lu", 209 as->code)) 210 { 211 struct MHD_Response *resp; 212 MHD_RESULT mres; 213 214 GNUNET_break (0 == fclose (f)); 215 resp = TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 216 "write"); 217 mres = MHD_queue_response (connection, 218 MHD_HTTP_INTERNAL_SERVER_ERROR, 219 resp); 220 MHD_destroy_response (resp); 221 if (MHD_YES != mres) 222 return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 223 return ANASTASIS_AUTHORIZATION_CRES_FAILED; 224 } 225 GNUNET_break (0 == fclose (f)); 226 } 227 228 /* Build HTTP response */ 229 { 230 struct MHD_Response *resp; 231 232 if (0.0 < TALER_pattern_matches (mime, 233 "application/json")) 234 { 235 resp = TALER_MHD_MAKE_JSON_PACK ( 236 GNUNET_JSON_pack_string ("challenge_type", 237 "FILE_WRITTEN"), 238 GNUNET_JSON_pack_string ("filename", 239 as->filename)); 240 } 241 else 242 { 243 size_t response_size; 244 char *response; 245 246 response_size = GNUNET_asprintf (&response, 247 _ ("Challenge written to file")); 248 resp = MHD_create_response_from_buffer (response_size, 249 response, 250 MHD_RESPMEM_MUST_COPY); 251 GNUNET_free (response); 252 TALER_MHD_add_global_headers (resp, 253 false); 254 GNUNET_break (MHD_YES == 255 MHD_add_response_header (resp, 256 MHD_HTTP_HEADER_CONTENT_TYPE, 257 "text/plain")); 258 } 259 260 { 261 MHD_RESULT mres; 262 263 mres = MHD_queue_response (connection, 264 MHD_HTTP_OK, 265 resp); 266 MHD_destroy_response (resp); 267 if (MHD_YES != mres) 268 return ANASTASIS_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED; 269 return ANASTASIS_AUTHORIZATION_CRES_SUCCESS; 270 } 271 } 272 } 273 274 275 /** 276 * Free internal state associated with @a as. 277 * 278 * @param as state to clean up 279 */ 280 static void 281 file_cleanup (struct ANASTASIS_AUTHORIZATION_State *as) 282 { 283 GNUNET_free (as->filename); 284 GNUNET_free (as); 285 } 286 287 288 /** 289 * Initialize File based authorization plugin 290 * 291 * @param cls a configuration instance 292 * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin` 293 */ 294 void * 295 libanastasis_plugin_authorization_file_init (void *cls); 296 297 /* declaration to fix compiler warning */ 298 void * 299 libanastasis_plugin_authorization_file_init (void *cls) 300 { 301 const struct ANASTASIS_AuthorizationContext *ac = cls; 302 struct ANASTASIS_AuthorizationPlugin *plugin; 303 304 plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin); 305 plugin->cls = (void *) ac; 306 plugin->retry_counter = INITIAL_RETRY_COUNTER; 307 plugin->code_validity_period = GNUNET_TIME_UNIT_MINUTES; 308 plugin->code_rotation_period = GNUNET_TIME_UNIT_MINUTES; 309 plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_MINUTES; 310 plugin->validate = &file_validate; 311 plugin->start = &file_start; 312 plugin->challenge = &file_challenge; 313 plugin->cleanup = &file_cleanup; 314 return plugin; 315 } 316 317 318 /** 319 * Unload authorization plugin 320 * 321 * @param cls a `struct ANASTASIS_AuthorizationPlugin` 322 * @return NULL (always) 323 */ 324 void * 325 libanastasis_plugin_authorization_file_done (void *cls); 326 327 /* declaration to fix compiler warning */ 328 void * 329 libanastasis_plugin_authorization_file_done (void *cls) 330 { 331 struct ANASTASIS_AuthorizationPlugin *plugin = cls; 332 333 GNUNET_free (plugin); 334 return NULL; 335 }