tor  0.4.2.1-alpha-dev
hs_config.c
Go to the documentation of this file.
1 /* Copyright (c) 2017-2019, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
26 #define HS_CONFIG_PRIVATE
27 
28 #include "feature/hs/hs_common.h"
29 #include "feature/hs/hs_config.h"
30 #include "feature/hs/hs_client.h"
31 #include "feature/hs/hs_service.h"
34 #include "lib/encoding/confline.h"
36 
37 /* Using the given list of services, stage them into our global state. Every
38  * service version are handled. This function can remove entries in the given
39  * service_list.
40  *
41  * Staging a service means that we take all services in service_list and we
42  * put them in the staging list (global) which acts as a temporary list that
43  * is used by the service loading key process. In other words, staging a
44  * service puts it in a list to be considered when loading the keys and then
45  * moved to the main global list. */
46 static void
47 stage_services(smartlist_t *service_list)
48 {
49  tor_assert(service_list);
50 
51  /* This is v2 specific. Trigger service pruning which will make sure the
52  * just configured services end up in the main global list. It should only
53  * be done in non validation mode because v2 subsystem handles service
54  * object differently. */
55  rend_service_prune_list();
56 
57  /* Cleanup v2 service from the list, we don't need those object anymore
58  * because we validated them all against the others and we want to stage
59  * only >= v3 service. And remember, v2 has a different object type which is
60  * shadow copied from an hs_service_t type. */
61  SMARTLIST_FOREACH_BEGIN(service_list, hs_service_t *, s) {
62  if (s->config.version == HS_VERSION_TWO) {
63  SMARTLIST_DEL_CURRENT(service_list, s);
64  hs_service_free(s);
65  }
66  } SMARTLIST_FOREACH_END(s);
67 
68  /* This is >= v3 specific. Using the newly configured service list, stage
69  * them into our global state. Every object ownership is lost after. */
70  hs_service_stage_services(service_list);
71 }
72 
73 /* Validate the given service against all service in the given list. If the
74  * service is ephemeral, this function ignores it. Services with the same
75  * directory path aren't allowed and will return an error. If a duplicate is
76  * found, 1 is returned else 0 if none found. */
77 static int
78 service_is_duplicate_in_list(const smartlist_t *service_list,
79  const hs_service_t *service)
80 {
81  int ret = 0;
82 
83  tor_assert(service_list);
84  tor_assert(service);
85 
86  /* Ephemeral service don't have a directory configured so no need to check
87  * for a service in the list having the same path. */
88  if (service->config.is_ephemeral) {
89  goto end;
90  }
91 
92  /* XXX: Validate if we have any service that has the given service dir path.
93  * This has two problems:
94  *
95  * a) It's O(n^2), but the same comment from the bottom of
96  * rend_config_services() should apply.
97  *
98  * b) We only compare directory paths as strings, so we can't
99  * detect two distinct paths that specify the same directory
100  * (which can arise from symlinks, case-insensitivity, bind
101  * mounts, etc.).
102  *
103  * It also can't detect that two separate Tor instances are trying
104  * to use the same HiddenServiceDir; for that, we would need a
105  * lock file. But this is enough to detect a simple mistake that
106  * at least one person has actually made. */
107  SMARTLIST_FOREACH_BEGIN(service_list, const hs_service_t *, s) {
108  if (!strcmp(s->config.directory_path, service->config.directory_path)) {
109  log_warn(LD_REND, "Another hidden service is already configured "
110  "for directory %s",
111  escaped(service->config.directory_path));
112  ret = 1;
113  goto end;
114  }
115  } SMARTLIST_FOREACH_END(s);
116 
117  end:
118  return ret;
119 }
120 
121 /* Helper function: Given an configuration option name, its value, a minimum
122  * min and a maxium max, parse the value as a uint64_t. On success, ok is set
123  * to 1 and ret is the parsed value. On error, ok is set to 0 and ret must be
124  * ignored. This function logs both on error and success. */
125 static uint64_t
126 helper_parse_uint64(const char *opt, const char *value, uint64_t min,
127  uint64_t max, int *ok)
128 {
129  uint64_t ret = 0;
130 
131  tor_assert(opt);
132  tor_assert(value);
133  tor_assert(ok);
134 
135  *ok = 0;
136  ret = tor_parse_uint64(value, 10, min, max, ok, NULL);
137  if (!*ok) {
138  log_warn(LD_CONFIG, "%s must be between %" PRIu64 " and %"PRIu64
139  ", not %s.",
140  opt, min, max, value);
141  goto err;
142  }
143  log_info(LD_CONFIG, "%s was parsed to %" PRIu64, opt, ret);
144  err:
145  return ret;
146 }
147 
153 helper_parse_circuit_id_protocol(const char *key, const char *value, int *ok)
154 {
155  tor_assert(value);
156  tor_assert(ok);
157 
159  *ok = 0;
160 
161  if (! strcasecmp(value, "haproxy")) {
162  *ok = 1;
164  } else if (! strcasecmp(value, "none")) {
165  *ok = 1;
167  } else {
168  log_warn(LD_CONFIG, "%s must be 'haproxy' or 'none'.", key);
169  goto err;
170  }
171 
172  err:
173  return ret;
174 }
175 
176 /* Return the service version by trying to learn it from the key on disk if
177  * any. If nothing is found, the current service configured version is
178  * returned. */
179 static int
180 config_learn_service_version(hs_service_t *service)
181 {
182  int version;
183 
184  tor_assert(service);
185 
186  version = hs_service_get_version_from_key(service);
187  if (version < 0) {
188  version = service->config.version;
189  }
190 
191  return version;
192 }
193 
194 /* Return true iff the given options starting at line_ for a hidden service
195  * contains at least one invalid option. Each hidden service option don't
196  * apply to all versions so this function can find out. The line_ MUST start
197  * right after the HiddenServiceDir line of this service.
198  *
199  * This is mainly for usability so we can inform the user of any invalid
200  * option for the hidden service version instead of silently ignoring. */
201 static int
202 config_has_invalid_options(const config_line_t *line_,
203  const hs_service_t *service)
204 {
205  int ret = 0;
206  const char **optlist;
207  const config_line_t *line;
208 
209  tor_assert(service);
210  tor_assert(service->config.version <= HS_VERSION_MAX);
211 
212  /* List of options that a v3 service doesn't support thus must exclude from
213  * its configuration. */
214  const char *opts_exclude_v3[] = {
215  "HiddenServiceAuthorizeClient",
216  NULL /* End marker. */
217  };
218 
219  const char *opts_exclude_v2[] = {
220  "HiddenServiceExportCircuitID",
221  "HiddenServiceEnableIntroDoSDefense",
222  "HiddenServiceEnableIntroDoSRatePerSec",
223  "HiddenServiceEnableIntroDoSBurstPerSec",
224  NULL /* End marker. */
225  };
226 
227  /* Defining the size explicitly allows us to take advantage of the compiler
228  * which warns us if we ever bump the max version but forget to grow this
229  * array. The plus one is because we have a version 0 :). */
230  struct {
231  const char **list;
232  } exclude_lists[HS_VERSION_MAX + 1] = {
233  { NULL }, /* v0. */
234  { NULL }, /* v1. */
235  { opts_exclude_v2 }, /* v2 */
236  { opts_exclude_v3 }, /* v3. */
237  };
238 
239  optlist = exclude_lists[service->config.version].list;
240  if (optlist == NULL) {
241  /* No exclude options to look at for this version. */
242  goto end;
243  }
244  for (int i = 0; optlist[i]; i++) {
245  const char *opt = optlist[i];
246  for (line = line_; line; line = line->next) {
247  if (!strcasecmp(line->key, "HiddenServiceDir")) {
248  /* We just hit the next hidden service, stop right now. */
249  goto end;
250  }
251  if (!strcasecmp(line->key, opt)) {
252  log_warn(LD_CONFIG, "Hidden service option %s is incompatible with "
253  "version %" PRIu32 " of service in %s",
254  opt, service->config.version,
255  service->config.directory_path);
256  ret = 1;
257  /* Continue the loop so we can find all possible options. */
258  continue;
259  }
260  }
261  }
262  end:
263  return ret;
264 }
265 
266 /* Validate service configuration. This is used when loading the configuration
267  * and once we've setup a service object, it's config object is passed to this
268  * function for further validation. This does not validate service key
269  * material. Return 0 if valid else -1 if invalid. */
270 static int
271 config_validate_service(const hs_service_config_t *config)
272 {
273  tor_assert(config);
274 
275  /* Amount of ports validation. */
276  if (!config->ports || smartlist_len(config->ports) == 0) {
277  log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured.",
278  escaped(config->directory_path));
279  goto invalid;
280  }
281 
282  /* DoS validation values. */
283  if (config->has_dos_defense_enabled &&
284  (config->intro_dos_burst_per_sec < config->intro_dos_rate_per_sec)) {
285  log_warn(LD_CONFIG, "Hidden service DoS defenses burst (%" PRIu32 ") can "
286  "not be smaller than the rate value (%" PRIu32 ").",
287  config->intro_dos_burst_per_sec, config->intro_dos_rate_per_sec);
288  goto invalid;
289  }
290 
291  /* Valid. */
292  return 0;
293  invalid:
294  return -1;
295 }
296 
297 /* Configuration funcion for a version 3 service. The line_ must be pointing
298  * to the directive directly after a HiddenServiceDir. That way, when hitting
299  * the next HiddenServiceDir line or reaching the end of the list of lines, we
300  * know that we have to stop looking for more options. The given service
301  * object must be already allocated and passed through
302  * config_generic_service() prior to calling this function.
303  *
304  * Return 0 on success else a negative value. */
305 static int
306 config_service_v3(const config_line_t *line_,
307  hs_service_config_t *config)
308 {
309  int have_num_ip = 0;
310  bool export_circuit_id = false; /* just to detect duplicate options */
311  bool dos_enabled = false, dos_rate_per_sec = false;
312  bool dos_burst_per_sec = false;
313  const char *dup_opt_seen = NULL;
314  const config_line_t *line;
315 
316  tor_assert(config);
317 
318  for (line = line_; line; line = line->next) {
319  int ok = 0;
320  if (!strcasecmp(line->key, "HiddenServiceDir")) {
321  /* We just hit the next hidden service, stop right now. */
322  break;
323  }
324  /* Number of introduction points. */
325  if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) {
326  config->num_intro_points =
327  (unsigned int) helper_parse_uint64(line->key, line->value,
329  HS_CONFIG_V3_MAX_INTRO_POINTS,
330  &ok);
331  if (!ok || have_num_ip) {
332  if (have_num_ip)
333  dup_opt_seen = line->key;
334  goto err;
335  }
336  have_num_ip = 1;
337  continue;
338  }
339  if (!strcasecmp(line->key, "HiddenServiceExportCircuitID")) {
340  config->circuit_id_protocol =
341  helper_parse_circuit_id_protocol(line->key, line->value, &ok);
342  if (!ok || export_circuit_id) {
343  if (export_circuit_id) {
344  dup_opt_seen = line->key;
345  }
346  goto err;
347  }
348  export_circuit_id = true;
349  continue;
350  }
351  if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSDefense")) {
352  config->has_dos_defense_enabled =
353  (unsigned int) helper_parse_uint64(line->key, line->value,
354  HS_CONFIG_V3_DOS_DEFENSE_DEFAULT,
355  1, &ok);
356  if (!ok || dos_enabled) {
357  if (dos_enabled) {
358  dup_opt_seen = line->key;
359  }
360  goto err;
361  }
362  dos_enabled = true;
363  continue;
364  }
365  if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSRatePerSec")) {
366  config->intro_dos_rate_per_sec =
367  (unsigned int) helper_parse_uint64(line->key, line->value,
368  HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN,
369  HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX, &ok);
370  if (!ok || dos_rate_per_sec) {
371  if (dos_rate_per_sec) {
372  dup_opt_seen = line->key;
373  }
374  goto err;
375  }
376  dos_rate_per_sec = true;
377  log_info(LD_REND, "Service INTRO2 DoS defenses rate set to: %" PRIu32,
378  config->intro_dos_rate_per_sec);
379  continue;
380  }
381  if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSBurstPerSec")) {
382  config->intro_dos_burst_per_sec =
383  (unsigned int) helper_parse_uint64(line->key, line->value,
384  HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN,
385  HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX, &ok);
386  if (!ok || dos_burst_per_sec) {
387  if (dos_burst_per_sec) {
388  dup_opt_seen = line->key;
389  }
390  goto err;
391  }
392  dos_burst_per_sec = true;
393  log_info(LD_REND, "Service INTRO2 DoS defenses burst set to: %" PRIu32,
394  config->intro_dos_burst_per_sec);
395  continue;
396  }
397  }
398 
399  /* We do not load the key material for the service at this stage. This is
400  * done later once tor can confirm that it is in a running state. */
401 
402  /* We are about to return a fully configured service so do one last pass of
403  * validation at it. */
404  if (config_validate_service(config) < 0) {
405  goto err;
406  }
407 
408  return 0;
409  err:
410  if (dup_opt_seen) {
411  log_warn(LD_CONFIG, "Duplicate directive %s.", dup_opt_seen);
412  }
413  return -1;
414 }
415 
416 /* Configure a service using the given options in line_ and options. This is
417  * called for any service regardless of its version which means that all
418  * directives in this function are generic to any service version. This
419  * function will also check the validity of the service directory path.
420  *
421  * The line_ must be pointing to the directive directly after a
422  * HiddenServiceDir. That way, when hitting the next HiddenServiceDir line or
423  * reaching the end of the list of lines, we know that we have to stop looking
424  * for more options.
425  *
426  * Return 0 on success else -1. */
427 static int
428 config_generic_service(const config_line_t *line_,
429  const or_options_t *options,
430  hs_service_t *service)
431 {
432  int dir_seen = 0;
433  const config_line_t *line;
434  hs_service_config_t *config;
435  /* If this is set, we've seen a duplicate of this option. Keep the string
436  * so we can log the directive. */
437  const char *dup_opt_seen = NULL;
438  /* These variables will tell us if we ever have duplicate. */
439  int have_version = 0, have_allow_unknown_ports = 0;
440  int have_dir_group_read = 0, have_max_streams = 0;
441  int have_max_streams_close = 0;
442 
443  tor_assert(line_);
444  tor_assert(options);
445  tor_assert(service);
446 
447  /* Makes thing easier. */
448  config = &service->config;
449 
450  /* The first line starts with HiddenServiceDir so we consider what's next is
451  * the configuration of the service. */
452  for (line = line_; line ; line = line->next) {
453  int ok = 0;
454 
455  /* This indicate that we have a new service to configure. */
456  if (!strcasecmp(line->key, "HiddenServiceDir")) {
457  /* This function only configures one service at a time so if we've
458  * already seen one, stop right now. */
459  if (dir_seen) {
460  break;
461  }
462  /* Ok, we've seen one and we are about to configure it. */
463  dir_seen = 1;
464  config->directory_path = tor_strdup(line->value);
465  log_info(LD_CONFIG, "HiddenServiceDir=%s. Configuring...",
466  escaped(config->directory_path));
467  continue;
468  }
469  if (BUG(!dir_seen)) {
470  goto err;
471  }
472  /* Version of the service. */
473  if (!strcasecmp(line->key, "HiddenServiceVersion")) {
474  service->config.version =
475  (uint32_t) helper_parse_uint64(line->key, line->value, HS_VERSION_MIN,
476  HS_VERSION_MAX, &ok);
477  if (!ok || have_version) {
478  if (have_version)
479  dup_opt_seen = line->key;
480  goto err;
481  }
482  have_version = service->config.hs_version_explicitly_set = 1;
483  continue;
484  }
485  /* Virtual port. */
486  if (!strcasecmp(line->key, "HiddenServicePort")) {
487  char *err_msg = NULL;
488  /* XXX: Can we rename this? */
489  rend_service_port_config_t *portcfg =
490  rend_service_parse_port_config(line->value, " ", &err_msg);
491  if (!portcfg) {
492  if (err_msg) {
493  log_warn(LD_CONFIG, "%s", err_msg);
494  }
495  tor_free(err_msg);
496  goto err;
497  }
498  tor_assert(!err_msg);
499  smartlist_add(config->ports, portcfg);
500  log_info(LD_CONFIG, "HiddenServicePort=%s for %s",
501  line->value, escaped(config->directory_path));
502  continue;
503  }
504  /* Do we allow unknown ports. */
505  if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
506  config->allow_unknown_ports =
507  (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
508  if (!ok || have_allow_unknown_ports) {
509  if (have_allow_unknown_ports)
510  dup_opt_seen = line->key;
511  goto err;
512  }
513  have_allow_unknown_ports = 1;
514  continue;
515  }
516  /* Directory group readable. */
517  if (!strcasecmp(line->key, "HiddenServiceDirGroupReadable")) {
518  config->dir_group_readable =
519  (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
520  if (!ok || have_dir_group_read) {
521  if (have_dir_group_read)
522  dup_opt_seen = line->key;
523  goto err;
524  }
525  have_dir_group_read = 1;
526  continue;
527  }
528  /* Maximum streams per circuit. */
529  if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
530  config->max_streams_per_rdv_circuit =
531  helper_parse_uint64(line->key, line->value, 0,
532  HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT, &ok);
533  if (!ok || have_max_streams) {
534  if (have_max_streams)
535  dup_opt_seen = line->key;
536  goto err;
537  }
538  have_max_streams = 1;
539  continue;
540  }
541  /* Maximum amount of streams before we close the circuit. */
542  if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
543  config->max_streams_close_circuit =
544  (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
545  if (!ok || have_max_streams_close) {
546  if (have_max_streams_close)
547  dup_opt_seen = line->key;
548  goto err;
549  }
550  have_max_streams_close = 1;
551  continue;
552  }
553  }
554 
555  /* Check if we are configured in non anonymous mode meaning every service
556  * becomes a single onion service. */
557  if (rend_service_non_anonymous_mode_enabled(options)) {
558  config->is_single_onion = 1;
559  }
560 
561  /* Success */
562  return 0;
563  err:
564  if (dup_opt_seen) {
565  log_warn(LD_CONFIG, "Duplicate directive %s.", dup_opt_seen);
566  }
567  return -1;
568 }
569 
570 /* Configure a service using the given line and options. This function will
571  * call the corresponding configuration function for a specific service
572  * version and validate the service against the other ones. On success, add
573  * the service to the given list and return 0. On error, nothing is added to
574  * the list and a negative value is returned. */
575 static int
576 config_service(const config_line_t *line, const or_options_t *options,
577  smartlist_t *service_list)
578 {
579  int ret;
580  hs_service_t *service = NULL;
581 
582  tor_assert(line);
583  tor_assert(options);
584  tor_assert(service_list);
585 
586  /* We have a new hidden service. */
587  service = hs_service_new(options);
588 
589  /* We'll configure that service as a generic one and then pass it to a
590  * specific function according to the configured version number. */
591  if (config_generic_service(line, options, service) < 0) {
592  goto err;
593  }
594 
595  tor_assert(service->config.version <= HS_VERSION_MAX);
596 
597  /* Check permission on service directory that was just parsed. And this must
598  * be done regardless of the service version. Do not ask for the directory
599  * to be created, this is done when the keys are loaded because we could be
600  * in validation mode right now. */
601  if (hs_check_service_private_dir(options->User,
602  service->config.directory_path,
603  service->config.dir_group_readable,
604  0) < 0) {
605  goto err;
606  }
607 
608  /* We'll try to learn the service version here by loading the key(s) if
609  * present and we did not set HiddenServiceVersion. Depending on the key
610  * format, we can figure out the service version. */
611  if (!service->config.hs_version_explicitly_set) {
612  service->config.version = config_learn_service_version(service);
613  }
614 
615  /* We make sure that this set of options for a service are valid that is for
616  * instance an option only for v2 is not used for v3. */
617  if (config_has_invalid_options(line->next, service)) {
618  goto err;
619  }
620 
621  /* Different functions are in charge of specific options for a version. We
622  * start just after the service directory line so once we hit another
623  * directory line, the function knows that it has to stop parsing. */
624  switch (service->config.version) {
625  case HS_VERSION_TWO:
626  ret = rend_config_service(line->next, options, &service->config);
627  break;
628  case HS_VERSION_THREE:
629  ret = config_service_v3(line->next, &service->config);
630  break;
631  default:
632  /* We do validate before if we support the parsed version. */
633  tor_assert_nonfatal_unreached();
634  goto err;
635  }
636  if (ret < 0) {
637  goto err;
638  }
639 
640  /* We'll check if this service can be kept depending on the others
641  * configured previously. */
642  if (service_is_duplicate_in_list(service_list, service)) {
643  goto err;
644  }
645 
646  /* Passes, add it to the given list. */
647  smartlist_add(service_list, service);
648 
649  return 0;
650 
651  err:
652  hs_service_free(service);
653  return -1;
654 }
655 
656 /* From a set of <b>options</b>, setup every hidden service found. Return 0 on
657  * success or -1 on failure. If <b>validate_only</b> is set, parse, warn and
658  * return as normal, but don't actually change the configured services. */
659 int
660 hs_config_service_all(const or_options_t *options, int validate_only)
661 {
662  int dir_option_seen = 0, ret = -1;
663  const config_line_t *line;
664  smartlist_t *new_service_list = NULL;
665 
666  tor_assert(options);
667 
668  /* Newly configured service are put in that list which is then used for
669  * validation and staging for >= v3. */
670  new_service_list = smartlist_new();
671 
672  for (line = options->RendConfigLines; line; line = line->next) {
673  /* Ignore all directives that aren't the start of a service. */
674  if (strcasecmp(line->key, "HiddenServiceDir")) {
675  if (!dir_option_seen) {
676  log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
677  line->key);
678  goto err;
679  }
680  continue;
681  }
682  /* Flag that we've seen a directory directive and we'll use it to make
683  * sure that the torrc options ordering is actually valid. */
684  dir_option_seen = 1;
685 
686  /* Try to configure this service now. On success, it will be added to the
687  * list and validated against the service in that same list. */
688  if (config_service(line, options, new_service_list) < 0) {
689  goto err;
690  }
691  }
692 
693  /* In non validation mode, we'll stage those services we just successfully
694  * configured. Service ownership is transferred from the list to the global
695  * state. If any service is invalid, it will be removed from the list and
696  * freed. All versions are handled in that function. */
697  if (!validate_only) {
698  stage_services(new_service_list);
699  } else {
700  /* We've just validated that we were able to build a clean working list of
701  * services. We don't need those objects anymore. */
702  SMARTLIST_FOREACH(new_service_list, hs_service_t *, s,
703  hs_service_free(s));
704  /* For the v2 subsystem, the configuration function adds the service
705  * object to the staging list and it is transferred in the main list
706  * through the prunning process. In validation mode, we thus have to purge
707  * the staging list so it's not kept in memory as valid service. */
708  rend_service_free_staging_list();
709  }
710 
711  /* Success. Note that the service list has no ownership of its content. */
712  ret = 0;
713  goto end;
714 
715  err:
716  SMARTLIST_FOREACH(new_service_list, hs_service_t *, s, hs_service_free(s));
717 
718  end:
719  smartlist_free(new_service_list);
720  /* Tor main should call the free all function on error. */
721  return ret;
722 }
723 
724 /* From a set of <b>options</b>, setup every client authorization found.
725  * Return 0 on success or -1 on failure. If <b>validate_only</b> is set,
726  * parse, warn and return as normal, but don't actually change the
727  * configured state. */
728 int
729 hs_config_client_auth_all(const or_options_t *options, int validate_only)
730 {
731  int ret = -1;
732 
733  /* Configure v2 authorization. */
734  if (rend_parse_service_authorization(options, validate_only) < 0) {
735  goto done;
736  }
737 
738  /* Configure v3 authorization. */
739  if (hs_config_client_authorization(options, validate_only) < 0) {
740  goto done;
741  }
742 
743  /* Success. */
744  ret = 0;
745  done:
746  return ret;
747 }
Header for confline.c.
Header file containing common data for the whole HS subsytem.
Header file containing configuration ABI/API for the HS subsytem.
static hs_circuit_id_protocol_t helper_parse_circuit_id_protocol(const char *key, const char *value, int *ok)
Definition: hs_config.c:153
Header file containing service data for the HS subsytem.
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
uint64_t tor_parse_uint64(const char *s, int base, uint64_t min, uint64_t max, int *ok, char **next)
Definition: parse_int.c:107
Header file containing client data for the HS subsytem.
void smartlist_add(smartlist_t *sl, void *element)
The or_options_t structure, which represents Tor's configuration.
#define tor_free(p)
Definition: malloc.h:52
#define SMARTLIST_DEL_CURRENT(sl, var)
tor_assert(buffer)
rend_service_port_config_t * rend_service_parse_port_config(const char *string, const char *sep, char **err_msg_out)
Definition: rendservice.c:419
#define LD_REND
Definition: log.h:82
Header file for rendservice.c.
hs_circuit_id_protocol_t
Definition: hs_service.h:185
#define NUM_INTRO_POINTS_DEFAULT
Definition: hs_common.h:32
struct config_line_t * RendConfigLines
#define SMARTLIST_FOREACH(sl, type, var, cmd)
const char * escaped(const char *s)
Definition: escape.c:126
Header file for rendclient.c.
int rend_parse_service_authorization(const or_options_t *options, int validate_only)
Definition: rendclient.c:1170
#define LD_CONFIG
Definition: log.h:66