Committer : entrope
CVSROOT : /cvsroot/undernet-ircu
Module : ircu2.10
Commit time: 2005-10-31 23:17:39 UTC
Modified files:
ChangeLog ircd/channel.c ircd/m_burst.c ircd/m_join.c
ircd/m_mode.c ircd/s_err.c
Log message:
Make empty -A channels into zombies ("zannels") to avoid +A hijacks.
---------------------- diff included ----------------------
Index: ircu2.10/ChangeLog
diff -u ircu2.10/ChangeLog:1.731 ircu2.10/ChangeLog:1.732
--- ircu2.10/ChangeLog:1.731 Sun Oct 30 18:55:06 2005
+++ ircu2.10/ChangeLog Mon Oct 31 15:17:28 2005
@@ -1,5 +1,36 @@
2005-10-30 Michael Poole <[EMAIL PROTECTED]>
+ * ircd/channel.c (mode_parse_apass): Move all send_reply() errors
+ inside an if (MyUser(state->sptr)) test.
+
+ * ircd/m_join.c (m_join): Reorganize zannel join check to match
+ surrounding code.
+
+2005-10-30 Carlo Wood <[EMAIL PROTECTED]>
+
+ * ircd/channel.c (sub1_from_channel): Delay destruction for -A
+ channels. They become zombie channels (zannels).
+ (mode_parse_upass): Add duration to ERR_NOMANAGER message.
+ (mode_parse_apass): Likewise. Unconditionally set the member who
+ sets Apass as oplevel 0. Clear Upass when clearing Apass.
+ (joinbuf_join): Remove code to pass oplevel in JOIN.
+
+ * ircd/m_burst.c (ms_burst): Handle zannels.
+
+ * ircd/m_join.c (m_join): Handle a join to a zannel. If the user
+ is joining with ops and/or an oplevel, send those.
+ (ms_join): Stop trying to parse oplevels in JOIN. Copy join
+ timestamp when a user joins a zannel.
+
+ * ircd/m_mode.c (ms_mode): Never generate HACK3. Silently allow a
+ user to op himself if he is the only one in a channel.
+
+ * ircd/s_err.c (ERR_UPASSSET): Remove extra space.
+ (ERR_UPASSNOTSET): Likewise.
+ (ERR_NOMANAGER): Add field for channel lifetime.
+
+2005-10-30 Michael Poole <[EMAIL PROTECTED]>
+
* ircd/m_join.c (m_join): Reorganize to reduce edit distance from
previous version and match code on u2_10_12_branch.
Index: ircu2.10/ircd/channel.c
diff -u ircu2.10/ircd/channel.c:1.156 ircu2.10/ircd/channel.c:1.157
--- ircu2.10/ircd/channel.c:1.156 Thu Oct 6 16:49:25 2005
+++ ircu2.10/ircd/channel.c Mon Oct 31 15:17:28 2005
@@ -19,7 +19,7 @@
*/
/** @file
* @brief Channel management and maintenance
- * @version $Id: channel.c,v 1.156 2005/10/06 23:49:25 entrope Exp $
+ * @version $Id: channel.c,v 1.157 2005/10/31 23:17:28 entrope Exp $
*/
#include "config.h"
@@ -258,18 +258,37 @@
chptr->users = 0;
- /* There is a semantics problem here: Assuming no fragments across a
- * split, a channel without Apass could be maliciously destroyed and
- * recreated, and someone could set apass on the new instance.
- *
- * This could be fixed by preserving the empty non-Apass channel for
- * the same time as if it had an Apass (but removing +i and +l), and
- * reopping the first user to rejoin. However, preventing net rides
- * requires a backwards-incompatible protocol change..
+ /*
+ * Also channels without Apass set need to be kept alive,
+ * otherwise Bad Guys(tm) would be able to takeover
+ * existing channels too easily, and then set an Apass!
+ * However, if a channel without Apass becomes empty
+ * then we try to be kind to them and remove possible
+ * limiting modes.
+ */
+ chptr->mode.mode &= ~MODE_INVITEONLY;
+ chptr->mode.limit = 0;
+ /*
+ * We do NOT reset a possible key or bans because when
+ * the 'channel owners' can't get in because of a key
+ * or ban then apparently there was a fight/takeover
+ * on the channel and we want them to contact IRC opers
+ * who then will educate them on the use of Apass/Upass.
*/
- if (!chptr->mode.apass[0]) /* If no Apass, destroy now. */
- destruct_channel(chptr);
- else if (TStime() - chptr->creationtime < 172800) /* Channel younger than
48 hours? */
+ if (!chptr->mode.apass[0]) /* If no Apass, reset all
modes. */
+ {
+ struct Ban *link, *next;
+ chptr->mode.mode = 0;
+ *chptr->mode.key = '\0';
+ while (chptr->invites)
+ del_invite(chptr->invites->value.cptr, chptr);
+ for (link = chptr->banlist; link; link = next) {
+ next = link->next;
+ free_ban(link);
+ }
+ chptr->banlist = NULL;
+ }
+ if (TStime() - chptr->creationtime < 172800) /* Channel younger than 48
hours? */
schedule_destruct_event_1m(chptr); /* Get rid of it in
approximately 4-5 minutes */
else
schedule_destruct_event_48h(chptr); /* Get rid of it in
approximately 48 hours */
@@ -2360,7 +2379,9 @@
send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
state->chptr->chname);
} else {
- send_reply(state->sptr, ERR_NOMANAGER, state->chptr->chname);
+ send_reply(state->sptr, ERR_NOMANAGER, state->chptr->chname,
+ (TStime() - state->chptr->creationtime < 172800) ?
+ "approximately 4-5 minutes" : "approximately 48 hours");
}
return;
}
@@ -2452,31 +2473,48 @@
return;
}
- /* If a non-service user is trying to force it, refuse. */
- if (state->flags & MODE_PARSE_FORCE && MyUser(state->sptr)
- && !HasPriv(state->sptr, PRIV_APASS_OPMODE)) {
- send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
- state->chptr->chname);
- return;
- }
-
- /* Don't allow to change the Apass if the channel is older than 48 hours. */
- if (MyUser(state->sptr)
- && TStime() - state->chptr->creationtime >= 172800
- && !IsAnOper(state->sptr)) {
- send_reply(state->sptr, ERR_CHANSECURED, state->chptr->chname);
- return;
- }
-
- /* If they are not the channel manager, they are not allowed to change it */
- if (MyUser(state->sptr) && !(state->flags & MODE_PARSE_FORCE ||
IsChannelManager(state->member))) {
- if (*state->chptr->mode.apass) {
- send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
- state->chptr->chname);
+ if (MyUser(state->sptr)) {
+ if (state->flags & MODE_PARSE_FORCE) {
+ /* If an unprivileged oper is trying to force it, refuse. */
+ if (!HasPriv(state->sptr, PRIV_APASS_OPMODE)) {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ state->chptr->chname);
+ return;
+ }
} else {
- send_reply(state->sptr, ERR_NOMANAGER, state->chptr->chname);
+ /* If they are not the channel manager, they are not allowed to change
it. */
+ if (!IsChannelManager(state->member)) {
+ if (*state->chptr->mode.apass) {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ state->chptr->chname);
+ } else {
+ send_reply(state->sptr, ERR_NOMANAGER, state->chptr->chname,
+ (TStime() - state->chptr->creationtime < 172800) ?
+ "approximately 4-5 minutes" : "approximately 48 hours");
+ }
+ return;
+ }
+ /* Can't remove the Apass while Upass is still set. */
+ if (state->dir == MODE_DEL && *state->chptr->mode.upass) {
+ send_reply(state->sptr, ERR_UPASSSET, state->chptr->chname,
state->chptr->chname);
+ return;
+ }
+ /* Can't add an Apass if one is set, nor can one remove the wrong Apass.
*/
+ if ((state->dir == MODE_ADD && *state->chptr->mode.apass) ||
+ (state->dir == MODE_DEL && ircd_strcmp(state->chptr->mode.apass,
t_str))) {
+ send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
+ return;
+ }
+ }
+
+ /* Forbid removing the Apass if the channel is older than 48 hours
+ * unless an oper is doing it. */
+ if (TStime() - state->chptr->creationtime >= 172800
+ && state->dir == MODE_DEL
+ && !IsAnOper(state->sptr)) {
+ send_reply(state->sptr, ERR_CHANSECURED, state->chptr->chname);
+ return;
}
- return;
}
if (state->done & DONE_APASS) /* allow apass to be set only once */
@@ -2495,20 +2533,6 @@
if (!state->mbuf)
return;
- if (!(state->flags & MODE_PARSE_FORCE)) {
- /* can't remove the apass while upass is still set */
- if (state->dir == MODE_DEL && *state->chptr->mode.upass) {
- send_reply(state->sptr, ERR_UPASSSET, state->chptr->chname,
state->chptr->chname);
- return;
- }
- /* can't add an apass if one is set, nor can one remove the wrong apass */
- if ((state->dir == MODE_ADD && *state->chptr->mode.apass) ||
- (state->dir == MODE_DEL && ircd_strcmp(state->chptr->mode.apass,
t_str))) {
- send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
- return;
- }
- }
-
if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
!ircd_strcmp(state->chptr->mode.apass, t_str))
return; /* no apass change */
@@ -2537,11 +2561,20 @@
send_reply(state->sptr, RPL_APASSWARN_SECRET, state->chptr->chname,
state->chptr->mode.apass);
}
- /* Give the channel manager level 0 ops. */
- if (!(state->flags & MODE_PARSE_FORCE) &&
IsChannelManager(state->member))
+ /* Give the channel manager level 0 ops.
+ There should not be tested for IsChannelManager here because
+ on the local server it is impossible to set the apass if one
+ isn't a channel manager and remote servers might need to sync
+ the oplevel here: when someone creates a channel (and becomes
+ channel manager) during a net.break, and only sets the Apass
+ after the net rejoined, they will have oplevel MAXOPLEVEL on
+ all remote servers. */
+ if (state->member)
SetOpLevel(state->member, 0);
} else { /* remove the old apass */
*state->chptr->mode.apass = '\0';
+ /* Clear Upass so that there is never a Upass set when a zannel is
burst. */
+ *state->chptr->mode.upass = '\0';
if (MyUser(state->sptr))
send_reply(state->sptr, RPL_APASSWARN_CLEAR);
/* Revert everyone to MAXOPLEVEL. */
@@ -3308,17 +3341,10 @@
else
add_user_to_channel(chan, jbuf->jb_source, flags, oplevel);
- /* send notification to all servers */
+ /* send JOIN notification to all servers (CREATE is sent later). */
if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !is_local)
- {
- if (flags & CHFL_CHANOP) {
- assert(oplevel == 0 || oplevel == 1);
- sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
- "%u:%H %Tu", oplevel, chan, chan->creationtime);
- } else
- sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
- "%H %Tu", chan, chan->creationtime);
- }
+ sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
+ "%H %Tu", chan, chan->creationtime);
if (!((chan->mode.mode & MODE_DELJOINS) && !(flags &
CHFL_VOICED_OR_OPPED))) {
/* Send the notification to the channel */
Index: ircu2.10/ircd/m_burst.c
diff -u ircu2.10/ircd/m_burst.c:1.40 ircu2.10/ircd/m_burst.c:1.41
--- ircu2.10/ircd/m_burst.c:1.40 Mon Sep 26 19:41:57 2005
+++ ircu2.10/ircd/m_burst.c Mon Oct 31 15:17:29 2005
@@ -20,7 +20,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: m_burst.c,v 1.40 2005/09/27 02:41:57 entrope Exp $
+ * $Id: m_burst.c,v 1.41 2005/10/31 23:17:29 entrope Exp $
*/
/*
@@ -219,6 +219,57 @@
timestamp = atoi(parv[2]);
+ if (chptr->creationtime) /* 0 for new (empty) channels,
+ i.e. when this server just restarted. */
+ {
+ if (parc == 3) /* Zannel BURST? */
+ {
+ /* An empty channel without +A set, will cause a BURST message
+ with exactly 3 parameters (because all modes have been reset).
+ If the timestamp on such channels is only a few seconds older
+ from our own, then we ignore this burst: we do not deop our
+ own side.
+ Likewise, we expect the other (empty) side to copy our timestamp
+ from our own BURST message, even though it is slightly larger.
+
+ The reason for this is to allow people to join an empty
+ non-A channel (a zannel) during a net.split, and not be
+ deopped when the net reconnects (with another zannel). When
+ someone joins a split zannel, their side increments the TS by one.
+ If they cycle a few times then we still don't have a reason to
+ deop them. Theoretically I see no reason not to accept ANY timestamp,
+ but to be sure, we only accept timestamps that are just a few
+ seconds off (one second for each time they cycled the channel). */
+
+ /* Don't even deop users who cycled four times during the net.break. */
+ if (timestamp < chptr->creationtime &&
+ chptr->creationtime <= timestamp + 4 &&
+ chptr->users != 0) /* Only do this when WE have users, so that
+ if we do this the BURST that we sent has
+ parc > 3 and the other side will use the
+ test below: */
+ timestamp = chptr->creationtime; /* Do not deop our side. */
+ }
+ else if (chptr->creationtime < timestamp &&
+ timestamp <= chptr->creationtime + 4 &&
+ chptr->users == 0)
+ {
+ /* If one side of the net.junction does the above
+ timestamp = chptr->creationtime, then the other
+ side must do this: */
+ chptr->creationtime = timestamp; /* Use the same TS on both sides. */
+ }
+ /* In more complex cases, we might still end up with a
+ creationtime desync of a few seconds, but that should
+ be synced automatically rather quickly (every JOIN
+ caries a timestamp and will sync it; modes by users do
+ not carry timestamps and are accepted regardless).
+ Only when nobody joins the channel on the side with
+ the oldest timestamp before a new net.break occurs
+ precisely inbetween the desync, an unexpected bounce
+ might happen on reconnect. */
+ }
+
if (!chptr->creationtime || chptr->creationtime > timestamp) {
/*
* Kick local members if channel is +i or +k and our TS was larger
Index: ircu2.10/ircd/m_join.c
diff -u ircu2.10/ircd/m_join.c:1.37 ircu2.10/ircd/m_join.c:1.38
--- ircu2.10/ircd/m_join.c:1.37 Sun Oct 30 18:55:06 2005
+++ ircu2.10/ircd/m_join.c Mon Oct 31 15:17:29 2005
@@ -20,7 +20,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: m_join.c,v 1.37 2005/10/31 02:55:06 entrope Exp $
+ * $Id: m_join.c,v 1.38 2005/10/31 23:17:29 entrope Exp $
*/
#include "config.h"
@@ -182,15 +182,17 @@
int flags = CHFL_DEOPPED;
int err = 0;
- /* Check target change limits. */
-
/* Check Apass/Upass -- since we only ever look at a single
* "key" per channel now, this hampers brute force attacks. */
if (key && !strcmp(key, chptr->mode.apass))
flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
else if (key && !strcmp(key, chptr->mode.upass))
flags = CHFL_CHANOP;
- else if (IsInvited(sptr, chptr)) {
+ else if (chptr->users == 0 && !chptr->mode.apass[0]) {
+ /* Joining a zombie channel (zannel): give ops and increment TS. */
+ flags = CHFL_CHANOP;
+ chptr->creationtime++;
+ } else if (IsInvited(sptr, chptr)) {
/* Invites bypass these other checks. */
} else if (chptr->mode.mode & MODE_INVITEONLY)
err = ERR_INVITEONLYCHAN;
@@ -217,7 +219,7 @@
if (strcmp(chptr->mode.key, "OVERRIDE")
&& strcmp(chptr->mode.apass, "OVERRIDE")
&& strcmp(chptr->mode.upass, "OVERRIDE")) {
- send_reply(sptr, err, chptr->chname);
+ send_reply(sptr, ERR_DONTCHEAT, chptr->chname);
continue;
}
break;
@@ -242,6 +244,15 @@
}
joinbuf_join(&join, chptr, flags);
+ if (flags & CHFL_CHANOP) {
+ /* Send a MODE to the other servers. If the user used the A/U pass,
+ * let his server op him, otherwise let him op himself. */
+ struct ModeBuf mbuf;
+ modebuf_init(&mbuf, chptr->mode.apass[0] ? &me : sptr, cptr, chptr,
MODEBUF_DEST_SERVER);
+ modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr,
+ chptr->mode.apass[0] ? ((flags &
CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL);
+ modebuf_flush(&mbuf);
+ }
}
del_invite(sptr, chptr);
@@ -302,18 +313,7 @@
for (name = ircd_strtok(&p, chanlist, ","); name;
name = ircd_strtok(&p, 0, ",")) {
- if (name[0] == '0' && name[1] == ':')
- {
- flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
- name += 2;
- }
- else if (name[0] == '1' && name[1] == ':')
- {
- flags = CHFL_CHANOP;
- name += 2;
- }
- else
- flags = CHFL_DEOPPED;
+ flags = CHFL_DEOPPED;
if (IsLocalChannel(name) || !IsChannelName(name))
{
@@ -349,8 +349,14 @@
else
flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0;
/* Always copy the timestamp when it is older, that is the only way to
- ensure network-wide synchronization of creation times. */
- if (creation && creation < chptr->creationtime)
+ ensure network-wide synchronization of creation times.
+ We now also copy a creation time that only 1 second younger...
+ this is needed because the timestamp must be incremented
+ by one when someone joins an existing, but empty, channel.
+ However, this is only necessary when the channel is still
+ empty (also here) and when this channel doesn't have +A set.
+ */
+ if (creation && creation - ((!chptr->mode.apass[0] && chptr->users == 0)
? 1 : 0) <= chptr->creationtime)
chptr->creationtime = creation;
}
Index: ircu2.10/ircd/m_mode.c
diff -u ircu2.10/ircd/m_mode.c:1.15 ircu2.10/ircd/m_mode.c:1.16
--- ircu2.10/ircd/m_mode.c:1.15 Tue Sep 13 08:17:46 2005
+++ ircu2.10/ircd/m_mode.c Mon Oct 31 15:17:29 2005
@@ -20,7 +20,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: m_mode.c,v 1.15 2005/09/13 15:17:46 entrope Exp $
+ * $Id: m_mode.c,v 1.16 2005/10/31 23:17:29 entrope Exp $
*/
/*
@@ -179,12 +179,11 @@
MODEBUF_DEST_HACK4)); /* Send a HACK(4) message */
else
/* Servers need to be able to op people who join using the Apass
- * or upass, therefore we accept modes for channels with an Apass
- * without generating a HACK3. */
+ * or upass, as well as people joining a zannel, therefore we no
+ * longer generate HACK3. */
modebuf_init(&mbuf, sptr, cptr, chptr,
(MODEBUF_DEST_CHANNEL | /* Send mode to clients */
- MODEBUF_DEST_SERVER | /* Send mode to servers */
- (*chptr->mode.apass ? 0 : MODEBUF_DEST_HACK3)));
+ MODEBUF_DEST_SERVER)); /* Send mode to servers */
mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
(MODE_PARSE_SET | /* Set the mode */
@@ -192,7 +191,9 @@
MODE_PARSE_FORCE), /* And force it to be accepted */
NULL);
} else {
- if (!(member = find_member_link(chptr, sptr)) || !IsChanOp(member)) {
+ if (!(member = find_member_link(chptr, sptr))
+ /* Allow people to op themselves on an empty channel. */
+ || (!IsChanOp(member) && chptr->users > 1)) {
modebuf_init(&mbuf, sptr, cptr, chptr,
(MODEBUF_DEST_SERVER | /* Send mode to server */
MODEBUF_DEST_HACK2 | /* Send a HACK(2) message */
Index: ircu2.10/ircd/s_err.c
diff -u ircu2.10/ircd/s_err.c:1.72 ircu2.10/ircd/s_err.c:1.73
--- ircu2.10/ircd/s_err.c:1.72 Mon Aug 29 14:39:26 2005
+++ ircu2.10/ircd/s_err.c Mon Oct 31 15:17:29 2005
@@ -18,7 +18,7 @@
*/
/** @file
* @brief Error handling support.
- * @version $Id: s_err.c,v 1.72 2005/08/29 21:39:26 entrope Exp $
+ * @version $Id: s_err.c,v 1.73 2005/10/31 23:17:29 entrope Exp $
*/
#include "config.h"
@@ -1158,13 +1158,13 @@
/* 562 */
{ ERR_CHANSECURED, "%s :Channel is older than 48 hours and secured. Cannot
change Admin pass anymore", "562" },
/* 563 */
- { ERR_UPASSSET, "%s :Cannot remove Admin pass (+A) while User pass (+U) is
still set. First use /MODE %s -U <userpass>", "563" },
+ { ERR_UPASSSET, "%s :Cannot remove Admin pass (+A) while User pass (+U) is
still set. First use /MODE %s -U <userpass>", "563" },
/* 564 */
- { ERR_UPASSNOTSET, "%s :Cannot set user pass (+U) until Admin pass (+A) is
set. First use /MODE %s +A <adminpass>", "564" },
+ { ERR_UPASSNOTSET, "%s :Cannot set user pass (+U) until Admin pass (+A) is
set. First use /MODE %s +A <adminpass>", "564" },
/* 565 */
{ 0 },
/* 566 */
- { ERR_NOMANAGER, "%s :Re-create the channel. The channel must be completely
empty before it can be recreated.", "566" },
+ { ERR_NOMANAGER, "%s :Re-create the channel. The channel must be completely
empty for a period of %s before it can be recreated.", "566" },
/* 567 */
{ ERR_UPASS_SAME_APASS, "%s :Cannot use the same pass for both admin (+A)
and user (+U) pass.", "567" },
/* 568 */
----------------------- End of diff -----------------------
_______________________________________________
Patches mailing list
[email protected]
http://undernet.sbg.org/mailman/listinfo/patches