+2005-09-01 Michael Poole <mdpoole@troilus.org>
+
+ * RELEASE.NOTES: Bump revision date and highlight this change.
+
+ * include/channel.h (MAGIC_OPER_OVERRIDE): Remove.
+ (can_join): Remove declaration.
+
+ * ircd/channel.c (compall): Remove.
+ (can_join): Remove.
+
+ * ircd/m_join.c (m_join): Remove redundant check for control
+ characters (clean_channelname() will get them). Reorganize initial
+ flags calculation. Accept channel keys like RFC 1459 says; this
+ requires the old compall()/can_join() logic to modify 'keys', so
+ inline the code and reorganize it.
+
2005-08-30 Michael Poole <mdpoole@troilus.org>
* include/channel.h (PASSLEN): Remove; use KEYLEN instead.
Release notes for ircu2.10.12
-Last updated: 14 Jan 2005
+Last updated: 1 Sep 2005
Written by Michael Poole <mdpoole@troilus.org>
Based on earlier documents by Kev <klmitch@mit.edu> and
Braden <dbtem@yahoo.com>.
ircu2.10.11, but some features (notably IPv6 support and oplevels) are
not supported by ircu2.10.11.
+Semantic Changes (TAKE NOTE):
+
+Channel keys and passwords (see the "oplevels" enhancement below)
+listed in a JOIN are now only checked against the corresponding
+channel. In ircu2.10.11, "JOIN #a,#b key" would attempt to use "key"
+as the key for both #a and #b. ircu2.10.12 will only attempt to use
+it as the key for #a. ircu2.10.12's behavior matches that documented
+in RFC 1459.
+
Enhancements:
The configuration file format has changed to one that is easier to
*/
#define MAGIC_REMOTE_JOIN_TS 1270080000
-/**
- * used in can_join to determine if an oper forced a join on a channel
- */
-#define MAGIC_OPER_OVERRIDE 1000
extern const char* const PartFmt1;
const struct Client* cptr);
extern int sub1_from_channel(struct Channel* chptr);
extern int destruct_channel(struct Channel* chptr);
-extern int can_join(struct Client *sptr, struct Channel *chptr, char *key);
extern void add_user_to_channel(struct Channel* chptr, struct Client* who,
unsigned int flags, int oplevel);
extern void make_zombie(struct Membership* member, struct Client* who,
send_reply(cptr, RPL_ENDOFBANLIST, chptr->chname);
}
-/** Check a key against a keyring.
- * We are now treating the key part of /join channellist key as a key
- * ring; that is, we try one key against the actual channel key, and if that
- * doesn't work, we try the next one, and so on. -Kev -Texaco
- * Returns: 0 on match, 1 otherwise
- * This version contributed by SeKs \<intru@info.polymtl.ca\>
- *
- * @param key Key to check
- * @param keyring Comma separated list of keys
- *
- * @returns True if the key was found and matches, false otherwise.
- */
-static int compall(char *key, char *keyring)
-{
- char *p1;
-
-top:
- p1 = key; /* point to the key... */
- while (*p1 && *p1 == *keyring)
- { /* step through the key and ring until they
- don't match... */
- p1++;
- keyring++;
- }
-
- if (!*p1 && (!*keyring || *keyring == ','))
- /* ok, if we're at the end of the and also at the end of one of the keys
- in the keyring, we have a match */
- return 0;
-
- if (!*keyring) /* if we're at the end of the key ring, there
- weren't any matches, so we return 1 */
- return 1;
-
- /* Not at the end of the key ring, so step
- through to the next key in the ring: */
- while (*keyring && *(keyring++) != ',');
-
- goto top; /* and check it against the key */
-}
-
-/** Returns if a user can join a channel with a specific key.
- *
- * @param sptr The client trying to join
- * @param chptr The channel to join
- * @param key The key to use
- *
- * @returns any error that occurred bit-wise OR'd with MAGIC_OPER_OVERRIDE
- * if the oper used the magic key, 0 if no error occurred.
- */
-int can_join(struct Client *sptr, struct Channel *chptr, char *key)
-{
- int overrideJoin = 0;
-
- /*
- * Now a banned user CAN join if invited -- Nemesi
- * Now a user CAN escape channel limit if invited -- bfriendly
- * Now a user CAN escape anything if invited -- Isomer
- */
-
- if (IsInvited(sptr, chptr))
- return 0;
-
- /* An oper can force a join on a local channel using "OVERRIDE" as the key.
- a HACK(4) notice will be sent if he would not have been supposed
- to join normally. */
- if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) &&
- !BadPtr(key) && compall("OVERRIDE",chptr->mode.key) != 0 &&
- compall("OVERRIDE",key) == 0)
- overrideJoin = MAGIC_OPER_OVERRIDE;
-
- if (chptr->mode.mode & MODE_INVITEONLY)
- return overrideJoin + ERR_INVITEONLYCHAN;
-
- if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
- return overrideJoin + ERR_CHANNELISFULL;
-
- if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr))
- return overrideJoin + ERR_NEEDREGGEDNICK;
-
- if (find_ban(sptr, chptr->banlist))
- return overrideJoin + ERR_BANNEDFROMCHAN;
-
- /*
- * now using compall (above) to test against a whole key ring -Kev
- */
- if (*chptr->mode.key && (EmptyString(key) || compall(chptr->mode.key, key)))
- return overrideJoin + ERR_BADCHANNELKEY;
-
- if (overrideJoin)
- return ERR_DONTCHEAT;
-
- return 0;
-}
-
/** Remove bells and commas from channel name
*
* @param cn Channel name to clean, modified in place.
struct JoinBuf join;
struct JoinBuf create;
struct Gline *gline;
- unsigned int flags = 0;
- int i, j, k = 0;
+ unsigned int flags;
char *p = 0;
char *chanlist;
char *name;
continue;
}
- /* This checks if the channel contains control codes and rejects em
- * until they are gone, then we will do it otherwise - *SOB Mode*
- */
- for (k = 0, j = 0; name[j]; j++)
- if (IsCntrl(name[j]))
- k++;
- if (k > 0)
- {
- send_reply(sptr, ERR_NOSUCHCHANNEL, name);
- continue;
- }
-
/* BADCHANed channel */
if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) &&
GlineIsActive(gline) && !IsAnOper(sptr)) {
continue;
}
- if ((chptr = FindChannel(name)))
- {
- if (find_member_link(chptr, sptr))
- continue; /* already on channel */
-
- flags = CHFL_DEOPPED;
- }
- else
+ if (!(chptr = FindChannel(name)))
flags = CHFL_CHANOP;
+ else if (find_member_link(chptr, sptr))
+ continue; /* already on channel */
+ else
+ flags = CHFL_DEOPPED;
/* disallow creating local channels */
if ((name[0] == '&') && !chptr && !feature_bool(FEAT_LOCAL_CHANNELS)) {
}
if (chptr) {
+ char *key = 0;
int is_level0_op = 0;
- if (!BadPtr(keys) && *chptr->mode.apass) {
- /* Don't use compall for the apass, only a single key is allowed. */
- if (strcmp(chptr->mode.apass, keys) == 0) {
- is_level0_op = 1;
- flags &= ~CHFL_DEOPPED;
- flags |= CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
- }
- else if (*chptr->mode.upass && strcmp(chptr->mode.upass, keys) == 0) {
- is_level0_op = 1;
- flags &= ~CHFL_DEOPPED;
- flags |= CHFL_CHANOP;
- }
- }
+ int err = 0;
+
+ /* Check target change limits. */
if (check_target_limit(sptr, chptr, chptr->chname, 0))
- continue; /* exceeded target limit */
- else if (!is_level0_op && (i = can_join(sptr, chptr, keys))) {
- if (i > MAGIC_OPER_OVERRIDE)
- { /* oper overrode mode */
- switch (i - MAGIC_OPER_OVERRIDE)
- {
- case ERR_CHANNELISFULL: /* figure out which mode */
- i = 'l';
- break;
-
- case ERR_INVITEONLYCHAN:
- i = 'i';
- break;
-
- case ERR_BANNEDFROMCHAN:
- i = 'b';
- break;
-
- case ERR_BADCHANNELKEY:
- i = 'k';
- break;
-
- case ERR_NEEDREGGEDNICK:
- i = 'r';
- break;
-
- default:
- i = '?';
- break;
- }
-
- /* send accountability notice */
- sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H "
- "(overriding +%c)", sptr, chptr, i);
- }
- else
- {
- send_reply(sptr, i, chptr->chname);
+ continue;
+
+ /* If we have any more keys, take the first for this channel. */
+ if (!BadPtr(keys)
+ && (keys = strchr(key = keys, ',')))
+ *keys++ = '\0';
+
+ /* Check Apass/Upass -- since we only ever look at a single
+ * "key" per channel now, this hampers brute force attacks. */
+ if (!BadPtr(key) && !strcmp(key, chptr->mode.apass)) {
+ is_level0_op = 1;
+ flags = (flags & ~CHFL_DEOPPED) | CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
+ } else if (!BadPtr(key) && !strcmp(key, chptr->mode.upass)) {
+ is_level0_op = 1;
+ flags = (flags & ~CHFL_DEOPPED) | CHFL_CHANOP;
+ } else if (IsInvited(sptr, chptr)) {
+ /* Invites bypass these other checks. */
+ } else if (chptr->mode.mode & MODE_INVITEONLY)
+ err = ERR_INVITEONLYCHAN;
+ else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit))
+ err = ERR_CHANNELISFULL;
+ else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr))
+ err = ERR_NEEDREGGEDNICK;
+ else if (find_ban(sptr, chptr->banlist))
+ err = ERR_BANNEDFROMCHAN;
+ else if (*chptr->mode.key && strcmp(chptr->mode.key, key))
+ err = ERR_BADCHANNELKEY;
+
+ /* An oper with WALK_LCHAN privilege can join a local channel
+ * he otherwise could not join by using "OVERRIDE" as the key.
+ * This will generate a HACK(4) notice, but fails if the oper
+ * could normally join the channel. */
+ if (IsLocalChannel(chptr->chname)
+ && HasPriv(sptr, PRIV_WALK_LCHAN)
+ && !is_level0_op
+ && !BadPtr(key)
+ && !strcmp("OVERRIDE", key))
+ {
+ switch (err) {
+ case 0:
+ send_reply(sptr, ERR_DONTCHEAT, chptr->chname);
continue;
- }
- } /* else if ((i = can_join(sptr, chptr, keys))) */
+ case ERR_INVITEONLYCHAN: err = 'i'; break;
+ case ERR_CHANNELISFULL: err = 'l'; break;
+ case ERR_BANNEDFROMCHAN: err = 'b'; break;
+ case ERR_BADCHANNELKEY: err = 'k'; break;
+ case ERR_NEEDREGGEDNICK: err = 'r'; break;
+ default: err = '?'; break;
+ }
+ /* send accountability notice */
+ sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H "
+ "(overriding +%c)", sptr, chptr, err);
+ err = 0;
+ }
+
+ /* Is there some reason the user may not join? */
+ if (err) {
+ send_reply(sptr, err, chptr->chname);
+ continue;
+ }
joinbuf_join(&join, chptr, flags);
} else if (!(chptr = get_channel(sptr, name, CGT_CREATE)))