tor-commits
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
July 2017
- 14 participants
- 1743 discussions

[tor-browser-bundle/master] Build go-webrtc and snowflake in the mac pluggable-transports descriptor.
by gk@torproject.org 14 Jul '17
by gk@torproject.org 14 Jul '17
14 Jul '17
commit 26e0cd44f2886bfad1c3d30844ff7a21eb9d0478
Author: David Fifield <david(a)bamsoftware.com>
Date: Wed Jul 5 22:57:22 2017 -0700
Build go-webrtc and snowflake in the mac pluggable-transports descriptor.
I had to apply two tricks to get a reproducible snowflake-client.
The first is to use faketime to eliminate some timestamps. There were 11
variable timestamps in the file. Through experimentation, I found that
10 of them were dependent on the Go runtime (recompiling Go caused them
to change) and 1 was dependent on snowflake-client itself (recompiling
snowflake-client with the same runtime changed only that 1 timestamp).
The underlying issue has to do with clang 3.8.0 on Darwin embedding
timestamps, unsolved in the Go issue tracker as of 13 days ago.
https://github.com/golang/go/issues/9206#issuecomment-310476743
The second is a sed command to clobber embedded paths of the form
/tmp/go-buildXXXXXXXXX and /tmp/go-link-XXXXXXXXX. Their presence is
caused by some combination of Clang and Darwin, and there is as yet no
known workaround upstream.
---
.../mac/gitian-pluggable-transports.yml | 71 +++++++++++++++++++++-
gitian/mkbundle-mac.sh | 2 +-
2 files changed, 71 insertions(+), 2 deletions(-)
diff --git a/gitian/descriptors/mac/gitian-pluggable-transports.yml b/gitian/descriptors/mac/gitian-pluggable-transports.yml
index 75ad899..ebfcaa9 100644
--- a/gitian/descriptors/mac/gitian-pluggable-transports.yml
+++ b/gitian/descriptors/mac/gitian-pluggable-transports.yml
@@ -6,7 +6,9 @@ suites:
architectures:
- "amd64"
packages:
+- "faketime"
- "unzip"
+- "pkg-config"
- "zip"
reference_datetime: "2000-01-01 00:00:00"
remotes:
@@ -24,6 +26,12 @@ remotes:
"dir": "goxnet"
- "url": "https://git.torproject.org/pluggable-transports/obfs4.git"
"dir": "obfs4"
+- "url": "https://github.com/keroserene/go-webrtc.git"
+ "dir": "go-webrtc"
+- "url": "https://git.torproject.org/pluggable-transports/snowflake.git"
+ "dir": "snowflake"
+- "url": "https://github.com/dchest/uniuri.git"
+ "dir": "uniuri"
files:
- "versions"
- "go14.tar.gz"
@@ -31,6 +39,7 @@ files:
- "clang-linux64-jessie-utils.zip"
- "cctools.tar.gz"
- "MacOSX10.7.sdk.tar.gz"
+- "webrtc-mac64-gbuilt.zip"
- "dzip.sh"
script: |
INSTDIR="$HOME/install"
@@ -59,6 +68,8 @@ script: |
tar xaf MacOSX10.7.sdk.tar.gz
# Preparing clang for cross-compilation, setting the proper flags and
# variables
+ # "go link" expects to find a program called "dsymutil" exactly.
+ ln -sf x86_64-apple-darwin10-dsymutil $HOME/build/cctools/bin/dsymutil
# ld needs libLTO.so from llvm
export LD_LIBRARY_PATH="$HOME/build/clang/lib"
export PATH="$HOME/build/cctools/bin:$PATH"
@@ -93,7 +104,12 @@ script: |
export GOARCH=amd64
tar xvf go.tar.gz
cd go/src
- CGO_ENABLED=1 CC_FOR_TARGET="$CC_FOR_TARGET" CC= CFLAGS= LDFLAGS= ./make.bash
+ # faketime is needed because clang 3.8.0 on Darwin embeds the timestamps of
+ # certain intermediate object files (including those that arise here while
+ # compiling the Go runtime itself). Without this, variable timestamps would
+ # end up in snowflake-client.
+ # https://github.com/golang/go/issues/9206#issuecomment-310476743
+ CGO_ENABLED=1 CC_FOR_TARGET="$CC_FOR_TARGET" CC= CFLAGS= LDFLAGS= faketime -f "$REFERENCE_DATETIME" ./make.bash
cd ../..
export PATH="$PATH:$PWD/go/bin"
@@ -138,6 +154,14 @@ script: |
go install github.com/dchest/siphash
cd ..
+ # Building go uniuri
+ cd uniuri
+ find -type f -print0 | xargs -0 touch --date="$REFERENCE_DATETIME"
+ mkdir -p "$GOPATH/src/github.com/dchest/"
+ ln -sf "$PWD" "$GOPATH/src/github.com/dchest/uniuri"
+ go install github.com/dchest/uniuri
+ cd ..
+
# Building golang.org/x/crypto (obfs4proxy > 0.0.3 || Go >= 1.4)
cd goxcrypto
find -type f -print0 | xargs -0 touch --date="$REFERENCE_DATETIME"
@@ -166,6 +190,51 @@ script: |
cp -a obfs4proxy $PTDIR
cd ../..
+ unzip webrtc-mac64-gbuilt.zip
+ export SDKROOT="$PWD/MacOSX10.7.sdk"
+
+ # Building go-webrtc
+ cd go-webrtc
+ # Replace the prebuilt webrtc library with our own one.
+ rm -rf include/ lib/
+ ln -sf ../webrtc/{include,lib} .
+ find -type f -print0 | xargs -0 touch --date="$REFERENCE_DATETIME"
+ mkdir -p "$GOPATH/src/github.com/keroserene/"
+ ln -sf "$PWD" "$GOPATH/src/github.com/keroserene/go-webrtc"
+ CFLAGS="$FLAGS -mmacosx-version-min=10.7"
+ CXXFLAGS="$FLAGS -stdlib=libc++ -mmacosx-version-min=10.7"
+ LDFLAGS="$FLAGS -stdlib=libc++ -mmacosx-version-min=10.7"
+ GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="$CFLAGS" CGO_CXXFLAGS="$CXXFLAGS" CGO_LDFLAGS="$LDFLAGS" CC="$HOME/build/clang/bin/clang" CXX="$HOME/build/clang/bin/clang++" go install github.com/keroserene/go-webrtc
+ cd ..
+
+ # Building snowflake
+ cd snowflake
+ find -type f -print0 | xargs -0 touch --date="$REFERENCE_DATETIME"
+ cd client
+ # See the faketime comment above. Without faketime, snowflake-client would
+ # contain the timestamp of the temporary client.a file created during
+ # "go build".
+ GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="$CFLAGS" CGO_CXXFLAGS="$CXXFLAGS" CGO_LDFLAGS="$LDFLAGS" CC="$HOME/build/clang/bin/clang" CXX="$HOME/build/clang/bin/clang++" faketime -f "$REFERENCE_DATETIME" go build -ldflags '-s'
+ # Hack: Overwrite variable absolute paths embedded in the binary. clang 3.8.0
+ # on Darwin embeds such paths and the issue is unsolved in upstream Go as of
+ # 2016-06-28:
+ # https://github.com/golang/go/issues/9206#issuecomment-310476743
+ # The two kinds of paths are ("000000000" stands for 9 random digits):
+ # /tmp/go-build000000000
+ # /tmp/go-link-000000000
+ # Such paths are the output of ioutil.TempDir("", "go-build") and
+ # ioutil.TempDir("", "go-link-").
+ cp -a client client.stomped
+ sed -i -E -e 's#(/tmp/go-build|/tmp/go-link-)[0-9]{9}/#\1XXXXXXXXX/#g' client.stomped
+ # Sanity check: make sure the file actually changed. If it did not, it could
+ # mean that a change in go or clang has made this step unnecessary.
+ cmp client client.stomped && (echo "No paths replaced in snowflake-client. Maybe the replacement failed or is no longer needed. Check descriptors/mac/gitian-pluggable-transports.yml"; exit 1)
+ cp -a client.stomped $PTDIR/snowflake-client
+ cd ..
+ mkdir -p $INSTDIR/Docs/snowflake
+ cp -a README.md LICENSE $INSTDIR/Docs/snowflake
+ cd ..
+
# Grabbing the result
cd $INSTDIR
~/build/dzip.sh pluggable-transports-mac64-gbuilt.zip TorBrowserBundle.app
diff --git a/gitian/mkbundle-mac.sh b/gitian/mkbundle-mac.sh
index 646aca6..7bf546e 100755
--- a/gitian/mkbundle-mac.sh
+++ b/gitian/mkbundle-mac.sh
@@ -240,7 +240,7 @@ then
echo "****** Starting Pluggable Transports Component of Mac Bundle (5/6 for Mac) ******"
echo
- ./bin/gbuild -j $NUM_PROCS -m $VM_MEMORY --commit goptlib=$GOPTLIB_TAG,meek=$MEEK_TAG,ed25519=$GOED25519_TAG,siphash=$GOSIPHASH_TAG,goxcrypto=$GO_X_CRYPTO_TAG,goxnet=$GO_X_NET_TAG,obfs4=$OBFS4_TAG $DESCRIPTOR_DIR/mac/gitian-pluggable-transports.yml
+ ./bin/gbuild -j $NUM_PROCS -m $VM_MEMORY --commit goptlib=$GOPTLIB_TAG,meek=$MEEK_TAG,ed25519=$GOED25519_TAG,siphash=$GOSIPHASH_TAG,goxcrypto=$GO_X_CRYPTO_TAG,goxnet=$GO_X_NET_TAG,obfs4=$OBFS4_TAG,go-webrtc=$GO_WEBRTC_TAG,snowflake=$SNOWFLAKE_TAG,uniuri=$UNIURI_TAG $DESCRIPTOR_DIR/mac/gitian-pluggable-transports.yml
if [ $? -ne 0 ];
then
#mv var/build.log ./firefox-fail-mac.log.`date +%Y%m%d%H%M%S`
1
0

14 Jul '17
commit ce3fbdf44272e3334d935044599a3b42cf7ff87c
Author: David Fifield <david(a)bamsoftware.com>
Date: Wed Jul 5 23:10:10 2017 -0700
Bug 22831: Enable Snowflake for mac.
---
Bundle-Data/PTConfigs/mac/torrc-defaults-appendix | 3 +++
gitian/mkbundle-mac.sh | 5 ++---
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/Bundle-Data/PTConfigs/mac/torrc-defaults-appendix b/Bundle-Data/PTConfigs/mac/torrc-defaults-appendix
index 271b36c..06dbec9 100644
--- a/Bundle-Data/PTConfigs/mac/torrc-defaults-appendix
+++ b/Bundle-Data/PTConfigs/mac/torrc-defaults-appendix
@@ -3,3 +3,6 @@ ClientTransportPlugin obfs2,obfs3,obfs4,scramblesuit exec PluggableTransports/ob
## meek configuration
ClientTransportPlugin meek exec PluggableTransports/meek-client-torbrowser -- PluggableTransports/meek-client
+
+## snowflake configuration
+ClientTransportPlugin snowflake exec PluggableTransports/snowflake-client -url https://snowflake-reg.appspot.com/ -front www.google.com -ice stun:stun.l.google.com:19302
diff --git a/gitian/mkbundle-mac.sh b/gitian/mkbundle-mac.sh
index 7bf546e..4f850c6 100755
--- a/gitian/mkbundle-mac.sh
+++ b/gitian/mkbundle-mac.sh
@@ -56,9 +56,8 @@ $WRAPPER_DIR/build-helpers/dzip.sh $GITIAN_DIR/inputs/tbb-docs.zip ./Docs/
rm -f $GITIAN_DIR/inputs/TorBrowser.app.meek-http-helper.zip
(cd PTConfigs/mac && $WRAPPER_DIR/build-helpers/dzip.sh $GITIAN_DIR/inputs/TorBrowser.app.meek-http-helper.zip TorBrowser.app.meek-http-helper)
cp PTConfigs/mac/torrc-defaults-appendix $GITIAN_DIR/inputs/torrc-defaults-appendix-mac
-# FTE is temporarily removed due to bug 18495 and snowflake is Linux-only for
-# now.
- grep -Ev 'default_bridge\.fte|default_bridge\.snowflake' PTConfigs/bridge_prefs.js > $GITIAN_DIR/inputs/bridge_prefs.js
+# FTE is temporarily removed due to bug 18495.
+ grep -Ev 'default_bridge\.fte' PTConfigs/bridge_prefs.js > $GITIAN_DIR/inputs/bridge_prefs.js
cp PTConfigs/meek-http-helper-user.js $GITIAN_DIR/inputs/
cp mac-tor.sh $GITIAN_DIR/inputs/
1
0

[stem/master] Unexpected exceptions during async tests deadlocked
by atagar@torproject.org 14 Jul '17
by atagar@torproject.org 14 Jul '17
14 Jul '17
commit 7901ff113353b9a62e9bf99682eb45933c1fcbcd
Author: Damian Johnson <atagar(a)torproject.org>
Date: Thu Jul 13 21:43:22 2017 -0700
Unexpected exceptions during async tests deadlocked
When an asynchrously ran test raised a non-assertion exception the subsiquent
recv call on the pipe would never complete...
https://trac.torproject.org/projects/tor/ticket/22902
---
stem/util/test_tools.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/stem/util/test_tools.py b/stem/util/test_tools.py
index cab96e5..3094f5c 100644
--- a/stem/util/test_tools.py
+++ b/stem/util/test_tools.py
@@ -33,8 +33,9 @@ import linecache
import multiprocessing
import os
import re
-import time
import threading
+import time
+import traceback
import unittest
import stem.prereq
@@ -167,6 +168,8 @@ class AsyncTest(object):
conn.send(AsyncResult('failure', str(exc)))
except SkipTest as exc:
conn.send(AsyncResult('skipped', str(exc)))
+ except:
+ conn.send(AsyncResult('error', traceback.format_exc()))
finally:
conn.close()
@@ -207,6 +210,8 @@ class AsyncTest(object):
if test and self._result.type == 'failure':
test.fail(self._result.msg)
+ elif test and self._result.type == 'error':
+ test.fail(self._result.msg)
elif test and self._result.type == 'skipped':
test.skipTest(self._result.msg)
1
0
commit f673f594dc4bb98369601b0023cfb9b5f785bfd3
Author: Damian Johnson <atagar(a)torproject.org>
Date: Thu Jul 13 14:37:21 2017 -0700
Add Roya
---
about/en/corepeople_alternate.wml | 29 +++++++++++++++++++----------
images/people/roya.png | Bin 0 -> 36754 bytes
2 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/about/en/corepeople_alternate.wml b/about/en/corepeople_alternate.wml
index c21323e..2936a7c 100644
--- a/about/en/corepeople_alternate.wml
+++ b/about/en/corepeople_alternate.wml
@@ -569,12 +569,21 @@ table.people tr td:first-child {
<tr>
<td>
+ <a id="roya"></a>
+ <div class="photo"><img src="$(IMGROOT)/people/roya.png" alt="roya" /></div>
+ <div class="name"><a href="#roya">Roya Ensafi</a></div>
+ <div class="description">Detects and understands how to manipulate and circumvent Tor network censorship attempts.</div>
+ </td>
+
+ <td>
<a id="sebastian"></a>
<div class="photo"><img src="$(IMGROOT)/people/no_photo.png" alt="no photo" /></div>
<div class="name"><a href="#sebastian">Sebastian Hahn</a></div>
<div class="description">Helps people around the world use and understand Tor better. Generally helps everything run smoothly. Runs one of the directory authorities.</div>
</td>
+ </tr>
+ <tr>
<td>
<a id="serene"></a>
<div class="photo"><img src="$(IMGROOT)/people/serene.png" alt="serene" /></div>
@@ -583,9 +592,7 @@ table.people tr td:first-child {
<div class="name"><a href="#serene">Serene</a></div>
<div class="description">Snowflake and Pluggable Transports developer.</div>
</td>
- </tr>
- <tr>
<td>
<a id="ssteele"></a>
<div class="photo"><img src="$(IMGROOT)/people/ssteele.png" alt="ssteele" /></div>
@@ -595,7 +602,9 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> ssteele</div>
<div class="description">Executive Director of the Tor Project, Inc.</div>
</td>
+ </tr>
+ <tr>
<td>
<a id="hiro"></a>
<div class="photo"><img src="$(IMGROOT)/people/hiro.png" alt="hiro" /></div>
@@ -605,9 +614,7 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> hiro</div>
<div class="description">Hiro builds and runs Tor Project's web applications and services.</div>
</td>
- </tr>
- <tr>
<td>
<a id="stephw"></a>
<div class="photo"><img src="$(IMGROOT)/people/stephw.png" alt="stephw" /></div>
@@ -617,7 +624,9 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> stephw</div>
<div class="description">Directs communications to consistently articulate and promote the Tor Project's mission.</div>
</td>
+ </tr>
+ <tr>
<td>
<a id="sjmurdoch"></a>
<div class="photo"><img src="$(IMGROOT)/people/sjmurdoch.png" alt="sjmurdoch" /></div>
@@ -627,9 +636,7 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> sjmurdoch</div>
<div class="description"><a href="http://www.cs.ucl.ac.uk/staff/s.murdoch/">Principal Research Fellow</a> at University College London and original creator of the <a href="https://www.torproject.org/projects/torbrowser.html.en">Tor Browser</a>.</div>
</td>
- </tr>
- <tr>
<td>
<a id="sukhe"></a>
<div class="photo"><img src="$(IMGROOT)/people/no_photo.png" alt="no photo" /></div>
@@ -638,7 +645,9 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> sukhe</div>
<div class="description"><a href="https://trac.torproject.org/projects/tor/wiki/torbirdy">TorBirdy</a> and <a href="https://trac.torproject.org/projects/tor/wiki/doc/TorMessenger">Tor Messenger</a> developer. Volunteer on the <a href="https://trac.torproject.org/projects/tor/wiki/org/teams/CommunityTeam">Community Team</a>.</div>
</td>
+ </tr>
+ <tr>
<td>
<a id="teor"></a>
<div class="photo"><img src="$(IMGROOT)/people/no_photo.png" alt="no photo" /></div>
@@ -647,9 +656,7 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> teor</div>
<div class="description">Onion services developer, and core member of Tor dev team. Researches Tor network measurement and onion service scalability.</div>
</td>
- </tr>
- <tr>
<td>
<a id="tjr"></a>
<div class="photo"><img src="$(IMGROOT)/people/tjr.png" alt="tjr" /></div>
@@ -659,7 +666,9 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> tjr</div>
<div class="description">Maintains <a href="https://consensus-health.torproject.org/">Consensus Health </a>, runs one of the bandwidth authorities, and contributes to Tor Browser.</div>
</td>
+ </tr>
+ <tr>
<td>
<a id="t0mmy"></a>
<div class="photo"><img src="$(IMGROOT)/people/t0mmy.png" alt="t0mmy" /></div>
@@ -668,9 +677,7 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> t0mmy</div>
<div class="description">Writes and edits grant proposals, one-pagers, blogposts, etc.</div>
</td>
- </tr>
- <tr>
<td>
<a id="wseltzer"></a>
<div class="photo"><img src="$(IMGROOT)/people/wseltzer.png" alt="wseltzer" /></div>
@@ -680,7 +687,9 @@ table.people tr td:first-child {
<div class="field"><b>IRC:</b> wseltzer</div>
<div class="description"><a href="https://wendy.seltzer.org/">Techie lawyer</a> and former Tor board member. She works on legal and policy support for private and secure communications.</div>
</td>
+ </tr>
+ <tr>
<td>
<a id="yawning"></a>
<div class="photo"><img src="$(IMGROOT)/people/no_photo.png" alt="no photo" /></div>
diff --git a/images/people/roya.png b/images/people/roya.png
new file mode 100644
index 0000000..e19e23a
Binary files /dev/null and b/images/people/roya.png differ
1
0

13 Jul '17
commit c086a59ea1fe63e38b6f83fa0c2c19bf495e977d
Author: David Goulet <dgoulet(a)torproject.org>
Date: Fri Jan 13 16:00:07 2017 -0500
prop224: Configure v3 service from options
This commit adds the support in the HS subsystem for loading a service from a
set of or_options_t and put them in a staging list.
To achieve this, service accessors have been created and a global hash map
containing service object indexed by master public key. However, this is not
used for now. It's ground work for registration process.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/circuitlist.h | 1 +
src/or/hs_common.h | 2 +-
src/or/hs_config.c | 318 +++++++++++++++++++++++++++++++++++++--------
src/or/hs_config.h | 2 +
src/or/hs_service.c | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++-
src/or/hs_service.h | 15 ++-
src/or/rendservice.c | 55 +++-----
src/or/rendservice.h | 2 +-
8 files changed, 648 insertions(+), 107 deletions(-)
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index d647062..2f76252 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -48,6 +48,7 @@ origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data(
origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
const uint8_t *digest, uint8_t purpose);
origin_circuit_t *circuit_get_next_service_intro_circ(origin_circuit_t *start);
+origin_circuit_t *circuit_get_next_service_hsdir_circ(origin_circuit_t *start);
origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose,
extend_info_t *info, int flags);
void circuit_mark_all_unused_circs(void);
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 8016535..d1bc5ac 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -22,7 +22,7 @@
/** Try to maintain this many intro points per service by default. */
#define NUM_INTRO_POINTS_DEFAULT 3
-/** Maximum number of intro points per service. */
+/** Maximum number of intro points per generic and version 2 service. */
#define NUM_INTRO_POINTS_MAX 10
/** Number of extra intro points we launch if our set of intro nodes is empty.
* See proposal 155, section 4. */
diff --git a/src/or/hs_config.c b/src/or/hs_config.c
index 6326e90..6bb422d 100644
--- a/src/or/hs_config.c
+++ b/src/or/hs_config.c
@@ -31,20 +31,171 @@
#include "hs_service.h"
#include "rendservice.h"
-/* Configuration handler for a version 3 service. Return 0 on success else a
- * negative value. */
+/* Using the given list of services, stage them into our global state. Every
+ * service version are handled. This function can remove entries in the given
+ * service_list.
+ *
+ * Staging a service means that we take all services in service_list and we
+ * put them in the staging list (global) which acts as a temporary list that
+ * is used by the service loading key process. In other words, staging a
+ * service puts it in a list to be considered when loading the keys and then
+ * moved to the main global list. */
+static void
+stage_services(smartlist_t *service_list)
+{
+ tor_assert(service_list);
+
+ /* This is v2 specific. Trigger service pruning which will make sure the
+ * just configured services end up in the main global list. It should only
+ * be done in non validation mode because v2 subsystem handles service
+ * object differently. */
+ rend_service_prune_list();
+
+ /* Cleanup v2 service from the list, we don't need those object anymore
+ * because we validated them all against the others and we want to stage
+ * only >= v3 service. And remember, v2 has a different object type which is
+ * shadow copied from an hs_service_t type. */
+ SMARTLIST_FOREACH_BEGIN(service_list, hs_service_t *, s) {
+ if (s->version == HS_VERSION_TWO) {
+ SMARTLIST_DEL_CURRENT(service_list, s);
+ hs_service_free(s);
+ }
+ } SMARTLIST_FOREACH_END(s);
+
+ /* This is >= v3 specific. Using the newly configured service list, stage
+ * them into our global state. Every object ownership is lost after. */
+ hs_service_stage_services(service_list);
+}
+
+/* Validate the given service against all service in the given list. If the
+ * service is ephemeral, this function ignores it. Services with the same
+ * directory path aren't allowed and will return an error. If a duplicate is
+ * found, 1 is returned else 0 if none found. */
+static int
+service_is_duplicate_in_list(const smartlist_t *service_list,
+ const hs_service_t *service)
+{
+ int ret = 0;
+
+ tor_assert(service_list);
+ tor_assert(service);
+
+ /* Ephemeral service don't have a directory configured so no need to check
+ * for a service in the list having the same path. */
+ if (service->config.is_ephemeral) {
+ goto end;
+ }
+
+ /* XXX: Validate if we have any service that has the given service dir path.
+ * This has two problems:
+ *
+ * a) It's O(n^2), but the same comment from the bottom of
+ * rend_config_services() should apply.
+ *
+ * b) We only compare directory paths as strings, so we can't
+ * detect two distinct paths that specify the same directory
+ * (which can arise from symlinks, case-insensitivity, bind
+ * mounts, etc.).
+ *
+ * It also can't detect that two separate Tor instances are trying
+ * to use the same HiddenServiceDir; for that, we would need a
+ * lock file. But this is enough to detect a simple mistake that
+ * at least one person has actually made. */
+ SMARTLIST_FOREACH_BEGIN(service_list, const hs_service_t *, s) {
+ if (!strcmp(s->config.directory_path, service->config.directory_path)) {
+ log_warn(LD_REND, "Another hidden service is already configured "
+ "for directory %s",
+ escaped(service->config.directory_path));
+ ret = 1;
+ goto end;
+ }
+ } SMARTLIST_FOREACH_END(s);
+
+ end:
+ return ret;
+}
+
+/* Validate service configuration. This is used when loading the configuration
+ * and once we've setup a service object, it's config object is passed to this
+ * function for further validation. This does not validate service key
+ * material. Return 0 if valid else -1 if invalid. */
+static int
+config_validate_service(const hs_service_config_t *config)
+{
+ tor_assert(config);
+
+ /* Amount of ports validation. */
+ if (!config->ports || smartlist_len(config->ports) == 0) {
+ log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured.",
+ escaped(config->directory_path));
+ goto invalid;
+ }
+
+ /* Valid. */
+ return 0;
+ invalid:
+ return -1;
+}
+
+/* Configuration handler for a version 3 service. The line_ must be pointing
+ * to the directive directly after a HiddenServiceDir. That way, when hitting
+ * the next HiddenServiceDir line or reaching the end of the list of lines, we
+ * know that we have to stop looking for more options. The given service
+ * object must be already allocated and passed through
+ * config_generic_service() prior to calling this function.
+ *
+ * Return 0 on success else a negative value. */
static int
-config_service_v3(const config_line_t *line,
- const or_options_t *options, int validate_only,
+config_service_v3(const config_line_t *line_,
+ const or_options_t *options,
hs_service_t *service)
{
- (void) line;
- (void) service;
- (void) validate_only;
(void) options;
- /* XXX: Configure a v3 service with specific options. */
- /* XXX: Add service to v3 list and pruning on reload. */
+ const config_line_t *line;
+ hs_service_config_t *config;
+
+ tor_assert(service);
+
+ config = &service->config;
+
+ for (line = line_; line; line = line->next) {
+ if (!strcasecmp(line->key, "HiddenServiceDir")) {
+ /* We just hit the next hidden service, stop right now. */
+ break;
+ }
+ /* Number of introduction points. */
+ if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) {
+ int ok = 0;
+ config->num_intro_points =
+ (unsigned int) tor_parse_ulong(line->value, 10,
+ NUM_INTRO_POINTS_DEFAULT,
+ HS_CONFIG_V3_MAX_INTRO_POINTS,
+ &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG, "HiddenServiceNumIntroductionPoints "
+ "should be between %d and %d, not %s",
+ NUM_INTRO_POINTS_DEFAULT, HS_CONFIG_V3_MAX_INTRO_POINTS,
+ line->value);
+ goto err;
+ }
+ log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s",
+ config->num_intro_points, escaped(config->directory_path));
+ continue;
+ }
+ }
+
+ /* We do not load the key material for the service at this stage. This is
+ * done later once tor can confirm that it is in a running state. */
+
+ /* We are about to return a fully configured service so do one last pass of
+ * validation at it. */
+ if (config_validate_service(config) < 0) {
+ goto err;
+ }
+
return 0;
+ err:
+ return -1;
}
/* Configure a service using the given options in line_ and options. This is
@@ -98,7 +249,7 @@ config_generic_service(const config_line_t *line_,
/* Version of the service. */
if (!strcasecmp(line->key, "HiddenServiceVersion")) {
service->version = (uint32_t) tor_parse_ulong(line->value,
- 10, HS_VERSION_TWO,
+ 10, HS_VERSION_MIN,
HS_VERSION_MAX,
&ok, NULL);
if (!ok) {
@@ -164,13 +315,13 @@ config_generic_service(const config_line_t *line_,
}
/* Maximum streams per circuit. */
if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
- config->max_streams_per_rdv_circuit = tor_parse_uint64(line->value,
- 10, 0, 65535,
- &ok, NULL);
+ config->max_streams_per_rdv_circuit =
+ tor_parse_uint64(line->value, 10, 0,
+ HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT, &ok, NULL);
if (!ok) {
log_warn(LD_CONFIG,
"HiddenServiceMaxStreams should be between 0 and %d, not %s",
- 65535, line->value);
+ HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT, line->value);
goto err;
}
log_info(LD_CONFIG,
@@ -197,12 +348,6 @@ config_generic_service(const config_line_t *line_,
}
}
- /* Check permission on service directory. */
- if (hs_check_service_private_dir(options->User, config->directory_path,
- config->dir_group_readable, 0) < 0) {
- goto err;
- }
-
/* Check if we are configured in non anonymous mode and single hop mode
* meaning every service become single onion. */
if (rend_service_allow_non_anonymous_connection(options) &&
@@ -220,7 +365,6 @@ config_generic_service(const config_line_t *line_,
static int
(*config_service_handlers[])(const config_line_t *line,
const or_options_t *options,
- int validate_only,
hs_service_t *service) =
{
NULL, /* v0 */
@@ -229,64 +373,124 @@ static int
config_service_v3, /* v3 */
};
+/* Configure a service using the given line and options. This function will
+ * call the corresponding version handler and validate the service against the
+ * other one. On success, add the service to the given list and return 0. On
+ * error, nothing is added to the list and a negative value is returned. */
+static int
+config_service(const config_line_t *line, const or_options_t *options,
+ smartlist_t *service_list)
+{
+ hs_service_t *service = NULL;
+
+ tor_assert(line);
+ tor_assert(options);
+ tor_assert(service_list);
+
+ /* We have a new hidden service. */
+ service = hs_service_new(options);
+ /* We'll configure that service as a generic one and then pass it to the
+ * specific handler according to the configured version number. */
+ if (config_generic_service(line, options, service) < 0) {
+ goto err;
+ }
+ tor_assert(service->version <= HS_VERSION_MAX);
+ /* Check permission on service directory that was just parsed. And this must
+ * be done regardless of the service version. Do not ask for the directory
+ * to be created, this is done when the keys are loaded because we could be
+ * in validation mode right now. */
+ if (hs_check_service_private_dir(options->User,
+ service->config.directory_path,
+ service->config.dir_group_readable,
+ 0) < 0) {
+ goto err;
+ }
+ /* The handler is in charge of specific options for a version. We start
+ * after this service directory line so once we hit another directory
+ * line, the handler knows that it has to stop. */
+ if (config_service_handlers[service->version](line->next, options,
+ service) < 0) {
+ goto err;
+ }
+ /* We'll check if this service can be kept depending on the others
+ * configured previously. */
+ if (service_is_duplicate_in_list(service_list, service)) {
+ goto err;
+ }
+ /* Passes, add it to the given list. */
+ smartlist_add(service_list, service);
+ return 0;
+
+ err:
+ hs_service_free(service);
+ return -1;
+}
+
/* From a set of <b>options</b>, setup every hidden service found. Return 0 on
* success or -1 on failure. If <b>validate_only</b> is set, parse, warn and
* return as normal, but don't actually change the configured services. */
int
hs_config_service_all(const or_options_t *options, int validate_only)
{
- int dir_option_seen = 0;
- hs_service_t *service = NULL;
+ int dir_option_seen = 0, ret = -1;
const config_line_t *line;
+ smartlist_t *new_service_list = NULL;
tor_assert(options);
+ /* Newly configured service are put in that list which is then used for
+ * validation and staging for >= v3. */
+ new_service_list = smartlist_new();
+
for (line = options->RendConfigLines; line; line = line->next) {
- if (!strcasecmp(line->key, "HiddenServiceDir")) {
- /* We have a new hidden service. */
- service = hs_service_new(options);
- /* We'll configure that service as a generic one and then pass it to the
- * specific handler according to the configured version number. */
- if (config_generic_service(line, options, service) < 0) {
+ /* Ignore all directives that aren't the start of a service. */
+ if (strcasecmp(line->key, "HiddenServiceDir")) {
+ if (!dir_option_seen) {
+ log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
+ line->key);
goto err;
}
- tor_assert(service->version <= HS_VERSION_MAX);
- /* The handler is in charge of specific options for a version. We start
- * after this service directory line so once we hit another directory
- * line, the handler knows that it has to stop. */
- if (config_service_handlers[service->version](line->next, options,
- validate_only,
- service) < 0) {
- goto err;
- }
- /* Whatever happens, on success we loose the ownership of the service
- * object so we nullify the pointer to be safe. */
- service = NULL;
- /* Flag that we've seen a directory directive and we'll use that to make
- * sure that the torrc options ordering are actually valid. */
- dir_option_seen = 1;
continue;
}
- /* The first line must be a directory option else tor is misconfigured. */
- if (!dir_option_seen) {
- log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
- line->key);
+ /* Flag that we've seen a directory directive and we'll use it to make
+ * sure that the torrc options ordering is actually valid. */
+ dir_option_seen = 1;
+
+ /* Try to configure this service now. On success, it will be added to the
+ * list and validated against the service in that same list. */
+ if (config_service(line, options, new_service_list) < 0) {
goto err;
}
}
+ /* In non validation mode, we'll stage those services we just successfully
+ * configured. Service ownership is transfered from the list to the global
+ * state. If any service is invalid, it will be removed from the list and
+ * freed. All versions are handled in that function. */
if (!validate_only) {
- /* Trigger service pruning which will make sure the just configured
- * services end up in the main global list. This is v2 specific. */
- rend_service_prune_list();
- /* XXX: Need the v3 one. */
+ stage_services(new_service_list);
+ } else {
+ /* We've just validated that we were able to build a clean working list of
+ * services. We don't need those objects anymore. */
+ SMARTLIST_FOREACH(new_service_list, hs_service_t *, s,
+ hs_service_free(s));
+ /* For the v2 subsystem, the configuration handler adds the service object
+ * to the staging list and it is transferred in the main list through the
+ * prunning process. In validation mode, we thus have to purge the staging
+ * list so it's not kept in memory as valid service. */
+ rend_service_free_staging_list();
}
- /* Success. */
- return 0;
+ /* Success. Note that the service list has no ownership of its content. */
+ ret = 0;
+ goto end;
+
err:
- hs_service_free(service);
- /* Tor main should call the free all function. */
- return -1;
+ SMARTLIST_FOREACH(new_service_list, hs_service_t *, s, hs_service_free(s));
+
+ end:
+ smartlist_free(new_service_list);
+ /* Tor main should call the free all function on error. */
+ return ret;
}
diff --git a/src/or/hs_config.h b/src/or/hs_config.h
index f420791..2f8cbdc 100644
--- a/src/or/hs_config.h
+++ b/src/or/hs_config.h
@@ -11,6 +11,8 @@
#include "or.h"
+/* Max value for HiddenServiceMaxStreams */
+#define HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT 65535
/* Maximum number of intro points per version 3 services. */
#define HS_CONFIG_V3_MAX_INTRO_POINTS 20
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 16ffc48..b267e1d 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -7,18 +7,127 @@
**/
#include "or.h"
+#include "circuitlist.h"
+#include "config.h"
#include "relay.h"
#include "rendservice.h"
-#include "circuitlist.h"
-#include "circpathbias.h"
+#include "router.h"
+#include "hs_common.h"
+#include "hs_config.h"
#include "hs_intropoint.h"
#include "hs_service.h"
-#include "hs_common.h"
#include "hs/cell_establish_intro.h"
#include "hs/cell_common.h"
+/* Staging list of service object. When configuring service, we add them to
+ * this list considered a staging area and they will get added to our global
+ * map once the keys have been loaded. These two steps are seperated because
+ * loading keys requires that we are an actual running tor process. */
+static smartlist_t *hs_service_staging_list;
+
+/* Helper: Function to compare two objects in the service map. Return 1 if the
+ * two service have the same master public identity key. */
+static inline int
+hs_service_ht_eq(const hs_service_t *first, const hs_service_t *second)
+{
+ tor_assert(first);
+ tor_assert(second);
+ /* Simple key compare. */
+ return ed25519_pubkey_eq(&first->keys.identity_pk,
+ &second->keys.identity_pk);
+}
+
+/* Helper: Function for the service hash table code below. The key used is the
+ * master public identity key which is ultimately the onion address. */
+static inline unsigned int
+hs_service_ht_hash(const hs_service_t *service)
+{
+ tor_assert(service);
+ return (unsigned int) siphash24g(service->keys.identity_pk.pubkey,
+ sizeof(service->keys.identity_pk.pubkey));
+}
+
+/* For the service global hash map, we define a specific type for it which
+ * will make it safe to use and specific to some controlled parameters such as
+ * the hashing function and how to compare services. */
+typedef HT_HEAD(hs_service_ht, hs_service_t) hs_service_ht;
+
+/* This is _the_ global hash map of hidden services which indexed the service
+ * contained in it by master public identity key which is roughly the onion
+ * address of the service. */
+static struct hs_service_ht *hs_service_map;
+
+/* Register the service hash table. */
+HT_PROTOTYPE(hs_service_ht, /* Name of hashtable. */
+ hs_service_t, /* Object contained in the map. */
+ hs_service_node, /* The name of the HT_ENTRY member. */
+ hs_service_ht_hash, /* Hashing function. */
+ hs_service_ht_eq) /* Compare function for objects. */
+
+HT_GENERATE2(hs_service_ht, hs_service_t, hs_service_node,
+ hs_service_ht_hash, hs_service_ht_eq,
+ 0.6, tor_reallocarray, tor_free_)
+
+/* Query the given service map with a public key and return a service object
+ * if found else NULL. It is also possible to set a directory path in the
+ * search query. If pk is NULL, then it will be set to zero indicating the
+ * hash table to compare the directory path instead. */
+static hs_service_t *
+find_service(hs_service_ht *map, const ed25519_public_key_t *pk)
+{
+ hs_service_t dummy_service = {0};
+ tor_assert(map);
+ tor_assert(pk);
+ ed25519_pubkey_copy(&dummy_service.keys.identity_pk, pk);
+ return HT_FIND(hs_service_ht, map, &dummy_service);
+}
+
+/* Register the given service in the given map. If the service already exists
+ * in the map, -1 is returned. On success, 0 is returned and the service
+ * ownership has been transfered to the global map. */
+static int
+register_service(hs_service_ht *map, hs_service_t *service)
+{
+ tor_assert(map);
+ tor_assert(service);
+ tor_assert(!ed25519_public_key_is_zero(&service->keys.identity_pk));
+
+ if (find_service(map, &service->keys.identity_pk)) {
+ /* Existing service with the same key. Do not register it. */
+ return -1;
+ }
+ /* Taking ownership of the object at this point. */
+ HT_INSERT(hs_service_ht, map, service);
+ return 0;
+}
+
+/* Remove a given service from the given map. If service is NULL or the
+ * service key is unset, return gracefully. */
+static void
+remove_service(hs_service_ht *map, hs_service_t *service)
+{
+ hs_service_t *elm;
+
+ tor_assert(map);
+
+ /* Ignore if no service or key is zero. */
+ if (BUG(service == NULL) ||
+ BUG(ed25519_public_key_is_zero(&service->keys.identity_pk))) {
+ return;
+ }
+
+ elm = HT_REMOVE(hs_service_ht, map, service);
+ if (elm) {
+ tor_assert(elm == service);
+ } else {
+ log_warn(LD_BUG, "Could not find service in the global map "
+ "while removing service %s",
+ escaped(service->config.directory_path));
+ }
+}
+
/* Set the default values for a service configuration object <b>c</b>. */
static void
set_service_default_config(hs_service_config_t *c,
@@ -37,6 +146,239 @@ set_service_default_config(hs_service_config_t *c,
c->is_ephemeral = 0;
}
+/* Helper: Function that needs to return 1 for the HT for each loop which
+ * frees every service in an hash map. */
+static int
+ht_free_service_(struct hs_service_t *service, void *data)
+{
+ (void) data;
+ hs_service_free(service);
+ /* This function MUST return 1 so the given object is then removed from the
+ * service map leading to this free of the object being safe. */
+ return 1;
+}
+
+/* Free every service that can be found in the global map. Once done, clear
+ * and free the global map. */
+static void
+service_free_all(void)
+{
+ if (hs_service_map == NULL) {
+ return;
+ }
+ /* The free helper function returns 1 so this is safe. */
+ hs_service_ht_HT_FOREACH_FN(hs_service_map, ht_free_service_, NULL);
+ HT_CLEAR(hs_service_ht, hs_service_map);
+ tor_free(hs_service_map);
+ hs_service_map = NULL;
+ /* Cleanup staging list. */
+ SMARTLIST_FOREACH(hs_service_staging_list, hs_service_t *, s,
+ hs_service_free(s));
+ smartlist_free(hs_service_staging_list);
+ hs_service_staging_list = NULL;
+}
+
+/* Close all rendezvous circuits for the given service. */
+static void
+close_service_rp_circuits(hs_service_t *service)
+{
+ tor_assert(service);
+ /* XXX: To implement. */
+ return;
+}
+
+/* Close the circuit(s) for the given map of introduction points. */
+static void
+close_intro_circuits(hs_service_intropoints_t *intro_points)
+{
+ tor_assert(intro_points);
+
+ DIGEST256MAP_FOREACH(intro_points->map, key,
+ const hs_service_intro_point_t *, ip) {
+ origin_circuit_t *ocirc =
+ hs_circuitmap_get_intro_circ_v3_service_side(
+ &ip->auth_key_kp.pubkey);
+ if (ocirc) {
+ hs_circuitmap_remove_circuit(TO_CIRCUIT(ocirc));
+ /* Reason is FINISHED because service has been removed and thus the
+ * circuit is considered old/uneeded. */
+ circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
+ }
+ } DIGEST256MAP_FOREACH_END;
+}
+
+/* Close all introduction circuits for the given service. */
+static void
+close_service_intro_circuits(hs_service_t *service)
+{
+ tor_assert(service);
+
+ if (service->desc_current) {
+ close_intro_circuits(&service->desc_current->intro_points);
+ }
+ if (service->desc_next) {
+ close_intro_circuits(&service->desc_next->intro_points);
+ }
+}
+
+/* Close any circuits related to the given service. */
+static void
+close_service_circuits(hs_service_t *service)
+{
+ tor_assert(service);
+
+ /* Only support for version >= 3. */
+ if (BUG(service->version < HS_VERSION_THREE)) {
+ return;
+ }
+ /* Close intro points. */
+ close_service_intro_circuits(service);
+ /* Close rendezvous points. */
+ close_service_rp_circuits(service);
+}
+
+/* Move introduction points from the src descriptor to the dst descriptor. The
+ * destination service intropoints are wiped out if any before moving. */
+static void
+move_descriptor_intro_points(hs_service_descriptor_t *src,
+ hs_service_descriptor_t *dst)
+{
+ tor_assert(src);
+ tor_assert(dst);
+
+ /* XXX: Free dst introduction points. */
+ dst->intro_points.map = src->intro_points.map;
+ /* Nullify the source. */
+ src->intro_points.map = NULL;
+}
+
+/* Move introduction points from the src service to the dst service. The
+ * destination service intropoints are wiped out if any before moving. */
+static void
+move_intro_points(hs_service_t *src, hs_service_t *dst)
+{
+ tor_assert(src);
+ tor_assert(dst);
+
+ /* Cleanup destination. */
+ if (src->desc_current && dst->desc_current) {
+ move_descriptor_intro_points(src->desc_current, dst->desc_current);
+ }
+ if (src->desc_next && dst->desc_next) {
+ move_descriptor_intro_points(src->desc_next, dst->desc_next);
+ }
+}
+
+/* Move every ephemeral services from the src service map to the dst service
+ * map. It is possible that a service can't be register to the dst map which
+ * won't stop the process of moving them all but will trigger a log warn. */
+static void
+move_ephemeral_services(hs_service_ht *src, hs_service_ht *dst)
+{
+ hs_service_t **iter, **next;
+
+ tor_assert(src);
+ tor_assert(dst);
+
+ /* Iterate over the map to find ephemeral service and move them to the other
+ * map. We loop using this method to have a safe removal process. */
+ for (iter = HT_START(hs_service_ht, src); iter != NULL; iter = next) {
+ hs_service_t *s = *iter;
+ if (!s->config.is_ephemeral) {
+ /* Yeah, we are in a very manual loop :). */
+ next = HT_NEXT(hs_service_ht, src, iter);
+ continue;
+ }
+ /* Remove service from map and then register to it to the other map.
+ * Reminder that "*iter" and "s" are the same thing. */
+ next = HT_NEXT_RMV(hs_service_ht, src, iter);
+ if (register_service(dst, s) < 0) {
+ log_warn(LD_BUG, "Ephemeral service key is already being used. "
+ "Skipping.");
+ }
+ }
+}
+
+/* Register services that are in the list. Once this function returns, the
+ * global service map will be set with the right content and all non surviving
+ * services will be cleaned up. */
+void
+hs_service_register_services(smartlist_t *new_service_list)
+{
+ struct hs_service_ht *new_service_map;
+ hs_service_t *s, **iter;
+
+ tor_assert(new_service_list);
+
+ /* We'll save us some allocation and computing time. */
+ if (smartlist_len(new_service_list) == 0) {
+ return;
+ }
+
+ /* Allocate a new map that will replace the current one. */
+ new_service_map = tor_malloc_zero(sizeof(*new_service_map));
+ HT_INIT(hs_service_ht, new_service_map);
+
+ /* First step is to transfer all ephemeral services from the current global
+ * map to the new one we are constructing. We do not prune ephemeral
+ * services as the only way to kill them is by deleting it from the control
+ * port or stopping the tor daemon. */
+ move_ephemeral_services(hs_service_map, new_service_map);
+
+ SMARTLIST_FOREACH_BEGIN(new_service_list, hs_service_t *, snew) {
+ /* Check if that service is already in our global map and if so, we'll
+ * transfer the intro points to it. */
+ s = find_service(hs_service_map, &snew->keys.identity_pk);
+ if (s) {
+ /* Pass ownership of intro points from s (the current service) to snew
+ * (the newly configured one). */
+ move_intro_points(s, snew);
+ /* Remove the service from the global map because after this, we need to
+ * go over the remaining service in that map that aren't surviving the
+ * reload to close their circuits. */
+ remove_service(hs_service_map, s);
+ }
+ /* Great, this service is now ready to be added to our new map. */
+ if (BUG(register_service(new_service_map, snew) < 0)) {
+ /* This should never happen because prior to registration, we validate
+ * every service against the entire set. Not being able to register a
+ * service means we failed to validate correctly. In that case, don't
+ * break tor and ignore the service but tell user. */
+ log_warn(LD_BUG, "Unable to register service with directory %s",
+ snew->config.directory_path);
+ SMARTLIST_DEL_CURRENT(new_service_list, snew);
+ hs_service_free(snew);
+ }
+ } SMARTLIST_FOREACH_END(snew);
+
+ /* Close any circuits associated with the non surviving services. Every
+ * service in the current global map are roaming. */
+ HT_FOREACH(iter, hs_service_ht, hs_service_map) {
+ close_service_circuits(*iter);
+ }
+
+ /* Time to make the switch. We'll wipe the current list and switch. */
+ service_free_all();
+ hs_service_map = new_service_map;
+}
+
+/* Put all service object in the given service list. After this, the caller
+ * looses ownership of every elements in the list and responsible to free the
+ * list pointer. */
+void
+hs_service_stage_services(const smartlist_t *service_list)
+{
+ tor_assert(service_list);
+ /* This list is freed at registration time but this function can be called
+ * multiple time. */
+ if (hs_service_staging_list == NULL) {
+ hs_service_staging_list = smartlist_new();
+ }
+ /* Add all service object to our staging list. Caller is responsible for
+ * freeing the service_list. */
+ smartlist_add_all(hs_service_staging_list, service_list);
+}
+
/* Allocate and initilize a service object. The service configuration will
* contain the default values. Return the newly allocated object pointer. This
* function can't fail. */
@@ -101,14 +443,22 @@ hs_service_free(hs_service_t *service)
void
hs_service_init(void)
{
- return;
+ /* Should never be called twice. */
+ tor_assert(!hs_service_map);
+ tor_assert(!hs_service_staging_list);
+
+ hs_service_map = tor_malloc_zero(sizeof(struct hs_service_ht));
+ HT_INIT(hs_service_ht, hs_service_map);
+
+ hs_service_staging_list = smartlist_new();
}
-/* Release all global the storage of hidden service subsystem. */
+/* Release all global storage of the hidden service subsystem. */
void
hs_service_free_all(void)
{
rend_service_free_all();
+ service_free_all();
}
/* XXX We don't currently use these functions, apart from generating unittest
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index ec47cb7..8d92e75 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -11,9 +11,10 @@
#include "crypto_curve25519.h"
#include "crypto_ed25519.h"
+#include "replaycache.h"
+
#include "hs_descriptor.h"
#include "hs_intropoint.h"
-#include "replaycache.h"
/* Trunnel */
#include "hs/cell_establish_intro.h"
@@ -171,6 +172,10 @@ typedef struct hs_service_t {
/* Protocol version of the service. Specified by HiddenServiceVersion. */
uint32_t version;
+ /* Hashtable node: use to look up the service by its master public identity
+ * key in the service global map. */
+ HT_ENTRY(hs_service_t) hs_service_node;
+
/* Service state which contains various flags and counters. */
hs_service_state_t state;
@@ -192,12 +197,16 @@ typedef struct hs_service_t {
/* API */
-int hs_service_config_all(const or_options_t *options, int validate_only);
+/* Global initializer and cleanup function. */
void hs_service_init(void);
void hs_service_free_all(void);
-void hs_service_free(hs_service_t *service);
+/* Service new/free functions. */
hs_service_t *hs_service_new(const or_options_t *options);
+void hs_service_free(hs_service_t *service);
+
+void hs_service_register_services(smartlist_t *new_service_list);
+void hs_service_stage_services(const smartlist_t *service_list);
/* These functions are only used by unit tests and we need to expose them else
* hs_service.o ends up with no symbols in libor.a which makes clang throw a
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 695668b..e86119f 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -231,8 +231,20 @@ rend_service_free(rend_service_t *service)
tor_free(service);
}
-/** Release all the storage held in rend_service_list.
- */
+/* Release all the storage held in rend_service_staging_list. */
+void
+rend_service_free_staging_list(void)
+{
+ if (rend_service_staging_list) {
+ SMARTLIST_FOREACH(rend_service_staging_list, rend_service_t*, ptr,
+ rend_service_free(ptr));
+ smartlist_free(rend_service_staging_list);
+ rend_service_staging_list = NULL;
+ }
+}
+
+/** Release all the storage held in both rend_service_list and
+ * rend_service_staging_list. */
void
rend_service_free_all(void)
{
@@ -242,12 +254,7 @@ rend_service_free_all(void)
smartlist_free(rend_service_list);
rend_service_list = NULL;
}
- if (rend_service_staging_list) {
- SMARTLIST_FOREACH(rend_service_staging_list, rend_service_t*, ptr,
- rend_service_free(ptr));
- smartlist_free(rend_service_staging_list);
- rend_service_staging_list = NULL;
- }
+ rend_service_free_staging_list();
}
/* Validate a <b>service</b>. Use the <b>service_list</b> to make sure there
@@ -257,8 +264,6 @@ static int
rend_validate_service(const smartlist_t *service_list,
const rend_service_t *service)
{
- int dupe = 0;
-
tor_assert(service_list);
tor_assert(service);
@@ -291,34 +296,6 @@ rend_validate_service(const smartlist_t *service_list,
goto invalid;
}
- /* XXX This duplicate check has two problems:
- *
- * a) It's O(n^2), but the same comment from the bottom of
- * rend_config_services() should apply.
- *
- * b) We only compare directory paths as strings, so we can't
- * detect two distinct paths that specify the same directory
- * (which can arise from symlinks, case-insensitivity, bind
- * mounts, etc.).
- *
- * It also can't detect that two separate Tor instances are trying
- * to use the same HiddenServiceDir; for that, we would need a
- * lock file. But this is enough to detect a simple mistake that
- * at least one person has actually made.
- */
- if (!rend_service_is_ephemeral(service)) {
- /* Skip dupe for ephemeral services. */
- SMARTLIST_FOREACH(service_list, rend_service_t *, ptr,
- dupe = dupe ||
- !strcmp(ptr->directory, service->directory));
- if (dupe) {
- log_warn(LD_REND, "Another hidden service is already configured for "
- "directory %s.",
- rend_service_escaped_dir(service));
- goto invalid;
- }
- }
-
/* Valid. */
return 0;
invalid:
@@ -662,10 +639,8 @@ service_shadow_copy(rend_service_t *service, hs_service_t *hs_service)
int
rend_config_service(const config_line_t *line_,
const or_options_t *options,
- int validate_only,
hs_service_t *hs_service)
{
- (void) validate_only;
const config_line_t *line;
rend_service_t *service = NULL;
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 90f854e..361a119 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -143,9 +143,9 @@ STATIC void rend_service_prune_list_impl_(void);
int num_rend_services(void);
int rend_config_service(const config_line_t *line_,
const or_options_t *options,
- int validate_only,
hs_service_t *hs_service);
void rend_service_prune_list(void);
+void rend_service_free_staging_list(void);
int rend_service_load_all_keys(const smartlist_t *service_list);
void rend_services_add_filenames_to_lists(smartlist_t *open_lst,
smartlist_t *stat_lst);
1
0

13 Jul '17
commit 02e2edeb33224461d1fbb879722c0948171b9688
Author: David Goulet <dgoulet(a)torproject.org>
Date: Mon Jan 16 13:19:44 2017 -0500
prop224: Add hs_config.{c|h} with a refactoring
Add the hs_config.{c|h} files contains everything that the HS subsystem needs
to load and configure services. Ultimately, it should also contain client
functions such as client authorization.
This comes with a big refactoring of rend_config_services() which has now
changed to only configure a single service and it is stripped down of the
common directives which are now part of the generic handler.
This is ground work for prop224 of course but only touches version 2 services
and add XXX note for version 3.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/config.c | 5 +-
src/or/hs_common.h | 3 +
src/or/hs_config.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/or/hs_config.h | 19 ++++
src/or/hs_service.c | 85 +++++++++++++++
src/or/hs_service.h | 6 ++
src/or/include.am | 24 +++--
src/or/main.c | 2 +-
src/or/rendservice.c | 293 +++++++++++++++++----------------------------------
src/or/rendservice.h | 10 +-
src/test/test_hs.c | 22 ++--
11 files changed, 533 insertions(+), 228 deletions(-)
diff --git a/src/or/config.c b/src/or/config.c
index 5b5bb90..062ab27 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -91,6 +91,7 @@
#include "relay.h"
#include "rendclient.h"
#include "rendservice.h"
+#include "hs_config.h"
#include "rephist.h"
#include "router.h"
#include "sandbox.h"
@@ -1681,7 +1682,7 @@ options_act(const or_options_t *old_options)
sweep_bridge_list();
}
- if (running_tor && rend_config_services(options, 0)<0) {
+ if (running_tor && hs_config_service_all(options, 0)<0) {
log_warn(LD_BUG,
"Previously validated hidden services line could not be added!");
return -1;
@@ -4009,7 +4010,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
COMPLAIN("V3AuthVotingInterval does not divide evenly into 24 hours.");
}
- if (rend_config_services(options, 1) < 0)
+ if (hs_config_service_all(options, 1) < 0)
REJECT("Failed to configure rendezvous options. See logs for details.");
/* Parse client-side authorization for hidden services. */
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 872fed7..abc44c0 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -16,6 +16,9 @@
#define HS_VERSION_TWO 2
/* Version 3 of the protocol (prop224). */
#define HS_VERSION_THREE 3
+/* Earliest and latest version we support. */
+#define HS_VERSION_MIN HS_VERSION_TWO
+#define HS_VERSION_MAX HS_VERSION_THREE
/** Try to maintain this many intro points per service by default. */
#define NUM_INTRO_POINTS_DEFAULT 3
diff --git a/src/or/hs_config.c b/src/or/hs_config.c
new file mode 100644
index 0000000..6326e90
--- /dev/null
+++ b/src/or/hs_config.c
@@ -0,0 +1,292 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_config.c
+ * \brief Implement hidden service configuration subsystem.
+ *
+ * \details
+ *
+ * This file has basically one main entry point: hs_config_service_all(). It
+ * takes the torrc options and configure hidden service from it. In validate
+ * mode, nothing is added to the global service list or keys are not generated
+ * nor loaded.
+ *
+ * A service is configured in two steps. It is first created using the tor
+ * options and then put in a staging list. It will stay there until
+ * hs_service_load_all_keys() is called. That function is responsible to
+ * load/generate the keys for the service in the staging list and if
+ * successful, transfert the service to the main global service list where
+ * at that point it is ready to be used.
+ *
+ * Configuration handlers are per-version (see config_service_handlers[]) and
+ * there is a main generic one for every option that is common to all version
+ * (config_generic_service).
+ **/
+
+#define HS_CONFIG_PRIVATE
+
+#include "hs_common.h"
+#include "hs_config.h"
+#include "hs_service.h"
+#include "rendservice.h"
+
+/* Configuration handler for a version 3 service. Return 0 on success else a
+ * negative value. */
+static int
+config_service_v3(const config_line_t *line,
+ const or_options_t *options, int validate_only,
+ hs_service_t *service)
+{
+ (void) line;
+ (void) service;
+ (void) validate_only;
+ (void) options;
+ /* XXX: Configure a v3 service with specific options. */
+ /* XXX: Add service to v3 list and pruning on reload. */
+ return 0;
+}
+
+/* Configure a service using the given options in line_ and options. This is
+ * called for any service regardless of its version which means that all
+ * directives in this function are generic to any service version. This
+ * function will also check the validity of the service directory path.
+ *
+ * The line_ must be pointing to the directive directly after a
+ * HiddenServiceDir. That way, when hitting the next HiddenServiceDir line or
+ * reaching the end of the list of lines, we know that we have to stop looking
+ * for more options.
+ *
+ * Return 0 on success else -1. */
+static int
+config_generic_service(const config_line_t *line_,
+ const or_options_t *options,
+ hs_service_t *service)
+{
+ int ok, dir_seen = 0;
+ const config_line_t *line;
+ hs_service_config_t *config;
+
+ tor_assert(line_);
+ tor_assert(options);
+ tor_assert(service);
+
+ /* Makes thing easier. */
+ config = &service->config;
+ memset(config, 0, sizeof(*config));
+
+ /* The first line starts with HiddenServiceDir so we consider what's next is
+ * the configuration of the service. */
+ for (line = line_; line ; line = line->next) {
+ /* This indicate that we have a new service to configure. */
+ if (!strcasecmp(line->key, "HiddenServiceDir")) {
+ /* This function only configures one service at a time so if we've
+ * already seen one, stop right now. */
+ if (dir_seen) {
+ break;
+ }
+ /* Ok, we've seen one and we are about to configure it. */
+ dir_seen = 1;
+ config->directory_path = tor_strdup(line->value);
+ log_info(LD_CONFIG, "HiddenServiceDir=%s. Configuring...",
+ escaped(config->directory_path));
+ continue;
+ }
+ if (BUG(!dir_seen)) {
+ goto err;
+ }
+ /* Version of the service. */
+ if (!strcasecmp(line->key, "HiddenServiceVersion")) {
+ service->version = (uint32_t) tor_parse_ulong(line->value,
+ 10, HS_VERSION_TWO,
+ HS_VERSION_MAX,
+ &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceVersion be between %u and %u, not %s",
+ HS_VERSION_TWO, HS_VERSION_MAX, line->value);
+ goto err;
+ }
+ log_info(LD_CONFIG, "HiddenServiceVersion=%" PRIu32 " for %s",
+ service->version, escaped(config->directory_path));
+ continue;
+ }
+ /* Virtual port. */
+ if (!strcasecmp(line->key, "HiddenServicePort")) {
+ char *err_msg = NULL;
+ /* XXX: Can we rename this? */
+ rend_service_port_config_t *portcfg =
+ rend_service_parse_port_config(line->value, " ", &err_msg);
+ if (!portcfg) {
+ if (err_msg) {
+ log_warn(LD_CONFIG, "%s", err_msg);
+ }
+ tor_free(err_msg);
+ goto err;
+ }
+ tor_assert(!err_msg);
+ smartlist_add(config->ports, portcfg);
+ log_info(LD_CONFIG, "HiddenServicePort=%s for %s",
+ line->value, escaped(config->directory_path));
+ continue;
+ }
+ /* Do we allow unknown ports. */
+ if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
+ config->allow_unknown_ports = (unsigned int) tor_parse_long(line->value,
+ 10, 0, 1,
+ &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s",
+ line->value);
+ goto err;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceAllowUnknownPorts=%u for %s",
+ config->allow_unknown_ports, escaped(config->directory_path));
+ continue;
+ }
+ /* Directory group readable. */
+ if (!strcasecmp(line->key, "HiddenServiceDirGroupReadable")) {
+ config->dir_group_readable = (unsigned int) tor_parse_long(line->value,
+ 10, 0, 1,
+ &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceDirGroupReadable should be 0 or 1, not %s",
+ line->value);
+ goto err;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceDirGroupReadable=%u for %s",
+ config->dir_group_readable, escaped(config->directory_path));
+ continue;
+ }
+ /* Maximum streams per circuit. */
+ if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
+ config->max_streams_per_rdv_circuit = tor_parse_uint64(line->value,
+ 10, 0, 65535,
+ &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceMaxStreams should be between 0 and %d, not %s",
+ 65535, line->value);
+ goto err;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceMaxStreams=%" PRIu64 " for %s",
+ config->max_streams_per_rdv_circuit,
+ escaped(config->directory_path));
+ continue;
+ }
+ /* Maximum amount of streams before we close the circuit. */
+ if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
+ config->max_streams_close_circuit =
+ (unsigned int) tor_parse_long(line->value, 10, 0, 1, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, "
+ "not %s", line->value);
+ goto err;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceMaxStreamsCloseCircuit=%u for %s",
+ config->max_streams_close_circuit,
+ escaped(config->directory_path));
+ continue;
+ }
+ }
+
+ /* Check permission on service directory. */
+ if (hs_check_service_private_dir(options->User, config->directory_path,
+ config->dir_group_readable, 0) < 0) {
+ goto err;
+ }
+
+ /* Check if we are configured in non anonymous mode and single hop mode
+ * meaning every service become single onion. */
+ if (rend_service_allow_non_anonymous_connection(options) &&
+ rend_service_non_anonymous_mode_enabled(options)) {
+ config->is_single_onion = 1;
+ }
+
+ /* Success */
+ return 0;
+ err:
+ return -1;
+}
+
+/* Configuration handler indexed by version number. */
+static int
+ (*config_service_handlers[])(const config_line_t *line,
+ const or_options_t *options,
+ int validate_only,
+ hs_service_t *service) =
+{
+ NULL, /* v0 */
+ NULL, /* v1 */
+ rend_config_service, /* v2 */
+ config_service_v3, /* v3 */
+};
+
+/* From a set of <b>options</b>, setup every hidden service found. Return 0 on
+ * success or -1 on failure. If <b>validate_only</b> is set, parse, warn and
+ * return as normal, but don't actually change the configured services. */
+int
+hs_config_service_all(const or_options_t *options, int validate_only)
+{
+ int dir_option_seen = 0;
+ hs_service_t *service = NULL;
+ const config_line_t *line;
+
+ tor_assert(options);
+
+ for (line = options->RendConfigLines; line; line = line->next) {
+ if (!strcasecmp(line->key, "HiddenServiceDir")) {
+ /* We have a new hidden service. */
+ service = hs_service_new(options);
+ /* We'll configure that service as a generic one and then pass it to the
+ * specific handler according to the configured version number. */
+ if (config_generic_service(line, options, service) < 0) {
+ goto err;
+ }
+ tor_assert(service->version <= HS_VERSION_MAX);
+ /* The handler is in charge of specific options for a version. We start
+ * after this service directory line so once we hit another directory
+ * line, the handler knows that it has to stop. */
+ if (config_service_handlers[service->version](line->next, options,
+ validate_only,
+ service) < 0) {
+ goto err;
+ }
+ /* Whatever happens, on success we loose the ownership of the service
+ * object so we nullify the pointer to be safe. */
+ service = NULL;
+ /* Flag that we've seen a directory directive and we'll use that to make
+ * sure that the torrc options ordering are actually valid. */
+ dir_option_seen = 1;
+ continue;
+ }
+ /* The first line must be a directory option else tor is misconfigured. */
+ if (!dir_option_seen) {
+ log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
+ line->key);
+ goto err;
+ }
+ }
+
+ if (!validate_only) {
+ /* Trigger service pruning which will make sure the just configured
+ * services end up in the main global list. This is v2 specific. */
+ rend_service_prune_list();
+ /* XXX: Need the v3 one. */
+ }
+
+ /* Success. */
+ return 0;
+ err:
+ hs_service_free(service);
+ /* Tor main should call the free all function. */
+ return -1;
+}
+
diff --git a/src/or/hs_config.h b/src/or/hs_config.h
new file mode 100644
index 0000000..08072d1
--- /dev/null
+++ b/src/or/hs_config.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_config.h
+ * \brief Header file containing configuration ABI/API for the HS subsytem.
+ **/
+
+#ifndef TOR_HS_CONFIG_H
+#define TOR_HS_CONFIG_H
+
+#include "or.h"
+
+/* API */
+
+int hs_config_service_all(const or_options_t *options, int validate_only);
+
+#endif /* TOR_HS_CONFIG_H */
+
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 205ef11..c62aa8b 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -19,6 +19,91 @@
#include "hs/cell_establish_intro.h"
#include "hs/cell_common.h"
+/* Set the default values for a service configuration object <b>c</b>. */
+static void
+set_service_default_config(hs_service_config_t *c,
+ const or_options_t *options)
+{
+ tor_assert(c);
+ c->ports = smartlist_new();
+ c->directory_path = NULL;
+ c->descriptor_post_period = options->RendPostPeriod;
+ c->max_streams_per_rdv_circuit = 0;
+ c->max_streams_close_circuit = 0;
+ c->num_intro_points = NUM_INTRO_POINTS_DEFAULT;
+ c->allow_unknown_ports = 0;
+ c->is_single_onion = 0;
+ c->dir_group_readable = 0;
+ c->is_ephemeral = 0;
+}
+
+/* Allocate and initilize a service object. The service configuration will
+ * contain the default values. Return the newly allocated object pointer. This
+ * function can't fail. */
+hs_service_t *
+hs_service_new(const or_options_t *options)
+{
+ hs_service_t *service = tor_malloc_zero(sizeof(hs_service_t));
+ /* Set default configuration value. */
+ set_service_default_config(&service->config, options);
+ /* Set the default service version. */
+ service->version = HS_SERVICE_DEFAULT_VERSION;
+ return service;
+}
+
+/* Free the given <b>service</b> object and all its content. This function
+ * also takes care of wiping service keys from memory. It is safe to pass a
+ * NULL pointer. */
+void
+hs_service_free(hs_service_t *service)
+{
+ if (service == NULL) {
+ return;
+ }
+
+ /* Free descriptors. */
+ if (service->desc_current) {
+ hs_descriptor_free(service->desc_current->desc);
+ /* Wipe keys. */
+ memwipe(&service->desc_current->signing_kp, 0,
+ sizeof(service->desc_current->signing_kp));
+ memwipe(&service->desc_current->blinded_kp, 0,
+ sizeof(service->desc_current->blinded_kp));
+ /* XXX: Free intro points. */
+ tor_free(service->desc_current);
+ }
+ if (service->desc_next) {
+ hs_descriptor_free(service->desc_next->desc);
+ /* Wipe keys. */
+ memwipe(&service->desc_next->signing_kp, 0,
+ sizeof(service->desc_next->signing_kp));
+ memwipe(&service->desc_next->blinded_kp, 0,
+ sizeof(service->desc_next->blinded_kp));
+ /* XXX: Free intro points. */
+ tor_free(service->desc_next);
+ }
+
+ /* Free service configuration. */
+ tor_free(service->config.directory_path);
+ if (service->config.ports) {
+ SMARTLIST_FOREACH(service->config.ports, rend_service_port_config_t *, p,
+ rend_service_port_config_free(p););
+ smartlist_free(service->config.ports);
+ }
+
+ /* Wipe service keys. */
+ memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk));
+
+ tor_free(service);
+}
+
+/* Release all global the storage of hidden service subsystem. */
+void
+hs_service_free_all(void)
+{
+ rend_service_free_all();
+}
+
/* XXX We don't currently use these functions, apart from generating unittest
data. When we start implementing the service-side support for prop224 we
should revisit these functions and use them. */
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index fa5dd54..d29a478 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -192,6 +192,12 @@ typedef struct hs_service_t {
/* API */
+int hs_service_config_all(const or_options_t *options, int validate_only);
+void hs_service_free_all(void);
+
+void hs_service_free(hs_service_t *service);
+hs_service_t *hs_service_new(const or_options_t *options);
+
/* These functions are only used by unit tests and we need to expose them else
* hs_service.o ends up with no symbols in libor.a which makes clang throw a
* warning at compile time. See #21825. */
diff --git a/src/or/include.am b/src/or/include.am
index 2f9f1a9..15b86ef 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -50,19 +50,20 @@ LIBTOR_A_SOURCES = \
src/or/dnsserv.c \
src/or/fp_pair.c \
src/or/geoip.c \
+ src/or/entrynodes.c \
+ src/or/ext_orport.c \
+ src/or/hibernate.c \
src/or/hs_cache.c \
+ src/or/hs_circuit.c \
src/or/hs_circuitmap.c \
+ src/or/hs_client.c \
src/or/hs_common.c \
- src/or/hs_circuit.c \
+ src/or/hs_config.c \
src/or/hs_descriptor.c \
src/or/hs_ident.c \
src/or/hs_intropoint.c \
src/or/hs_ntor.c \
src/or/hs_service.c \
- src/or/hs_client.c \
- src/or/entrynodes.c \
- src/or/ext_orport.c \
- src/or/hibernate.c \
src/or/keypin.c \
src/or/main.c \
src/or/microdesc.c \
@@ -183,15 +184,16 @@ ORHEADERS = \
src/or/entrynodes.h \
src/or/hibernate.h \
src/or/hs_cache.h \
- src/or/hs_common.h \
src/or/hs_circuit.h \
+ src/or/hs_circuitmap.h \
+ src/or/hs_client.h \
+ src/or/hs_common.h \
+ src/or/hs_config.h \
src/or/hs_descriptor.h \
src/or/hs_ident.h \
- src/or/hs_intropoint.h \
- src/or/hs_circuitmap.h \
- src/or/hs_ntor.h \
- src/or/hs_service.h \
- src/or/hs_client.h \
+ src/or/hs_intropoint.h \
+ src/or/hs_ntor.h \
+ src/or/hs_service.h \
src/or/keypin.h \
src/or/main.h \
src/or/microdesc.h \
diff --git a/src/or/main.c b/src/or/main.c
index 5fa3869..8c269fd 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -3216,7 +3216,7 @@ tor_free_all(int postfork)
networkstatus_free_all();
addressmap_free_all();
dirserv_free_all();
- rend_service_free_all();
+ hs_service_free_all();
rend_cache_free_all();
rend_service_authorization_free_all();
hs_cache_free_all();
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index b8e704e..695668b 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -236,13 +236,18 @@ rend_service_free(rend_service_t *service)
void
rend_service_free_all(void)
{
- if (!rend_service_list)
- return;
-
- SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
- rend_service_free(ptr));
- smartlist_free(rend_service_list);
- rend_service_list = NULL;
+ if (rend_service_list) {
+ SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
+ rend_service_free(ptr));
+ smartlist_free(rend_service_list);
+ rend_service_list = NULL;
+ }
+ if (rend_service_staging_list) {
+ SMARTLIST_FOREACH(rend_service_staging_list, rend_service_t*, ptr,
+ rend_service_free(ptr));
+ smartlist_free(rend_service_staging_list);
+ rend_service_staging_list = NULL;
+ }
}
/* Validate a <b>service</b>. Use the <b>service_list</b> to make sure there
@@ -335,6 +340,7 @@ rend_add_service(smartlist_t *service_list, rend_service_t *service)
/* We must have a service list, even if it's a temporary one, so we can
* check for duplicate services */
if (BUG(!s_list)) {
+ rend_service_free(service);
return -1;
}
@@ -496,41 +502,6 @@ rend_service_port_config_free(rend_service_port_config_t *p)
tor_free(p);
}
-/* Check the directory for <b>service</b>, and add the service to
- * <b>service_list</b>, or to the global list if <b>service_list</b> is NULL.
- * Only add the service to the list if <b>validate_only</b> is false.
- * If <b>validate_only</b> is true, free the service.
- * If <b>service</b> is NULL, ignore it, and return 0.
- * Returns 0 on success, and -1 on failure.
- * Takes ownership of <b>service</b>, either freeing it, or adding it to the
- * global service list.
- */
-STATIC int
-rend_service_check_dir_and_add(smartlist_t *service_list,
- const or_options_t *options,
- rend_service_t *service,
- int validate_only)
-{
- if (!service) {
- /* It is ok for a service to be NULL, this means there are no services */
- return 0;
- }
-
- if (rend_service_check_private_dir(options, service, !validate_only)
- < 0) {
- rend_service_free(service);
- return -1;
- }
-
- smartlist_t *s_list = rend_get_service_list_mutable(service_list);
- /* We must have a service list, even if it's a temporary one, so we can
- * check for duplicate services */
- if (BUG(!s_list)) {
- return -1;
- }
- return rend_add_service(s_list, service);
-}
-
/* Helper: Actual implementation of the pruning on reload which we've
* decoupled in order to make the unit test workeable without ugly hacks.
* Furthermore, this function does NOT free any memory but will nullify the
@@ -657,19 +628,51 @@ rend_service_prune_list(void)
}
}
-/** Set up rend_service_list, based on the values of HiddenServiceDir and
- * HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
- * failure. (If <b>validate_only</b> is set, parse, warn and return as
- * normal, but don't actually change the configured services.)
- */
+/* Copy all the relevant data that the hs_service object contains over to the
+ * rend_service_t object. The reason to do so is because when configuring a
+ * service, we go through a generic handler that creates an hs_service_t
+ * object which so we have to copy the parsed values to a rend service object
+ * which is version 2 specific. */
+static void
+service_shadow_copy(rend_service_t *service, hs_service_t *hs_service)
+{
+ hs_service_config_t *config;
+
+ tor_assert(service);
+ tor_assert(hs_service);
+
+ config = &hs_service->config;
+ service->directory = tor_strdup(config->directory_path);
+ service->dir_group_readable = config->dir_group_readable;
+ service->allow_unknown_ports = config->allow_unknown_ports;
+ service->max_streams_per_circuit = config->max_streams_per_rdv_circuit;
+ service->max_streams_close_circuit = config->max_streams_close_circuit;
+ service->n_intro_points_wanted = config->num_intro_points;
+ /* Switching ownership of the ports to the rend service object. */
+ smartlist_add_all(service->ports, config->ports);
+ smartlist_free(hs_service->config.ports);
+ hs_service->config.ports = NULL;
+}
+
+/* Parse the hidden service configuration starting at <b>line_</b> using the
+ * already configured generic service in <b>hs_service</b>. This function will
+ * translate the service object to a rend_service_t and add it to the
+ * temporary list if valid. If <b>validate_only</b> is set, parse, warn and
+ * return as normal but don't actually add the service to the list. */
int
-rend_config_services(const or_options_t *options, int validate_only)
+rend_config_service(const config_line_t *line_,
+ const or_options_t *options,
+ int validate_only,
+ hs_service_t *hs_service)
{
- config_line_t *line;
+ (void) validate_only;
+ const config_line_t *line;
rend_service_t *service = NULL;
- rend_service_port_config_t *portcfg;
- int ok = 0;
- int rv = -1;
+
+ /* line_ can be NULL which would mean that the service configuration only
+ * have one line that is the directory directive. */
+ tor_assert(options);
+ tor_assert(hs_service);
/* Use the staging service list so that we can check then do the pruning
* process using the main list at the end. */
@@ -677,100 +680,23 @@ rend_config_services(const or_options_t *options, int validate_only)
rend_service_staging_list = smartlist_new();
}
- for (line = options->RendConfigLines; line; line = line->next) {
+ /* Initialize service. */
+ service = tor_malloc_zero(sizeof(rend_service_t));
+ service->intro_period_started = time(NULL);
+ service->ports = smartlist_new();
+ /* From the hs_service object which has been used to load the generic
+ * options, we'll copy over the useful data to the rend_service_t object. */
+ service_shadow_copy(service, hs_service);
+
+ for (line = line_; line; line = line->next) {
if (!strcasecmp(line->key, "HiddenServiceDir")) {
- if (service) {
- /* Validate and register the service we just finished parsing this
- * code registers every service except the last one parsed, which is
- * validated and registered below the loop. */
- if (rend_validate_service(rend_service_staging_list, service) < 0) {
- goto free_and_return;
- }
- if (rend_service_check_dir_and_add(rend_service_staging_list, options,
- service, validate_only) < 0) {
- /* The above frees the service on error so nullify the pointer. */
- service = NULL;
- goto free_and_return;
- }
- }
- service = tor_malloc_zero(sizeof(rend_service_t));
- service->directory = tor_strdup(line->value);
- service->ports = smartlist_new();
- service->intro_period_started = time(NULL);
- service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
- continue;
- }
- if (!service) {
- log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
- line->key);
- goto free_and_return;
+ /* We just hit the next hidden service, stop right now. */
+ break;
}
- if (!strcasecmp(line->key, "HiddenServicePort")) {
- char *err_msg = NULL;
- portcfg = rend_service_parse_port_config(line->value, " ", &err_msg);
- if (!portcfg) {
- if (err_msg)
- log_warn(LD_CONFIG, "%s", err_msg);
- tor_free(err_msg);
- goto free_and_return;
- }
- tor_assert(!err_msg);
- smartlist_add(service->ports, portcfg);
- } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
- service->allow_unknown_ports = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s",
- line->value);
- goto free_and_return;
- }
- log_info(LD_CONFIG,
- "HiddenServiceAllowUnknownPorts=%d for %s",
- (int)service->allow_unknown_ports,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key,
- "HiddenServiceDirGroupReadable")) {
- service->dir_group_readable = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceDirGroupReadable should be 0 or 1, not %s",
- line->value);
- goto free_and_return;
- }
- log_info(LD_CONFIG,
- "HiddenServiceDirGroupReadable=%d for %s",
- service->dir_group_readable,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
- service->max_streams_per_circuit = (int)tor_parse_long(line->value,
- 10, 0, 65535, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceMaxStreams should be between 0 and %d, not %s",
- 65535, line->value);
- goto free_and_return;
- }
- log_info(LD_CONFIG,
- "HiddenServiceMaxStreams=%d for %s",
- service->max_streams_per_circuit,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
- service->max_streams_close_circuit = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, "
- "not %s",
- line->value);
- goto free_and_return;
- }
- log_info(LD_CONFIG,
- "HiddenServiceMaxStreamsCloseCircuit=%d for %s",
- (int)service->max_streams_close_circuit,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) {
+ /* Number of introduction points. */
+ if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) {
+ int ok = 0;
+ /* Those are specific defaults for version 2. */
service->n_intro_points_wanted =
(unsigned int) tor_parse_long(line->value, 10,
0, NUM_INTRO_POINTS_MAX, &ok, NULL);
@@ -779,12 +705,13 @@ rend_config_services(const or_options_t *options, int validate_only)
"HiddenServiceNumIntroductionPoints "
"should be between %d and %d, not %s",
0, NUM_INTRO_POINTS_MAX, line->value);
- goto free_and_return;
+ goto err;
}
log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s",
- service->n_intro_points_wanted,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
+ service->n_intro_points_wanted, escaped(service->directory));
+ continue;
+ }
+ if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
/* Parse auth type and comma-separated list of client names and add a
* rend_authorized_client_t for each client to the service's list
* of authorized clients. */
@@ -794,7 +721,7 @@ rend_config_services(const or_options_t *options, int validate_only)
if (service->auth_type != REND_NO_AUTH) {
log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
"lines for a single service.");
- goto free_and_return;
+ goto err;
}
type_names_split = smartlist_new();
smartlist_split_string(type_names_split, line->value, " ", 0, 2);
@@ -802,7 +729,8 @@ rend_config_services(const or_options_t *options, int validate_only)
log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
"should have been prevented when parsing the "
"configuration.");
- goto free_and_return;
+ smartlist_free(type_names_split);
+ goto err;
}
authname = smartlist_get(type_names_split, 0);
if (!strcasecmp(authname, "basic")) {
@@ -816,7 +744,7 @@ rend_config_services(const or_options_t *options, int validate_only)
(char *) smartlist_get(type_names_split, 0));
SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
smartlist_free(type_names_split);
- goto free_and_return;
+ goto err;
}
service->clients = smartlist_new();
if (smartlist_len(type_names_split) < 2) {
@@ -853,7 +781,7 @@ rend_config_services(const or_options_t *options, int validate_only)
client_name, REND_CLIENTNAME_MAX_LEN);
SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
smartlist_free(clients);
- goto free_and_return;
+ goto err;
}
client = tor_malloc_zero(sizeof(rend_authorized_client_t));
client->client_name = tor_strdup(client_name);
@@ -875,56 +803,29 @@ rend_config_services(const or_options_t *options, int validate_only)
smartlist_len(service->clients),
service->auth_type == REND_BASIC_AUTH ? 512 : 16,
service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
- goto free_and_return;
- }
- } else {
- tor_assert(!strcasecmp(line->key, "HiddenServiceVersion"));
- if (strcmp(line->value, "2")) {
- log_warn(LD_CONFIG,
- "The only supported HiddenServiceVersion is 2.");
- goto free_and_return;
+ goto err;
}
+ continue;
}
}
- /* Validate the last service that we just parsed. */
- if (service &&
- rend_validate_service(rend_service_staging_list, service) < 0) {
- goto free_and_return;
- }
- /* register the final service after we have finished parsing all services
- * this code only registers the last service, other services are registered
- * within the loop. It is ok for this service to be NULL, it is ignored. */
- if (rend_service_check_dir_and_add(rend_service_staging_list, options,
- service, validate_only) < 0) {
- /* Service object is freed on error so nullify pointer. */
- service = NULL;
- goto free_and_return;
+ /* Validate the service just parsed. */
+ if (rend_validate_service(rend_service_staging_list, service) < 0) {
+ /* Service is in the staging list so don't try to free it. */
+ goto err;
}
- /* The service is in the staging list so nullify pointer to avoid double
- * free of this object in case of error because we lost ownership of it at
- * this point. */
- service = NULL;
- /* Free the newly added services if validating */
- if (validate_only) {
- rv = 0;
- goto free_and_return;
+ /* Add it to the temporary list which we will use to prune our current
+ * list if any after configuring all services. */
+ if (rend_add_service(rend_service_staging_list, service) < 0) {
+ /* The object has been freed on error already. */
+ service = NULL;
+ goto err;
}
- /* This could be a reload of configuration so try to prune the main list
- * using the staging one. And we know we are not in validate mode here.
- * After this, the main and staging list will point to the right place and
- * be in a quiescent usable state. */
- rend_service_prune_list();
-
return 0;
- free_and_return:
+ err:
rend_service_free(service);
- SMARTLIST_FOREACH(rend_service_staging_list, rend_service_t *, ptr,
- rend_service_free(ptr));
- smartlist_free(rend_service_staging_list);
- rend_service_staging_list = NULL;
- return rv;
+ return -1;
}
/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, using
@@ -1548,9 +1449,9 @@ rend_service_load_keys(rend_service_t *s)
char *fname = NULL;
char buf[128];
- /* Make sure the directory was created and single onion poisoning was
- * checked before calling this function */
- if (BUG(rend_service_check_private_dir(get_options(), s, 0) < 0))
+ /* Create the directory if needed which will also poison it in case of
+ * single onion service. */
+ if (rend_service_check_private_dir(get_options(), s, 1) < 0)
goto err;
/* Load key */
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 1583a60..90f854e 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -13,6 +13,7 @@
#define TOR_RENDSERVICE_H
#include "or.h"
+#include "hs_service.h"
typedef struct rend_intro_cell_s rend_intro_cell_t;
typedef struct rend_service_port_config_s rend_service_port_config_t;
@@ -119,10 +120,6 @@ typedef struct rend_service_t {
STATIC void rend_service_free(rend_service_t *service);
STATIC char *rend_service_sos_poison_path(const rend_service_t *service);
-STATIC int rend_service_check_dir_and_add(smartlist_t *service_list,
- const or_options_t *options,
- rend_service_t *service,
- int validate_only);
STATIC int rend_service_verify_single_onion_poison(
const rend_service_t *s,
const or_options_t *options);
@@ -144,7 +141,10 @@ STATIC void rend_service_prune_list_impl_(void);
#endif /* RENDSERVICE_PRIVATE */
int num_rend_services(void);
-int rend_config_services(const or_options_t *options, int validate_only);
+int rend_config_service(const config_line_t *line_,
+ const or_options_t *options,
+ int validate_only,
+ hs_service_t *hs_service);
void rend_service_prune_list(void);
int rend_service_load_all_keys(const smartlist_t *service_list);
void rend_services_add_filenames_to_lists(smartlist_t *open_lst,
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index 178f94d..093c7ec 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -10,6 +10,7 @@
#define CIRCUITBUILD_PRIVATE
#define RENDCOMMON_PRIVATE
#define RENDSERVICE_PRIVATE
+#define HS_SERVICE_PRIVATE
#include "or.h"
#include "test.h"
@@ -661,17 +662,8 @@ test_single_onion_poisoning(void *arg)
smartlist_t *services = smartlist_new();
char *poison_path = NULL;
- /* No services, no service to verify, no problem! */
- mock_options->HiddenServiceSingleHopMode = 0;
- mock_options->HiddenServiceNonAnonymousMode = 0;
- ret = rend_config_services(mock_options, 1);
- tt_assert(ret == 0);
-
- /* Either way, no problem. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
- ret = rend_config_services(mock_options, 1);
- tt_assert(ret == 0);
/* Create the data directory, and, if the correct bit in arg is set,
* create a directory for that service.
@@ -726,8 +718,10 @@ test_single_onion_poisoning(void *arg)
tt_assert(ret == 0);
/* Add the first service */
- ret = rend_service_check_dir_and_add(services, mock_options, service_1, 0);
- tt_assert(ret == 0);
+ ret = hs_check_service_private_dir(mock_options->User, service_1->directory,
+ service_1->dir_group_readable, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ smartlist_add(services, service_1);
/* But don't add the second service yet. */
/* Service directories, but no previous keys, no problem! */
@@ -805,8 +799,10 @@ test_single_onion_poisoning(void *arg)
tt_assert(ret == 0);
/* Now add the second service: it has no key and no poison file */
- ret = rend_service_check_dir_and_add(services, mock_options, service_2, 0);
- tt_assert(ret == 0);
+ ret = hs_check_service_private_dir(mock_options->User, service_2->directory,
+ service_2->dir_group_readable, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ smartlist_add(services, service_2);
/* A new service, and an existing poisoned service. Not ok. */
mock_options->HiddenServiceSingleHopMode = 0;
1
0
commit b03853b65f109ed6a34ba2924fe3b00d56131ff5
Author: David Goulet <dgoulet(a)torproject.org>
Date: Sun Jan 15 10:09:13 2017 -0500
prop224: Initial import of hs_service_t
This object is the foundation of proposal 224 service work. It will change
and be adapted as it's being used more and more in the codebase. So, this
version is just a basic skeleton one that *will* change.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_service.h | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 182 insertions(+), 2 deletions(-)
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index 3302592..fa5dd54 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -3,15 +3,195 @@
/**
* \file hs_service.h
- * \brief Header file for hs_service.c.
+ * \brief Header file containing service data for the HS subsytem.
**/
#ifndef TOR_HS_SERVICE_H
#define TOR_HS_SERVICE_H
-#include "or.h"
+#include "crypto_curve25519.h"
+#include "crypto_ed25519.h"
+#include "hs_descriptor.h"
+#include "hs_intropoint.h"
+#include "replaycache.h"
+
+/* Trunnel */
#include "hs/cell_establish_intro.h"
+/* When loading and configuring a service, this is the default version it will
+ * be configured for as it is possible that no HiddenServiceVersion is
+ * present. */
+#define HS_SERVICE_DEFAULT_VERSION HS_VERSION_TWO
+
+/* Service side introduction point. */
+typedef struct hs_service_intro_point_t {
+ /* Top level intropoint "shared" data between client/service. */
+ hs_intropoint_t base;
+
+ /* Authentication keypair used to create the authentication certificate
+ * which is published in the descriptor. */
+ ed25519_keypair_t auth_key_kp;
+
+ /* Encryption private key. */
+ curve25519_secret_key_t enc_key_sk;
+
+ /* Amount of INTRODUCE2 cell accepted from this intro point. */
+ uint64_t introduce2_count;
+
+ /* Maximum number of INTRODUCE2 cell this intro point should accept. */
+ uint64_t introduce2_max;
+
+ /* The time at which this intro point should expire and stop being used. */
+ time_t time_to_expire;
+
+ /* The amount of circuit creation we've made to this intro point. This is
+ * incremented every time we do a circuit relaunch on this intro point which
+ * is triggered when the circuit dies but the node is still in the
+ * consensus. After MAX_INTRO_POINT_CIRCUIT_RETRIES, we give up on it. */
+ uint32_t circuit_retries;
+
+ /* Set if this intro point has an established circuit. */
+ unsigned int circuit_established : 1;
+
+ /* Replay cache recording the encrypted part of an INTRODUCE2 cell that the
+ * circuit associated with this intro point has received. This is used to
+ * prevent replay attacks. */
+ replaycache_t *replay_cache;
+} hs_service_intro_point_t;
+
+/* Object handling introduction points of a service. */
+typedef struct hs_service_intropoints_t {
+ /* The time at which we've started our retry period to build circuits. We
+ * don't want to stress circuit creation so we can only retry for a certain
+ * time and then after we stop and wait. */
+ time_t retry_period_started;
+
+ /* Number of circuit we've launched during a single retry period. */
+ unsigned int num_circuits_launched;
+
+ /* Contains the current hs_service_intro_point_t objects indexed by
+ * authentication public key. */
+ digest256map_t *map;
+} hs_service_intropoints_t;
+
+/* Representation of a service descriptor. */
+typedef struct hs_service_descriptor_t {
+ /* Decoded descriptor. This object is used for encoding when the service
+ * publishes the descriptor. */
+ hs_descriptor_t *desc;
+
+ /* Descriptor signing keypair. */
+ ed25519_keypair_t signing_kp;
+
+ /* Blinded keypair derived from the master identity public key. */
+ ed25519_keypair_t blinded_kp;
+
+ /* When is the next time when we should upload the descriptor. */
+ time_t next_upload_time;
+
+ /* Introduction points assign to this descriptor which contains
+ * hs_service_intropoints_t object indexed by authentication key (the RSA
+ * key if the node is legacy). */
+ hs_service_intropoints_t intro_points;
+} hs_service_descriptor_t;
+
+/* Service key material. */
+typedef struct hs_service_keys_t {
+ /* Master identify public key. */
+ ed25519_public_key_t identity_pk;
+ /* Master identity private key. */
+ ed25519_secret_key_t identity_sk;
+ /* True iff the key is kept offline which means the identity_sk MUST not be
+ * used in that case. */
+ unsigned int is_identify_key_offline : 1;
+} hs_service_keys_t;
+
+/* Service configuration. The following are set from the torrc options either
+ * set by the configuration file or by the control port. Nothing else should
+ * change those values. */
+typedef struct hs_service_config_t {
+ /* List of rend_service_port_config_t */
+ smartlist_t *ports;
+
+ /* Path on the filesystem where the service persistent data is stored. NULL
+ * if the service is ephemeral. Specified by HiddenServiceDir option. */
+ char *directory_path;
+
+ /* The time period after which a descriptor is uploaded to the directories
+ * in seconds. Specified by RendPostPeriod option. */
+ uint32_t descriptor_post_period;
+
+ /* The maximum number of simultaneous streams per rendezvous circuit that
+ * are allowed to be created. No limit if 0. Specified by
+ * HiddenServiceMaxStreams option. */
+ uint64_t max_streams_per_rdv_circuit;
+
+ /* If true, we close circuits that exceed the max_streams_per_rdv_circuit
+ * limit. Specified by HiddenServiceMaxStreamsCloseCircuit option. */
+ unsigned int max_streams_close_circuit : 1;
+
+ /* How many introduction points this service has. Specified by
+ * HiddenServiceNumIntroductionPoints option. */
+ unsigned int num_intro_points;
+
+ /* True iff we allow request made on unknown ports. Specified by
+ * HiddenServiceAllowUnknownPorts option. */
+ unsigned int allow_unknown_ports : 1;
+
+ /* If true, this service is a Single Onion Service. Specified by
+ * HiddenServiceSingleHopMode and HiddenServiceNonAnonymousMode options. */
+ unsigned int is_single_onion : 1;
+
+ /* If true, allow group read permissions on the directory_path. Specified by
+ * HiddenServiceDirGroupReadable option. */
+ unsigned int dir_group_readable : 1;
+
+ /* Is this service ephemeral? */
+ unsigned int is_ephemeral : 1;
+} hs_service_config_t;
+
+/* Service state. */
+typedef struct hs_service_state_t {
+ /* The time at which we've started our retry period to build circuits. We
+ * don't want to stress circuit creation so we can only retry for a certain
+ * time and then after we stop and wait. */
+ time_t intro_circ_retry_started_time;
+
+ /* Number of circuit we've launched during a single retry period. This
+ * should never go over MAX_INTRO_CIRCS_PER_PERIOD. */
+ unsigned int num_intro_circ_launched;
+
+ /* Indicate that the service has entered the overlap period. We use this
+ * flag to check for descriptor rotation. */
+ unsigned int in_overlap_period : 1;
+} hs_service_state_t;
+
+/* Representation of a service running on this tor instance. */
+typedef struct hs_service_t {
+ /* Protocol version of the service. Specified by HiddenServiceVersion. */
+ uint32_t version;
+
+ /* Service state which contains various flags and counters. */
+ hs_service_state_t state;
+
+ /* Key material of the service. */
+ hs_service_keys_t keys;
+
+ /* Configuration of the service. */
+ hs_service_config_t config;
+
+ /* Current descriptor. */
+ hs_service_descriptor_t *desc_current;
+ /* Next descriptor that we need for the overlap period for which we have to
+ * keep two sets of opened introduction point circuits. */
+ hs_service_descriptor_t *desc_next;
+
+ /* XXX: Credential (client auth.) #20700. */
+
+} hs_service_t;
+
+/* API */
+
/* These functions are only used by unit tests and we need to expose them else
* hs_service.o ends up with no symbols in libor.a which makes clang throw a
* warning at compile time. See #21825. */
1
0
commit b50f39fb6fc9d9a4bbb86d760291d6e88bf0987a
Author: David Goulet <dgoulet(a)torproject.org>
Date: Sun Jan 15 10:13:37 2017 -0500
prop224: Add common intropoint object
Groundwork for more prop224 service and client code. This object contains
common data that both client and service uses.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_intropoint.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/or/hs_intropoint.h b/src/or/hs_intropoint.h
index 163ed81..bfb1331 100644
--- a/src/or/hs_intropoint.h
+++ b/src/or/hs_intropoint.h
@@ -9,6 +9,9 @@
#ifndef TOR_HS_INTRO_H
#define TOR_HS_INTRO_H
+#include "crypto_curve25519.h"
+#include "torcert.h"
+
/* Authentication key type in an ESTABLISH_INTRO cell. */
enum hs_intro_auth_key_type {
HS_INTRO_AUTH_KEY_TYPE_LEGACY0 = 0x00,
@@ -24,6 +27,15 @@ typedef enum {
HS_INTRO_ACK_STATUS_CANT_RELAY = 0x0003,
} hs_intro_ack_status_t;
+/* Object containing introduction point common data between the service and
+ * the client side. */
+typedef struct hs_intropoint_t {
+ /* Authentication key certificate from the descriptor. */
+ tor_cert_t *auth_key_cert;
+ /* A list of link specifier. */
+ smartlist_t *link_specifiers;
+} hs_intropoint_t;
+
int hs_intro_received_establish_intro(or_circuit_t *circ,
const uint8_t *request,
size_t request_len);
1
0

[tor/master] prop224: Add a init/free_all function for the whole subsystem
by nickm@torproject.org 13 Jul '17
by nickm@torproject.org 13 Jul '17
13 Jul '17
commit 765ed5dac160b28fb658560e8f39d1d7ab3d1c75
Author: David Goulet <dgoulet(a)torproject.org>
Date: Mon Jan 16 13:29:03 2017 -0500
prop224: Add a init/free_all function for the whole subsystem
Introduces hs_init() located in hs_common.c which initialize the entire HS v3
subsystem. This is done _prior_ to the options being loaded because we need to
allocate global data structure before we load the configuration.
The hs_free_all() is added to release everything from tor_free_all().
Note that both functions do NOT handle v2 service subsystem but does handle
the common interface that both v2 and v3 needs such as the cache and
circuitmap.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_common.c | 22 ++++++++++++++++++++++
src/or/hs_common.h | 3 +++
src/or/hs_service.c | 7 +++++++
src/or/hs_service.h | 1 +
src/or/main.c | 10 +++-------
5 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index 4250812..b524296 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -15,7 +15,9 @@
#include "config.h"
#include "networkstatus.h"
+#include "hs_cache.h"
#include "hs_common.h"
+#include "hs_service.h"
#include "rendcommon.h"
/* Make sure that the directory for <b>service</b> is private, using the config
@@ -344,3 +346,23 @@ rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out)
}
}
+/* Initialize the entire HS subsytem. This is called in tor_init() before any
+ * torrc options are loaded. Only for >= v3. */
+void
+hs_init(void)
+{
+ hs_circuitmap_init();
+ hs_service_init();
+ hs_cache_init();
+}
+
+/* Release and cleanup all memory of the HS subsystem (all version). This is
+ * called by tor_free_all(). */
+void
+hs_free_all(void)
+{
+ hs_circuitmap_free_all();
+ hs_service_free_all();
+ hs_cache_free_all();
+}
+
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index abc44c0..8016535 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -58,6 +58,9 @@ typedef enum {
HS_AUTH_KEY_TYPE_ED25519 = 2,
} hs_auth_key_type_t;
+void hs_init(void);
+void hs_free_all(void);
+
int hs_check_service_private_dir(const char *username, const char *path,
unsigned int dir_group_readable,
unsigned int create);
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index c62aa8b..16ffc48 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -97,6 +97,13 @@ hs_service_free(hs_service_t *service)
tor_free(service);
}
+/* Initialize the service HS subsystem. */
+void
+hs_service_init(void)
+{
+ return;
+}
+
/* Release all global the storage of hidden service subsystem. */
void
hs_service_free_all(void)
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index d29a478..ec47cb7 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -193,6 +193,7 @@ typedef struct hs_service_t {
/* API */
int hs_service_config_all(const or_options_t *options, int validate_only);
+void hs_service_init(void);
void hs_service_free_all(void);
void hs_service_free(hs_service_t *service);
diff --git a/src/or/main.c b/src/or/main.c
index 8c269fd..204b3f3 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -2499,9 +2499,6 @@ do_main_loop(void)
}
}
- /* Initialize relay-side HS circuitmap */
- hs_circuitmap_init();
-
/* set up once-a-second callback. */
if (! second_timer) {
struct timeval one_second;
@@ -3014,9 +3011,10 @@ tor_init(int argc, char *argv[])
rep_hist_init();
/* Initialize the service cache. */
rend_cache_init();
- hs_cache_init();
addressmap_init(); /* Init the client dns cache. Do it always, since it's
* cheap. */
+ /* Initialize the HS subsystem. */
+ hs_init();
{
/* We search for the "quiet" option first, since it decides whether we
@@ -3216,10 +3214,8 @@ tor_free_all(int postfork)
networkstatus_free_all();
addressmap_free_all();
dirserv_free_all();
- hs_service_free_all();
rend_cache_free_all();
rend_service_authorization_free_all();
- hs_cache_free_all();
rep_hist_free_all();
dns_free_all();
clear_pending_onions();
@@ -3232,7 +3228,6 @@ tor_free_all(int postfork)
connection_edge_free_all();
scheduler_free_all();
nodelist_free_all();
- hs_circuitmap_free_all();
microdesc_free_all();
routerparse_free_all();
ext_orport_free_all();
@@ -3241,6 +3236,7 @@ tor_free_all(int postfork)
protover_free_all();
bridges_free_all();
consdiffmgr_free_all();
+ hs_free_all();
if (!postfork) {
config_free_all();
or_state_free_all();
1
0

[tor/master] hs: Use v3 maximum intro points value when decoding v3
by nickm@torproject.org 13 Jul '17
by nickm@torproject.org 13 Jul '17
13 Jul '17
commit 74193b932115a82417dc312721ffe0a10a7ed6dc
Author: David Goulet <dgoulet(a)torproject.org>
Date: Wed May 10 09:37:41 2017 -0400
hs: Use v3 maximum intro points value when decoding v3
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_config.h | 3 +++
src/or/hs_descriptor.c | 6 ++++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/or/hs_config.h b/src/or/hs_config.h
index 08072d1..f420791 100644
--- a/src/or/hs_config.h
+++ b/src/or/hs_config.h
@@ -11,6 +11,9 @@
#include "or.h"
+/* Maximum number of intro points per version 3 services. */
+#define HS_CONFIG_V3_MAX_INTRO_POINTS 20
+
/* API */
int hs_config_service_all(const or_options_t *options, int validate_only);
diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index b55f966..2393eac 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -62,6 +62,7 @@
#include "parsecommon.h"
#include "rendcache.h"
#include "hs_cache.h"
+#include "hs_config.h"
#include "torcert.h" /* tor_cert_encode_ed22519() */
/* Constant string value used for the descriptor format. */
@@ -2035,10 +2036,11 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
decode_intro_points(desc, desc_encrypted_out, message);
/* Validation of maximum introduction points allowed. */
- if (smartlist_len(desc_encrypted_out->intro_points) > MAX_INTRO_POINTS) {
+ if (smartlist_len(desc_encrypted_out->intro_points) >
+ HS_CONFIG_V3_MAX_INTRO_POINTS) {
log_warn(LD_REND, "Service descriptor contains too many introduction "
"points. Maximum allowed is %d but we have %d",
- MAX_INTRO_POINTS,
+ HS_CONFIG_V3_MAX_INTRO_POINTS,
smartlist_len(desc_encrypted_out->intro_points));
goto err;
}
1
0