ソースを参照

Merge remote-tracking branch 'origin/master' into websockets_draft.2

tags/v2.1.0-rc1
Shivaram Lingamneni 4年前
コミット
25813f6d3a

+ 3
- 3
README.md ファイルの表示

4
 
4
 
5
 * Being simple to set up and use
5
 * Being simple to set up and use
6
 * Combining the features of an ircd, a services framework, and a bouncer (integrated account management, history storage, and bouncer functionality)
6
 * Combining the features of an ircd, a services framework, and a bouncer (integrated account management, history storage, and bouncer functionality)
7
-* Bleeding-edge [IRCv3 support](http://ircv3.net/software/servers.html), suitable for use as an IRCv3 reference implementation
7
+* Bleeding-edge [IRCv3 support](https://ircv3.net/software/servers.html), suitable for use as an IRCv3 reference implementation
8
 * Highly customizable via a rehashable (i.e., reloadable at runtime) YAML config
8
 * Highly customizable via a rehashable (i.e., reloadable at runtime) YAML config
9
 
9
 
10
 Oragono is a fork of the [Ergonomadic](https://github.com/jlatt/ergonomadic) IRC daemon <3
10
 Oragono is a fork of the [Ergonomadic](https://github.com/jlatt/ergonomadic) IRC daemon <3
39
 * automated client connection limits
39
 * automated client connection limits
40
 * passwords stored with [bcrypt](https://godoc.org/golang.org/x/crypto)
40
 * passwords stored with [bcrypt](https://godoc.org/golang.org/x/crypto)
41
 * banning ips/nets and masks with `KLINE` and `DLINE`
41
 * banning ips/nets and masks with `KLINE` and `DLINE`
42
-* [IRCv3 support](http://ircv3.net/software/servers.html)
42
+* [IRCv3 support](https://ircv3.net/software/servers.html)
43
 * a heavy focus on developing with [specifications](https://oragono.io/specs.html)
43
 * a heavy focus on developing with [specifications](https://oragono.io/specs.html)
44
 
44
 
45
 ## Installation
45
 ## Installation
112
 
112
 
113
 ### How to register a channel
113
 ### How to register a channel
114
 
114
 
115
-1. Register your account with `/NS REGISTER <username> <email> <password>`
115
+1. Register your account with `/NS REGISTER <password>`
116
 2. Join the channel with `/join #channel`
116
 2. Join the channel with `/join #channel`
117
 3. Register the channel with `/CS REGISTER #channel`
117
 3. Register the channel with `/CS REGISTER #channel`
118
 
118
 

+ 10
- 0
conventional.yaml ファイルの表示

434
         offer-list:
434
         offer-list:
435
             #- "oragono.test"
435
             #- "oragono.test"
436
 
436
 
437
+    # modes that are set by default when a user connects
438
+    # if unset, no user modes will be set by default
439
+    # +i is invisible (a user's channels are hidden from whois replies)
440
+    # see  /QUOTE HELP umodes  for more user modes
441
+    # default-user-modes: +i
442
+
437
     # support for deferring password checking to an external LDAP server
443
     # support for deferring password checking to an external LDAP server
438
     # you should probably ignore this section! consult the grafana docs for details:
444
     # you should probably ignore this section! consult the grafana docs for details:
439
     # https://grafana.com/docs/grafana/latest/auth/ldap/
445
     # https://grafana.com/docs/grafana/latest/auth/ldap/
495
         # how many channels can each account register?
501
         # how many channels can each account register?
496
         max-channels-per-account: 15
502
         max-channels-per-account: 15
497
 
503
 
504
+    # as a crude countermeasure against spambots, anonymous connections younger
505
+    # than this value will get an empty response to /LIST (a time period of 0 disables)
506
+    list-delay: 0s
507
+
498
 # operator classes
508
 # operator classes
499
 oper-classes:
509
 oper-classes:
500
     # local operator
510
     # local operator

+ 19
- 0
distrib/systemd/oragono.service ファイルの表示

1
+[Unit]
2
+Description=oragono
3
+After=network.target
4
+# If you are using MySQL for history storage, comment out the above line
5
+# and uncomment these two instead:
6
+# Requires=mysql.service
7
+# After=network.target mysql.service
8
+
9
+[Service]
10
+Type=simple
11
+User=oragono
12
+WorkingDirectory=/home/oragono
13
+ExecStart=/home/oragono/oragono run --conf /home/oragono/ircd.yaml
14
+ExecReload=/bin/kill -HUP $MAINPID
15
+Restart=on-failure
16
+LimitNOFILE=1048576
17
+
18
+[Install]
19
+WantedBy=multi-user.target

+ 38
- 27
docs/MANUAL.md ファイルの表示

22
 - Installing
22
 - Installing
23
     - Windows
23
     - Windows
24
     - macOS / Linux / Raspberry Pi
24
     - macOS / Linux / Raspberry Pi
25
+    - Productionizing
25
     - Upgrading
26
     - Upgrading
26
 - Features
27
 - Features
27
     - User Accounts
28
     - User Accounts
120
 1. Create a volume for persistent data: `docker volume create oragono-data`
121
 1. Create a volume for persistent data: `docker volume create oragono-data`
121
 1. Run the container, exposing the default ports: `docker run -d --name oragono -v oragono-data:/ircd-data -p 6667:6667 -p 6697:6697 oragono/oragono:latest`
122
 1. Run the container, exposing the default ports: `docker run -d --name oragono -v oragono-data:/ircd-data -p 6667:6667 -p 6697:6697 oragono/oragono:latest`
122
 
123
 
123
-For further information and a sample docker-compose file see the separate [Docker documentation](https://github.com/oragono/oragono/blog/master/distrib/docker/README.md).
124
+For further information and a sample docker-compose file see the separate [Docker documentation](https://github.com/oragono/oragono/blob/master/distrib/docker/README.md).
124
 
125
 
125
 
126
 
126
-## Running oragono as a service on Linux
127
+## Productionizing
127
 
128
 
128
 The recommended way to operate oragono as a service on Linux is via systemd. This provides a standard interface for starting, stopping, and rehashing (via `systemctl reload`) the service. It also captures oragono's loglines (sent to stderr in the default configuration) and writes them to the system journal.
129
 The recommended way to operate oragono as a service on Linux is via systemd. This provides a standard interface for starting, stopping, and rehashing (via `systemctl reload`) the service. It also captures oragono's loglines (sent to stderr in the default configuration) and writes them to the system journal.
129
 
130
 
130
-If you're using Arch, the abovementioned AUR package bundles a systemd file for starting and stopping the server. If you're rolling your own deployment, here's an [example](https://github.com/darwin-network/slash/blob/master/etc/systemd/system/ircd.service) of a systemd unit file that can be used to run Oragono as an unprivileged role user.
131
+The only major distribution that currently packages Oragono is Arch Linux; the aforementioned AUR package includes a systemd unit file. However, it should be fairly straightforward to set up a productionized Oragono on any Linux distribution. Here's a quickstart guide for Debian/Ubuntu:
131
 
132
 
132
-On a non-systemd system, oragono can be configured to log to a file and used [logrotate(8)](https://linux.die.net/man/8/logrotate), since it will reopen its log files (as well as rehashing the config file) upon receiving a SIGHUP.
133
+1. Create a dedicated, unprivileged role user who will own the oragono process and all its associated files: `adduser --system --group oragono`. This user now has a home directory at `/home/oragono`.
134
+1. Copy the executable binary `oragono`, the config file `ircd.yaml`, the database `ircd.db`, and the self-signed TLS certificate (`tls.crt` and `tls.key`) to `/home/oragono`. Ensure that they are all owned by the new oragono role user: `sudo chown oragono:oragono /home/oragono/*`. Ensure that the configuration file logs to stderr.
135
+1. Install our example [oragono.service](https://github.com/oragono/oragono/blob/master/distrib/systemd/oragono.service) file to `/etc/systemd/system/oragono.service`.
136
+1. Enable and start the new service with the following commands:
137
+    1. `systemctl daemon-reload`
138
+    1. `systemctl enable oragono.service`
139
+    1. `systemctl start oragono.service`
140
+    1. Confirm that the service started correctly with `systemctl status oragono.service`
141
+
142
+The other major hurdle for productionizing (but one well worth the effort) is obtaining valid TLS certificates for your domain, if you haven't already done so:
143
+
144
+1. The simplest way to get valid TLS certificates is from [Let's Encrypt](https://letsencrypt.org/) with [Certbot](https://certbot.eff.org/). The correct procedure will depend on whether you are already running a web server on port 80. If you are, follow the guides on the Certbot website; if you aren't, you can use `certbot certonly --standalone --preferred-challenges http -d example.com` (replace `example.com` with your domain).
145
+1. At this point, you should have certificates available at `/etc/letsencrypt/live/example.com` (replacing `example.com` with your domain). You should serve `fullchain.pem` as the certificate and `privkey.pem` as its private key. However, these files are owned by root and the private key is not readable by the oragono role user, so you won't be able to use them directly in their current locations. You can write a post-renewal hook for certbot to make copies of these certificates accessible to the oragono role user. For example, install the following script as `/etc/letsencrypt/renewal-hooks/post/install-oragono-certificates`, again replacing `example.com` with your domain name, and chmod it 0755:
146
+
147
+````bash
148
+#!/bin/bash
149
+
150
+set -eu
151
+
152
+umask 077
153
+cp /etc/letsencrypt/live/example.com/fullchain.pem /home/oragono/tls.crt
154
+cp /etc/letsencrypt/live/example.com/privkey.pem /home/oragono/tls.key
155
+chown oragono:oragono /home/oragono/tls.*
156
+# rehash oragono, which will reload the certificates:
157
+systemctl reload oragono.service
158
+````
159
+
160
+Executing this script manually will install the certificates for the first time and perform a rehash, enabling them.
161
+
162
+If you are using Certbot 0.29.0 or higher, you can also change the ownership of the files under `/etc/letsencrypt` so that the oragono user can read them, as described in the [UnrealIRCd documentation](https://www.unrealircd.org/docs/Setting_up_certbot_for_use_with_UnrealIRCd#Tweaking_permissions_on_the_key_file).
163
+
164
+On a non-systemd system, oragono can be configured to log to a file and used [logrotate(8)](https://linux.die.net/man/8/logrotate), since it will reopen its log files (as well as rehashing the config file) upon receiving a SIGHUP. To rehash manually outside the context of log rotation, you can use `killall -HUP oragono` or `pkill -HUP oragono`.
133
 
165
 
134
 
166
 
135
 ## Upgrading to a new version of Oragono
167
 ## Upgrading to a new version of Oragono
366
 
398
 
367
 # IRC over TLS
399
 # IRC over TLS
368
 
400
 
369
-IRC has traditionally been available over both plaintext (on port 6667) and SSL/TLS (on port 6697). We recommend that you make your server available exclusively via TLS, since exposing plaintext access allows for unauthorized interception or modification of user data or passwords. While the default config file exposes a plaintext public port, it also contains instructions on how to disable it or replace it with a 'dummy' plaintext listener that simply directs users to reconnect using TLS.
370
-
371
-
372
-## How do I use Let's Encrypt certificates?
373
-
374
-[Let's Encrypt](https://letsencrypt.org) is a widely recognized certificate authority that provides free certificates. Here's a quick-start guide for using those certificates with Oragono:
375
-
376
-1. Follow this [guidance](https://letsencrypt.org/getting-started/) from Let's Encrypt to create your certificates.
377
-2. You should now have a set of `pem` files, Mainly, we're interested in your `live/` Let's Encrypt directory (e.g. `/etc/letsencrypt/live/<site>/`).
378
-3. Here are how the config file keys map to LE files:
379
-    - `cert: tls.crt` is `live/<site>/fullchain.pem`
380
-    - ` key: tls.key` is `live/<site>/privkey.pem`
381
-4. You may need to copy the `pem` files to another directory so Oragono can read them, or similarly use a script like [this one](https://github.com/darwin-network/slash/blob/master/etc/bin/install-lecerts) to automagically do something similar.
382
-5. By default, `certbot` will automatically renew your certificates. Oragono will only reread certificates when it is restarted, or during a rehash (e.g., on receiving the `/rehash` command or the `SIGHUP` signal). You can add an executable script to `/etc/letsencrypt/renewal-hooks/post` that can perform the rehash. Here's one example of such a script:
383
-
384
-```bash
385
-#!/bin/bash
386
-pkill -HUP oragono
387
-```
388
-
389
-The main issues you'll run into are going to be permissions issues. This is because by default, certbot will generate certificates that non-root users can't (and probably shouldn't) read. If you run into trouble, look over the script in step **4** and/or make sure you're copying the files to somewhere else, as well as giving them correct permissions with `chown`, `chgrp` and `chmod`.
390
-
391
-On other platforms or with alternative ACME tools, you may need to use other steps or the specific files may be named differently.
401
+IRC has traditionally been available over both plaintext (on port 6667) and SSL/TLS (on port 6697). We recommend that you make your server available exclusively via TLS, since exposing plaintext access allows for unauthorized interception or modification of user data or passwords. The default config file no longer exposes a plaintext port, so if you haven't modified your `listeners` section, you're good to go.
392
 
402
 
403
+For a quickstart guide to obtaining valid TLS certificates from Let's Encrypt, see the "productionizing" section of the manual above.
393
 
404
 
394
 ## How can I "redirect" users from plaintext to TLS?
405
 ## How can I "redirect" users from plaintext to TLS?
395
 
406
 

+ 5
- 0
irc/accounts.go ファイルの表示

1174
 	var channelsStr string
1174
 	var channelsStr string
1175
 	keepProtections := false
1175
 	keepProtections := false
1176
 	am.server.store.Update(func(tx *buntdb.Tx) error {
1176
 	am.server.store.Update(func(tx *buntdb.Tx) error {
1177
+		// get the unfolded account name; for an active account, this is
1178
+		// stored under accountNameKey, for an unregistered account under unregisteredKey
1177
 		accountName, _ = tx.Get(accountNameKey)
1179
 		accountName, _ = tx.Get(accountNameKey)
1180
+		if accountName == "" {
1181
+			accountName, _ = tx.Get(unregisteredKey)
1182
+		}
1178
 		if erase {
1183
 		if erase {
1179
 			tx.Delete(unregisteredKey)
1184
 			tx.Delete(unregisteredKey)
1180
 		} else {
1185
 		} else {

+ 48
- 12
irc/channel.go ファイルの表示

541
 
541
 
542
 func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
542
 func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
543
 	channel.stateMutex.RLock()
543
 	channel.stateMutex.RLock()
544
+	founder := channel.registeredFounder
544
 	clientModes := channel.members[client]
545
 	clientModes := channel.members[client]
545
 	targetModes := channel.members[target]
546
 	targetModes := channel.members[target]
546
 	channel.stateMutex.RUnlock()
547
 	channel.stateMutex.RUnlock()
547
 
548
 
549
+	if founder != "" && founder == client.Account() {
550
+		// #950: founder can kick or whatever without actually having the +q mode
551
+		return true
552
+	}
553
+
548
 	return channelUserModeHasPrivsOver(clientModes.HighestChannelUserMode(), targetModes.HighestChannelUserMode())
554
 	return channelUserModeHasPrivsOver(clientModes.HighestChannelUserMode(), targetModes.HighestChannelUserMode())
549
 }
555
 }
550
 
556
 
1064
 				message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
1070
 				message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
1065
 				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1071
 				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1066
 			}
1072
 			}
1073
+		case history.Topic:
1074
+			if eventPlayback {
1075
+				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message)
1076
+			} else {
1077
+				message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message)
1078
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1079
+			}
1080
+		case history.Mode:
1081
+			params := make([]string, len(item.Message.Split)+1)
1082
+			params[0] = chname
1083
+			for i, pair := range item.Message.Split {
1084
+				params[i+1] = pair.Message
1085
+			}
1086
+			if eventPlayback {
1087
+				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "MODE", params...)
1088
+			} else {
1089
+				message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " "))
1090
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1091
+			}
1067
 		}
1092
 		}
1068
 	}
1093
 	}
1069
 }
1094
 }
1113
 	}
1138
 	}
1114
 
1139
 
1115
 	channel.stateMutex.Lock()
1140
 	channel.stateMutex.Lock()
1141
+	chname := channel.name
1116
 	channel.topic = topic
1142
 	channel.topic = topic
1117
 	channel.topicSetBy = client.nickMaskString
1143
 	channel.topicSetBy = client.nickMaskString
1118
 	channel.topicSetTime = time.Now().UTC()
1144
 	channel.topicSetTime = time.Now().UTC()
1119
 	channel.stateMutex.Unlock()
1145
 	channel.stateMutex.Unlock()
1120
 
1146
 
1121
-	prefix := client.NickMaskString()
1147
+	details := client.Details()
1148
+	message := utils.MakeMessage(topic)
1149
+	rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, nil, "TOPIC", chname, topic)
1122
 	for _, member := range channel.Members() {
1150
 	for _, member := range channel.Members() {
1123
 		for _, session := range member.Sessions() {
1151
 		for _, session := range member.Sessions() {
1124
-			if session == rb.session {
1125
-				rb.Add(nil, prefix, "TOPIC", channel.name, topic)
1126
-			} else {
1127
-				session.Send(nil, prefix, "TOPIC", channel.name, topic)
1152
+			if session != rb.session {
1153
+				session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, nil, "TOPIC", chname, topic)
1128
 			}
1154
 			}
1129
 		}
1155
 		}
1130
 	}
1156
 	}
1131
 
1157
 
1158
+	channel.AddHistoryItem(history.Item{
1159
+		Type:        history.Topic,
1160
+		Nick:        details.nickMask,
1161
+		AccountName: details.accountName,
1162
+		Message:     message,
1163
+	})
1164
+
1132
 	channel.MarkDirty(IncludeTopic)
1165
 	channel.MarkDirty(IncludeTopic)
1133
 }
1166
 }
1134
 
1167
 
1251
 		}
1284
 		}
1252
 	}
1285
 	}
1253
 
1286
 
1254
-	channel.AddHistoryItem(history.Item{
1255
-		Type:        histType,
1256
-		Message:     message,
1257
-		Nick:        nickmask,
1258
-		AccountName: account,
1259
-		Tags:        clientOnlyTags,
1260
-	})
1287
+	// #959: don't save STATUSMSG
1288
+	if minPrefixMode == modes.Mode(0) {
1289
+		channel.AddHistoryItem(history.Item{
1290
+			Type:        histType,
1291
+			Message:     message,
1292
+			Nick:        nickmask,
1293
+			AccountName: account,
1294
+			Tags:        clientOnlyTags,
1295
+		})
1296
+	}
1261
 }
1297
 }
1262
 
1298
 
1263
 func (channel *Channel) applyModeToMember(client *Client, change modes.ModeChange, rb *ResponseBuffer) (applied bool, result modes.ModeChange) {
1299
 func (channel *Channel) applyModeToMember(client *Client, change modes.ModeChange, rb *ResponseBuffer) (applied bool, result modes.ModeChange) {

+ 3
- 3
irc/chanserv.go ファイルの表示

244
 				if member.Account() == change.Arg {
244
 				if member.Account() == change.Arg {
245
 					applied, change := channel.applyModeToMember(client, change, rb)
245
 					applied, change := channel.applyModeToMember(client, change, rb)
246
 					if applied {
246
 					if applied {
247
-						announceCmodeChanges(channel, modes.ModeChanges{change}, chanservMask, rb)
247
+						announceCmodeChanges(channel, modes.ModeChanges{change}, chanservMask, "*", rb)
248
 					}
248
 					}
249
 				}
249
 				}
250
 			}
250
 			}
291
 		},
291
 		},
292
 		rb)
292
 		rb)
293
 	if applied {
293
 	if applied {
294
-		announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, rb)
294
+		announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, "*", rb)
295
 	}
295
 	}
296
 
296
 
297
 	csNotice(rb, fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName))
297
 	csNotice(rb, fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName))
343
 		},
343
 		},
344
 		rb)
344
 		rb)
345
 	if applied {
345
 	if applied {
346
-		announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, rb)
346
+		announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, "*", rb)
347
 	}
347
 	}
348
 }
348
 }
349
 
349
 

+ 8
- 0
irc/client.go ファイルの表示

318
 	session.idletimer.Initialize(session)
318
 	session.idletimer.Initialize(session)
319
 	session.resetFakelag()
319
 	session.resetFakelag()
320
 
320
 
321
+	for _, defaultMode := range config.Accounts.defaultUserModes {
322
+		client.SetMode(defaultMode, true)
323
+	}
324
+
321
 	if conn.Config.TLSConfig != nil {
325
 	if conn.Config.TLSConfig != nil {
322
 		client.SetMode(modes.TLS, true)
326
 		client.SetMode(modes.TLS, true)
323
 		// error is not useful to us here anyways so we can ignore it
327
 		// error is not useful to us here anyways so we can ignore it
371
 		alwaysOn: true,
375
 		alwaysOn: true,
372
 	}
376
 	}
373
 
377
 
378
+	for _, defaultMode := range config.Accounts.defaultUserModes {
379
+		client.SetMode(defaultMode, true)
380
+	}
381
+
374
 	client.SetMode(modes.TLS, true)
382
 	client.SetMode(modes.TLS, true)
375
 	client.writerSemaphore.Initialize(1)
383
 	client.writerSemaphore.Initialize(1)
376
 	client.history.Initialize(0, 0)
384
 	client.history.Initialize(0, 0)

+ 10
- 1
irc/client_lookup_set.go ファイルの表示

205
 	// the client may just be changing case
205
 	// the client may just be changing case
206
 	if currentClient != nil && currentClient != client && session != nil {
206
 	if currentClient != nil && currentClient != client && session != nil {
207
 		// these conditions forbid reattaching to an existing session:
207
 		// these conditions forbid reattaching to an existing session:
208
-		if registered || !bouncerAllowed || account == "" || account != currentClient.Account() || client.HasMode(modes.TLS) != currentClient.HasMode(modes.TLS) {
208
+		if registered || !bouncerAllowed || account == "" || account != currentClient.Account() {
209
 			return "", errNicknameInUse
209
 			return "", errNicknameInUse
210
 		}
210
 		}
211
+		// check TLS modes
212
+		if client.HasMode(modes.TLS) != currentClient.HasMode(modes.TLS) {
213
+			if useAccountName {
214
+				// #955: this is fatal because they can't fix it by trying a different nick
215
+				return "", errInsecureReattach
216
+			} else {
217
+				return "", errNicknameInUse
218
+			}
219
+		}
211
 		reattachSuccessful, numSessions, lastSeen := currentClient.AddSession(session)
220
 		reattachSuccessful, numSessions, lastSeen := currentClient.AddSession(session)
212
 		if !reattachSuccessful {
221
 		if !reattachSuccessful {
213
 			return "", errNicknameInUse
222
 			return "", errNicknameInUse

+ 5
- 0
irc/config.go ファイルの表示

263
 		Exempted     []string
263
 		Exempted     []string
264
 		exemptedNets []net.IPNet
264
 		exemptedNets []net.IPNet
265
 	} `yaml:"require-sasl"`
265
 	} `yaml:"require-sasl"`
266
+	DefaultUserModes   *string `yaml:"default-user-modes"`
267
+	defaultUserModes   modes.Modes
266
 	LDAP               ldap.ServerConfig
268
 	LDAP               ldap.ServerConfig
267
 	LoginThrottling    ThrottleConfig `yaml:"login-throttling"`
269
 	LoginThrottling    ThrottleConfig `yaml:"login-throttling"`
268
 	SkipServerPassword bool           `yaml:"skip-server-password"`
270
 	SkipServerPassword bool           `yaml:"skip-server-password"`
552
 			OperatorOnly          bool `yaml:"operator-only"`
554
 			OperatorOnly          bool `yaml:"operator-only"`
553
 			MaxChannelsPerAccount int  `yaml:"max-channels-per-account"`
555
 			MaxChannelsPerAccount int  `yaml:"max-channels-per-account"`
554
 		}
556
 		}
557
+		ListDelay time.Duration `yaml:"list-delay"`
555
 	}
558
 	}
556
 
559
 
557
 	OperClasses map[string]*OperClassConfig `yaml:"oper-classes"`
560
 	OperClasses map[string]*OperClassConfig `yaml:"oper-classes"`
984
 		}
987
 		}
985
 	}
988
 	}
986
 
989
 
990
+	config.Accounts.defaultUserModes = ParseDefaultUserModes(config.Accounts.DefaultUserModes)
991
+
987
 	config.Accounts.RequireSasl.exemptedNets, err = utils.ParseNetList(config.Accounts.RequireSasl.Exempted)
992
 	config.Accounts.RequireSasl.exemptedNets, err = utils.ParseNetList(config.Accounts.RequireSasl.Exempted)
988
 	if err != nil {
993
 	if err != nil {
989
 		return nil, fmt.Errorf("Could not parse require-sasl exempted nets: %v", err.Error())
994
 		return nil, fmt.Errorf("Could not parse require-sasl exempted nets: %v", err.Error())

+ 1
- 0
irc/errors.go ファイルの表示

42
 	errNickMissing                    = errors.New("nick missing")
42
 	errNickMissing                    = errors.New("nick missing")
43
 	errNicknameInvalid                = errors.New("invalid nickname")
43
 	errNicknameInvalid                = errors.New("invalid nickname")
44
 	errNicknameInUse                  = errors.New("nickname in use")
44
 	errNicknameInUse                  = errors.New("nickname in use")
45
+	errInsecureReattach               = errors.New("insecure reattach")
45
 	errNicknameReserved               = errors.New("nickname is reserved")
46
 	errNicknameReserved               = errors.New("nickname is reserved")
46
 	errNickAccountMismatch            = errors.New(`Your nickname must match your account name; try logging out and logging back in with SASL`)
47
 	errNickAccountMismatch            = errors.New(`Your nickname must match your account name; try logging out and logging back in with SASL`)
47
 	errNoExistingBan                  = errors.New("Ban does not exist")
48
 	errNoExistingBan                  = errors.New("Ban does not exist")

+ 40
- 11
irc/handlers.go ファイルの表示

1408
 
1408
 
1409
 // LIST [<channel>{,<channel>}] [<elistcond>{,<elistcond>}]
1409
 // LIST [<channel>{,<channel>}] [<elistcond>{,<elistcond>}]
1410
 func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
1410
 func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
1411
+	config := server.Config()
1412
+	if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" && !client.HasMode(modes.Operator) {
1413
+		remaining := time.Until(client.ctime.Add(config.Channels.ListDelay))
1414
+		csNotice(rb, fmt.Sprintf(client.t("This server requires that you wait %v after connecting before you can use /LIST. You have %v left."), config.Channels.ListDelay, remaining))
1415
+		rb.Add(nil, server.name, RPL_LISTEND, client.Nick(), client.t("End of LIST"))
1416
+		return false
1417
+	}
1418
+
1411
 	// get channels
1419
 	// get channels
1412
 	var channels []string
1420
 	var channels []string
1413
 	for _, param := range msg.Params {
1421
 	for _, param := range msg.Params {
1520
 	}
1528
 	}
1521
 	// process mode changes, include list operations (an empty set of changes does a list)
1529
 	// process mode changes, include list operations (an empty set of changes does a list)
1522
 	applied := channel.ApplyChannelModeChanges(client, msg.Command == "SAMODE", changes, rb)
1530
 	applied := channel.ApplyChannelModeChanges(client, msg.Command == "SAMODE", changes, rb)
1523
-	announceCmodeChanges(channel, applied, client.NickMaskString(), rb)
1531
+	details := client.Details()
1532
+	announceCmodeChanges(channel, applied, details.nickMask, details.accountName, rb)
1524
 
1533
 
1525
 	return false
1534
 	return false
1526
 }
1535
 }
1527
 
1536
 
1528
-func announceCmodeChanges(channel *Channel, applied modes.ModeChanges, source string, rb *ResponseBuffer) {
1537
+func announceCmodeChanges(channel *Channel, applied modes.ModeChanges, source, accountName string, rb *ResponseBuffer) {
1529
 	// send out changes
1538
 	// send out changes
1530
 	if len(applied) > 0 {
1539
 	if len(applied) > 0 {
1531
-		//TODO(dan): we should change the name of String and make it return a slice here
1532
-		args := append([]string{channel.name}, applied.Strings()...)
1533
-		rb.Add(nil, source, "MODE", args...)
1540
+		message := utils.MakeMessage("")
1541
+		changeStrings := applied.Strings()
1542
+		for _, changeString := range changeStrings {
1543
+			message.Split = append(message.Split, utils.MessagePair{Message: changeString})
1544
+		}
1545
+		args := append([]string{channel.name}, changeStrings...)
1546
+		rb.AddFromClient(message.Time, message.Msgid, source, accountName, nil, "MODE", args...)
1534
 		for _, member := range channel.Members() {
1547
 		for _, member := range channel.Members() {
1535
 			for _, session := range member.Sessions() {
1548
 			for _, session := range member.Sessions() {
1536
 				if session != rb.session {
1549
 				if session != rb.session {
1537
-					session.Send(nil, source, "MODE", args...)
1550
+					session.sendFromClientInternal(false, message.Time, message.Msgid, source, accountName, nil, "MODE", args...)
1538
 				}
1551
 				}
1539
 			}
1552
 			}
1540
 		}
1553
 		}
1554
+		channel.AddHistoryItem(history.Item{
1555
+			Type:        history.Mode,
1556
+			Nick:        source,
1557
+			AccountName: accountName,
1558
+			Message:     message,
1559
+		})
1541
 	}
1560
 	}
1542
 }
1561
 }
1543
 
1562
 
2054
 	}
2073
 	}
2055
 
2074
 
2056
 	// must pass at least one check, and all enabled checks
2075
 	// must pass at least one check, and all enabled checks
2057
-	var checkPassed, checkFailed bool
2076
+	var checkPassed, checkFailed, passwordFailed bool
2058
 	oper := server.GetOperator(msg.Params[0])
2077
 	oper := server.GetOperator(msg.Params[0])
2059
 	if oper != nil {
2078
 	if oper != nil {
2060
 		if oper.Fingerprint != "" {
2079
 		if oper.Fingerprint != "" {
2065
 			}
2084
 			}
2066
 		}
2085
 		}
2067
 		if !checkFailed && oper.Pass != nil {
2086
 		if !checkFailed && oper.Pass != nil {
2068
-			if len(msg.Params) == 1 || bcrypt.CompareHashAndPassword(oper.Pass, []byte(msg.Params[1])) != nil {
2087
+			if len(msg.Params) == 1 {
2069
 				checkFailed = true
2088
 				checkFailed = true
2089
+			} else if bcrypt.CompareHashAndPassword(oper.Pass, []byte(msg.Params[1])) != nil {
2090
+				checkFailed = true
2091
+				passwordFailed = true
2070
 			} else {
2092
 			} else {
2071
 				checkPassed = true
2093
 				checkPassed = true
2072
 			}
2094
 			}
2075
 
2097
 
2076
 	if !checkPassed || checkFailed {
2098
 	if !checkPassed || checkFailed {
2077
 		rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.Nick(), client.t("Password incorrect"))
2099
 		rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.Nick(), client.t("Password incorrect"))
2078
-		client.Quit(client.t("Password incorrect"), rb.session)
2079
-		return true
2100
+		// #951: only disconnect them if we actually tried to check a password for them
2101
+		if passwordFailed {
2102
+			client.Quit(client.t("Password incorrect"), rb.session)
2103
+			return true
2104
+		} else {
2105
+			return false
2106
+		}
2080
 	}
2107
 	}
2081
 
2108
 
2082
-	applyOper(client, oper, rb)
2109
+	if oper != nil {
2110
+		applyOper(client, oper, rb)
2111
+	}
2083
 	return false
2112
 	return false
2084
 }
2113
 }
2085
 
2114
 

+ 1
- 0
irc/history/history.go ファイルの表示

22
 	Mode
22
 	Mode
23
 	Tagmsg
23
 	Tagmsg
24
 	Nick
24
 	Nick
25
+	Topic
25
 )
26
 )
26
 
27
 
27
 const (
28
 const (

+ 26
- 8
irc/modes.go ファイルの表示

20
 	DefaultChannelModes = modes.Modes{
20
 	DefaultChannelModes = modes.Modes{
21
 		modes.NoOutside, modes.OpOnlyTopic,
21
 		modes.NoOutside, modes.OpOnlyTopic,
22
 	}
22
 	}
23
+
24
+	// DefaultUserModes are set on all users when they login.
25
+	// this can be overridden in the `accounts` config, with the `default-user-modes` key
26
+	DefaultUserModes = modes.Modes{}
23
 )
27
 )
24
 
28
 
25
 // ApplyUserModeChanges applies the given changes, and returns the applied changes.
29
 // ApplyUserModeChanges applies the given changes, and returns the applied changes.
102
 	return applied
106
 	return applied
103
 }
107
 }
104
 
108
 
109
+// parseDefaultModes uses the provided mode change parser to parse the rawModes.
110
+func parseDefaultModes(rawModes string, parser func(params ...string) (modes.ModeChanges, map[rune]bool)) modes.Modes {
111
+	modeChangeStrings := strings.Fields(rawModes)
112
+	modeChanges, _ := parser(modeChangeStrings...)
113
+	defaultModes := make(modes.Modes, 0)
114
+	for _, modeChange := range modeChanges {
115
+		if modeChange.Op == modes.Add {
116
+			defaultModes = append(defaultModes, modeChange.Mode)
117
+		}
118
+	}
119
+	return defaultModes
120
+}
121
+
105
 // ParseDefaultChannelModes parses the `default-modes` line of the config
122
 // ParseDefaultChannelModes parses the `default-modes` line of the config
106
 func ParseDefaultChannelModes(rawModes *string) modes.Modes {
123
 func ParseDefaultChannelModes(rawModes *string) modes.Modes {
107
 	if rawModes == nil {
124
 	if rawModes == nil {
108
 		// not present in config, fall back to compile-time default
125
 		// not present in config, fall back to compile-time default
109
 		return DefaultChannelModes
126
 		return DefaultChannelModes
110
 	}
127
 	}
111
-	modeChangeStrings := strings.Fields(*rawModes)
112
-	modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...)
113
-	defaultChannelModes := make(modes.Modes, 0)
114
-	for _, modeChange := range modeChanges {
115
-		if modeChange.Op == modes.Add {
116
-			defaultChannelModes = append(defaultChannelModes, modeChange.Mode)
117
-		}
128
+	return parseDefaultModes(*rawModes, modes.ParseChannelModeChanges)
129
+}
130
+
131
+// ParseDefaultUserModes parses the `default-user-modes` line of the config
132
+func ParseDefaultUserModes(rawModes *string) modes.Modes {
133
+	if rawModes == nil {
134
+		// not present in config, fall back to compile-time default
135
+		return DefaultUserModes
118
 	}
136
 	}
119
-	return defaultChannelModes
137
+	return parseDefaultModes(*rawModes, modes.ParseUserModeChanges)
120
 }
138
 }
121
 
139
 
122
 // ApplyChannelModeChanges applies a given set of mode changes.
140
 // ApplyChannelModeChanges applies a given set of mode changes.

+ 25
- 0
irc/modes_test.go ファイルの表示

35
 	}
35
 	}
36
 }
36
 }
37
 
37
 
38
+func TestParseDefaultUserModes(t *testing.T) {
39
+	iR := "+iR"
40
+	i := "+i"
41
+	empty := ""
42
+	rminusi := "+R -i"
43
+
44
+	var parseTests = []struct {
45
+		raw      *string
46
+		expected modes.Modes
47
+	}{
48
+		{&iR, modes.Modes{modes.Invisible, modes.RegisteredOnly}},
49
+		{&i, modes.Modes{modes.Invisible}},
50
+		{&empty, modes.Modes{}},
51
+		{&rminusi, modes.Modes{modes.RegisteredOnly}},
52
+		{nil, modes.Modes{}},
53
+	}
54
+
55
+	for _, testcase := range parseTests {
56
+		result := ParseDefaultUserModes(testcase.raw)
57
+		if !reflect.DeepEqual(result, testcase.expected) {
58
+			t.Errorf("expected modes %s, got %s", testcase.expected, result)
59
+		}
60
+	}
61
+}
62
+
38
 func TestUmodeGreaterThan(t *testing.T) {
63
 func TestUmodeGreaterThan(t *testing.T) {
39
 	if !umodeGreaterThan(modes.Halfop, modes.Voice) {
64
 	if !umodeGreaterThan(modes.Halfop, modes.Voice) {
40
 		t.Errorf("expected Halfop > Voice")
65
 		t.Errorf("expected Halfop > Voice")

+ 5
- 6
irc/nickname.go ファイルの表示

25
 	restrictedSkeletons       = make(map[string]bool)
25
 	restrictedSkeletons       = make(map[string]bool)
26
 )
26
 )
27
 
27
 
28
-// returns whether the change succeeded or failed
29
-func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) bool {
28
+func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) error {
30
 	currentNick := client.Nick()
29
 	currentNick := client.Nick()
31
 	details := target.Details()
30
 	details := target.Details()
32
 	if details.nick == nickname {
31
 	if details.nick == nickname {
33
-		return true
32
+		return nil
34
 	}
33
 	}
35
 	hadNick := details.nick != "*"
34
 	hadNick := details.nick != "*"
36
 	origNickMask := details.nickMask
35
 	origNickMask := details.nickMask
52
 		rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
51
 		rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
53
 	}
52
 	}
54
 	if err != nil {
53
 	if err != nil {
55
-		return false
54
+		return err
56
 	}
55
 	}
57
 
56
 
58
 	message := utils.MakeMessage("")
57
 	message := utils.MakeMessage("")
88
 		client.server.monitorManager.AlertAbout(target, true)
87
 		client.server.monitorManager.AlertAbout(target, true)
89
 		target.nickTimer.Touch(rb)
88
 		target.nickTimer.Touch(rb)
90
 	} // else: these will be deferred to the end of registration (see #572)
89
 	} // else: these will be deferred to the end of registration (see #572)
91
-	return true
90
+	return nil
92
 }
91
 }
93
 
92
 
94
 func (server *Server) RandomlyRename(client *Client) {
93
 func (server *Server) RandomlyRename(client *Client) {
124
 	if !client.registered {
123
 	if !client.registered {
125
 		return true
124
 		return true
126
 	}
125
 	}
127
-	if !performNickChange(client.server, client, client, rb.session, client.AccountName(), rb) {
126
+	if performNickChange(client.server, client, client, rb.session, client.AccountName(), rb) != nil {
128
 		client.server.accounts.Logout(client)
127
 		client.server.accounts.Logout(client)
129
 		nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
128
 		nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
130
 		return false
129
 		return false

+ 2
- 0
irc/nickserv.go ファイルの表示

841
 	if erase {
841
 	if erase {
842
 		// account may not be in a loadable state, e.g., if it was unregistered
842
 		// account may not be in a loadable state, e.g., if it was unregistered
843
 		accountName = username
843
 		accountName = username
844
+		// make the confirmation code nondeterministic for ERASE
845
+		registeredAt = server.ctime
844
 	} else {
846
 	} else {
845
 		account, err := server.accounts.LoadAccount(username)
847
 		account, err := server.accounts.LoadAccount(username)
846
 		if err == errAccountDoesNotExist {
848
 		if err == errAccountDoesNotExist {

+ 7
- 4
irc/server.go ファイルの表示

438
 	}
438
 	}
439
 
439
 
440
 	rb := NewResponseBuffer(session)
440
 	rb := NewResponseBuffer(session)
441
-	nickAssigned := performNickChange(server, c, c, session, c.preregNick, rb)
441
+	nickError := performNickChange(server, c, c, session, c.preregNick, rb)
442
 	rb.Send(true)
442
 	rb.Send(true)
443
-	if !nickAssigned {
443
+	if nickError == errInsecureReattach {
444
+		c.Quit(c.t("You can't mix secure and insecure connections to this account"), nil)
445
+		return true
446
+	} else if nickError != nil {
444
 		c.preregNick = ""
447
 		c.preregNick = ""
445
-		return
448
+		return false
446
 	}
449
 	}
447
 
450
 
448
 	if session.client != c {
451
 	if session.client != c {
450
 		// we'll play the reg burst later, on the new goroutine associated with
453
 		// we'll play the reg burst later, on the new goroutine associated with
451
 		// (thisSession, otherClient). This is to avoid having to transfer state
454
 		// (thisSession, otherClient). This is to avoid having to transfer state
452
 		// like nickname, hostname, etc. to show the correct values in the reg burst.
455
 		// like nickname, hostname, etc. to show the correct values in the reg burst.
453
-		return
456
+		return false
454
 	}
457
 	}
455
 
458
 
456
 	// check KLINEs
459
 	// check KLINEs

+ 2
- 2
languages/ro-RO-irc.lang.json ファイルの表示

113
   "Former Core Developers:": "Foști dezvoltatori:",
113
   "Former Core Developers:": "Foști dezvoltatori:",
114
   "Founder: %s": "Fondator: %s",
114
   "Founder: %s": "Fondator: %s",
115
   "GHOSTed by %s": "%s a folosit GHOST",
115
   "GHOSTed by %s": "%s a folosit GHOST",
116
-  "Given current server settings, the channel history setting is: %s": "",
116
+  "Given current server settings, the channel history setting is: %s": "Conform setărilor actuale ale serverului, setarea pentru istoricul de canal este: %s",
117
   "Given current server settings, your client is always-on": "Conform setărilor actuale ale serverului, clientul tău are activă opțiunea de conectare permanentă",
117
   "Given current server settings, your client is always-on": "Conform setărilor actuale ale serverului, clientul tău are activă opțiunea de conectare permanentă",
118
   "Given current server settings, your client is not always-on": "Conform setărilor actuale ale serverului, clientul tău nu are activă opțiunea de conectare permanentă",
118
   "Given current server settings, your client is not always-on": "Conform setărilor actuale ale serverului, clientul tău nu are activă opțiunea de conectare permanentă",
119
   "Given current server settings, your direct message history setting is: %s": "Conform setărilor actuale ale serverului, setarea aferentă istoricului de mesaje este: %s",
119
   "Given current server settings, your direct message history setting is: %s": "Conform setărilor actuale ale serverului, setarea aferentă istoricului de mesaje este: %s",
228
   "Successfully ungrouped nick %s with your account": "Pseudonimul %s a fost degrupat de la contul tău, cu succes",
228
   "Successfully ungrouped nick %s with your account": "Pseudonimul %s a fost degrupat de la contul tău, cu succes",
229
   "Successfully unpurged channel %s from the server": "Canalul %s nu mai este purjat din server",
229
   "Successfully unpurged channel %s from the server": "Canalul %s nu mai este purjat din server",
230
   "Successfully unregistered account %s": "Contul %s a fost șters cu succes",
230
   "Successfully unregistered account %s": "Contul %s a fost șters cu succes",
231
-  "That account is set to always-on; try logging out and logging back in with SASL": "",
231
+  "That account is set to always-on; try logging out and logging back in with SASL": "Acel cont are activă setarea conectare-permanentă; încearcă să te deconectezi și să te reconectezi cu SASL",
232
   "That certificate fingerprint is already associated with another account": "Amprenta de certificat este deja asociată unui alt cont",
232
   "That certificate fingerprint is already associated with another account": "Amprenta de certificat este deja asociată unui alt cont",
233
   "That certificate fingerprint was already authorized": "Amprenta certificatului a fost autorizată deja",
233
   "That certificate fingerprint was already authorized": "Amprenta certificatului a fost autorizată deja",
234
   "That channel is not registered": "Acel canal nu este înregistrat",
234
   "That channel is not registered": "Acel canal nu este înregistrat",

+ 10
- 0
oragono.yaml ファイルの表示

455
         offer-list:
455
         offer-list:
456
             #- "oragono.test"
456
             #- "oragono.test"
457
 
457
 
458
+    # modes that are set by default when a user connects
459
+    # if unset, no user modes will be set by default
460
+    # +i is invisible (a user's channels are hidden from whois replies)
461
+    # see  /QUOTE HELP umodes  for more user modes
462
+    default-user-modes: +i
463
+
458
     # support for deferring password checking to an external LDAP server
464
     # support for deferring password checking to an external LDAP server
459
     # you should probably ignore this section! consult the grafana docs for details:
465
     # you should probably ignore this section! consult the grafana docs for details:
460
     # https://grafana.com/docs/grafana/latest/auth/ldap/
466
     # https://grafana.com/docs/grafana/latest/auth/ldap/
516
         # how many channels can each account register?
522
         # how many channels can each account register?
517
         max-channels-per-account: 15
523
         max-channels-per-account: 15
518
 
524
 
525
+    # as a crude countermeasure against spambots, anonymous connections younger
526
+    # than this value will get an empty response to /LIST (a time period of 0 disables)
527
+    list-delay: 0s
528
+
519
 # operator classes
529
 # operator classes
520
 oper-classes:
530
 oper-classes:
521
     # local operator
531
     # local operator

読み込み中…
キャンセル
保存