frosix_authorization_plugin_file.c (10030B)
1 /* 2 This file is part of Frosix 3 Copyright (C) 2019 Anastasis SARL 4 5 Frosix 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 Frosix 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 Frosix; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file frosix_authorization_plugin_file.c 18 * @brief authorization plugin file based for testing 19 * @author Dominik Meister 20 */ 21 #include "platform.h" 22 #include "frosix_authorization_plugin.h" 23 #include <taler/taler_mhd_lib.h> 24 #include <gnunet/gnunet_db_lib.h> 25 #include "frosix_database_lib.h" 26 #include "frosix.h" 27 28 /** 29 * How many retries do we allow per code? 30 */ 31 #define INITIAL_RETRY_COUNTER 3 32 33 34 /** 35 * Saves the state of a authorization process 36 */ 37 struct FROSIX_AUTHORIZATION_State 38 { 39 /** 40 * UUID of the challenge which is authorised 41 */ 42 struct FROSIX_ChallengeIdP truth_uuid; 43 44 /** 45 * Code which is sent to the user (here saved into a file) 46 */ 47 uint64_t code; 48 49 /** 50 * holds the truth information 51 */ 52 char *filename; 53 54 /** 55 * closure 56 */ 57 void *cls; 58 }; 59 60 61 /** 62 * Validate @a data is a well-formed input into the challenge method, 63 * i.e. @a data is a well-formed phone number for sending an SMS, or 64 * a well-formed e-mail address for sending an e-mail. Not expected to 65 * check that the phone number or e-mail account actually exists. 66 * 67 * To be possibly used before issuing a 402 payment required to the client. 68 * 69 * @param cls closure with a `const struct ANASTASIS_AuthorizationContext *` 70 * @param connection HTTP client request (for queuing response) 71 * @param truth_mime mime type of @e data 72 * @param data input to validate (i.e. is it a valid phone number, etc.) 73 * @param data_length number of bytes in @a data 74 * @return #GNUNET_OK if @a data is valid, 75 * #GNUNET_NO if @a data is invalid and a reply was successfully queued on @a connection 76 * #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on @a connection 77 */ 78 static enum GNUNET_GenericReturnValue 79 file_validate (void *cls, 80 struct MHD_Connection *connection, 81 const char *truth_mime, 82 const char *data, 83 size_t data_length) 84 { 85 char *filename; 86 bool flag; 87 88 (void) cls; 89 if (NULL == data) 90 return GNUNET_SYSERR; 91 filename = GNUNET_STRINGS_data_to_string_alloc (data, 92 data_length); 93 flag = false; 94 for (size_t i = 0; i<strlen (filename); i++) 95 { 96 if ( (filename[i] == ' ') || 97 (filename[i] == '/') ) 98 { 99 flag = true; 100 break; 101 } 102 } 103 if (flag) 104 return GNUNET_SYSERR; 105 GNUNET_free (filename); 106 return GNUNET_OK; 107 } 108 109 110 /** 111 * Begin issuing authentication challenge to user based on @a data. 112 * I.e. start to send SMS or e-mail or launch video identification. 113 * 114 * @param cls closure 115 * @param trigger function to call when we made progress 116 * @param trigger_cls closure for @a trigger 117 * @param truth_uuid Identifier of the challenge, to be (if possible) included in the 118 * interaction with the user 119 * @param code secret code that the user has to provide back to satisfy the challenge in 120 * the main anastasis protocol 121 * @param data input to validate (i.e. is it a valid phone number, etc.) 122 * @param data_length number of bytes in @a data 123 * @return state to track progress on the authorization operation, NULL on failure 124 */ 125 static struct FROSIX_AUTHORIZATION_State * 126 file_start (void *cls, 127 GNUNET_SCHEDULER_TaskCallback trigger, 128 void *trigger_cls, 129 const struct FROSIX_ChallengeIdP *truth_uuid, 130 uint64_t code, 131 const void *data, 132 size_t data_length) 133 { 134 // const struct FROSIX_AuthorizationContext *ac = cls; 135 struct FROSIX_AUTHORIZATION_State *as; 136 // enum GNUNET_DB_QueryStatus qs; 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 FROSIX_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 FROSIX_AUTHORIZATION_ChallengeResult 168 file_challenge (struct FROSIX_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 FROSIX_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 203 return FROSIX_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 FROSIX_AUTHORIZATION_CRES_FAILED_REPLY_FAILED; 223 return FROSIX_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 (TALER_MHD_xmime_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 GNUNET_break (MHD_YES == 254 MHD_add_response_header (resp, 255 MHD_HTTP_HEADER_CONTENT_TYPE, 256 "text/plain")); 257 } 258 259 { 260 MHD_RESULT mres; 261 262 mres = MHD_queue_response (connection, 263 MHD_HTTP_OK, 264 resp); 265 MHD_destroy_response (resp); 266 if (MHD_YES != mres) 267 return FROSIX_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED; 268 return FROSIX_AUTHORIZATION_CRES_SUCCESS; 269 } 270 } 271 } 272 273 274 /** 275 * Free internal state associated with @a as. 276 * 277 * @param as state to clean up 278 */ 279 static void 280 file_cleanup (struct FROSIX_AUTHORIZATION_State *as) 281 { 282 GNUNET_free (as->filename); 283 GNUNET_free (as); 284 } 285 286 287 /** 288 * Initialize File based authorization plugin 289 * 290 * @param cls a configuration instance 291 * @return NULL on error, otherwise a `struct FROSIX_AuthorizationPlugin` 292 */ 293 void * 294 libfrosix_plugin_authorization_file_init (void *cls) 295 { 296 const struct FROSIX_AuthorizationContext *ac = cls; 297 struct FROSIX_AuthorizationPlugin *plugin; 298 299 plugin = GNUNET_new (struct FROSIX_AuthorizationPlugin); 300 plugin->cls = (void *) ac; 301 plugin->retry_counter = INITIAL_RETRY_COUNTER; 302 plugin->code_validity_period = GNUNET_TIME_UNIT_MINUTES; 303 plugin->code_rotation_period = GNUNET_TIME_UNIT_MINUTES; 304 plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_MINUTES; 305 plugin->validate = &file_validate; 306 plugin->start = &file_start; 307 plugin->challenge = &file_challenge; 308 plugin->cleanup = &file_cleanup; 309 return plugin; 310 } 311 312 313 /** 314 * Unload authorization plugin 315 * 316 * @param cls a `struct FROSIX_AuthorizationPlugin` 317 * @return NULL (always) 318 */ 319 void * 320 libfrosix_plugin_authorization_file_done (void *cls) 321 { 322 struct FROSIX_AuthorizationPlugin *plugin = cls; 323 324 GNUNET_free (plugin); 325 return NULL; 326 }