do_challenge_address.c (6848B)
1 /* 2 This file is part of Challenger 3 Copyright (C) 2023 Taler Systems SA 4 5 Challenger is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Challenger 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/challengerdb/do_challenge_address.c 18 * @brief Implementation of the do_challenge_address function for Postgres 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <taler/taler_error_codes.h> 23 #include <taler/taler_dbevents.h> 24 #include <taler/taler_pq_lib.h> 25 #include "do_challenge_address.h" 26 #include "pg_helper.h" 27 28 29 enum GNUNET_DB_QueryStatus 30 CHALLENGERDB_do_challenge_address ( 31 struct CHALLENGERDB_PostgresContext *ctx, 32 const struct CHALLENGER_ValidationNonceP *nonce, 33 const json_t *address, 34 struct GNUNET_TIME_Relative retransmission_frequency, 35 uint32_t *tan, 36 char **state, 37 struct GNUNET_TIME_Absolute *last_tx_time, 38 uint32_t *auth_attempts_left, 39 uint32_t *pin_transmissions_left, 40 bool *pin_transmit, 41 char **client_redirect_uri, 42 bool *address_refused, 43 bool *solved, 44 bool *failed) 45 { 46 struct GNUNET_TIME_Absolute now 47 = GNUNET_TIME_absolute_get (); 48 /* We must gate retransmission on 49 last_tx_time + retransmission_frequency <= now 50 but 'last_tx_time' is only read (under FOR UPDATE) inside the stored 51 procedure, so we cannot form that sum here. We therefore pass the 52 equivalent, inverted form: the *newest* 'last_tx_time' for which a 53 retransmission is still due. The SQL then merely checks 54 last_tx_time <= retransmit_cutoff. 55 Note that this is deliberately a subtraction from 'now', not 56 'now + retransmission_frequency': the value is compared against a 57 timestamp in the *past*. GNUNET_TIME_absolute_subtract() saturates at 58 zero rather than underflowing, so a FOREVER frequency degrades to 59 "never *re*transmit" (the initial TAN, sent when last_tx_time is still 0, 60 is unaffected). */ 61 struct GNUNET_TIME_Absolute retransmit_cutoff 62 = GNUNET_TIME_absolute_subtract (now, 63 retransmission_frequency); 64 struct GNUNET_PQ_QueryParam params[] = { 65 GNUNET_PQ_query_param_auto_from_type (nonce), 66 TALER_PQ_query_param_json (address), 67 GNUNET_PQ_query_param_absolute_time (&retransmit_cutoff), 68 GNUNET_PQ_query_param_absolute_time (&now), 69 GNUNET_PQ_query_param_uint32 (tan), 70 GNUNET_PQ_query_param_end 71 }; 72 bool not_found; 73 bool no_last_tan; 74 uint32_t tan_out; 75 struct GNUNET_PQ_ResultSpec rs[] = { 76 GNUNET_PQ_result_spec_bool ("not_found", 77 ¬_found), 78 GNUNET_PQ_result_spec_absolute_time ("last_tx_time", 79 last_tx_time), 80 GNUNET_PQ_result_spec_allow_null ( 81 GNUNET_PQ_result_spec_uint32 ("last_pin", 82 &tan_out), 83 &no_last_tan), 84 GNUNET_PQ_result_spec_bool ("pin_transmit", 85 pin_transmit), 86 GNUNET_PQ_result_spec_uint32 ("auth_attempts_left", 87 auth_attempts_left), 88 GNUNET_PQ_result_spec_uint32 ("pin_transmissions_left", 89 pin_transmissions_left), 90 GNUNET_PQ_result_spec_allow_null ( 91 GNUNET_PQ_result_spec_string ("client_redirect_uri", 92 client_redirect_uri), 93 NULL), 94 GNUNET_PQ_result_spec_allow_null ( 95 GNUNET_PQ_result_spec_string ("state", 96 state), 97 NULL), 98 GNUNET_PQ_result_spec_bool ("address_refused", 99 address_refused), 100 GNUNET_PQ_result_spec_bool ("solved", 101 solved), 102 GNUNET_PQ_result_spec_bool ("failed", 103 failed), 104 GNUNET_PQ_result_spec_end 105 }; 106 enum GNUNET_DB_QueryStatus qs; 107 108 *client_redirect_uri = NULL; 109 no_last_tan = true; 110 PREPARE (ctx, 111 "do_challenge_address", 112 "SELECT " 113 " out_not_found AS not_found" 114 ",out_last_tx_time AS last_tx_time" 115 ",out_pin_transmit AS pin_transmit" 116 ",out_last_pin AS last_pin" 117 ",out_state AS state" 118 ",out_auth_attempts_left AS auth_attempts_left" 119 ",out_pin_transmissions_left AS pin_transmissions_left" 120 ",out_client_redirect_uri AS client_redirect_uri" 121 ",out_address_refused AS address_refused" 122 ",out_solved AS solved" 123 ",out_failed AS failed" 124 " FROM challenger_do_challenge_set_address_and_pin" 125 " ($1,$2,$3,$4,$5);"); 126 qs = GNUNET_PQ_eval_prepared_singleton_select (ctx->conn, 127 "do_challenge_address", 128 params, 129 rs); 130 if (qs <= 0) 131 return qs; 132 if (not_found) 133 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 134 if (! no_last_tan) 135 *tan = tan_out; 136 return qs; 137 } 138 139 140 enum GNUNET_DB_QueryStatus 141 CHALLENGERDB_do_challenge_address_confirm_pin ( 142 struct CHALLENGERDB_PostgresContext *ctx, 143 const struct CHALLENGER_ValidationNonceP *nonce, 144 uint32_t *auth_attempts_left) 145 { 146 struct GNUNET_PQ_QueryParam params[] = { 147 GNUNET_PQ_query_param_auto_from_type (nonce), 148 GNUNET_PQ_query_param_end 149 }; 150 struct GNUNET_PQ_ResultSpec rs[] = { 151 GNUNET_PQ_result_spec_uint32 ("auth_attempts_left", 152 auth_attempts_left), 153 GNUNET_PQ_result_spec_end 154 }; 155 156 PREPARE (ctx, 157 "do_challenge_address_confirm_pin", 158 "UPDATE validations SET" 159 " last_pin=pending_pin" 160 " ,pending_pin=NULL" 161 " ,auth_attempts_left=3" 162 " WHERE nonce=$1" 163 /* nothing to promote if we did not just transmit a TAN */ 164 " AND pending_pin IS NOT NULL" 165 /* never resurrect an already solved validation */ 166 " AND auth_attempts_left >= 0" 167 /* nor a failed one, which may already have been reported to 168 the client; this only matters if the user spent their last 169 guess while the last TAN was still in transit */ 170 " AND NOT failed" 171 " RETURNING auth_attempts_left;"); 172 return GNUNET_PQ_eval_prepared_singleton_select ( 173 ctx->conn, 174 "do_challenge_address_confirm_pin", 175 params, 176 rs); 177 }