Преглед изворни кода

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
     - Channel Modes
32
     - Channel Modes
33
     - Channel Prefixes
33
     - Channel Prefixes
34
 - Commands
34
 - Commands
35
-- Integrating with other software
35
+- Working with other software
36
     - HOPM
36
     - HOPM
37
+    - ZNC
38
+    - Tor
37
 - Acknowledgements
39
 - Acknowledgements
38
 
40
 
39
 
41
 
541
 --------------------------------------------------------------------------------------------
543
 --------------------------------------------------------------------------------------------
542
 
544
 
543
 
545
 
544
-# Integrating with other software
546
+# Working with other software
545
 
547
 
546
 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.
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
 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.
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
 	idletimer          *IdleTimer
65
 	idletimer          *IdleTimer
66
 	invitedTo          map[string]bool
66
 	invitedTo          map[string]bool
67
 	isDestroyed        bool
67
 	isDestroyed        bool
68
+	isTor              bool
68
 	isQuitting         bool
69
 	isQuitting         bool
69
 	languages          []string
70
 	languages          []string
70
 	loginThrottle      connection_limits.GenericThrottle
71
 	loginThrottle      connection_limits.GenericThrottle
117
 	accountName        string
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
 	now := time.Now()
123
 	now := time.Now()
123
 	config := server.Config()
124
 	config := server.Config()
124
 	fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest
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
 	client := &Client{
127
 	client := &Client{
127
 		atime:        now,
128
 		atime:        now,
128
 		capabilities: caps.NewSet(),
129
 		capabilities: caps.NewSet(),
131
 		channels:     make(ChannelSet),
132
 		channels:     make(ChannelSet),
132
 		ctime:        now,
133
 		ctime:        now,
133
 		flags:        modes.NewModeSet(),
134
 		flags:        modes.NewModeSet(),
135
+		isTor:        conn.IsTor,
134
 		languages:    server.Languages().Default(),
136
 		languages:    server.Languages().Default(),
135
 		loginThrottle: connection_limits.GenericThrottle{
137
 		loginThrottle: connection_limits.GenericThrottle{
136
 			Duration: config.Accounts.LoginThrottling.Duration,
138
 			Duration: config.Accounts.LoginThrottling.Duration,
145
 		history:        history.NewHistoryBuffer(config.History.ClientLength),
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
 	client.recomputeMaxlens()
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
 		// error is not useful to us here anyways so we can ignore it
154
 		// error is not useful to us here anyways so we can ignore it
159
 		client.certfp, _ = client.socket.CertFP()
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
 		if err == nil {
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
 		} else {
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
 func (client *Client) isAuthorized(config *Config) bool {
205
 func (client *Client) isAuthorized(config *Config) bool {
193
 	saslSent := client.account != ""
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
 		return false
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
 func (client *Client) resetFakelag() {
219
 func (client *Client) resetFakelag() {
296
 
313
 
297
 	client.resetFakelag()
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
 	firstLine := true
316
 	firstLine := true
304
 
317
 
305
 	for {
318
 	for {
315
 			break
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
 		// special-cased handling of PROXY protocol, see `handleProxyCommand` for details:
335
 		// special-cased handling of PROXY protocol, see `handleProxyCommand` for details:
321
 		if firstLine {
336
 		if firstLine {
403
 		return
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
 	err := server.clients.Resume(client, oldClient)
426
 	err := server.clients.Resume(client, oldClient)
407
 	if err != nil {
427
 	if err != nil {
408
 		client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection"))
428
 		client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection"))
882
 	}
902
 	}
883
 
903
 
884
 	// remove from connection limits
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
 	client.server.resumeManager.Delete(client)
911
 	client.server.resumeManager.Delete(client)

+ 23
- 0
irc/config.go Прегледај датотеку

249
 	Cooldown          time.Duration
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
 // Config defines the overall configuration.
261
 // Config defines the overall configuration.
253
 type Config struct {
262
 type Config struct {
254
 	Network struct {
263
 	Network struct {
263
 		Listen               []string
272
 		Listen               []string
264
 		UnixBindMode         os.FileMode                 `yaml:"unix-bind-mode"`
273
 		UnixBindMode         os.FileMode                 `yaml:"unix-bind-mode"`
265
 		TLSListeners         map[string]*TLSListenConfig `yaml:"tls-listeners"`
274
 		TLSListeners         map[string]*TLSListenConfig `yaml:"tls-listeners"`
275
+		TorListeners         TorListenersConfig          `yaml:"tor-listeners"`
266
 		STS                  STSConfig
276
 		STS                  STSConfig
267
 		CheckIdent           bool `yaml:"check-ident"`
277
 		CheckIdent           bool `yaml:"check-ident"`
268
 		MOTD                 string
278
 		MOTD                 string
694
 		config.History.ClientLength = 0
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
 	return config, nil
720
 	return config, nil
698
 }
721
 }

+ 55
- 0
irc/connection_limits/tor.go Прегледај датотеку

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
 
47
 
48
 // ApplyProxiedIP applies the given IP to the client.
48
 // ApplyProxiedIP applies the given IP to the client.
49
 func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (success bool) {
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
 	// ensure IP is sane
56
 	// ensure IP is sane
51
 	parsedProxiedIP := net.ParseIP(proxiedIP).To16()
57
 	parsedProxiedIP := net.ParseIP(proxiedIP).To16()
52
 	if parsedProxiedIP == nil {
58
 	if parsedProxiedIP == nil {
61
 	}
67
 	}
62
 
68
 
63
 	// given IP is sane! override the client's current IP
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
 	client.stateMutex.Lock()
74
 	client.stateMutex.Lock()
75
+	defer client.stateMutex.Unlock()
66
 	client.proxiedIP = parsedProxiedIP
76
 	client.proxiedIP = parsedProxiedIP
67
 	client.rawHostname = rawHostname
77
 	client.rawHostname = rawHostname
68
-	client.stateMutex.Unlock()
69
 	// nickmask will be updated when the client completes registration
78
 	// nickmask will be updated when the client completes registration
70
-
71
 	// set tls info
79
 	// set tls info
72
 	client.certfp = ""
80
 	client.certfp = ""
73
 	client.SetMode(modes.TLS, tls)
81
 	client.SetMode(modes.TLS, tls)

+ 7
- 0
irc/getters.go Прегледај датотеку

149
 	client.stateMutex.Unlock()
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
 func (client *Client) AwayMessage() (result string) {
159
 func (client *Client) AwayMessage() (result string) {
153
 	client.stateMutex.RLock()
160
 	client.stateMutex.RLock()
154
 	result = client.awayMessage
161
 	result = client.awayMessage

+ 22
- 2
irc/handlers.go Прегледај датотеку

1878
 	targets := strings.Split(msg.Params[0], ",")
1878
 	targets := strings.Split(msg.Params[0], ",")
1879
 	message := msg.Params[1]
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
 	// split privmsg
1886
 	// split privmsg
1882
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
1887
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
1883
 
1888
 
1924
 			msgid := server.generateMessageID()
1929
 			msgid := server.generateMessageID()
1925
 			// restrict messages appropriately when +R is set
1930
 			// restrict messages appropriately when +R is set
1926
 			// intentionally make the sending user think the message went through fine
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
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1935
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1929
 			}
1936
 			}
1930
 			nickMaskString := client.NickMaskString()
1937
 			nickMaskString := client.NickMaskString()
2081
 	return false
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
 // PRIVMSG <target>{,<target>} <message>
2097
 // PRIVMSG <target>{,<target>} <message>
2085
 func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2098
 func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2086
 	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
2099
 	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
2087
 	targets := strings.Split(msg.Params[0], ",")
2100
 	targets := strings.Split(msg.Params[0], ",")
2088
 	message := msg.Params[1]
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
 	// split privmsg
2108
 	// split privmsg
2091
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
2109
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
2092
 
2110
 
2136
 			msgid := server.generateMessageID()
2154
 			msgid := server.generateMessageID()
2137
 			// restrict messages appropriately when +R is set
2155
 			// restrict messages appropriately when +R is set
2138
 			// intentionally make the sending user think the message went through fine
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
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2160
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2141
 			}
2161
 			}
2142
 			nickMaskString := client.NickMaskString()
2162
 			nickMaskString := client.NickMaskString()

+ 4
- 1
irc/resume.go Прегледај датотеку

52
 }
52
 }
53
 
53
 
54
 // VerifyToken looks up the client corresponding to a resume token, returning
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
 func (rm *ResumeManager) VerifyToken(token string) (client *Client) {
57
 func (rm *ResumeManager) VerifyToken(token string) (client *Client) {
57
 	if len(token) != 2*utils.SecretTokenLength {
58
 	if len(token) != 2*utils.SecretTokenLength {
58
 		return
59
 		return
68
 			// disallow resume of an unregistered client; this prevents the use of
69
 			// disallow resume of an unregistered client; this prevents the use of
69
 			// resume as an auth bypass
70
 			// resume as an auth bypass
70
 			if pair.client.Registered() {
71
 			if pair.client.Registered() {
72
+				// consume the token, ensuring that at most one resume can succeed
73
+				delete(rm.resumeIDtoCreds, id)
71
 				return pair.client
74
 				return pair.client
72
 			}
75
 			}
73
 		}
76
 		}

+ 5
- 0
irc/roleplay.go Прегледај датотеку

19
 	if isAction {
19
 	if isAction {
20
 		message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
20
 		message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
21
 	} else {
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
 		message = fmt.Sprintf("%s (%s)", message, client.nick)
27
 		message = fmt.Sprintf("%s (%s)", message, client.nick)
23
 	}
28
 	}
24
 
29
 

+ 59
- 26
irc/server.go Прегледај датотеку

21
 	"time"
21
 	"time"
22
 
22
 
23
 	"github.com/goshuirc/irc-go/ircfmt"
23
 	"github.com/goshuirc/irc-go/ircfmt"
24
-	"github.com/goshuirc/irc-go/ircmsg"
25
 	"github.com/oragono/oragono/irc/caps"
24
 	"github.com/oragono/oragono/irc/caps"
26
 	"github.com/oragono/oragono/irc/connection_limits"
25
 	"github.com/oragono/oragono/irc/connection_limits"
27
 	"github.com/oragono/oragono/irc/isupport"
26
 	"github.com/oragono/oragono/irc/isupport"
34
 
33
 
35
 var (
34
 var (
36
 	// common error line to sub values into
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
 	// supportedUserModesString acts as a cache for when we introduce users
38
 	// supportedUserModesString acts as a cache for when we introduce users
43
 	supportedUserModesString = modes.SupportedUserModes.String()
39
 	supportedUserModesString = modes.SupportedUserModes.String()
57
 type ListenerWrapper struct {
53
 type ListenerWrapper struct {
58
 	listener   net.Listener
54
 	listener   net.Listener
59
 	tlsConfig  *tls.Config
55
 	tlsConfig  *tls.Config
56
+	isTor      bool
60
 	shouldStop bool
57
 	shouldStop bool
61
 	// protects atomic update of tlsConfig and shouldStop:
58
 	// protects atomic update of tlsConfig and shouldStop:
62
 	configMutex sync.Mutex // tier 1
59
 	configMutex sync.Mutex // tier 1
91
 	signals                chan os.Signal
88
 	signals                chan os.Signal
92
 	snomasks               *SnoManager
89
 	snomasks               *SnoManager
93
 	store                  *buntdb.DB
90
 	store                  *buntdb.DB
91
+	torLimiter             connection_limits.TorLimiter
94
 	whoWas                 *WhoWasList
92
 	whoWas                 *WhoWasList
95
 	stats                  *Stats
93
 	stats                  *Stats
96
 	semaphores             *ServerSemaphores
94
 	semaphores             *ServerSemaphores
108
 type clientConn struct {
106
 type clientConn struct {
109
 	Conn  net.Conn
107
 	Conn  net.Conn
110
 	IsTLS bool
108
 	IsTLS bool
109
+	IsTor bool
111
 }
110
 }
112
 
111
 
113
 // NewServer returns a new Oragono server.
112
 // NewServer returns a new Oragono server.
245
 }
244
 }
246
 
245
 
247
 func (server *Server) acceptClient(conn clientConn) {
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
 	server.logger.Info("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
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
 func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
270
 func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
303
 	return false, ""
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
 // IRC protocol listeners
322
 // IRC protocol listeners
308
 //
323
 //
309
 
324
 
310
 // createListener starts a given listener.
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
 	// make listener
327
 	// make listener
313
 	var listener net.Listener
328
 	var listener net.Listener
314
 	var err error
329
 	var err error
331
 	wrapper := ListenerWrapper{
346
 	wrapper := ListenerWrapper{
332
 		listener:   listener,
347
 		listener:   listener,
333
 		tlsConfig:  tlsConfig,
348
 		tlsConfig:  tlsConfig,
349
+		isTor:      isTor,
334
 		shouldStop: false,
350
 		shouldStop: false,
335
 	}
351
 	}
336
 
352
 
342
 			conn, err := listener.Accept()
358
 			conn, err := listener.Accept()
343
 
359
 
344
 			// synchronously access config data:
360
 			// synchronously access config data:
345
-			// whether TLS is enabled and whether we should stop listening
346
 			wrapper.configMutex.Lock()
361
 			wrapper.configMutex.Lock()
347
 			shouldStop = wrapper.shouldStop
362
 			shouldStop = wrapper.shouldStop
348
 			tlsConfig = wrapper.tlsConfig
363
 			tlsConfig = wrapper.tlsConfig
364
+			isTor = wrapper.isTor
349
 			wrapper.configMutex.Unlock()
365
 			wrapper.configMutex.Unlock()
350
 
366
 
351
 			if err == nil {
367
 			if err == nil {
355
 				newConn := clientConn{
371
 				newConn := clientConn{
356
 					Conn:  conn,
372
 					Conn:  conn,
357
 					IsTLS: tlsConfig != nil,
373
 					IsTLS: tlsConfig != nil,
374
+					IsTor: isTor,
358
 				}
375
 				}
359
 				// hand off the connection
376
 				// hand off the connection
360
 				go server.acceptClient(newConn)
377
 				go server.acceptClient(newConn)
515
 		rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine)
532
 		rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine)
516
 	}
533
 	}
517
 	if client.HasMode(modes.Operator) || client == target {
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
 	if target.HasMode(modes.TLS) {
537
 	if target.HasMode(modes.TLS) {
521
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, cnick, tnick, client.t("is using a secure connection"))
538
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, cnick, tnick, client.t("is using a secure connection"))
631
 		return err
648
 		return err
632
 	}
649
 	}
633
 
650
 
651
+	tlConf := &config.Server.TorListeners
652
+	server.torLimiter.Configure(tlConf.MaxConnections, tlConf.ThrottleDuration, tlConf.MaxConnectionsPerDuration)
653
+
634
 	// reload logging config
654
 	// reload logging config
635
 	wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO()
655
 	wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO()
636
 	err = server.logger.ApplyConfig(config.Logging)
656
 	err = server.logger.ApplyConfig(config.Logging)
908
 }
928
 }
909
 
929
 
910
 func (server *Server) setupListeners(config *Config) (err error) {
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
 		server.logger.Info("listeners",
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
 		return
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
 	// update or destroy all existing listeners
952
 	// update or destroy all existing listeners
924
 	for addr := range server.listeners {
953
 	for addr := range server.listeners {
925
 		currentListener := server.listeners[addr]
954
 		currentListener := server.listeners[addr]
935
 		// its next Accept(). this is like sending over a buffered channel of
964
 		// its next Accept(). this is like sending over a buffered channel of
936
 		// size 1, but where sending a second item overwrites the buffered item
965
 		// size 1, but where sending a second item overwrites the buffered item
937
 		// instead of blocking.
966
 		// instead of blocking.
967
+		tlsConfig := tlsListeners[addr]
968
+		isTor := isTorListener(addr)
938
 		currentListener.configMutex.Lock()
969
 		currentListener.configMutex.Lock()
939
 		currentListener.shouldStop = !stillConfigured
970
 		currentListener.shouldStop = !stillConfigured
940
-		currentListener.tlsConfig = tlsListeners[addr]
971
+		currentListener.tlsConfig = tlsConfig
972
+		currentListener.isTor = isTor
941
 		currentListener.configMutex.Unlock()
973
 		currentListener.configMutex.Unlock()
942
 
974
 
943
 		if stillConfigured {
975
 		if stillConfigured {
944
-			logListener(addr, currentListener.tlsConfig)
976
+			logListener(addr, tlsConfig, isTor)
945
 		} else {
977
 		} else {
946
 			// tell the listener it should stop by interrupting its Accept() call:
978
 			// tell the listener it should stop by interrupting its Accept() call:
947
 			currentListener.listener.Close()
979
 			currentListener.listener.Close()
955
 		_, exists := server.listeners[newaddr]
987
 		_, exists := server.listeners[newaddr]
956
 		if !exists {
988
 		if !exists {
957
 			// make new listener
989
 			// make new listener
990
+			isTor := isTorListener(newaddr)
958
 			tlsConfig := tlsListeners[newaddr]
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
 			if listenerErr != nil {
993
 			if listenerErr != nil {
961
 				server.logger.Error("server", "couldn't listen on", newaddr, listenerErr.Error())
994
 				server.logger.Error("server", "couldn't listen on", newaddr, listenerErr.Error())
962
 				err = listenerErr
995
 				err = listenerErr
963
 				continue
996
 				continue
964
 			}
997
 			}
965
 			server.listeners[newaddr] = listener
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
 )
10
 )
11
 
11
 
12
 var (
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
 const (
17
 const (

+ 24
- 0
oragono.yaml Прегледај датотеку

33
             key: tls.key
33
             key: tls.key
34
             cert: tls.crt
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
     # strict transport security, to get clients to automagically use TLS
60
     # strict transport security, to get clients to automagically use TLS
37
     sts:
61
     sts:
38
         # whether to advertise STS
62
         # whether to advertise STS

Loading…
Откажи
Сачувај