瀏覽代碼

Merge pull request #430 from slingamn/tor.2

add Tor support (plus a few random changes)
tags/v1.1.0-rc1
Daniel Oaks 5 年之前
父節點
當前提交
e18bb864e2
No account linked to committer's email address
共有 12 個文件被更改,包括 327 次插入83 次删除
  1. 48
    2
      docs/MANUAL.md
  2. 67
    47
      irc/client.go
  3. 23
    0
      irc/config.go
  4. 55
    0
      irc/connection_limits/tor.go
  5. 11
    3
      irc/gateways.go
  6. 7
    0
      irc/getters.go
  7. 22
    2
      irc/handlers.go
  8. 4
    1
      irc/resume.go
  9. 5
    0
      irc/roleplay.go
  10. 59
    26
      irc/server.go
  11. 2
    2
      irc/utils/crypto.go
  12. 24
    0
      oragono.yaml

+ 48
- 2
docs/MANUAL.md 查看文件

@@ -32,8 +32,10 @@ _Copyright © 2018 Daniel Oaks <daniel@danieloaks.net>_
32 32
     - Channel Modes
33 33
     - Channel Prefixes
34 34
 - Commands
35
-- Integrating with other software
35
+- Working with other software
36 36
     - HOPM
37
+    - ZNC
38
+    - Tor
37 39
 - Acknowledgements
38 40
 
39 41
 
@@ -541,7 +543,7 @@ We may add some additional notes here for specific commands down the line, but r
541 543
 --------------------------------------------------------------------------------------------
542 544
 
543 545
 
544
-# Integrating with other software
546
+# Working with other software
545 547
 
546 548
 Oragono should interoperate with most IRC-based software, including bots. If you have problems getting your preferred software to work with Oragono, feel free to report it to us. If the root cause is a bug in Oragono, we'll fix it.
547 549
 
@@ -601,6 +603,50 @@ kline = "DLINE ANDKILL 2h %i :Open proxy found on your host.";
601 603
 Versions of ZNC prior to 1.7 have a [bug](https://github.com/znc/znc/issues/1212) in their SASL implementation that renders them incompatible with Oragono. However, you should be able to authenticate from ZNC using its [NickServ](https://wiki.znc.in/Nickserv) module.
602 604
 
603 605
 
606
+## Tor
607
+
608
+Oragono has code support for adding an .onion address to an IRC server, or operating an IRC server as a Tor hidden service. This is subtle, so you should be familiar with the [Tor Project](https://www.torproject.org/) and the concept of a [hidden service](https://www.torproject.org/docs/tor-onion-service.html.en).
609
+
610
+There are two possible ways to serve Oragono over Tor. One is to add a .onion address to a server that also serves non-Tor clients, and whose IP address is public information. This is relatively straightforward. Add a separate listener, for example `127.0.0.2:6668`, to Oragono's `server.listen`, then add it to `server.tor-listeners.listeners`. Then configure Tor like this:
611
+
612
+````
613
+HiddenServiceDir /var/lib/tor/oragono_hidden_service
614
+HiddenServicePort 6667 127.0.0.2:6668
615
+
616
+# these are optional, but can be used to speed up the circuits in the case
617
+# where the server's own IP is public information (clients will remain anonymous):
618
+HiddenServiceNonAnonymousMode 1
619
+HiddenServiceSingleHopMode 1
620
+````
621
+
622
+Tor provides end-to-end encryption for hidden services, so there's no need to enable TLS in Oragono for the listener (`127.0.0.2:6668` in this example). Doing so is not recommended, given the difficulty in obtaining a TLS certificate valid for an .onion address.
623
+
624
+The second way is to run Oragono as a true hidden service, where the server's actual IP address is a secret. This requires hardening measures on the Oragono side:
625
+
626
+* Oragono should not accept any connections on its public interfaces. You should remove any listener that starts with the address of a public interface, or with `:`, which means "listen on all available interfaces". You should listen only on `127.0.0.1:6667` and a Unix domain socket such as `/hidden_service_sockets/oragono.sock`.
627
+* In this mode, it is especially important that all operator passwords are strong and all operators are trusted (operators have a larger attack surface to deanonymize the server).
628
+* Tor hidden services are at risk of being deanonymized if a client can trick the server into performing a non-Tor network request. Oragono should not perform any such requests (such as hostname resolution or ident lookups) in response to input received over a correctly configured Tor listener. However, Oragono has not been thoroughly audited against such deanonymization attacks --- therefore, Oragono should be deployed with additional sandboxing to protect against this:
629
+  * Oragono should run with no direct network connectivity, e.g., by running in its own Linux network namespace. systemd implements this with the [PrivateNetwork](https://www.freedesktop.org/software/systemd/man/systemd.exec.html) configuration option: add `PrivateNetwork=true` to Oragono's systemd unit file.
630
+  * Since the loopback adapters are local to a specific network namespace, and the Tor daemon will run in the root namespace, Tor will be unable to connect to Oragono over loopback TCP. Instead, Oragono must listen on a named Unix domain socket that the Tor daemon can connect to. However, distributions typically package Tor with its own hardening profiles, which restrict which sockets it can access. Below is a recipe for configuring this with the official Tor packages for Debian:
631
+
632
+1. Create a directory with `0777` permissions such as `/hidden_service_sockets`.
633
+1. Configure Oragono to listen on `/hidden_service_sockets/oragono.sock`, and add this socket to `server.tor-listeners.listeners`.
634
+1. Ensure that Oragono has no direct network access as described above, e.g., with `PrivateNetwork=true`.
635
+1. Next, modify Tor's apparmor profile so that it can connect to this socket, by adding the line `  /hidden_service_sockets/** rw,` to `/etc/apparmor.d/local/system_tor`.
636
+1. Finally, configure Tor with:
637
+
638
+````
639
+HiddenServiceDir /var/lib/tor/oragono_hidden_service
640
+HiddenServicePort 6667 unix:/hidden_service_sockets/oragono.sock
641
+# DO NOT enable HiddenServiceNonAnonymousMode
642
+````
643
+
644
+Instructions on how client software should connect to an .onion address are outside the scope of this manual. However:
645
+
646
+1. [Hexchat](https://hexchat.github.io/) is known to support .onion addresses, once it has been configured to use a local Tor daemon as a SOCKS proxy (Settings -> Preferences -> Network Setup -> Proxy Server).
647
+1. Pidgin should work with [torsocks](https://trac.torproject.org/projects/tor/wiki/doc/torsocks).
648
+
649
+
604 650
 --------------------------------------------------------------------------------------------
605 651
 
606 652
 

+ 67
- 47
irc/client.go 查看文件

@@ -65,6 +65,7 @@ type Client struct {
65 65
 	idletimer          *IdleTimer
66 66
 	invitedTo          map[string]bool
67 67
 	isDestroyed        bool
68
+	isTor              bool
68 69
 	isQuitting         bool
69 70
 	languages          []string
70 71
 	loginThrottle      connection_limits.GenericThrottle
@@ -117,12 +118,12 @@ type ClientDetails struct {
117 118
 	accountName        string
118 119
 }
119 120
 
120
-// NewClient sets up a new client and starts its goroutine.
121
-func NewClient(server *Server, conn net.Conn, isTLS bool) {
121
+// NewClient sets up a new client and runs its goroutine.
122
+func RunNewClient(server *Server, conn clientConn) {
122 123
 	now := time.Now()
123 124
 	config := server.Config()
124 125
 	fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest
125
-	socket := NewSocket(conn, fullLineLenLimit*2, config.Server.MaxSendQBytes)
126
+	socket := NewSocket(conn.Conn, fullLineLenLimit*2, config.Server.MaxSendQBytes)
126 127
 	client := &Client{
127 128
 		atime:        now,
128 129
 		capabilities: caps.NewSet(),
@@ -131,6 +132,7 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) {
131 132
 		channels:     make(ChannelSet),
132 133
 		ctime:        now,
133 134
 		flags:        modes.NewModeSet(),
135
+		isTor:        conn.IsTor,
134 136
 		languages:    server.Languages().Default(),
135 137
 		loginThrottle: connection_limits.GenericThrottle{
136 138
 			Duration: config.Accounts.LoginThrottling.Duration,
@@ -145,58 +147,73 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) {
145 147
 		history:        history.NewHistoryBuffer(config.History.ClientLength),
146 148
 	}
147 149
 
148
-	remoteAddr := conn.RemoteAddr()
149
-	client.realIP = utils.AddrToIP(remoteAddr)
150
-	if client.realIP == nil {
151
-		server.logger.Error("internal", "bad remote address", remoteAddr.String())
152
-		return
153
-	}
154 150
 	client.recomputeMaxlens()
155
-	if isTLS {
156
-		client.SetMode(modes.TLS, true)
157 151
 
152
+	if conn.IsTLS {
153
+		client.SetMode(modes.TLS, true)
158 154
 		// error is not useful to us here anyways so we can ignore it
159 155
 		client.certfp, _ = client.socket.CertFP()
160 156
 	}
161
-	if config.Server.CheckIdent && !utils.AddrIsUnix(remoteAddr) {
162
-		_, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
163
-		if err != nil {
164
-			server.logger.Error("internal", "bad server address", err.Error())
165
-			return
166
-		}
167
-		serverPort, _ := strconv.Atoi(serverPortString)
168
-		clientHost, clientPortString, err := net.SplitHostPort(conn.RemoteAddr().String())
169
-		if err != nil {
170
-			server.logger.Error("internal", "bad client address", err.Error())
171
-			return
157
+
158
+	if conn.IsTor {
159
+		client.SetMode(modes.TLS, true)
160
+		client.realIP = utils.IPv4LoopbackAddress
161
+		client.rawHostname = config.Server.TorListeners.Vhost
162
+	} else {
163
+		remoteAddr := conn.Conn.RemoteAddr()
164
+		client.realIP = utils.AddrToIP(remoteAddr)
165
+		// Set the hostname for this client
166
+		// (may be overridden by a later PROXY command from stunnel)
167
+		client.rawHostname = utils.LookupHostname(client.realIP.String())
168
+		if config.Server.CheckIdent && !utils.AddrIsUnix(remoteAddr) {
169
+			client.doIdentLookup(conn.Conn)
172 170
 		}
173
-		clientPort, _ := strconv.Atoi(clientPortString)
171
+	}
172
+
173
+	client.run()
174
+}
175
+
176
+func (client *Client) doIdentLookup(conn net.Conn) {
177
+	_, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
178
+	if err != nil {
179
+		client.server.logger.Error("internal", "bad server address", err.Error())
180
+		return
181
+	}
182
+	serverPort, _ := strconv.Atoi(serverPortString)
183
+	clientHost, clientPortString, err := net.SplitHostPort(conn.RemoteAddr().String())
184
+	if err != nil {
185
+		client.server.logger.Error("internal", "bad client address", err.Error())
186
+		return
187
+	}
188
+	clientPort, _ := strconv.Atoi(clientPortString)
174 189
 
175
-		client.Notice(client.t("*** Looking up your username"))
176
-		resp, err := ident.Query(clientHost, serverPort, clientPort, IdentTimeoutSeconds)
190
+	client.Notice(client.t("*** Looking up your username"))
191
+	resp, err := ident.Query(clientHost, serverPort, clientPort, IdentTimeoutSeconds)
192
+	if err == nil {
193
+		err := client.SetNames(resp.Identifier, "", true)
177 194
 		if err == nil {
178
-			err := client.SetNames(resp.Identifier, "", true)
179
-			if err == nil {
180
-				client.Notice(client.t("*** Found your username"))
181
-				// we don't need to updateNickMask here since nickMask is not used for anything yet
182
-			} else {
183
-				client.Notice(client.t("*** Got a malformed username, ignoring"))
184
-			}
195
+			client.Notice(client.t("*** Found your username"))
196
+			// we don't need to updateNickMask here since nickMask is not used for anything yet
185 197
 		} else {
186
-			client.Notice(client.t("*** Could not find your username"))
198
+			client.Notice(client.t("*** Got a malformed username, ignoring"))
187 199
 		}
200
+	} else {
201
+		client.Notice(client.t("*** Could not find your username"))
188 202
 	}
189
-	go client.run()
190 203
 }
191 204
 
192 205
 func (client *Client) isAuthorized(config *Config) bool {
193 206
 	saslSent := client.account != ""
194
-	passRequirementMet := (config.Server.passwordBytes == nil) || client.sentPassCommand || (config.Accounts.SkipServerPassword && saslSent)
195
-	if !passRequirementMet {
207
+	// PASS requirement
208
+	if (config.Server.passwordBytes != nil) && !client.sentPassCommand && !(config.Accounts.SkipServerPassword && saslSent) {
196 209
 		return false
197 210
 	}
198
-	saslRequirementMet := !config.Accounts.RequireSasl.Enabled || saslSent || utils.IPInNets(client.IP(), config.Accounts.RequireSasl.exemptedNets)
199
-	return saslRequirementMet
211
+	// Tor connections may be required to authenticate with SASL
212
+	if client.isTor && config.Server.TorListeners.RequireSasl && !saslSent {
213
+		return false
214
+	}
215
+	// finally, enforce require-sasl
216
+	return !config.Accounts.RequireSasl.Enabled || saslSent || utils.IPInNets(client.IP(), config.Accounts.RequireSasl.exemptedNets)
200 217
 }
201 218
 
202 219
 func (client *Client) resetFakelag() {
@@ -296,10 +313,6 @@ func (client *Client) run() {
296 313
 
297 314
 	client.resetFakelag()
298 315
 
299
-	// Set the hostname for this client
300
-	// (may be overridden by a later PROXY command from stunnel)
301
-	client.rawHostname = utils.LookupHostname(client.realIP.String())
302
-
303 316
 	firstLine := true
304 317
 
305 318
 	for {
@@ -315,7 +328,9 @@ func (client *Client) run() {
315 328
 			break
316 329
 		}
317 330
 
318
-		client.server.logger.Debug("userinput", client.nick, "<- ", line)
331
+		if client.server.logger.IsLoggingRawIO() {
332
+			client.server.logger.Debug("userinput", client.nick, "<- ", line)
333
+		}
319 334
 
320 335
 		// special-cased handling of PROXY protocol, see `handleProxyCommand` for details:
321 336
 		if firstLine {
@@ -403,6 +418,11 @@ func (client *Client) tryResume() (success bool) {
403 418
 		return
404 419
 	}
405 420
 
421
+	if oldClient.isTor != client.isTor {
422
+		client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection from Tor to non-Tor or vice versa"))
423
+		return
424
+	}
425
+
406 426
 	err := server.clients.Resume(client, oldClient)
407 427
 	if err != nil {
408 428
 		client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection"))
@@ -882,10 +902,10 @@ func (client *Client) destroy(beingResumed bool) {
882 902
 	}
883 903
 
884 904
 	// remove from connection limits
885
-	ipaddr := client.IP()
886
-	// this check shouldn't be required but eh
887
-	if ipaddr != nil {
888
-		client.server.connectionLimiter.RemoveClient(ipaddr)
905
+	if client.isTor {
906
+		client.server.torLimiter.RemoveClient()
907
+	} else {
908
+		client.server.connectionLimiter.RemoveClient(client.IP())
889 909
 	}
890 910
 
891 911
 	client.server.resumeManager.Delete(client)

+ 23
- 0
irc/config.go 查看文件

@@ -249,6 +249,15 @@ type FakelagConfig struct {
249 249
 	Cooldown          time.Duration
250 250
 }
251 251
 
252
+type TorListenersConfig struct {
253
+	Listeners                 []string
254
+	RequireSasl               bool `yaml:"require-sasl"`
255
+	Vhost                     string
256
+	MaxConnections            int           `yaml:"max-connections"`
257
+	ThrottleDuration          time.Duration `yaml:"throttle-duration"`
258
+	MaxConnectionsPerDuration int           `yaml:"max-connections-per-duration"`
259
+}
260
+
252 261
 // Config defines the overall configuration.
253 262
 type Config struct {
254 263
 	Network struct {
@@ -263,6 +272,7 @@ type Config struct {
263 272
 		Listen               []string
264 273
 		UnixBindMode         os.FileMode                 `yaml:"unix-bind-mode"`
265 274
 		TLSListeners         map[string]*TLSListenConfig `yaml:"tls-listeners"`
275
+		TorListeners         TorListenersConfig          `yaml:"tor-listeners"`
266 276
 		STS                  STSConfig
267 277
 		CheckIdent           bool `yaml:"check-ident"`
268 278
 		MOTD                 string
@@ -694,5 +704,18 @@ func LoadConfig(filename string) (config *Config, err error) {
694 704
 		config.History.ClientLength = 0
695 705
 	}
696 706
 
707
+	for _, listenAddress := range config.Server.TorListeners.Listeners {
708
+		found := false
709
+		for _, configuredListener := range config.Server.Listen {
710
+			if listenAddress == configuredListener {
711
+				found = true
712
+				break
713
+			}
714
+		}
715
+		if !found {
716
+			return nil, fmt.Errorf("%s is configured as a Tor listener, but is not in server.listen", listenAddress)
717
+		}
718
+	}
719
+
697 720
 	return config, nil
698 721
 }

+ 55
- 0
irc/connection_limits/tor.go 查看文件

@@ -0,0 +1,55 @@
1
+// Copyright (c) 2019 Shivaram Lingamneni <slingamn@cs.stanford.edu>
2
+// released under the MIT license
3
+
4
+package connection_limits
5
+
6
+import (
7
+	"errors"
8
+	"sync"
9
+	"time"
10
+)
11
+
12
+var (
13
+	ErrLimitExceeded    = errors.New("too many concurrent connections")
14
+	ErrThrottleExceeded = errors.New("too many recent connection attempts")
15
+)
16
+
17
+// TorLimiter is a combined limiter and throttler for use on connections
18
+// proxied from a Tor hidden service (so we don't have meaningful IPs,
19
+// a notion of CIDR width, etc.)
20
+type TorLimiter struct {
21
+	sync.Mutex
22
+
23
+	numConnections int
24
+	maxConnections int
25
+	throttle       GenericThrottle
26
+}
27
+
28
+func (tl *TorLimiter) Configure(maxConnections int, duration time.Duration, maxConnectionsPerDuration int) {
29
+	tl.Lock()
30
+	defer tl.Unlock()
31
+	tl.maxConnections = maxConnections
32
+	tl.throttle.Duration = duration
33
+	tl.throttle.Limit = maxConnectionsPerDuration
34
+}
35
+
36
+func (tl *TorLimiter) AddClient() error {
37
+	tl.Lock()
38
+	defer tl.Unlock()
39
+
40
+	if tl.maxConnections != 0 && tl.maxConnections <= tl.numConnections {
41
+		return ErrLimitExceeded
42
+	}
43
+	throttled, _ := tl.throttle.Touch()
44
+	if throttled {
45
+		return ErrThrottleExceeded
46
+	}
47
+	tl.numConnections += 1
48
+	return nil
49
+}
50
+
51
+func (tl *TorLimiter) RemoveClient() {
52
+	tl.Lock()
53
+	tl.numConnections -= 1
54
+	tl.Unlock()
55
+}

+ 11
- 3
irc/gateways.go 查看文件

@@ -47,6 +47,12 @@ func (wc *webircConfig) Populate() (err error) {
47 47
 
48 48
 // ApplyProxiedIP applies the given IP to the client.
49 49
 func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (success bool) {
50
+	// PROXY and WEBIRC are never accepted from a Tor listener, even if the address itself
51
+	// is whitelisted:
52
+	if client.isTor {
53
+		return false
54
+	}
55
+
50 56
 	// ensure IP is sane
51 57
 	parsedProxiedIP := net.ParseIP(proxiedIP).To16()
52 58
 	if parsedProxiedIP == nil {
@@ -61,13 +67,15 @@ func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (success bool)
61 67
 	}
62 68
 
63 69
 	// given IP is sane! override the client's current IP
64
-	rawHostname := utils.LookupHostname(parsedProxiedIP.String())
70
+	ipstring := parsedProxiedIP.String()
71
+	client.server.logger.Info("localconnect-ip", "Accepted proxy IP for client", ipstring)
72
+	rawHostname := utils.LookupHostname(ipstring)
73
+
65 74
 	client.stateMutex.Lock()
75
+	defer client.stateMutex.Unlock()
66 76
 	client.proxiedIP = parsedProxiedIP
67 77
 	client.rawHostname = rawHostname
68
-	client.stateMutex.Unlock()
69 78
 	// nickmask will be updated when the client completes registration
70
-
71 79
 	// set tls info
72 80
 	client.certfp = ""
73 81
 	client.SetMode(modes.TLS, tls)

+ 7
- 0
irc/getters.go 查看文件

@@ -149,6 +149,13 @@ func (client *Client) SetRegistered() {
149 149
 	client.stateMutex.Unlock()
150 150
 }
151 151
 
152
+func (client *Client) RawHostname() (result string) {
153
+	client.stateMutex.Lock()
154
+	result = client.rawHostname
155
+	client.stateMutex.Unlock()
156
+	return
157
+}
158
+
152 159
 func (client *Client) AwayMessage() (result string) {
153 160
 	client.stateMutex.RLock()
154 161
 	result = client.awayMessage

+ 22
- 2
irc/handlers.go 查看文件

@@ -1878,6 +1878,11 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1878 1878
 	targets := strings.Split(msg.Params[0], ",")
1879 1879
 	message := msg.Params[1]
1880 1880
 
1881
+	if client.isTor && isRestrictedCTCPMessage(message) {
1882
+		rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor"))
1883
+		return false
1884
+	}
1885
+
1881 1886
 	// split privmsg
1882 1887
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
1883 1888
 
@@ -1924,7 +1929,9 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1924 1929
 			msgid := server.generateMessageID()
1925 1930
 			// restrict messages appropriately when +R is set
1926 1931
 			// intentionally make the sending user think the message went through fine
1927
-			if !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() {
1932
+			allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
1933
+			allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
1934
+			if allowedPlusR && allowedTor {
1928 1935
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1929 1936
 			}
1930 1937
 			nickMaskString := client.NickMaskString()
@@ -2081,12 +2088,23 @@ func pongHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2081 2088
 	return false
2082 2089
 }
2083 2090
 
2091
+func isRestrictedCTCPMessage(message string) bool {
2092
+	// block all CTCP privmsgs to Tor clients except for ACTION
2093
+	// DCC can potentially be used for deanonymization, the others for fingerprinting
2094
+	return strings.HasPrefix(message, "\x01") && !strings.HasPrefix(message, "\x01ACTION")
2095
+}
2096
+
2084 2097
 // PRIVMSG <target>{,<target>} <message>
2085 2098
 func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2086 2099
 	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
2087 2100
 	targets := strings.Split(msg.Params[0], ",")
2088 2101
 	message := msg.Params[1]
2089 2102
 
2103
+	if client.isTor && isRestrictedCTCPMessage(message) {
2104
+		rb.Add(nil, server.name, "NOTICE", client.t("CTCP messages are disabled over Tor"))
2105
+		return false
2106
+	}
2107
+
2090 2108
 	// split privmsg
2091 2109
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
2092 2110
 
@@ -2136,7 +2154,9 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
2136 2154
 			msgid := server.generateMessageID()
2137 2155
 			// restrict messages appropriately when +R is set
2138 2156
 			// intentionally make the sending user think the message went through fine
2139
-			if !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() {
2157
+			allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
2158
+			allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
2159
+			if allowedPlusR && allowedTor {
2140 2160
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2141 2161
 			}
2142 2162
 			nickMaskString := client.NickMaskString()

+ 4
- 1
irc/resume.go 查看文件

@@ -52,7 +52,8 @@ func (rm *ResumeManager) GenerateToken(client *Client) (token string) {
52 52
 }
53 53
 
54 54
 // VerifyToken looks up the client corresponding to a resume token, returning
55
-// nil if there is no such client or the token is invalid.
55
+// nil if there is no such client or the token is invalid. If successful,
56
+// the token is consumed and cannot be used to resume again.
56 57
 func (rm *ResumeManager) VerifyToken(token string) (client *Client) {
57 58
 	if len(token) != 2*utils.SecretTokenLength {
58 59
 		return
@@ -68,6 +69,8 @@ func (rm *ResumeManager) VerifyToken(token string) (client *Client) {
68 69
 			// disallow resume of an unregistered client; this prevents the use of
69 70
 			// resume as an auth bypass
70 71
 			if pair.client.Registered() {
72
+				// consume the token, ensuring that at most one resume can succeed
73
+				delete(rm.resumeIDtoCreds, id)
71 74
 				return pair.client
72 75
 			}
73 76
 		}

+ 5
- 0
irc/roleplay.go 查看文件

@@ -19,6 +19,11 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
19 19
 	if isAction {
20 20
 		message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
21 21
 	} else {
22
+		// block attempts to send CTCP messages to Tor clients
23
+		// TODO(#395) clean this up
24
+		if len(message) != 0 && message[0] == '\x01' {
25
+			return
26
+		}
22 27
 		message = fmt.Sprintf("%s (%s)", message, client.nick)
23 28
 	}
24 29
 

+ 59
- 26
irc/server.go 查看文件

@@ -21,7 +21,6 @@ import (
21 21
 	"time"
22 22
 
23 23
 	"github.com/goshuirc/irc-go/ircfmt"
24
-	"github.com/goshuirc/irc-go/ircmsg"
25 24
 	"github.com/oragono/oragono/irc/caps"
26 25
 	"github.com/oragono/oragono/irc/connection_limits"
27 26
 	"github.com/oragono/oragono/irc/isupport"
@@ -34,10 +33,7 @@ import (
34 33
 
35 34
 var (
36 35
 	// common error line to sub values into
37
-	errorMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "%s ")}[0]).Line()
38
-
39
-	// common error responses
40
-	couldNotParseIPMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "Unable to parse your IP address")}[0]).Line()
36
+	errorMsg = "ERROR :%s\r\n"
41 37
 
42 38
 	// supportedUserModesString acts as a cache for when we introduce users
43 39
 	supportedUserModesString = modes.SupportedUserModes.String()
@@ -57,6 +53,7 @@ var (
57 53
 type ListenerWrapper struct {
58 54
 	listener   net.Listener
59 55
 	tlsConfig  *tls.Config
56
+	isTor      bool
60 57
 	shouldStop bool
61 58
 	// protects atomic update of tlsConfig and shouldStop:
62 59
 	configMutex sync.Mutex // tier 1
@@ -91,6 +88,7 @@ type Server struct {
91 88
 	signals                chan os.Signal
92 89
 	snomasks               *SnoManager
93 90
 	store                  *buntdb.DB
91
+	torLimiter             connection_limits.TorLimiter
94 92
 	whoWas                 *WhoWasList
95 93
 	stats                  *Stats
96 94
 	semaphores             *ServerSemaphores
@@ -108,6 +106,7 @@ var (
108 106
 type clientConn struct {
109 107
 	Conn  net.Conn
110 108
 	IsTLS bool
109
+	IsTor bool
111 110
 }
112 111
 
113 112
 // NewServer returns a new Oragono server.
@@ -245,22 +244,27 @@ func (server *Server) Run() {
245 244
 }
246 245
 
247 246
 func (server *Server) acceptClient(conn clientConn) {
248
-	// check IP address
249
-	ipaddr := utils.AddrToIP(conn.Conn.RemoteAddr())
250
-	if ipaddr != nil {
251
-		isBanned, banMsg := server.checkBans(ipaddr)
252
-		if isBanned {
253
-			// this might not show up properly on some clients, but our objective here is just to close the connection out before it has a load impact on us
254
-			conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
255
-			conn.Conn.Close()
256
-			return
257
-		}
247
+	var isBanned bool
248
+	var banMsg string
249
+	var ipaddr net.IP
250
+	if conn.IsTor {
251
+		ipaddr = utils.IPv4LoopbackAddress
252
+		isBanned, banMsg = server.checkTorLimits()
253
+	} else {
254
+		ipaddr = utils.AddrToIP(conn.Conn.RemoteAddr())
255
+		isBanned, banMsg = server.checkBans(ipaddr)
256
+	}
257
+
258
+	if isBanned {
259
+		// this might not show up properly on some clients, but our objective here is just to close the connection out before it has a load impact on us
260
+		conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
261
+		conn.Conn.Close()
262
+		return
258 263
 	}
259 264
 
260 265
 	server.logger.Info("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
261
-	// prolly don't need to alert snomasks on this, only on connection reg
262 266
 
263
-	NewClient(server, conn.Conn, conn.IsTLS)
267
+	go RunNewClient(server, conn)
264 268
 }
265 269
 
266 270
 func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
@@ -303,12 +307,23 @@ func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
303 307
 	return false, ""
304 308
 }
305 309
 
310
+func (server *Server) checkTorLimits() (banned bool, message string) {
311
+	switch server.torLimiter.AddClient() {
312
+	case connection_limits.ErrLimitExceeded:
313
+		return true, "Too many clients from the Tor network"
314
+	case connection_limits.ErrThrottleExceeded:
315
+		return true, "Exceeded connection throttle for the Tor network"
316
+	default:
317
+		return false, ""
318
+	}
319
+}
320
+
306 321
 //
307 322
 // IRC protocol listeners
308 323
 //
309 324
 
310 325
 // createListener starts a given listener.
311
-func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMode os.FileMode) (*ListenerWrapper, error) {
326
+func (server *Server) createListener(addr string, tlsConfig *tls.Config, isTor bool, bindMode os.FileMode) (*ListenerWrapper, error) {
312 327
 	// make listener
313 328
 	var listener net.Listener
314 329
 	var err error
@@ -331,6 +346,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMod
331 346
 	wrapper := ListenerWrapper{
332 347
 		listener:   listener,
333 348
 		tlsConfig:  tlsConfig,
349
+		isTor:      isTor,
334 350
 		shouldStop: false,
335 351
 	}
336 352
 
@@ -342,10 +358,10 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMod
342 358
 			conn, err := listener.Accept()
343 359
 
344 360
 			// synchronously access config data:
345
-			// whether TLS is enabled and whether we should stop listening
346 361
 			wrapper.configMutex.Lock()
347 362
 			shouldStop = wrapper.shouldStop
348 363
 			tlsConfig = wrapper.tlsConfig
364
+			isTor = wrapper.isTor
349 365
 			wrapper.configMutex.Unlock()
350 366
 
351 367
 			if err == nil {
@@ -355,6 +371,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMod
355 371
 				newConn := clientConn{
356 372
 					Conn:  conn,
357 373
 					IsTLS: tlsConfig != nil,
374
+					IsTor: isTor,
358 375
 				}
359 376
 				// hand off the connection
360 377
 				go server.acceptClient(newConn)
@@ -515,7 +532,7 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
515 532
 		rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine)
516 533
 	}
517 534
 	if client.HasMode(modes.Operator) || client == target {
518
-		rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, cnick, tnick, fmt.Sprintf("%s@%s", target.username, utils.LookupHostname(target.IPString())), target.IPString(), client.t("Actual user@host, Actual IP"))
535
+		rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, cnick, tnick, fmt.Sprintf("%s@%s", targetInfo.username, target.RawHostname()), target.IPString(), client.t("Actual user@host, Actual IP"))
519 536
 	}
520 537
 	if target.HasMode(modes.TLS) {
521 538
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, cnick, tnick, client.t("is using a secure connection"))
@@ -631,6 +648,9 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
631 648
 		return err
632 649
 	}
633 650
 
651
+	tlConf := &config.Server.TorListeners
652
+	server.torLimiter.Configure(tlConf.MaxConnections, tlConf.ThrottleDuration, tlConf.MaxConnectionsPerDuration)
653
+
634 654
 	// reload logging config
635 655
 	wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO()
636 656
 	err = server.logger.ApplyConfig(config.Logging)
@@ -908,9 +928,9 @@ func (server *Server) loadDatastore(config *Config) error {
908 928
 }
909 929
 
910 930
 func (server *Server) setupListeners(config *Config) (err error) {
911
-	logListener := func(addr string, tlsconfig *tls.Config) {
931
+	logListener := func(addr string, tlsconfig *tls.Config, isTor bool) {
912 932
 		server.logger.Info("listeners",
913
-			fmt.Sprintf("now listening on %s, tls=%t.", addr, (tlsconfig != nil)),
933
+			fmt.Sprintf("now listening on %s, tls=%t, tor=%t.", addr, (tlsconfig != nil), isTor),
914 934
 		)
915 935
 	}
916 936
 
@@ -920,6 +940,15 @@ func (server *Server) setupListeners(config *Config) (err error) {
920 940
 		return
921 941
 	}
922 942
 
943
+	isTorListener := func(listener string) bool {
944
+		for _, torListener := range config.Server.TorListeners.Listeners {
945
+			if listener == torListener {
946
+				return true
947
+			}
948
+		}
949
+		return false
950
+	}
951
+
923 952
 	// update or destroy all existing listeners
924 953
 	for addr := range server.listeners {
925 954
 		currentListener := server.listeners[addr]
@@ -935,13 +964,16 @@ func (server *Server) setupListeners(config *Config) (err error) {
935 964
 		// its next Accept(). this is like sending over a buffered channel of
936 965
 		// size 1, but where sending a second item overwrites the buffered item
937 966
 		// instead of blocking.
967
+		tlsConfig := tlsListeners[addr]
968
+		isTor := isTorListener(addr)
938 969
 		currentListener.configMutex.Lock()
939 970
 		currentListener.shouldStop = !stillConfigured
940
-		currentListener.tlsConfig = tlsListeners[addr]
971
+		currentListener.tlsConfig = tlsConfig
972
+		currentListener.isTor = isTor
941 973
 		currentListener.configMutex.Unlock()
942 974
 
943 975
 		if stillConfigured {
944
-			logListener(addr, currentListener.tlsConfig)
976
+			logListener(addr, tlsConfig, isTor)
945 977
 		} else {
946 978
 			// tell the listener it should stop by interrupting its Accept() call:
947 979
 			currentListener.listener.Close()
@@ -955,15 +987,16 @@ func (server *Server) setupListeners(config *Config) (err error) {
955 987
 		_, exists := server.listeners[newaddr]
956 988
 		if !exists {
957 989
 			// make new listener
990
+			isTor := isTorListener(newaddr)
958 991
 			tlsConfig := tlsListeners[newaddr]
959
-			listener, listenerErr := server.createListener(newaddr, tlsConfig, config.Server.UnixBindMode)
992
+			listener, listenerErr := server.createListener(newaddr, tlsConfig, isTor, config.Server.UnixBindMode)
960 993
 			if listenerErr != nil {
961 994
 				server.logger.Error("server", "couldn't listen on", newaddr, listenerErr.Error())
962 995
 				err = listenerErr
963 996
 				continue
964 997
 			}
965 998
 			server.listeners[newaddr] = listener
966
-			logListener(newaddr, tlsConfig)
999
+			logListener(newaddr, tlsConfig, isTor)
967 1000
 		}
968 1001
 	}
969 1002
 

+ 2
- 2
irc/utils/crypto.go 查看文件

@@ -10,8 +10,8 @@ import (
10 10
 )
11 11
 
12 12
 var (
13
-	// standard b32 alphabet, but in lowercase for silly aesthetic reasons
14
-	b32encoder = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
13
+	// slingamn's own private b32 alphabet, removing 1, l, o, and 0
14
+	b32encoder = base32.NewEncoding("abcdefghijkmnpqrstuvwxyz23456789").WithPadding(base32.NoPadding)
15 15
 )
16 16
 
17 17
 const (

+ 24
- 0
oragono.yaml 查看文件

@@ -33,6 +33,30 @@ server:
33 33
             key: tls.key
34 34
             cert: tls.crt
35 35
 
36
+    # tor listeners: designate listeners for use by a tor hidden service / .onion address
37
+    # WARNING: if you are running oragono as a pure hidden service, see the
38
+    # anonymization / hardening recommendations in docs/MANUAL.md
39
+    tor-listeners:
40
+        # any connections that come in on these listeners will be considered
41
+        # Tor connections. it is strongly recommended that these listeners *not*
42
+        # be on public interfaces: they should be on 127.0.0.0/8 or unix domain
43
+        listeners:
44
+        #    - "/tmp/oragono_tor_sock"
45
+
46
+        # if this is true, connections from Tor must authenticate with SASL
47
+        require-sasl: false
48
+
49
+        # what hostname should be displayed for Tor connections?
50
+        vhost: "tor-network.onion"
51
+
52
+        # allow at most this many connections at once (0 for no limit):
53
+        max-connections: 64
54
+
55
+        # connection throttling (limit how many connection attempts are allowed at once):
56
+        throttle-duration: 10m
57
+        # set to 0 to disable throttling:
58
+        max-connections-per-duration: 64
59
+
36 60
     # strict transport security, to get clients to automagically use TLS
37 61
     sts:
38 62
         # whether to advertise STS

Loading…
取消
儲存