Support individual channel keys for pre-defined channels.

This patch introduces the new configuration variable "KeyFile" for
[Channel] sections in ngircd.conf. Here a file can be configured for each
pre-defined channel which contains individual channel keys for different
users. This file is line-based and must have the following syntax:

  <user>:<nick>:<key>

<user> and <nick> can contain the wildcard character "*".

Please not that these channel keys are only in effect, when the channel
has a regular key set using channel mode "k"!
This commit is contained in:
Alexander Barton 2009-01-01 22:26:13 +01:00
parent 2c1b6280fa
commit c5000694d1
7 changed files with 149 additions and 10 deletions

View File

@ -245,6 +245,10 @@
# initial channel password (mode k)
;Key = Secret
# Key file, syntax for each line: "<user>:<nick>:<key>".
# Default: none.
;KeyFile = /etc/ngircd/#chan.key
# maximum users per channel (mode l)
;MaxUsers = 23

View File

@ -319,7 +319,50 @@ Topic for this channel.
Initial channel modes.
.TP
\fBKey\fR
Sets initial channel key (only relevant if mode k is set).
Sets initial channel key (only relevant if channel mode "k" is set).
.TP
\fBKeyFile\fR
Path and file name of a "key file" containing individual channel keys for
different users. The file consists of plain text lines with the following
syntax (without spaces!):
.PP
.RS
.RS
.I user
:
.I nick
:
.I key
.RE
.PP
.I user
and
.I nick
can contain the wildcard character "*".
.br
.I key
is an arbitrary password.
.PP
Valid examples are:
.PP
.RS
*:*:KeY
.br
*:nick:123
.br
~user:*:xyz
.RE
.PP
The key file is read on each JOIN command when this channel has a key
(channel mode +k). Access is granted, if a) the channel key set using the
MODE +k command or b) one of the lines in the key file match.
.PP
.B Please note:
.br
The file is not reopened on each access, so you can modify and overwrite it
without problems, but moving or deleting the file will have not effect until
the daemon re-reads its configuration!
.RE
.TP
\fBMaxUsers\fR
Set maximum user limit for this channel (only relevant if channel mode "l"

View File

@ -22,6 +22,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <strings.h>
#include "defines.h"
@ -39,6 +40,7 @@
#include "lists.h"
#include "log.h"
#include "messages.h"
#include "match.h"
#include "exp.h"
@ -59,6 +61,9 @@ static CL2CHAN *Get_First_Cl2Chan PARAMS(( CLIENT *Client, CHANNEL *Chan ));
static CL2CHAN *Get_Next_Cl2Chan PARAMS(( CL2CHAN *Start, CLIENT *Client, CHANNEL *Chan ));
static void Delete_Channel PARAMS(( CHANNEL *Chan ));
static void Free_Channel PARAMS(( CHANNEL *Chan ));
static void Update_Predefined PARAMS((CHANNEL *Chan,
const struct Conf_Channel *Conf_Chan));
static void Set_Key_File PARAMS((CHANNEL *Chan, FILE *KeyFile));
GLOBAL void
@ -116,8 +121,10 @@ Channel_InitPredefined( void )
new_chan = Channel_Search(conf_chan->name);
if (new_chan) {
Log(LOG_INFO, "Can't create pre-defined channel \"%s\": name already in use.",
conf_chan->name);
Log(LOG_INFO,
"Can't create pre-defined channel \"%s\": name already in use.",
conf_chan->name);
Update_Predefined(new_chan, conf_chan);
continue;
}
@ -127,6 +134,8 @@ Channel_InitPredefined( void )
conf_chan->name);
continue;
}
Log(LOG_INFO, "Created pre-defined channel \"%s\"",
conf_chan->name);
Channel_ModeAdd(new_chan, 'P');
@ -139,8 +148,7 @@ Channel_InitPredefined( void )
Channel_SetKey(new_chan, conf_chan->key);
Channel_SetMaxUsers(new_chan, conf_chan->maxusers);
Log(LOG_INFO, "Created pre-defined channel \"%s\"",
conf_chan->name);
Update_Predefined(new_chan, conf_chan);
}
if (channel_count)
array_free(&Conf_Channels);
@ -153,6 +161,8 @@ Free_Channel(CHANNEL *chan)
array_free(&chan->topic);
Lists_Free(&chan->list_bans);
Lists_Free(&chan->list_invites);
if (Chan->keyfile)
fclose(Chan->keyfile);
free(chan);
}
@ -1051,6 +1061,44 @@ Channel_LogServer(char *msg)
} /* Channel_LogServer */
GLOBAL bool
Channel_CheckKey(CHANNEL *Chan, CLIENT *Client, const char *Key)
{
char line[COMMAND_LEN], *nick, *pass;
assert(Chan != NULL);
assert(Client != NULL);
assert(Key != NULL);
if (!strchr(Chan->modes, 'k'))
return true;
if (strcmp(Chan->key, Key) == 0)
return true;
if (!Chan->keyfile)
return false;
Chan->keyfile = freopen(NULL, "r", Chan->keyfile);
while (fgets(line, sizeof(line), Chan->keyfile) != NULL) {
ngt_TrimStr(line);
if (! (nick = strchr(line, ':')))
continue;
*nick++ = '\0';
if (!Match(line, Client_User(Client)))
continue;
if (! (pass = strchr(nick, ':')))
continue;
*pass++ = '\0';
if (!Match(nick, Client_ID(Client)))
continue;
if (strcmp(Key, pass) != 0)
continue;
return true;
}
return false;
} /* Channel_CheckKey */
static CL2CHAN *
Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan )
{
@ -1108,4 +1156,36 @@ Delete_Channel(CHANNEL *Chan)
} /* Delete_Channel */
static void
Update_Predefined(CHANNEL *Chan, const struct Conf_Channel *Conf_Chan)
{
FILE *fd;
if (! Conf_Chan->keyfile || ! *Conf_Chan->keyfile)
return;
fd = fopen(Conf_Chan->keyfile, "r");
if (! fd)
Log(LOG_ERR,
"Can't open channel key file for \"%s\", \"%s\": %s",
Conf_Chan->name, Conf_Chan->keyfile,
strerror(errno));
else
Set_Key_File(Chan, fd);
} /* Update_Predefined */
static void
Set_Key_File(CHANNEL *Chan, FILE *KeyFile)
{
assert(Chan != NULL);
if (Chan->keyfile)
fclose(Chan->keyfile);
Chan->keyfile = KeyFile;
Log(LOG_INFO|LOG_snotice,
"New local channel key file for \"%s\" activated.", Chan->name);
} /* Set_Key_File */
/* -eof- */

View File

@ -37,6 +37,7 @@ typedef struct _CHANNEL
unsigned long maxusers; /* Maximum number of members (mode "l") */
struct list_head list_bans; /* list head of banned users */
struct list_head list_invites; /* list head of invited users */
FILE *keyfile; /* handle of the channel key file */
} CHANNEL;
typedef struct _CLIENT2CHAN
@ -127,6 +128,9 @@ GLOBAL bool Channel_ShowInvites PARAMS((CLIENT *client, CHANNEL *c));
GLOBAL void Channel_LogServer PARAMS((char *msg));
GLOBAL bool Channel_CheckKey PARAMS((CHANNEL *Chan, CLIENT *Client,
const char *Key));
#define Channel_IsLocal(c) (Channel_Name(c)[0] == '&')

View File

@ -313,7 +313,8 @@ Conf_Test( void )
printf(" Modes = %s\n", predef_chan->modes);
printf(" Key = %s\n", predef_chan->key);
printf(" MaxUsers = %lu\n", predef_chan->maxusers);
printf(" Topic = %s\n\n", predef_chan->topic);
printf(" Topic = %s\n", predef_chan->topic);
printf(" KeyFile = %s\n\n", predef_chan->keyfile);
}
return (config_valid ? 0 : 1);
@ -1232,6 +1233,13 @@ Handle_CHANNEL(int Line, char *Var, char *Arg)
Config_Error_NaN(Line, Var);
return;
}
if (strcasecmp(Var, "KeyFile") == 0) {
/* channel keys */
len = strlcpy(chan->keyfile, Arg, sizeof(chan->keyfile));
if (len >= sizeof(chan->keyfile))
Config_Error_TooLong(Line, Var);
return;
}
Config_Error( LOG_ERR, "%s, line %d (section \"Channel\"): Unknown variable \"%s\"!",
NGIRCd_ConfFile, Line, Var );

View File

@ -72,6 +72,7 @@ struct Conf_Channel {
char modes[CHANNEL_MODE_LEN]; /* Initial channel modes */
char key[CLIENT_PASS_LEN]; /* Channel key ("password", mode "k" ) */
char topic[COMMAND_LEN]; /* Initial topic */
char keyfile[512]; /* Path and name of channel key file */
unsigned long maxusers; /* maximum usercount for this channel, mode "l" */
};

View File

@ -89,10 +89,9 @@ join_allowed(CLIENT *Client, CLIENT *target, CHANNEL *chan,
}
/* Is the channel protected by a key? */
if (strchr(channel_modes, 'k') &&
strcmp(Channel_Key(chan), key ? key : ""))
{
IRC_WriteStrClient(Client, ERR_BADCHANNELKEY_MSG, Client_ID(Client), channame);
if (!Channel_CheckKey(chan, target, key ? key : "")) {
IRC_WriteStrClient(Client, ERR_BADCHANNELKEY_MSG,
Client_ID(Client), channame);
return false;
}
/* Are there already too many members? */