Bladeren bron

Issue #68, initial dnsbl system

devel+dnsbl
moocow 6 jaren geleden
bovenliggende
commit
5e7aceb75e
8 gewijzigde bestanden met toevoegingen van 224 en 2 verwijderingen
  1. 5
    0
      irc/client.go
  2. 40
    0
      irc/config.go
  3. 142
    0
      irc/dnsbl.go
  4. 24
    1
      irc/getters.go
  5. 1
    1
      irc/handlers.go
  6. 4
    0
      irc/modes.go
  7. 5
    0
      irc/server.go
  8. 3
    0
      irc/sno/constants.go

+ 5
- 0
irc/client.go Bestand weergeven

@@ -82,6 +82,8 @@ type Client struct {
82 82
 	username           string
83 83
 	vhost              string
84 84
 	whoisLine          string
85
+	requireSasl        bool
86
+	requireSaslReason  string
85 87
 }
86 88
 
87 89
 // NewClient returns a client with all the appropriate info setup.
@@ -108,6 +110,9 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
108 110
 	}
109 111
 	client.languages = server.languages.Default()
110 112
 
113
+	// Check IP towards speified DNSBLs
114
+	server.ProcessBlacklist(client)
115
+
111 116
 	client.recomputeMaxlens()
112 117
 	if isTLS {
113 118
 		client.flags[modes.TLS] = true

+ 40
- 0
irc/config.go Bestand weergeven

@@ -90,6 +90,25 @@ type AccountRegistrationConfig struct {
90 90
 	AllowMultiplePerConnection bool `yaml:"allow-multiple-per-connection"`
91 91
 }
92 92
 
93
+type DnsblConfig struct {
94
+	Enabled bool
95
+	Channel string
96
+	Lists   []DnsblListEntry `yaml:"lists"`
97
+}
98
+
99
+type DnsblListEntry struct {
100
+	Host   string
101
+	Types  []string
102
+	Reply  map[string]DnsblListReply
103
+	Action string
104
+	Reason string
105
+}
106
+
107
+type DnsblListReply struct {
108
+	Action string
109
+	Reason string
110
+}
111
+
93 112
 type NickReservationMethod int
94 113
 
95 114
 const (
@@ -263,6 +282,7 @@ type Config struct {
263 282
 		LineLen        LineLenConfig `yaml:"linelen"`
264 283
 	}
265 284
 
285
+	Dnsbl   DnsblConfig
266 286
 	Fakelag FakelagConfig
267 287
 
268 288
 	Filename string
@@ -471,6 +491,26 @@ func LoadConfig(filename string) (config *Config, err error) {
471 491
 		newWebIRC = append(newWebIRC, webirc)
472 492
 	}
473 493
 	config.Server.WebIRC = newWebIRC
494
+
495
+	for id, list := range config.Dnsbl.Lists {
496
+		var action, reason = list.Action, list.Reason
497
+
498
+		var newDnsblListReply = make(map[string]DnsblListReply)
499
+		for key, reply := range list.Reply {
500
+			if reply.Action == "" {
501
+				reply.Action = action
502
+			}
503
+			if reply.Reason == "" {
504
+				reply.Reason = reason
505
+			}
506
+
507
+			for _, newKey := range strings.Split(key, ",") {
508
+				newDnsblListReply[newKey] = reply
509
+			}
510
+		}
511
+		config.Dnsbl.Lists[id].Reply = newDnsblListReply
512
+	}
513
+
474 514
 	// process limits
475 515
 	if config.Limits.LineLen.Tags < 512 || config.Limits.LineLen.Rest < 512 {
476 516
 		return nil, ErrLineLengthsTooSmall

+ 142
- 0
irc/dnsbl.go Bestand weergeven

@@ -0,0 +1,142 @@
1
+package irc
2
+
3
+import (
4
+	"fmt"
5
+	"net"
6
+	"sort"
7
+	"strings"
8
+
9
+	"github.com/oragono/oragono/irc/sno"
10
+)
11
+
12
+func ReverseAddress(ip net.IP) string {
13
+	// This is a IPv4 address
14
+	if ip.To4() != nil {
15
+		address := strings.Split(ip.String(), ".")
16
+
17
+		for i, j := 0, len(address)-1; i < j; i, j = i+1, j-1 {
18
+			address[i], address[j] = address[j], address[i]
19
+		}
20
+
21
+		return strings.Join(address, ".")
22
+	}
23
+
24
+	// fallback to returning the String of IP if it is not an IPv4 address
25
+	return ip.String()
26
+}
27
+
28
+func LastIpOctet(addr string) string {
29
+	address := strings.Split(addr, ".")
30
+
31
+	return address[len(address)-1]
32
+}
33
+
34
+func (server *Server) LookupBlacklistEntry(list *DnsblListEntry, client *Client) []string {
35
+	res, err := net.LookupHost(fmt.Sprintf("%s.%s", ReverseAddress(client.IP()), list.Host))
36
+
37
+	var entries []string
38
+	if err != nil {
39
+		server.logger.Info("dnsbl-lookup", fmt.Sprintf("DNSBL loopup failed: %s", err))
40
+		return entries
41
+	}
42
+
43
+	if len(res) > 0 {
44
+		for _, addr := range res {
45
+			entries = append(entries, LastIpOctet(addr))
46
+		}
47
+	}
48
+
49
+	return entries
50
+}
51
+
52
+func sendDnsblMessage(client *Client, message string) {
53
+	/*fmt.Printf(client.server.DnsblConfig().Channel)
54
+	if channel := client.server.DnsblConfig().Channel; channel != "" {
55
+		fmt.Printf(channel)
56
+		client.Send(nil, client.server.name, "PRIVMSG", channel, message)
57
+	}
58
+	*/
59
+	client.server.snomasks.Send(sno.Dnsbl, message)
60
+}
61
+
62
+// ProcessBlacklist does
63
+func (server *Server) ProcessBlacklist(client *Client) {
64
+
65
+	if !server.DnsblConfig().Enabled || len(server.DnsblConfig().Lists) == 0 {
66
+		// do nothing if dnsbl is disabled, empty lists is treated as if dnsbl was disabled
67
+		return
68
+	}
69
+
70
+	type DnsblTypeResponse struct {
71
+		Host   string
72
+		Action string
73
+		Reason string
74
+	}
75
+	var items = []DnsblTypeResponse{}
76
+	for _, list := range server.DnsblConfig().Lists {
77
+		response := DnsblTypeResponse{
78
+			Host:   list.Host,
79
+			Action: list.Action,
80
+			Reason: list.Reason,
81
+		}
82
+		// update action/reason if matched with new ...
83
+		for _, entry := range server.LookupBlacklistEntry(&list, client) {
84
+			if reply, exists := list.Reply[entry]; exists {
85
+				response.Action, response.Reason = reply.Action, reply.Reason
86
+			}
87
+			items = append(items, response)
88
+		}
89
+	}
90
+
91
+	// Sort responses so that require-sasl blocks come first. Otherwise A>B (allow>block, allow>notify, block>notify)
92
+	// so that responses come in this order:
93
+	// - require-sasl
94
+	// - allow
95
+	// - block
96
+	// - notify
97
+	sort.Slice(items, func(i, j int) bool {
98
+		if items[i].Action == "require-sasl" {
99
+			return true
100
+		}
101
+		return items[i].Action > items[j].Action
102
+	})
103
+
104
+	if len(items) > 0 {
105
+		item := items[0]
106
+		switch item.Action {
107
+		case "require-sasl":
108
+			sendDnsblMessage(client, fmt.Sprintf("Connecting client %s matched %s, requiring SASL to proceed", client.IP(), item.Host))
109
+			client.SetRequireSasl(true, item.Reason)
110
+
111
+		case "block":
112
+			sendDnsblMessage(client, fmt.Sprintf("Connecting client %s matched %s - killing", client.IP(), item.Host))
113
+			client.Quit(strings.Replace(item.Reason, "{ip}", client.IPString(), -1))
114
+
115
+		case "notify":
116
+			sendDnsblMessage(client, fmt.Sprintf("Connecting client %s matched %s", client.IP(), item.Host))
117
+
118
+		case "allow":
119
+			sendDnsblMessage(client, fmt.Sprintf("Allowing host %s [%s]", client.IP(), item.Host))
120
+		}
121
+	}
122
+
123
+	return
124
+}
125
+
126
+func connectionRequiresSasl(client *Client) bool {
127
+	sasl, reason := client.RequireSasl()
128
+
129
+	if !sasl {
130
+		return false
131
+	}
132
+
133
+	if client.Account() == "" {
134
+		sendDnsblMessage(client, fmt.Sprintf("Connecting client %s and did not authenticate through SASL - blocking connection", client.IP()))
135
+		client.Quit(strings.Replace(reason, "{ip}", client.IPString(), -1))
136
+		return true
137
+	}
138
+
139
+	sendDnsblMessage(client, fmt.Sprintf("Connecting client %s authenticated through SASL - allowing", client.IP()))
140
+
141
+	return false
142
+}

+ 24
- 1
irc/getters.go Bestand weergeven

@@ -4,9 +4,10 @@
4 4
 package irc
5 5
 
6 6
 import (
7
+	"sync/atomic"
8
+
7 9
 	"github.com/oragono/oragono/irc/isupport"
8 10
 	"github.com/oragono/oragono/irc/modes"
9
-	"sync/atomic"
10 11
 )
11 12
 
12 13
 func (server *Server) MaxSendQBytes() int {
@@ -74,6 +75,15 @@ func (server *Server) AccountConfig() *AccountConfig {
74 75
 	return &server.config.Accounts
75 76
 }
76 77
 
78
+func (server *Server) DnsblConfig() *DnsblConfig {
79
+	server.configurableStateMutex.RLock()
80
+	defer server.configurableStateMutex.RUnlock()
81
+	if server.config == nil {
82
+		return nil
83
+	}
84
+	return &server.config.Dnsbl
85
+}
86
+
77 87
 func (server *Server) FakelagConfig() *FakelagConfig {
78 88
 	server.configurableStateMutex.RLock()
79 89
 	defer server.configurableStateMutex.RUnlock()
@@ -175,6 +185,19 @@ func (client *Client) SetAuthorized(authorized bool) {
175 185
 	client.authorized = authorized
176 186
 }
177 187
 
188
+func (client *Client) RequireSasl() (bool, string) {
189
+	client.stateMutex.RLock()
190
+	defer client.stateMutex.RUnlock()
191
+	return client.requireSasl, client.requireSaslReason
192
+}
193
+
194
+func (client *Client) SetRequireSasl(required bool, reason string) {
195
+	client.stateMutex.Lock()
196
+	defer client.stateMutex.Unlock()
197
+	client.requireSasl = required
198
+	client.requireSaslReason = reason
199
+}
200
+
178 201
 func (client *Client) PreregNick() string {
179 202
 	client.stateMutex.RLock()
180 203
 	defer client.stateMutex.RUnlock()

+ 1
- 1
irc/handlers.go Bestand weergeven

@@ -1785,7 +1785,7 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1785 1785
 	server.snomasks.Send(sno.LocalOpers, fmt.Sprintf(ircfmt.Unescape("Client opered up $c[grey][$r%s$c[grey], $r%s$c[grey]]"), client.nickMaskString, client.operName))
1786 1786
 
1787 1787
 	// increase oper count
1788
-	server.stats.ChangeOperators(1)
1788
+	//server.stats.ChangeOperators(1)
1789 1789
 
1790 1790
 	// client may now be unthrottled by the fakelag system
1791 1791
 	client.resetFakelag()

+ 4
- 0
irc/modes.go Bestand weergeven

@@ -30,6 +30,10 @@ func (client *Client) applyUserModeChanges(force bool, changes modes.ModeChanges
30 30
 		case modes.Bot, modes.Invisible, modes.WallOps, modes.UserRoleplaying, modes.Operator, modes.LocalOperator, modes.RegisteredOnly:
31 31
 			switch change.Op {
32 32
 			case modes.Add:
33
+				if change.Mode == modes.Operator || change.Mode == modes.LocalOperator {
34
+					client.server.stats.ChangeOperators(1)
35
+				}
36
+
33 37
 				if !force && (change.Mode == modes.Operator || change.Mode == modes.LocalOperator) {
34 38
 					continue
35 39
 				}

+ 5
- 0
irc/server.go Bestand weergeven

@@ -446,6 +446,11 @@ func (server *Server) tryRegister(c *Client) {
446 446
 		return
447 447
 	}
448 448
 
449
+	if connectionRequiresSasl(c) {
450
+		c.destroy(false)
451
+		return
452
+	}
453
+
449 454
 	// client MUST send PASS (or AUTHENTICATE, if skip-server-password is set)
450 455
 	// before completing the other registration commands
451 456
 	if !c.Authorized() {

+ 3
- 0
irc/sno/constants.go Bestand weergeven

@@ -19,6 +19,7 @@ const (
19 19
 	Stats              Mask = 't'
20 20
 	LocalAccounts      Mask = 'u'
21 21
 	LocalXline         Mask = 'x'
22
+	Dnsbl              Mask = 'S'
22 23
 )
23 24
 
24 25
 var (
@@ -34,6 +35,7 @@ var (
34 35
 		Stats:              "STATS",
35 36
 		LocalAccounts:      "ACCOUNT",
36 37
 		LocalXline:         "XLINE",
38
+		Dnsbl:              "DNSBL",
37 39
 	}
38 40
 
39 41
 	// ValidMasks contains the snomasks that we support.
@@ -48,5 +50,6 @@ var (
48 50
 		Stats:              true,
49 51
 		LocalAccounts:      true,
50 52
 		LocalXline:         true,
53
+		Dnsbl:              true,
51 54
 	}
52 55
 )

Laden…
Annuleren
Opslaan