Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2024 Taler Systems SA
4 :
5 : TALER 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 : TALER 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 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 :
17 : /**
18 : * @file taler-auditor-httpd.c
19 : * @brief Serve the HTTP interface of the auditor
20 : * @defgroup request Request handling routines
21 : * @author Florian Dold
22 : * @author Benedikt Mueller
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include <gnunet/gnunet_util_lib.h>
27 : #include <jansson.h>
28 : #include <microhttpd.h>
29 : #include <pthread.h>
30 : #include <sys/resource.h>
31 : #include "taler/taler_mhd_lib.h"
32 : #include "auditordb_lib.h"
33 : #include "exchangedb_lib.h"
34 : #include "taler-auditor-httpd_spa.h"
35 : #include "taler-auditor-httpd_put-deposit-confirmation.h"
36 : #include "taler-auditor-httpd_get-monitoring-deposit-confirmations.h"
37 : #include "taler-auditor-httpd_get-monitoring-amount-arithmetic-inconsistency.h"
38 : #include "taler-auditor-httpd_get-monitoring-coin-inconsistency.h"
39 : #include "taler-auditor-httpd_get-monitoring-row-inconsistency.h"
40 : #include "taler-auditor-httpd_get-monitoring-emergency.h"
41 : #include "taler-auditor-httpd_get-monitoring-emergency-by-count.h"
42 : #include "taler-auditor-httpd_get-monitoring-early-aggregation.h"
43 : #include \
44 : "taler-auditor-httpd_get-monitoring-denomination-key-validity-withdraw-inconsistency.h"
45 : #include "taler-auditor-httpd_get-monitoring-purse-not-closed-inconsistencies.h"
46 : #include \
47 : "taler-auditor-httpd_get-monitoring-reserve-balance-insufficient-inconsistency.h"
48 : #include "taler-auditor-httpd_get-monitoring-bad-sig-losses.h"
49 : #include "taler-auditor-httpd_get-monitoring-aml-holds.h"
50 : #include "taler-auditor-httpd_get-monitoring-closure-lags.h"
51 : #include "taler-auditor-httpd_mhd.h"
52 : #include "taler-auditor-httpd.h"
53 : #include "taler-auditor-httpd_delete-generic.h"
54 : #include "taler-auditor-httpd_patch-generic-suppressed.h"
55 : #include "taler-auditor-httpd_get-monitoring-kycauth-in-inconsistency.h"
56 : #include "taler-auditor-httpd_get-monitoring-reserve-in-inconsistency.h"
57 : #include "taler-auditor-httpd_get-monitoring-reserve-not-closed-inconsistency.h"
58 : #include "taler-auditor-httpd_get-monitoring-denominations-without-sigs.h"
59 : #include "taler-auditor-httpd_get-monitoring-misattribution-in-inconsistency.h"
60 : #include "taler-auditor-httpd_get-monitoring-reserves.h"
61 : #include "taler-auditor-httpd_get-monitoring-pending-deposits.h"
62 : #include "taler-auditor-httpd_get-monitoring-purses.h"
63 : #include "taler-auditor-httpd_get-monitoring-historic-denomination-revenue.h"
64 : #include "taler-auditor-httpd_get-monitoring-historic-reserve-summary.h"
65 : #include "taler-auditor-httpd_get-monitoring-wire-format-inconsistency.h"
66 : #include "taler-auditor-httpd_get-monitoring-wire-out-inconsistency.h"
67 : #include \
68 : "taler-auditor-httpd_get-monitoring-reserve-balance-summary-wrong-inconsistency.h"
69 : #include "taler-auditor-httpd_get-monitoring-row-minor-inconsistencies.h"
70 : #include "taler-auditor-httpd_get-monitoring-fee-time-inconsistency.h"
71 : #include "taler-auditor-httpd_get-monitoring-balances.h"
72 : #include "taler-auditor-httpd_get-monitoring-progress.h"
73 : #include "exchange-database/preflight.h"
74 :
75 : /**
76 : * Auditor protocol version string.
77 : *
78 : * Taler protocol version in the format CURRENT:REVISION:AGE
79 : * as used by GNU libtool. See
80 : * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
81 : *
82 : * Please be very careful when updating and follow
83 : * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
84 : * precisely. Note that this version has NOTHING to do with the
85 : * release version, and the format is NOT the same that semantic
86 : * versioning uses either.
87 : */
88 : #define AUDITOR_PROTOCOL_VERSION "2:0:0"
89 :
90 : /**
91 : * Salt we use when doing the KDF for access.
92 : */
93 : #define KDF_SALT "auditor-standard-auth"
94 :
95 : /**
96 : * Backlog for listen operation on unix domain sockets.
97 : */
98 : #define UNIX_BACKLOG 500
99 :
100 : /**
101 : * Maximum number of concurrent connections per HTTP daemon.
102 : * TALER_MHD_daemon_start() keeps our descriptors below FD_SETSIZE;
103 : * this leaves ample room for the database connection and our other
104 : * descriptors. At the limit, MHD stops accepting until a connection
105 : * closes.
106 : */
107 : #define CONNECTION_LIMIT 300
108 :
109 : /**
110 : * Should we return "Connection: close" in each response?
111 : */
112 : static int auditor_connection_close;
113 :
114 : /**
115 : * The auditor's configuration.
116 : */
117 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
118 :
119 : /**
120 : * Our auditor database context.
121 : */
122 : struct TALER_AUDITORDB_PostgresContext *TAH_apg;
123 :
124 : /**
125 : * Our exchange database context.
126 : */
127 : struct TALER_EXCHANGEDB_PostgresContext *TAH_epg;
128 :
129 : /**
130 : * Public key of this auditor.
131 : */
132 : static struct TALER_AuditorPublicKeyP auditor_pub;
133 :
134 : /**
135 : * Exchange master public key (according to the
136 : * configuration). (global)
137 : */
138 : struct TALER_MasterPublicKeyP TAH_master_public_key;
139 :
140 : /**
141 : * Exchange master public key (according to the
142 : * configuration). (global)
143 : */
144 : struct TALER_MasterPublicKeyP TAH_master_public_key;
145 :
146 : char *TAH_spa_dir;
147 :
148 : /**
149 : * Default timeout in seconds for HTTP requests.
150 : */
151 : static unsigned int connection_timeout = 30;
152 :
153 : /**
154 : * Return value from main()
155 : */
156 : static int global_ret;
157 :
158 : /**
159 : * Disables authentication checks.
160 : */
161 : static int disable_auth;
162 :
163 : /**
164 : * True if we started any HTTP daemon.
165 : */
166 : static bool have_daemons;
167 :
168 : /**
169 : * Our currency.
170 : */
171 : char *TAH_currency;
172 :
173 : /**
174 : * Authorization code to use.
175 : */
176 : static struct GNUNET_HashCode TAH_auth;
177 :
178 : /**
179 : * Prefix required for the access token.
180 : */
181 : #define RFC_8959_PREFIX "secret-token:"
182 :
183 :
184 : /**
185 : * Function called whenever MHD is done with a request. If the
186 : * request was a POST, we may have stored a `struct Buffer *` in the
187 : * @a con_cls that might still need to be cleaned up. Call the
188 : * respective function to free the memory.
189 : *
190 : * @param cls client-defined closure
191 : * @param connection connection handle
192 : * @param con_cls value as set by the last call to
193 : * the #MHD_AccessHandlerCallback
194 : * @param toe reason for request termination
195 : * @see #MHD_OPTION_NOTIFY_COMPLETED
196 : * @ingroup request
197 : */
198 : static void
199 145 : handle_mhd_completion_callback (void *cls,
200 : struct MHD_Connection *connection,
201 : void **con_cls,
202 : enum MHD_RequestTerminationCode toe)
203 : {
204 : (void) cls;
205 : (void) connection;
206 : (void) toe;
207 145 : if (NULL == *con_cls)
208 145 : return;
209 0 : TALER_MHD_parse_post_cleanup_callback (*con_cls);
210 0 : *con_cls = NULL;
211 : }
212 :
213 :
214 : /**
215 : * Handle a "/config" request.
216 : *
217 : * @param rh context of the handler
218 : * @param connection the MHD connection to handle
219 : * @param[in,out] connection_cls the connection's closure (can be updated)
220 : * @param upload_data upload data
221 : * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
222 : * @param args NULL-terminated array of remaining parts of the URI broken up at '/'
223 : * @return MHD result code
224 : */
225 : static enum MHD_Result
226 9 : handle_config (struct TAH_RequestHandler *rh,
227 : struct MHD_Connection *connection,
228 : void **connection_cls,
229 : const char *upload_data,
230 : size_t *upload_data_size,
231 : const char *const args[])
232 : {
233 : static json_t *ver; /* we build the response only once, keep around for next query! */
234 :
235 : (void) rh;
236 : (void) upload_data;
237 : (void) upload_data_size;
238 : (void) connection_cls;
239 9 : if (NULL == ver)
240 : {
241 5 : ver = GNUNET_JSON_PACK (
242 : GNUNET_JSON_pack_string ("name",
243 : "taler-auditor"),
244 : GNUNET_JSON_pack_string ("version",
245 : AUDITOR_PROTOCOL_VERSION),
246 : GNUNET_JSON_pack_string ("build_version",
247 : PACKAGE_VERSION),
248 : GNUNET_JSON_pack_string ("implementation",
249 : "urn:net:taler:specs:taler-auditor:c-reference"),
250 : GNUNET_JSON_pack_string ("currency",
251 : TAH_currency),
252 : GNUNET_JSON_pack_data_auto ("auditor_public_key",
253 : &auditor_pub),
254 : GNUNET_JSON_pack_data_auto ("exchange_master_public_key",
255 : &TAH_master_public_key));
256 : }
257 9 : if (NULL == ver)
258 : {
259 0 : GNUNET_break (0);
260 0 : return MHD_NO;
261 : }
262 9 : return TALER_MHD_reply_json (connection,
263 : ver,
264 : MHD_HTTP_OK);
265 : }
266 :
267 :
268 : /**
269 : * Extract the token from authorization header value @a auth.
270 : *
271 : * @param auth pointer to authorization header value,
272 : * will be updated to point to the start of the token
273 : * or set to NULL if header value is invalid
274 : */
275 : static void
276 132 : extract_token (const char **auth)
277 : {
278 132 : const char *bearer = "Bearer ";
279 132 : const char *tok = *auth;
280 :
281 132 : if (0 != strncmp (tok,
282 : bearer,
283 : strlen (bearer)))
284 : {
285 0 : *auth = NULL;
286 0 : return;
287 : }
288 132 : tok += strlen (bearer);
289 132 : while (' ' == *tok)
290 0 : tok++;
291 132 : if (0 != strncasecmp (tok,
292 : RFC_8959_PREFIX,
293 : strlen (RFC_8959_PREFIX)))
294 : {
295 0 : *auth = NULL;
296 0 : return;
297 : }
298 132 : *auth = tok;
299 : }
300 :
301 :
302 : static enum GNUNET_GenericReturnValue
303 132 : check_auth (const char *token)
304 : {
305 : struct GNUNET_HashCode val;
306 :
307 132 : if (NULL == token)
308 0 : return GNUNET_SYSERR;
309 132 : token += strlen (RFC_8959_PREFIX);
310 132 : GNUNET_assert (GNUNET_YES ==
311 : GNUNET_CRYPTO_hkdf_gnunet (
312 : &val,
313 : sizeof (val),
314 : KDF_SALT,
315 : strlen (KDF_SALT),
316 : token,
317 : strlen (token)));
318 : /* We compare hashes instead of directly comparing
319 : tokens to minimize side-channel attacks on token length */
320 : return (0 ==
321 132 : GNUNET_memcmp_priv (&val,
322 : &TAH_auth))
323 : ? GNUNET_OK
324 132 : : GNUNET_SYSERR;
325 : }
326 :
327 :
328 : /**
329 : * Handle incoming HTTP request.
330 : *
331 : * @param cls closure for MHD daemon (unused)
332 : * @param connection the connection
333 : * @param url the requested url
334 : * @param method the method (POST, GET, ...)
335 : * @param version HTTP version (ignored)
336 : * @param upload_data request data
337 : * @param upload_data_size size of @a upload_data in bytes
338 : * @param con_cls closure for request (a `struct Buffer *`)
339 : * @return MHD result code
340 : */
341 : static enum MHD_Result
342 151 : handle_mhd_request (void *cls,
343 : struct MHD_Connection *connection,
344 : const char *url,
345 : const char *method,
346 : const char *version,
347 : const char *upload_data,
348 : size_t *upload_data_size,
349 : void **con_cls)
350 151 : {
351 : static struct TAH_RequestHandler handlers[] = {
352 : /* Our most popular handler (thus first!), used by merchants to
353 : probabilistically report us their deposit confirmations. */
354 : { .url = "/deposit-confirmation",
355 : .method = MHD_HTTP_METHOD_PUT,
356 : .mime_type = "application/json",
357 : .handler = &TAH_put_deposit_confirmation,
358 : .response_code = MHD_HTTP_NO_CONTENT},
359 : { .url = "/webui",
360 : .method = MHD_HTTP_METHOD_GET,
361 : .handler = &TAH_spa_handler},
362 : { .url = "/monitoring/deposit-confirmations",
363 : .method = MHD_HTTP_METHOD_GET,
364 : .mime_type = "application/json",
365 : .data = NULL,
366 : .data_size = 0,
367 : .handler = &TAH_get_monitoring_deposit_confirmations,
368 : .response_code = MHD_HTTP_OK,
369 : .requires_auth = true },
370 : { .url = "/monitoring/deposit-confirmations",
371 : .method = MHD_HTTP_METHOD_PATCH,
372 : .mime_type = "application/json",
373 : .handler = &TAH_patch_generic_suppressed,
374 : .response_code = MHD_HTTP_NO_CONTENT,
375 : .requires_auth = true,
376 : .table = TALER_AUDITORDB_DEPOSIT_CONFIRMATION },
377 : { .url = "/monitoring/pending-deposits",
378 : .method = MHD_HTTP_METHOD_GET,
379 : .mime_type = "application/json",
380 : .data = NULL,
381 : .data_size = 0,
382 : .handler = &TAH_get_monitoring_pending_deposits,
383 : .response_code = MHD_HTTP_OK,
384 : .requires_auth = true },
385 : { .url = "/monitoring/pending-deposits",
386 : .method = MHD_HTTP_METHOD_DELETE,
387 : .mime_type = "application/json",
388 : .handler = &TAH_delete_generic,
389 : .response_code = MHD_HTTP_NO_CONTENT,
390 : .requires_auth = true,
391 : .table = TALER_AUDITORDB_PENDING_DEPOSITS },
392 : { .url = "/monitoring/pending-deposits",
393 : .method = MHD_HTTP_METHOD_PATCH,
394 : .mime_type = "application/json",
395 : .handler = &TAH_patch_generic_suppressed,
396 : .response_code = MHD_HTTP_NO_CONTENT,
397 : .requires_auth = true,
398 : .table = TALER_AUDITORDB_PENDING_DEPOSITS },
399 : { .url = "/monitoring/early-aggregations",
400 : .method = MHD_HTTP_METHOD_GET,
401 : .mime_type = "application/json",
402 : .data = NULL,
403 : .data_size = 0,
404 : .handler = &TAH_get_monitoring_early_aggregation,
405 : .response_code = MHD_HTTP_OK,
406 : .requires_auth = true },
407 : { .url = "/monitoring/early-aggregations",
408 : .method = MHD_HTTP_METHOD_DELETE,
409 : .mime_type = "application/json",
410 : .handler = &TAH_delete_generic,
411 : .response_code = MHD_HTTP_NO_CONTENT,
412 : .requires_auth = true,
413 : .table = TALER_AUDITORDB_EARLY_AGGREGATIONS },
414 : { .url = "/monitoring/early-aggregations",
415 : .method = MHD_HTTP_METHOD_PATCH,
416 : .mime_type = "application/json",
417 : .handler = &TAH_patch_generic_suppressed,
418 : .response_code = MHD_HTTP_NO_CONTENT,
419 : .requires_auth = true,
420 : .table = TALER_AUDITORDB_EARLY_AGGREGATIONS },
421 : { .url = "/monitoring/deposit-confirmations",
422 : .method = MHD_HTTP_METHOD_DELETE,
423 : .mime_type = "application/json",
424 : .data = NULL,
425 : .data_size = 0,
426 : .handler = &TAH_delete_generic,
427 : .response_code = MHD_HTTP_OK,
428 : .requires_auth = true,
429 : .table = TALER_AUDITORDB_DEPOSIT_CONFIRMATION },
430 : { .url = "/monitoring/amount-arithmetic-inconsistencies",
431 : .method = MHD_HTTP_METHOD_GET,
432 : .mime_type = "application/json",
433 : .data = NULL,
434 : .data_size = 0,
435 : .handler = &TAH_get_monitoring_amount_arithmetic_inconsistency,
436 : .response_code = MHD_HTTP_OK,
437 : .requires_auth = true },
438 : { .url = "/monitoring/amount-arithmetic-inconsistencies",
439 : .method = MHD_HTTP_METHOD_DELETE,
440 : .mime_type = "application/json",
441 : .data = NULL,
442 : .data_size = 0,
443 : .handler = &TAH_delete_generic,
444 : .response_code = MHD_HTTP_OK,
445 : .requires_auth = true,
446 : .table = TALER_AUDITORDB_AMOUNT_ARITHMETIC_INCONSISTENCY },
447 : { .url = "/monitoring/amount-arithmetic-inconsistencies",
448 : .method = MHD_HTTP_METHOD_PATCH,
449 : .mime_type = "application/json",
450 : .data = NULL,
451 : .data_size = 0,
452 : .handler = &TAH_patch_generic_suppressed,
453 : .response_code = MHD_HTTP_OK,
454 : .requires_auth = true,
455 : .table = TALER_AUDITORDB_AMOUNT_ARITHMETIC_INCONSISTENCY },
456 : { .url = "/monitoring/coin-inconsistencies",
457 : .method = MHD_HTTP_METHOD_GET,
458 : .mime_type = "application/json",
459 : .data = NULL,
460 : .data_size = 0,
461 : .handler = &TAH_get_monitoring_coin_inconsistency,
462 : .response_code = MHD_HTTP_OK,
463 : .requires_auth = true },
464 : { .url = "/monitoring/coin-inconsistencies",
465 : .method = MHD_HTTP_METHOD_DELETE,
466 : .mime_type = "application/json",
467 : .data = NULL,
468 : .data_size = 0,
469 : .handler = &TAH_delete_generic,
470 : .response_code = MHD_HTTP_OK,
471 : .requires_auth = true,
472 : .table = TALER_AUDITORDB_COIN_INCONSISTENCY },
473 : { .url = "/monitoring/coin-inconsistencies",
474 : .method = MHD_HTTP_METHOD_PATCH,
475 : .mime_type = "application/json",
476 : .data = NULL,
477 : .data_size = 0,
478 : .handler = &TAH_patch_generic_suppressed,
479 : .response_code = MHD_HTTP_OK,
480 : .requires_auth = true,
481 : .table = TALER_AUDITORDB_COIN_INCONSISTENCY },
482 : { .url = "/monitoring/row-inconsistencies",
483 : .method = MHD_HTTP_METHOD_GET,
484 : .mime_type = "application/json",
485 : .data = NULL,
486 : .data_size = 0,
487 : .handler = &TAH_get_monitoring_row_inconsistency,
488 : .response_code = MHD_HTTP_OK,
489 : .requires_auth = true },
490 : { .url = "/monitoring/row-inconsistencies",
491 : .method = MHD_HTTP_METHOD_DELETE,
492 : .mime_type = "application/json",
493 : .data = NULL,
494 : .data_size = 0,
495 : .handler = &TAH_delete_generic,
496 : .response_code = MHD_HTTP_OK,
497 : .requires_auth = true,
498 : .table = TALER_AUDITORDB_ROW_INCONSISTENCY},
499 : { .url = "/monitoring/row-inconsistencies",
500 : .method = MHD_HTTP_METHOD_PATCH,
501 : .mime_type = "application/json",
502 : .data = NULL,
503 : .data_size = 0,
504 : .handler = &TAH_patch_generic_suppressed,
505 : .response_code = MHD_HTTP_OK,
506 : .requires_auth = true,
507 : .table = TALER_AUDITORDB_ROW_INCONSISTENCY },
508 : { .url = "/monitoring/bad-sig-losses",
509 : .method = MHD_HTTP_METHOD_GET,
510 : .mime_type = "application/json",
511 : .data = NULL,
512 : .data_size = 0,
513 : .handler = &TAH_get_monitoring_bad_sig_losses,
514 : .response_code = MHD_HTTP_OK,
515 : .requires_auth = true },
516 : { .url = "/monitoring/bad-sig-losses",
517 : .method = MHD_HTTP_METHOD_DELETE,
518 : .mime_type = "application/json",
519 : .data = NULL,
520 : .data_size = 0,
521 : .handler = &TAH_delete_generic,
522 : .response_code = MHD_HTTP_OK,
523 : .requires_auth = true,
524 : .table = TALER_AUDITORDB_BAD_SIG_LOSSES},
525 : { .url = "/monitoring/bad-sig-losses",
526 : .method = MHD_HTTP_METHOD_PATCH,
527 : .mime_type = "application/json",
528 : .data = NULL,
529 : .data_size = 0,
530 : .handler = &TAH_patch_generic_suppressed,
531 : .response_code = MHD_HTTP_OK,
532 : .requires_auth = true,
533 : .table = TALER_AUDITORDB_BAD_SIG_LOSSES },
534 : { .url = "/monitoring/aml-holds",
535 : .method = MHD_HTTP_METHOD_GET,
536 : .mime_type = "application/json",
537 : .data = NULL,
538 : .data_size = 0,
539 : .handler = &TAH_get_monitoring_aml_holds,
540 : .response_code = MHD_HTTP_OK,
541 : .requires_auth = true },
542 : { .url = "/monitoring/aml-holds",
543 : .method = MHD_HTTP_METHOD_DELETE,
544 : .mime_type = "application/json",
545 : .data = NULL,
546 : .data_size = 0,
547 : .handler = &TAH_delete_generic,
548 : .response_code = MHD_HTTP_OK,
549 : .requires_auth = true,
550 : .table = TALER_AUDITORDB_AML_HOLDS },
551 : { .url = "/monitoring/aml-holds",
552 : .method = MHD_HTTP_METHOD_PATCH,
553 : .mime_type = "application/json",
554 : .data = NULL,
555 : .data_size = 0,
556 : .handler = &TAH_patch_generic_suppressed,
557 : .response_code = MHD_HTTP_OK,
558 : .requires_auth = true,
559 : .table = TALER_AUDITORDB_AML_HOLDS },
560 : { .url = "/monitoring/closure-lags",
561 : .method = MHD_HTTP_METHOD_GET,
562 : .mime_type = "application/json",
563 : .data = NULL,
564 : .data_size = 0,
565 : .handler = &TAH_get_monitoring_closure_lags,
566 : .response_code = MHD_HTTP_OK,
567 : .requires_auth = true },
568 : { .url = "/monitoring/closure-lags",
569 : .method = MHD_HTTP_METHOD_DELETE,
570 : .mime_type = "application/json",
571 : .data = NULL,
572 : .data_size = 0,
573 : .handler = &TAH_delete_generic,
574 : .response_code = MHD_HTTP_OK,
575 : .requires_auth = true,
576 : .table = TALER_AUDITORDB_CLOSURE_LAGS },
577 : { .url = "/monitoring/closure-lags",
578 : .method = MHD_HTTP_METHOD_PATCH,
579 : .mime_type = "application/json",
580 : .data = NULL,
581 : .data_size = 0,
582 : .handler = &TAH_patch_generic_suppressed,
583 : .response_code = MHD_HTTP_OK,
584 : .requires_auth = true,
585 : .table = TALER_AUDITORDB_CLOSURE_LAGS },
586 : { .url = "/monitoring/emergencies",
587 : .method = MHD_HTTP_METHOD_GET,
588 : .mime_type = "application/json",
589 : .data = NULL,
590 : .data_size = 0,
591 : .handler = &TAH_get_monitoring_emergency,
592 : .response_code = MHD_HTTP_OK,
593 : .requires_auth = true },
594 : { .url = "/monitoring/emergencies",
595 : .method = MHD_HTTP_METHOD_DELETE,
596 : .mime_type = "application/json",
597 : .data = NULL,
598 : .data_size = 0,
599 : .handler = &TAH_delete_generic,
600 : .response_code = MHD_HTTP_OK,
601 : .requires_auth = true,
602 : .table = TALER_AUDITORDB_EMERGENCY },
603 : { .url = "/monitoring/emergencies",
604 : .method = MHD_HTTP_METHOD_PATCH,
605 : .mime_type = "application/json",
606 : .data = NULL,
607 : .data_size = 0,
608 : .handler = &TAH_patch_generic_suppressed,
609 : .response_code = MHD_HTTP_OK,
610 : .requires_auth = true,
611 : .table = TALER_AUDITORDB_EMERGENCY },
612 : { .url = "/monitoring/denomination-key-validity-withdraw-inconsistencies",
613 : .method = MHD_HTTP_METHOD_GET,
614 : .mime_type = "application/json",
615 : .data = NULL,
616 : .data_size = 0,
617 : .handler =
618 : &TAH_get_monitoring_denomination_key_validity_withdraw_inconsistency,
619 : .response_code = MHD_HTTP_OK,
620 : .requires_auth = true },
621 : { .url = "/monitoring/denomination-key-validity-withdraw-inconsistencies",
622 : .method = MHD_HTTP_METHOD_DELETE,
623 : .mime_type = "application/json",
624 : .data = NULL,
625 : .data_size = 0,
626 : .handler = &TAH_delete_generic,
627 : .response_code = MHD_HTTP_OK,
628 : .requires_auth = true,
629 : .table = TALER_AUDITORDB_DENOMINATION_KEY_VALIDITY_WITHDRAW_INCONSISTENCY}
630 : ,
631 : { .url = "/monitoring/denomination-key-validity-withdraw-inconsistencies",
632 : .method = MHD_HTTP_METHOD_PATCH,
633 : .mime_type = "application/json",
634 : .data = NULL,
635 : .data_size = 0,
636 : .handler = &TAH_patch_generic_suppressed,
637 : .response_code = MHD_HTTP_OK,
638 : .requires_auth = true,
639 : .table = TALER_AUDITORDB_DENOMINATION_KEY_VALIDITY_WITHDRAW_INCONSISTENCY}
640 : ,
641 : { .url = "/monitoring/reserve-balance-insufficient-inconsistencies",
642 : .method = MHD_HTTP_METHOD_GET,
643 : .mime_type = "application/json",
644 : .data = NULL,
645 : .data_size = 0,
646 : .handler = &TAH_get_monitoring_reserve_balance_insufficient_inconsistency,
647 : .response_code = MHD_HTTP_OK,
648 : .requires_auth = true },
649 : { .url = "/monitoring/reserve-balance-insufficient-inconsistencies",
650 : .method = MHD_HTTP_METHOD_DELETE,
651 : .mime_type = "application/json",
652 : .data = NULL,
653 : .data_size = 0,
654 : .handler = &TAH_delete_generic,
655 : .response_code = MHD_HTTP_OK,
656 : .requires_auth = true,
657 : .table = TALER_AUDITORDB_RESERVE_BALANCE_INSUFFICIENT_INCONSISTENCY },
658 : { .url = "/monitoring/reserve-balance-insufficient-inconsistencies",
659 : .method = MHD_HTTP_METHOD_PATCH,
660 : .mime_type = "application/json",
661 : .data = NULL,
662 : .data_size = 0,
663 : .handler = &TAH_patch_generic_suppressed,
664 : .response_code = MHD_HTTP_OK,
665 : .requires_auth = true,
666 : .table = TALER_AUDITORDB_RESERVE_BALANCE_INSUFFICIENT_INCONSISTENCY },
667 : { .url = "/monitoring/purse-not-closed-inconsistencies",
668 : .method = MHD_HTTP_METHOD_GET,
669 : .mime_type = "application/json",
670 : .data = NULL,
671 : .data_size = 0,
672 : .handler = &TAH_get_monitoring_purse_not_closed_inconsistencies,
673 : .response_code = MHD_HTTP_OK,
674 : .requires_auth = true },
675 : { .url = "/monitoring/purse-not-closed-inconsistencies",
676 : .method = MHD_HTTP_METHOD_DELETE,
677 : .mime_type = "application/json",
678 : .data = NULL,
679 : .data_size = 0,
680 : .handler = &TAH_delete_generic,
681 : .response_code = MHD_HTTP_OK,
682 : .requires_auth = true,
683 : .table = TALER_AUDITORDB_PURSE_NOT_CLOSED_INCONSISTENCY },
684 : { .url = "/monitoring/purse-not-closed-inconsistencies",
685 : .method = MHD_HTTP_METHOD_PATCH,
686 : .mime_type = "application/json",
687 : .data = NULL,
688 : .data_size = 0,
689 : .handler = &TAH_patch_generic_suppressed,
690 : .response_code = MHD_HTTP_OK,
691 : .requires_auth = true,
692 : .table = TALER_AUDITORDB_PURSE_NOT_CLOSED_INCONSISTENCY },
693 : { .url = "/monitoring/emergencies-by-count",
694 : .method = MHD_HTTP_METHOD_GET,
695 : .mime_type = "application/json",
696 : .data = NULL,
697 : .data_size = 0,
698 : .handler = &TAH_get_monitoring_emergency_by_count,
699 : .response_code = MHD_HTTP_OK,
700 : .requires_auth = true },
701 : { .url = "/monitoring/emergencies-by-count",
702 : .method = MHD_HTTP_METHOD_DELETE,
703 : .mime_type = "application/json",
704 : .data = NULL,
705 : .data_size = 0,
706 : .handler = &TAH_delete_generic,
707 : .response_code = MHD_HTTP_OK,
708 : .requires_auth = true,
709 : .table = TALER_AUDITORDB_EMERGENCY_BY_COUNT },
710 : { .url = "/monitoring/emergencies-by-count",
711 : .method = MHD_HTTP_METHOD_PATCH,
712 : .mime_type = "application/json",
713 : .data = NULL,
714 : .data_size = 0,
715 : .handler = &TAH_patch_generic_suppressed,
716 : .response_code = MHD_HTTP_OK,
717 : .requires_auth = true,
718 : .table = TALER_AUDITORDB_EMERGENCY_BY_COUNT },
719 : { .url = "/monitoring/reserve-in-inconsistencies",
720 : .method = MHD_HTTP_METHOD_GET,
721 : .mime_type = "application/json",
722 : .data = NULL,
723 : .data_size = 0,
724 : .handler = &TAH_get_monitoring_reserve_in_inconsistency,
725 : .response_code = MHD_HTTP_OK,
726 : .requires_auth = true },
727 : { .url = "/monitoring/reserve-in-inconsistencies",
728 : .method = MHD_HTTP_METHOD_DELETE,
729 : .mime_type = "application/json",
730 : .data = NULL,
731 : .data_size = 0,
732 : .handler = &TAH_delete_generic,
733 : .response_code = MHD_HTTP_OK,
734 : .requires_auth = true,
735 : .table = TALER_AUDITORDB_RESERVE_IN_INCONSISTENCY },
736 : { .url = "/monitoring/reserve-in-inconsistencies",
737 : .method = MHD_HTTP_METHOD_PATCH,
738 : .mime_type = "application/json",
739 : .data = NULL,
740 : .data_size = 0,
741 : .handler = &TAH_patch_generic_suppressed,
742 : .response_code = MHD_HTTP_OK,
743 : .requires_auth = true,
744 : .table = TALER_AUDITORDB_RESERVE_IN_INCONSISTENCY },
745 : { .url = "/monitoring/kycauth-in-inconsistencies",
746 : .method = MHD_HTTP_METHOD_GET,
747 : .mime_type = "application/json",
748 : .data = NULL,
749 : .data_size = 0,
750 : .handler = &TAH_get_monitoring_kycauth_in_inconsistency,
751 : .response_code = MHD_HTTP_OK,
752 : .requires_auth = true },
753 : { .url = "/monitoring/kycauth-in-inconsistencies",
754 : .method = MHD_HTTP_METHOD_DELETE,
755 : .mime_type = "application/json",
756 : .data = NULL,
757 : .data_size = 0,
758 : .handler = &TAH_delete_generic,
759 : .response_code = MHD_HTTP_OK,
760 : .requires_auth = true,
761 : .table = TALER_AUDITORDB_KYCAUTH_IN_INCONSISTENCY },
762 : { .url = "/monitoring/kycauth-in-inconsistencies",
763 : .method = MHD_HTTP_METHOD_PATCH,
764 : .mime_type = "application/json",
765 : .data = NULL,
766 : .data_size = 0,
767 : .handler = &TAH_patch_generic_suppressed,
768 : .response_code = MHD_HTTP_OK,
769 : .requires_auth = true,
770 : .table = TALER_AUDITORDB_KYCAUTH_IN_INCONSISTENCY },
771 : { .url = "/monitoring/reserve-not-closed-inconsistencies",
772 : .method = MHD_HTTP_METHOD_GET,
773 : .mime_type = "application/json",
774 : .data = NULL,
775 : .data_size = 0,
776 : .handler = &TAH_get_monitoring_reserve_not_closed_inconsistency,
777 : .response_code = MHD_HTTP_OK,
778 : .requires_auth = true },
779 : { .url = "/monitoring/reserve-not-closed-inconsistencies",
780 : .method = MHD_HTTP_METHOD_DELETE,
781 : .mime_type = "application/json",
782 : .data = NULL,
783 : .data_size = 0,
784 : .handler = &TAH_delete_generic,
785 : .response_code = MHD_HTTP_OK,
786 : .requires_auth = true,
787 : .table = TALER_AUDITORDB_RESERVE_NOT_CLOSED_INCONSISTENCY },
788 : { .url = "/monitoring/reserve-not-closed-inconsistencies",
789 : .method = MHD_HTTP_METHOD_PATCH,
790 : .mime_type = "application/json",
791 : .data = NULL,
792 : .data_size = 0,
793 : .handler = &TAH_patch_generic_suppressed,
794 : .response_code = MHD_HTTP_OK,
795 : .requires_auth = true,
796 : .table = TALER_AUDITORDB_RESERVE_NOT_CLOSED_INCONSISTENCY },
797 : { .url = "/monitoring/denominations-without-sigs",
798 : .method = MHD_HTTP_METHOD_GET,
799 : .mime_type = "application/json",
800 : .data = NULL,
801 : .data_size = 0,
802 : .handler = &TAH_get_monitoring_denominations_without_sigs,
803 : .response_code = MHD_HTTP_OK,
804 : .requires_auth = true },
805 : { .url = "/monitoring/denominations-without-sigs",
806 : .method = MHD_HTTP_METHOD_DELETE,
807 : .mime_type = "application/json",
808 : .data = NULL,
809 : .data_size = 0,
810 : .handler = &TAH_delete_generic,
811 : .response_code = MHD_HTTP_OK,
812 : .requires_auth = true,
813 : .table = TALER_AUDITORDB_DENOMINATIONS_WITHOUT_SIG },
814 : { .url = "/monitoring/denominations-without-sigs",
815 : .method = MHD_HTTP_METHOD_PATCH,
816 : .mime_type = "application/json",
817 : .data = NULL,
818 : .data_size = 0,
819 : .handler = &TAH_patch_generic_suppressed,
820 : .response_code = MHD_HTTP_OK,
821 : .requires_auth = true,
822 : .table = TALER_AUDITORDB_DENOMINATIONS_WITHOUT_SIG },
823 : { .url = "/monitoring/misattribution-in-inconsistencies",
824 : .method = MHD_HTTP_METHOD_GET,
825 : .mime_type = "application/json",
826 : .data = NULL,
827 : .data_size = 0,
828 : .handler = &TAH_get_monitoring_misattribution_in_inconsistency,
829 : .response_code = MHD_HTTP_OK,
830 : .requires_auth = true },
831 : { .url = "/monitoring/misattribution-in-inconsistencies",
832 : .method = MHD_HTTP_METHOD_DELETE,
833 : .mime_type = "application/json",
834 : .data = NULL,
835 : .data_size = 0,
836 : .handler = &TAH_delete_generic,
837 : .response_code = MHD_HTTP_OK,
838 : .requires_auth = true,
839 : .table = TALER_AUDITORDB_MISATTRIBUTION_IN_INCONSISTENCY },
840 : { .url = "/monitoring/misattribution-in-inconsistencies",
841 : .method = MHD_HTTP_METHOD_PATCH,
842 : .mime_type = "application/json",
843 : .data = NULL,
844 : .data_size = 0,
845 : .handler = &TAH_patch_generic_suppressed,
846 : .response_code = MHD_HTTP_OK,
847 : .requires_auth = true,
848 : .table = TALER_AUDITORDB_MISATTRIBUTION_IN_INCONSISTENCY },
849 : { .url = "/monitoring/reserves",
850 : .method = MHD_HTTP_METHOD_GET,
851 : .mime_type = "application/json",
852 : .data = NULL,
853 : .data_size = 0,
854 : .handler = &TAH_get_monitoring_reserves,
855 : .response_code = MHD_HTTP_OK,
856 : .requires_auth = true },
857 : { .url = "/monitoring/purses",
858 : .method = MHD_HTTP_METHOD_GET,
859 : .mime_type = "application/json",
860 : .data = NULL,
861 : .data_size = 0,
862 : .handler = &TAH_get_monitoring_purses,
863 : .response_code = MHD_HTTP_OK,
864 : .requires_auth = true },
865 : { .url = "/monitoring/historic-denomination-revenues",
866 : .method = MHD_HTTP_METHOD_GET,
867 : .mime_type = "application/json",
868 : .data = NULL,
869 : .data_size = 0,
870 : .handler = &TAH_get_monitoring_historic_denomination_revenue,
871 : .response_code = MHD_HTTP_OK,
872 : .requires_auth = true },
873 : { .url = "/monitoring/historic-reserve-summaries",
874 : .method = MHD_HTTP_METHOD_GET,
875 : .mime_type = "application/json",
876 : .data = NULL,
877 : .data_size = 0,
878 : .handler = &TAH_get_monitoring_historic_reserve_summary,
879 : .response_code = MHD_HTTP_OK,
880 : .requires_auth = true },
881 : { .url = "/monitoring/wire-format-inconsistencies",
882 : .method = MHD_HTTP_METHOD_GET,
883 : .mime_type = "application/json",
884 : .data = NULL,
885 : .data_size = 0,
886 : .handler = &TAH_get_monitoring_wire_format_inconsistency,
887 : .response_code = MHD_HTTP_OK,
888 : .requires_auth = true },
889 : { .url = "/monitoring/wire-format-inconsistencies",
890 : .method = MHD_HTTP_METHOD_DELETE,
891 : .mime_type = "application/json",
892 : .data = NULL,
893 : .data_size = 0,
894 : .handler = &TAH_delete_generic,
895 : .response_code = MHD_HTTP_OK,
896 : .requires_auth = true,
897 : .table = TALER_AUDITORDB_WIRE_FORMAT_INCONSISTENCY },
898 : { .url = "/monitoring/wire-format-inconsistencies",
899 : .method = MHD_HTTP_METHOD_PATCH,
900 : .mime_type = "application/json",
901 : .data = NULL,
902 : .data_size = 0,
903 : .handler = &TAH_patch_generic_suppressed,
904 : .response_code = MHD_HTTP_OK,
905 : .requires_auth = true,
906 : .table = TALER_AUDITORDB_WIRE_FORMAT_INCONSISTENCY },
907 : { .url = "/monitoring/wire-out-inconsistencies",
908 : .method = MHD_HTTP_METHOD_GET,
909 : .mime_type = "application/json",
910 : .data = NULL,
911 : .data_size = 0,
912 : .handler = &TAH_get_monitoring_wire_out_inconsistency,
913 : .response_code = MHD_HTTP_OK,
914 : .requires_auth = true },
915 : { .url = "/monitoring/wire-out-inconsistencies",
916 : .method = MHD_HTTP_METHOD_DELETE,
917 : .mime_type = "application/json",
918 : .data = NULL,
919 : .data_size = 0,
920 : .handler = &TAH_delete_generic,
921 : .response_code = MHD_HTTP_OK,
922 : .requires_auth = true,
923 : .table = TALER_AUDITORDB_WIRE_OUT_INCONSISTENCY },
924 : { .url = "/monitoring/wire-out-inconsistencies",
925 : .method = MHD_HTTP_METHOD_PATCH,
926 : .mime_type = "application/json",
927 : .data = NULL,
928 : .data_size = 0,
929 : .handler = &TAH_patch_generic_suppressed,
930 : .response_code = MHD_HTTP_OK,
931 : .requires_auth = true,
932 : .table = TALER_AUDITORDB_WIRE_OUT_INCONSISTENCY },
933 : { .url = "/monitoring/reserve-balance-summary-wrong-inconsistencies",
934 : .method = MHD_HTTP_METHOD_GET,
935 : .mime_type = "application/json",
936 : .data = NULL,
937 : .data_size = 0,
938 : .handler = &TAH_get_monitoring_reserve_balance_summary_wrong_inconsistency
939 : ,
940 : .response_code = MHD_HTTP_OK,
941 : .requires_auth = true },
942 : { .url = "/monitoring/reserve-balance-summary-wrong-inconsistencies",
943 : .method = MHD_HTTP_METHOD_DELETE,
944 : .mime_type = "application/json",
945 : .data = NULL,
946 : .data_size = 0,
947 : .handler = &TAH_delete_generic,
948 : .response_code = MHD_HTTP_OK,
949 : .requires_auth = true,
950 : .table = TALER_AUDITORDB_RESERVE_BALANCE_SUMMARY_WRONG_INCONSISTENCY },
951 : { .url = "/monitoring/reserve-balance-summary-wrong-inconsistencies",
952 : .method = MHD_HTTP_METHOD_PATCH,
953 : .mime_type = "application/json",
954 : .data = NULL,
955 : .data_size = 0,
956 : .handler = &TAH_patch_generic_suppressed,
957 : .response_code = MHD_HTTP_OK,
958 : .requires_auth = true,
959 : .table = TALER_AUDITORDB_RESERVE_BALANCE_SUMMARY_WRONG_INCONSISTENCY },
960 : { .url = "/monitoring/row-minor-inconsistencies",
961 : .method = MHD_HTTP_METHOD_GET,
962 : .mime_type = "application/json",
963 : .data = NULL,
964 : .data_size = 0,
965 : .handler = &TAH_get_monitoring_row_minor_inconsistencies,
966 : .response_code = MHD_HTTP_OK,
967 : .requires_auth = true },
968 : { .url = "/monitoring/row-minor-inconsistencies",
969 : .method = MHD_HTTP_METHOD_DELETE,
970 : .mime_type = "application/json",
971 : .data = NULL,
972 : .data_size = 0,
973 : .handler = &TAH_delete_generic,
974 : .response_code = MHD_HTTP_OK,
975 : .requires_auth = true,
976 : .table = TALER_AUDITORDB_ROW_MINOR_INCONSISTENCY },
977 : { .url = "/monitoring/row-minor-inconsistencies",
978 : .method = MHD_HTTP_METHOD_PATCH,
979 : .mime_type = "application/json",
980 : .data = NULL,
981 : .data_size = 0,
982 : .handler = &TAH_patch_generic_suppressed,
983 : .response_code = MHD_HTTP_OK,
984 : .requires_auth = true,
985 : .table = TALER_AUDITORDB_ROW_MINOR_INCONSISTENCY },
986 : { .url = "/monitoring/fee-time-inconsistencies",
987 : .method = MHD_HTTP_METHOD_GET,
988 : .mime_type = "application/json",
989 : .data = NULL,
990 : .data_size = 0,
991 : .handler = &TAH_get_monitoring_fee_time_inconsistency,
992 : .response_code = MHD_HTTP_OK,
993 : .requires_auth = true },
994 : { .url = "/monitoring/fee-time-inconsistencies",
995 : .method = MHD_HTTP_METHOD_DELETE,
996 : .mime_type = "application/json",
997 : .data = NULL,
998 : .data_size = 0,
999 : .handler = &TAH_delete_generic,
1000 : .response_code = MHD_HTTP_OK,
1001 : .requires_auth = true,
1002 : .table = TALER_AUDITORDB_FEE_TIME_INCONSISTENCY },
1003 : { .url = "/monitoring/fee-time-inconsistencies",
1004 : .method = MHD_HTTP_METHOD_PATCH,
1005 : .mime_type = "application/json",
1006 : .data = NULL,
1007 : .data_size = 0,
1008 : .handler = &TAH_patch_generic_suppressed,
1009 : .response_code = MHD_HTTP_OK,
1010 : .requires_auth = true,
1011 : .table = TALER_AUDITORDB_FEE_TIME_INCONSISTENCY },
1012 : { .url = "/monitoring/balances",
1013 : .method = MHD_HTTP_METHOD_GET,
1014 : .mime_type = "application/json",
1015 : .data = NULL,
1016 : .data_size = 0,
1017 : .handler = &TAH_get_monitoring_balances,
1018 : .response_code = MHD_HTTP_OK,
1019 : .requires_auth = true },
1020 : { .url = "/monitoring/progress",
1021 : .method = MHD_HTTP_METHOD_GET,
1022 : .mime_type = "application/json",
1023 : .data = NULL,
1024 : .data_size = 0,
1025 : .handler = &TAH_get_monitoring_progress,
1026 : .response_code = MHD_HTTP_OK,
1027 : .requires_auth = true },
1028 : { .url = "/config",
1029 : .method = MHD_HTTP_METHOD_GET,
1030 : .mime_type = "application/json",
1031 : .data = NULL,
1032 : .data_size = 0,
1033 : .handler = &handle_config,
1034 : .response_code = MHD_HTTP_OK,
1035 : .requires_auth = false },
1036 : /* /robots.txt: disallow everything */
1037 : { .url = "/robots.txt",
1038 : .method = MHD_HTTP_METHOD_GET,
1039 : .mime_type = "text/plain",
1040 : .data = "User-agent: *\nDisallow: /\n",
1041 : .data_size = 0,
1042 : .handler = &TAH_MHD_handler_static_response,
1043 : .response_code = MHD_HTTP_OK,
1044 : .requires_auth = false },
1045 : /* AGPL licensing page, redirect to source. As per the AGPL-license,
1046 : every deployment is required to offer the user a download of the
1047 : source. We make this easy by including a redirect t the source
1048 : here. */
1049 : { .url = "/agpl",
1050 : .method = MHD_HTTP_METHOD_GET,
1051 : .mime_type = "text/plain",
1052 : .data = NULL,
1053 : .data_size = 0,
1054 : .handler = &TAH_MHD_handler_agpl_redirect,
1055 : .response_code = MHD_HTTP_FOUND,
1056 : .requires_auth = false },
1057 : /* Landing page, for now tells humans to go away
1058 : * (NOTE: ideally, the reverse proxy will respond with a nicer page) */
1059 : { .url = "/",
1060 : .method = MHD_HTTP_METHOD_GET,
1061 : .mime_type = "text/plain",
1062 : .data =
1063 : "Hello, I'm the Taler auditor. This HTTP server is not for humans.\n",
1064 : .data_size = 0,
1065 : .handler = &TAH_MHD_handler_static_response,
1066 : .response_code = MHD_HTTP_OK,
1067 : .requires_auth = false },
1068 : { NULL, NULL, NULL, NULL, 0, NULL, 0, 0 }
1069 : };
1070 151 : unsigned int args_max = 3;
1071 151 : const char *args[args_max + 1];
1072 151 : size_t ulen = strlen (url) + 1;
1073 151 : char d[ulen];
1074 151 : /* const */ struct TAH_RequestHandler *match = NULL;
1075 151 : bool url_match = false;
1076 :
1077 : (void) cls;
1078 : (void) version;
1079 151 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1080 : "Handling request for URL '%s'\n",
1081 : url);
1082 151 : if (0 == strcasecmp (method,
1083 : MHD_HTTP_METHOD_HEAD))
1084 0 : method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
1085 151 : if (0 == strcasecmp (method,
1086 : MHD_HTTP_METHOD_OPTIONS) )
1087 0 : return TALER_MHD_reply_cors_preflight (connection);
1088 :
1089 151 : memset (&args,
1090 : 0,
1091 : sizeof (args));
1092 151 : GNUNET_memcpy (d,
1093 : url,
1094 : ulen);
1095 : {
1096 151 : unsigned int i = 0;
1097 :
1098 151 : for (args[i] = strtok (d,
1099 : "/");
1100 433 : NULL != args[i];
1101 282 : args[i] = strtok (NULL,
1102 : "/"))
1103 : {
1104 282 : i++;
1105 282 : if (i > args_max)
1106 : {
1107 : /* more than @e args_max segments in the URL */
1108 0 : GNUNET_break_op (0);
1109 0 : goto not_found;
1110 : }
1111 : }
1112 : }
1113 :
1114 6156 : for (unsigned int i = 0; NULL != handlers[i].url; i++)
1115 : {
1116 6156 : /* const */ struct TAH_RequestHandler *rh = &handlers[i];
1117 :
1118 6156 : if ( (0 == strcmp (url,
1119 6005 : rh->url)) ||
1120 6005 : ( (0 == strncmp (url,
1121 : rh->url,
1122 18 : strlen (rh->url))) &&
1123 18 : ('/' == url[strlen (rh->url)]) ) )
1124 : {
1125 151 : url_match = true;
1126 151 : if ( (NULL == rh->method) ||
1127 151 : (0 == strcasecmp (method,
1128 : rh->method)) )
1129 : {
1130 151 : match = rh;
1131 151 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1132 : "Matched %s\n",
1133 : rh->url);
1134 151 : break;
1135 : }
1136 : }
1137 : }
1138 151 : if (NULL == match)
1139 : {
1140 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1141 : "Could not find handler for `%s'\n",
1142 : url);
1143 0 : goto not_found;
1144 : }
1145 151 : if (match->requires_auth &&
1146 132 : (0 == disable_auth) )
1147 : {
1148 : const char *auth;
1149 :
1150 132 : auth = MHD_lookup_connection_value (connection,
1151 : MHD_HEADER_KIND,
1152 : MHD_HTTP_HEADER_AUTHORIZATION);
1153 132 : if (NULL == auth)
1154 : {
1155 0 : GNUNET_break_op (0);
1156 0 : return TALER_MHD_reply_with_error (
1157 : connection,
1158 : MHD_HTTP_UNAUTHORIZED,
1159 : TALER_EC_AUDITOR_GENERIC_UNAUTHORIZED,
1160 : "Check 'Authorization' header");
1161 : }
1162 132 : extract_token (&auth);
1163 132 : if (NULL == auth)
1164 0 : return TALER_MHD_reply_with_error (
1165 : connection,
1166 : MHD_HTTP_BAD_REQUEST,
1167 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1168 : "'" RFC_8959_PREFIX
1169 : "' prefix or 'Bearer' missing in 'Authorization' header");
1170 :
1171 132 : if (GNUNET_OK !=
1172 132 : check_auth (auth))
1173 : {
1174 0 : GNUNET_break_op (0);
1175 0 : return TALER_MHD_reply_with_error (
1176 : connection,
1177 : MHD_HTTP_UNAUTHORIZED,
1178 : TALER_EC_AUDITOR_GENERIC_UNAUTHORIZED,
1179 : "Check 'Authorization' header");
1180 : }
1181 : }
1182 :
1183 151 : return match->handler (match,
1184 : connection,
1185 : con_cls,
1186 : upload_data,
1187 : upload_data_size,
1188 : args);
1189 0 : not_found:
1190 0 : if (url_match)
1191 : {
1192 : /* The URL exists, but not for the requested HTTP method: respond with
1193 : 405 Method Not Allowed and an 'Allow' header listing the methods that
1194 : are supported for this URL (#9424). */
1195 0 : char allow[128] = "OPTIONS";
1196 0 : size_t aoff = strlen ("OPTIONS");
1197 : struct MHD_Response *resp;
1198 : enum MHD_Result ret;
1199 :
1200 0 : GNUNET_break_op (0);
1201 : /* OPTIONS is always supported (handled above); additionally list every
1202 : method registered for this URL. */
1203 0 : for (unsigned int i = 0; NULL != handlers[i].url; i++)
1204 : {
1205 0 : const struct TAH_RequestHandler *rh = &handlers[i];
1206 :
1207 0 : if (NULL == rh->method)
1208 0 : continue;
1209 0 : if ( (0 != strcmp (url,
1210 0 : rh->url)) &&
1211 0 : ! ( (0 == strncmp (url,
1212 0 : rh->url,
1213 0 : strlen (rh->url))) &&
1214 0 : ('/' == url[strlen (rh->url)]) ) )
1215 0 : continue;
1216 0 : GNUNET_assert (aoff + strlen (rh->method) + 3 < sizeof (allow));
1217 0 : memcpy (&allow[aoff],
1218 : ", ",
1219 : 2);
1220 0 : aoff += 2;
1221 0 : memcpy (&allow[aoff],
1222 0 : rh->method,
1223 0 : strlen (rh->method));
1224 0 : aoff += strlen (rh->method);
1225 0 : allow[aoff] = '\0';
1226 : }
1227 0 : resp = MHD_create_response_from_buffer (0,
1228 : NULL,
1229 : MHD_RESPMEM_PERSISTENT);
1230 0 : TALER_MHD_add_global_headers (resp,
1231 : false);
1232 0 : GNUNET_break (MHD_YES ==
1233 : MHD_add_response_header (resp,
1234 : MHD_HTTP_HEADER_ALLOW,
1235 : allow));
1236 0 : ret = MHD_queue_response (connection,
1237 : MHD_HTTP_METHOD_NOT_ALLOWED,
1238 : resp);
1239 0 : MHD_destroy_response (resp);
1240 0 : return ret;
1241 : }
1242 :
1243 : #define NOT_FOUND \
1244 : "<html><title>404: not found</title><body>auditor endpoints have been moved to /monitoring/...</body></html>"
1245 0 : return TALER_MHD_reply_static (connection,
1246 : MHD_HTTP_NOT_FOUND,
1247 : "text/html",
1248 : NOT_FOUND,
1249 : strlen (NOT_FOUND));
1250 : #undef NOT_FOUND
1251 : }
1252 :
1253 :
1254 : /**
1255 : * Load configuration parameters for the auditor
1256 : * server into the corresponding global variables.
1257 : *
1258 : * @return EXIT_SUCCESS on success, EXIT_NOTCONFIGURED for invalid settings,
1259 : * EXIT_FAILURE for an unavailable dependency
1260 : */
1261 : static int
1262 5 : auditor_serve_process_config (void)
1263 : {
1264 5 : bool have_auditor_public_key = false;
1265 :
1266 5 : if (NULL ==
1267 5 : (TAH_apg = TALER_AUDITORDB_connect (cfg)))
1268 : {
1269 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1270 : "Failed to initialize DB subsystem to interact with auditor database\n");
1271 0 : return EXIT_FAILURE;
1272 : }
1273 5 : if (NULL ==
1274 5 : (TAH_epg = TALER_EXCHANGEDB_connect (cfg)))
1275 : {
1276 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1277 : "Failed to initialize DB subsystem to query exchange database\n");
1278 0 : return EXIT_FAILURE;
1279 : }
1280 5 : if (GNUNET_SYSERR ==
1281 5 : TALER_EXCHANGEDB_preflight (TAH_epg))
1282 : {
1283 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1284 : "Failed to initialize DB subsystem to query exchange database\n");
1285 0 : return EXIT_FAILURE;
1286 : }
1287 5 : if (GNUNET_OK !=
1288 5 : TALER_config_get_currency (cfg,
1289 : "exchange",
1290 : &TAH_currency))
1291 : {
1292 0 : return EXIT_NOTCONFIGURED;
1293 : }
1294 :
1295 : {
1296 : char *master_public_key_str;
1297 :
1298 5 : if (GNUNET_OK !=
1299 5 : GNUNET_CONFIGURATION_get_value_string (cfg,
1300 : "exchange",
1301 : "MASTER_PUBLIC_KEY",
1302 : &master_public_key_str))
1303 : {
1304 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1305 : "exchange",
1306 : "MASTER_PUBLIC_KEY");
1307 0 : return EXIT_NOTCONFIGURED;
1308 : }
1309 5 : if (GNUNET_OK !=
1310 5 : GNUNET_CRYPTO_eddsa_public_key_from_string (
1311 : master_public_key_str,
1312 : strlen (master_public_key_str),
1313 : &TAH_master_public_key.eddsa_pub))
1314 : {
1315 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1316 : "exchange",
1317 : "MASTER_PUBLIC_KEY",
1318 : "invalid base32 encoding for a master public key");
1319 0 : GNUNET_free (master_public_key_str);
1320 0 : return EXIT_NOTCONFIGURED;
1321 : }
1322 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1323 : "Launching auditor for exchange `%s'...\n",
1324 : master_public_key_str);
1325 5 : GNUNET_free (master_public_key_str);
1326 : }
1327 :
1328 : {
1329 : char *pub;
1330 :
1331 5 : if (GNUNET_OK ==
1332 5 : GNUNET_CONFIGURATION_get_value_string (cfg,
1333 : "AUDITOR",
1334 : "PUBLIC_KEY",
1335 : &pub))
1336 : {
1337 5 : if (GNUNET_OK !=
1338 5 : GNUNET_CRYPTO_eddsa_public_key_from_string (pub,
1339 : strlen (pub),
1340 : &auditor_pub.eddsa_pub))
1341 : {
1342 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1343 : "Invalid public key given in auditor configuration.");
1344 0 : GNUNET_free (pub);
1345 0 : return EXIT_NOTCONFIGURED;
1346 : }
1347 5 : GNUNET_free (pub);
1348 5 : have_auditor_public_key = true;
1349 : }
1350 : }
1351 :
1352 5 : if (! have_auditor_public_key)
1353 : {
1354 : /* Fall back to trying to read private key */
1355 : char *auditor_key_file;
1356 : struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
1357 :
1358 0 : if (GNUNET_OK !=
1359 0 : GNUNET_CONFIGURATION_get_value_filename (cfg,
1360 : "auditor",
1361 : "AUDITOR_PRIV_FILE",
1362 : &auditor_key_file))
1363 : {
1364 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1365 : "AUDITOR",
1366 : "PUBLIC_KEY");
1367 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1368 : "AUDITOR",
1369 : "AUDITOR_PRIV_FILE");
1370 0 : return EXIT_NOTCONFIGURED;
1371 : }
1372 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1373 : "Loading auditor private key from %s\n",
1374 : auditor_key_file);
1375 0 : if (GNUNET_OK !=
1376 0 : GNUNET_CRYPTO_eddsa_key_from_file (auditor_key_file,
1377 : GNUNET_NO,
1378 : &eddsa_priv))
1379 : {
1380 : /* Both failed, complain! */
1381 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1382 : "AUDITOR",
1383 : "PUBLIC_KEY");
1384 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1385 : "Failed to initialize auditor key from file `%s'\n",
1386 : auditor_key_file);
1387 0 : GNUNET_free (auditor_key_file);
1388 0 : return 1;
1389 : }
1390 0 : GNUNET_free (auditor_key_file);
1391 0 : GNUNET_CRYPTO_eddsa_key_get_public (&eddsa_priv,
1392 : &auditor_pub.eddsa_pub);
1393 : }
1394 5 : if (GNUNET_OK ==
1395 5 : GNUNET_CONFIGURATION_get_value_filename (cfg,
1396 : "auditor",
1397 : "SPA_DIR",
1398 : &TAH_spa_dir))
1399 : {
1400 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1401 : "Loading auditor SPA from %s\n",
1402 : TAH_spa_dir);
1403 : }
1404 : else
1405 : {
1406 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1407 : "Loading auditor SPA from default location\n");
1408 : }
1409 5 : return EXIT_SUCCESS;
1410 : }
1411 :
1412 :
1413 : /**
1414 : * Load the access token clients must present to use the endpoints that
1415 : * require authentication, and derive #TAH_auth from it. The
1416 : * TALER_AUDITOR_ACCESS_TOKEN environment variable takes precedence over
1417 : * the ACCESS_TOKEN option in the [auditor] section. Clients send the
1418 : * token with the RFC 8959 "secret-token:" prefix; it MUST be stored with
1419 : * it.
1420 : *
1421 : * @return #GNUNET_OK on success, #GNUNET_SYSERR if no usable token is
1422 : * configured
1423 : */
1424 : static enum GNUNET_GenericReturnValue
1425 2 : load_access_token (void)
1426 : {
1427 : const char *tok;
1428 2 : char *cfg_tok = NULL;
1429 :
1430 2 : tok = getenv ("TALER_AUDITOR_ACCESS_TOKEN");
1431 4 : if ( (NULL == tok) &&
1432 : (GNUNET_OK ==
1433 2 : GNUNET_CONFIGURATION_get_value_string (cfg,
1434 : "auditor",
1435 : "ACCESS_TOKEN",
1436 : &cfg_tok)) )
1437 2 : tok = cfg_tok;
1438 2 : if (NULL == tok)
1439 : {
1440 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1441 : "No access token configured: set ACCESS_TOKEN in section [auditor] (or the TALER_AUDITOR_ACCESS_TOKEN environment variable), or pass -n to run without authentication\n");
1442 0 : return GNUNET_SYSERR;
1443 : }
1444 2 : if (0 != strncasecmp (tok,
1445 : RFC_8959_PREFIX,
1446 : strlen (RFC_8959_PREFIX)))
1447 : {
1448 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1449 : "The configured access token lacks the `%s' prefix\n",
1450 : RFC_8959_PREFIX);
1451 0 : GNUNET_free (cfg_tok);
1452 0 : return GNUNET_SYSERR;
1453 : }
1454 2 : tok += strlen (RFC_8959_PREFIX);
1455 2 : if ('\0' == *tok)
1456 : {
1457 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1458 : "The configured access token is empty\n");
1459 0 : GNUNET_free (cfg_tok);
1460 0 : return GNUNET_SYSERR;
1461 : }
1462 2 : GNUNET_assert (GNUNET_YES ==
1463 : GNUNET_CRYPTO_hkdf_gnunet (
1464 : &TAH_auth,
1465 : sizeof (TAH_auth),
1466 : KDF_SALT,
1467 : strlen (KDF_SALT),
1468 : tok,
1469 : strlen (tok)));
1470 2 : GNUNET_free (cfg_tok);
1471 2 : return GNUNET_OK;
1472 : }
1473 :
1474 :
1475 : /**
1476 : * Function run on shutdown.
1477 : *
1478 : * @param cls NULL
1479 : */
1480 : static void
1481 5 : do_shutdown (void *cls)
1482 : {
1483 : (void) cls;
1484 5 : TALER_MHD_daemons_halt ();
1485 5 : TEAH_put_deposit_confirmation_done ();
1486 5 : TALER_MHD_daemons_destroy ();
1487 5 : if (NULL != TAH_apg)
1488 : {
1489 5 : TALER_AUDITORDB_disconnect (TAH_apg);
1490 5 : TAH_apg = NULL;
1491 : }
1492 5 : if (NULL != TAH_epg)
1493 : {
1494 5 : TALER_EXCHANGEDB_disconnect (TAH_epg);
1495 5 : TAH_epg = NULL;
1496 : }
1497 5 : }
1498 :
1499 :
1500 : /**
1501 : * Callback invoked on every listen socket to start the
1502 : * respective MHD HTTP daemon.
1503 : *
1504 : * @param cls unused
1505 : * @param lsock the listen socket
1506 : */
1507 : static void
1508 10 : start_daemon (void *cls,
1509 : int lsock)
1510 : {
1511 : struct MHD_Daemon *mhd;
1512 :
1513 : (void) cls;
1514 10 : GNUNET_assert (-1 != lsock);
1515 10 : mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
1516 : | MHD_USE_PIPE_FOR_SHUTDOWN
1517 : | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
1518 : | MHD_USE_TCP_FASTOPEN,
1519 : 0,
1520 : NULL, NULL,
1521 : &handle_mhd_request, NULL,
1522 : MHD_OPTION_LISTEN_SOCKET,
1523 : lsock,
1524 : MHD_OPTION_EXTERNAL_LOGGER,
1525 : &TALER_MHD_handle_logs,
1526 : NULL,
1527 : MHD_OPTION_NOTIFY_COMPLETED,
1528 : &handle_mhd_completion_callback,
1529 : NULL,
1530 : MHD_OPTION_CONNECTION_TIMEOUT,
1531 : connection_timeout,
1532 : MHD_OPTION_CONNECTION_LIMIT,
1533 : (unsigned int) CONNECTION_LIMIT,
1534 : MHD_OPTION_END);
1535 10 : if (NULL == mhd)
1536 : {
1537 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1538 : "Failed to launch HTTP daemon.\n");
1539 0 : GNUNET_SCHEDULER_shutdown ();
1540 0 : return;
1541 : }
1542 10 : have_daemons = true;
1543 10 : TALER_MHD_daemon_start (mhd);
1544 : }
1545 :
1546 :
1547 : /**
1548 : * Main function that will be run by the scheduler.
1549 : *
1550 : * @param cls closure
1551 : * @param args remaining command-line arguments
1552 : * @param cfgfile name of the configuration file used (for saving, can be
1553 : * NULL!)
1554 : * @param config configuration
1555 : */
1556 : static void
1557 5 : run (void *cls,
1558 : char *const *args,
1559 : const char *cfgfile,
1560 : const struct GNUNET_CONFIGURATION_Handle *config)
1561 : {
1562 : enum TALER_MHD_GlobalOptions go;
1563 : enum GNUNET_GenericReturnValue ret;
1564 :
1565 : (void) cls;
1566 : (void) args;
1567 : (void) cfgfile;
1568 5 : go = TALER_MHD_GO_NONE;
1569 5 : if (auditor_connection_close)
1570 0 : go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
1571 5 : TALER_MHD_setup (go);
1572 5 : cfg = config;
1573 7 : if ( (0 == disable_auth) &&
1574 2 : (GNUNET_OK != load_access_token ()) )
1575 : {
1576 : /* Failing open here would publish the entire audit findings
1577 : database, including the DELETE and PATCH handlers, to anyone
1578 : able to reach the port. -n disables authentication on
1579 : purpose. */
1580 0 : global_ret = EXIT_NOTCONFIGURED;
1581 0 : GNUNET_SCHEDULER_shutdown ();
1582 0 : return;
1583 : }
1584 :
1585 5 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1586 : NULL);
1587 5 : if (EXIT_SUCCESS !=
1588 5 : (global_ret = auditor_serve_process_config ()))
1589 : {
1590 0 : GNUNET_SCHEDULER_shutdown ();
1591 0 : return;
1592 : }
1593 5 : if (GNUNET_OK !=
1594 5 : TAH_spa_init ())
1595 : {
1596 0 : global_ret = EXIT_NOTCONFIGURED;
1597 0 : GNUNET_SCHEDULER_shutdown ();
1598 0 : return;
1599 : }
1600 5 : TEAH_put_deposit_confirmation_init ();
1601 5 : ret = TALER_MHD_listen_bind (cfg,
1602 : "auditor",
1603 : &start_daemon,
1604 : NULL);
1605 5 : switch (ret)
1606 : {
1607 0 : case GNUNET_SYSERR:
1608 0 : global_ret = EXIT_NOTCONFIGURED;
1609 0 : GNUNET_SCHEDULER_shutdown ();
1610 0 : return;
1611 0 : case GNUNET_NO:
1612 0 : if (! have_daemons)
1613 : {
1614 0 : global_ret = EXIT_FAILURE;
1615 0 : GNUNET_SCHEDULER_shutdown ();
1616 0 : return;
1617 : }
1618 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1619 : "Could not open all configured listen sockets\n");
1620 0 : break;
1621 5 : case GNUNET_OK:
1622 5 : break;
1623 : }
1624 5 : global_ret = EXIT_SUCCESS;
1625 : }
1626 :
1627 :
1628 : /**
1629 : * The main function of the taler-auditor-httpd server ("the auditor").
1630 : *
1631 : * @param argc number of arguments from the command line
1632 : * @param argv command line arguments
1633 : * @return 0 ok, 1 on error
1634 : */
1635 : int
1636 5 : main (int argc,
1637 : char *const *argv)
1638 : {
1639 5 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
1640 5 : GNUNET_GETOPT_option_flag ('C',
1641 : "connection-close",
1642 : "force HTTP connections to be closed after each request",
1643 : &auditor_connection_close),
1644 5 : GNUNET_GETOPT_option_flag ('n',
1645 : "no-authentication",
1646 : "disable authentication checks",
1647 : &disable_auth),
1648 5 : GNUNET_GETOPT_option_uint ('t',
1649 : "timeout",
1650 : "SECONDS",
1651 : "after how long do connections timeout by default (in seconds)",
1652 : &connection_timeout),
1653 5 : GNUNET_GETOPT_option_help (
1654 : TALER_AUDITOR_project_data (),
1655 : "HTTP server providing a RESTful API to access a Taler auditor"),
1656 5 : GNUNET_GETOPT_option_version (VERSION),
1657 : GNUNET_GETOPT_OPTION_END
1658 : };
1659 : int ret;
1660 :
1661 5 : ret = GNUNET_PROGRAM_run (
1662 : TALER_AUDITOR_project_data (),
1663 : argc, argv,
1664 : "taler-auditor-httpd",
1665 : "Taler auditor HTTP service",
1666 : options,
1667 : &run, NULL);
1668 5 : if (GNUNET_SYSERR == ret)
1669 0 : return EXIT_NOTCONFIGURED;
1670 5 : if (GNUNET_NO == ret)
1671 0 : return EXIT_SUCCESS;
1672 5 : return global_ret;
1673 : }
1674 :
1675 :
1676 : /* end of taler-auditor-httpd.c */
|