Updated to 6.0.4

This commit is contained in:
Dionysus 2022-11-19 23:12:40 -05:00
parent e3f051076e
commit 3d97a9500c
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
106 changed files with 3377 additions and 1297 deletions

2
Config
View File

@ -375,7 +375,7 @@ echo "We will now ask you a number of questions. You can just press ENTER to acc
echo ""
# This needs to be updated each release so auto-upgrading works for settings, modules, etc!!:
UNREALRELEASES="unrealircd-6.0.2 unrealircd-6.0.1.1 unrealircd-6.0.1 unrealircd-6.0.0 unrealircd-6.0.0-rc2 unrealircd-6.0.0-rc1 unrealircd-6.0.0-beta4 unrealircd-6.0.0-beta3 unrealircd-6.0.0-beta2 unrealircd-6.0.0-beta1 unrealircd-5.2.3 unrealircd-5.2.2 unrealircd-5.2.1.1 unrealircd-5.2.1 unrealircd-5.2.1-rc1 unrealircd-5.2.0.2 unrealircd-5.2.0.1 unrealircd-5.2.0 unrealircd-5.2.0-rc1 unrealircd-5.0.9.1 unrealircd-5.0.9 unrealircd-5.0.9-rc1 unrealircd-5.0.8 unrealircd-5.0.8-rc1 unrealircd-5.0.7 unrealircd-5.0.7-rc1 unrealircd-5.0.6"
UNREALRELEASES="unrealircd-6.0.4.1 unrealircd-6.0.4 unrealircd-6.0.4-rc2 unrealircd-6.0.4-rc1 unrealircd-6.0.3 unrealircd-6.0.2 unrealircd-6.0.1.1 unrealircd-6.0.1 unrealircd-6.0.0 unrealircd-6.0.0-rc2 unrealircd-6.0.0-rc1 unrealircd-6.0.0-beta4 unrealircd-6.0.0-beta3 unrealircd-6.0.0-beta2 unrealircd-6.0.0-beta1 unrealircd-5.2.3 unrealircd-5.2.2 unrealircd-5.2.1.1 unrealircd-5.2.1 unrealircd-5.2.1-rc1 unrealircd-5.2.0.2 unrealircd-5.2.0.1 unrealircd-5.2.0 unrealircd-5.2.0-rc1 unrealircd-5.0.9.1 unrealircd-5.0.9 unrealircd-5.0.9-rc1 unrealircd-5.0.8 unrealircd-5.0.8-rc1 unrealircd-5.0.7 unrealircd-5.0.7-rc1 unrealircd-5.0.6"
if [ -f "config.settings" ]; then
. ./config.settings
else

View File

@ -178,7 +178,7 @@ EXP_OBJ_FILES=src/ircd_vars.obj src/channel.obj src/send.obj src/socket.obj \
src/fdlist.obj src/dbuf.obj \
src/hash.obj src/parse.obj \
src/whowas.obj \
src/misc.obj src/match.obj src/crule.obj \
src/securitygroup.obj src/misc.obj src/match.obj src/crule.obj \
src/debug.obj src/support.obj src/list.obj \
src/serv.obj src/user.obj \
src/version.obj src/ircsprintf.obj \
@ -254,6 +254,7 @@ DLL_FILES=\
src/modules/close.dll \
src/modules/connect.dll \
src/modules/connthrottle.dll \
src/modules/creationtime.dll \
src/modules/cycle.dll \
src/modules/dccallow.dll \
src/modules/dccdeny.dll \
@ -278,6 +279,7 @@ DLL_FILES=\
src/modules/geoip_base.dll \
src/modules/geoip_classic.dll \
src/modules/geoip_csv.dll \
src/modules/geoip-tag.dll \
src/modules/globops.dll \
src/modules/help.dll \
src/modules/hideserver.dll \
@ -364,6 +366,7 @@ DLL_FILES=\
src/modules/svsnline.dll \
src/modules/svsnolag.dll \
src/modules/svsnoop.dll \
src/modules/svso.dll \
src/modules/svspart.dll \
src/modules/svssilence.dll \
src/modules/svssno.dll \
@ -379,6 +382,7 @@ DLL_FILES=\
src/modules/trace.dll \
src/modules/tsctl.dll \
src/modules/typing-indicator.dll \
src/modules/channel-context.dll \
src/modules/umode2.dll \
src/modules/unreal_server_compat.dll \
src/modules/unsqline.dll \
@ -502,6 +506,9 @@ src/conf_preprocessor.obj: src/conf_preprocessor.c $(INCLUDES)
src/debug.obj: src/debug.c $(INCLUDES)
$(CC) $(CFLAGS) src/debug.c
src/securitygroup.obj: src/securitygroup.c $(INCLUDES)
$(CC) $(CFLAGS) src/securitygroup.c
src/misc.obj: src/misc.c $(INCLUDES) ./include/dbuf.h
$(CC) $(CFLAGS) src/misc.c
@ -828,6 +835,9 @@ src/modules/connect.dll: src/modules/connect.c $(INCLUDES)
src/modules/connthrottle.dll: src/modules/connthrottle.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/connthrottle.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/connthrottle.pdb $(MODLFLAGS)
src/modules/creationtime.dll: src/modules/creationtime.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/creationtime.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/creationtime.pdb $(MODLFLAGS)
src/modules/cycle.dll: src/modules/cycle.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/cycle.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/cycle.pdb $(MODLFLAGS)
@ -900,6 +910,9 @@ src/modules/geoip_classic.dll: src/modules/geoip_classic.c $(INCLUDES)
src/modules/geoip_csv.dll: src/modules/geoip_csv.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/geoip_csv.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/geoip_csv.pdb $(MODLFLAGS)
src/modules/geoip-tag.dll: src/modules/geoip-tag.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/geoip-tag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/geoip-tag.pdb $(MODLFLAGS)
src/modules/geoip_maxmind.dll: src/modules/geoip_maxmind.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/geoip_maxmind.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/geoip_maxmind.pdb $(MODLFLAGS)
@ -1161,6 +1174,9 @@ src/modules/svsnolag.dll: src/modules/svsnolag.c $(INCLUDES)
src/modules/svsnoop.dll: src/modules/svsnoop.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/svsnoop.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsnoop.pdb $(MODLFLAGS)
src/modules/svso.dll: src/modules/svso.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/svso.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svso.pdb $(MODLFLAGS)
src/modules/svspart.dll: src/modules/svspart.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/svspart.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svspart.pdb $(MODLFLAGS)
@ -1206,6 +1222,9 @@ src/modules/tsctl.dll: src/modules/tsctl.c $(INCLUDES)
src/modules/typing-indicator.dll: src/modules/typing-indicator.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/typing-indicator.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/typing-indicator.pdb $(MODLFLAGS)
src/modules/channel-context.dll: src/modules/channel-context.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/channel-context.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/channel-context.pdb $(MODLFLAGS)
src/modules/umode2.dll: src/modules/umode2.c $(INCLUDES)
$(CC) $(MODCFLAGS) src/modules/umode2.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/umode2.pdb $(MODLFLAGS)

View File

@ -13,10 +13,17 @@ online documentation.
* UnrealIRCd 6 is the *stable* series since December 2021. All new features go in there.
* UnrealIRCd 5 is the *oldstable* series. It will receive bug fixes until
July 1, 2022 plus another 12 months of security fixes.
* For full details of release scheduling and EOL dates, see
[UnrealIRCd releases](https://www.unrealircd.org/docs/UnrealIRCd_releases) on the wiki
## How to get started
Please consult our excellent online documentation at https://www.unrealircd.org/docs/
when setting up the IRCd!
### Use the wiki!
**IMPORTANT:** We recommend you follow our installation guide on the wiki instead of the
steps in this README. The wiki has more detailed information and is more easy to navigate.
* [Installing from source for *NIX](https://www.unrealircd.org/docs/Installing_from_source)
* [Installating instructions for Windows](https://www.unrealircd.org/docs/Installing_(Windows))
Please consult the online documentation at https://www.unrealircd.org/docs/ when setting up the IRCd!
### Step 1: Installation
#### Windows
@ -36,11 +43,11 @@ Do the following steps under a separate account for running UnrealIRCd,
* Now change to the directory where you installed UnrealIRCd, e.g. `cd /home/xxxx/unrealircd`
### Step 2: Configuration
Configuration files are stored in the conf/ folder by default (eg: /home/xxxx/unrealircd/conf)
Configuration files are stored in the `conf/` folder by default (eg: `/home/xxxx/unrealircd/conf`)
#### Create a configuration file
If you are new, then you need to create your own configuration file:
Copy conf/examples/example.conf to conf/ and call it unrealircd.conf.
Copy `conf/examples/example.conf` to `conf/` and call it `unrealircd.conf`.
Then open it in an editor and carefully modify it using the documentation and FAQ as a guide (see below).
### Step 3: Booting

70
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for unrealircd 6.0.3.
# Generated by GNU Autoconf 2.69 for unrealircd 6.0.4.2.
#
# Report bugs to <https://bugs.unrealircd.org/>.
#
@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='unrealircd'
PACKAGE_TARNAME='unrealircd'
PACKAGE_VERSION='6.0.3'
PACKAGE_STRING='unrealircd 6.0.3'
PACKAGE_VERSION='6.0.4.2'
PACKAGE_STRING='unrealircd 6.0.4.2'
PACKAGE_BUGREPORT='https://bugs.unrealircd.org/'
PACKAGE_URL='https://unrealircd.org/'
@ -1347,7 +1347,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures unrealircd 6.0.3 to adapt to many kinds of systems.
\`configure' configures unrealircd 6.0.4.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1413,7 +1413,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of unrealircd 6.0.3:";;
short | recursive ) echo "Configuration of unrealircd 6.0.4.2:";;
esac
cat <<\_ACEOF
@ -1589,7 +1589,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
unrealircd configure 6.0.3
unrealircd configure 6.0.4.2
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@ -1958,7 +1958,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by unrealircd $as_me 6.0.3, which was
It was created by unrealircd $as_me 6.0.4.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@ -2366,7 +2366,7 @@ _ACEOF
# Minor version number (e.g.: Z in X.Y.Z)
UNREAL_VERSION_MINOR="3"
UNREAL_VERSION_MINOR="4"
cat >>confdefs.h <<_ACEOF
#define UNREAL_VERSION_MINOR $UNREAL_VERSION_MINOR
@ -2376,7 +2376,7 @@ _ACEOF
# The version suffix such as a beta marker or release candidate
# marker. (e.g.: -rcX for unrealircd-3.2.9-rcX). This macro is a
# string instead of an integer because it contains arbitrary data.
UNREAL_VERSION_SUFFIX=""
UNREAL_VERSION_SUFFIX=".2"
cat >>confdefs.h <<_ACEOF
#define UNREAL_VERSION_SUFFIX "$UNREAL_VERSION_SUFFIX"
@ -5638,6 +5638,54 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat-overflow" >&5
$as_echo_n "checking whether C compiler accepts -Wformat-overflow... " >&6; }
if ${ax_cv_check_cflags__Werror___Wformat_overflow+:} false; then :
$as_echo_n "(cached) " >&6
else
ax_check_save_flags=$CFLAGS
CFLAGS="$CFLAGS -Werror -Wformat-overflow"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ax_cv_check_cflags__Werror___Wformat_overflow=yes
else
ax_cv_check_cflags__Werror___Wformat_overflow=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
CFLAGS=$ax_check_save_flags
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror___Wformat_overflow" >&5
$as_echo "$ax_cv_check_cflags__Werror___Wformat_overflow" >&6; }
if test x"$ax_cv_check_cflags__Werror___Wformat_overflow" = xyes; then :
CFLAGS="$CFLAGS -Wno-format-overflow"
else
:
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@ -9414,7 +9462,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by unrealircd $as_me 6.0.3, which was
This file was extended by unrealircd $as_me 6.0.4.2, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -9477,7 +9525,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
unrealircd config.status 6.0.3
unrealircd config.status 6.0.4.2
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@ -7,7 +7,7 @@ dnl src/windows/unrealinst.iss
dnl doc/Config.header
dnl src/version.c.SH
AC_INIT([unrealircd], [6.0.3], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/])
AC_INIT([unrealircd], [6.0.4.2], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/])
AC_CONFIG_SRCDIR([src/ircd.c])
AC_CONFIG_HEADER([include/setup.h])
AC_CONFIG_AUX_DIR([autoconf])
@ -34,13 +34,13 @@ UNREAL_VERSION_MAJOR=["0"]
AC_DEFINE_UNQUOTED([UNREAL_VERSION_MAJOR], [$UNREAL_VERSION_MAJOR], [Major version number (e.g.: Y for X.Y.Z)])
# Minor version number (e.g.: Z in X.Y.Z)
UNREAL_VERSION_MINOR=["3"]
UNREAL_VERSION_MINOR=["4"]
AC_DEFINE_UNQUOTED([UNREAL_VERSION_MINOR], [$UNREAL_VERSION_MINOR], [Minor version number (e.g.: Z for X.Y.Z)])
# The version suffix such as a beta marker or release candidate
# marker. (e.g.: -rcX for unrealircd-3.2.9-rcX). This macro is a
# string instead of an integer because it contains arbitrary data.
UNREAL_VERSION_SUFFIX=[""]
UNREAL_VERSION_SUFFIX=[".2"]
AC_DEFINE_UNQUOTED([UNREAL_VERSION_SUFFIX], ["$UNREAL_VERSION_SUFFIX"], [Version suffix such as a beta marker or release candidate marker. (e.g.: -rcX for unrealircd-3.2.9-rcX)])
AC_PATH_PROG(RM,rm)
@ -237,6 +237,8 @@ check_cc_flag([-Wformat-zero-length], [CFLAGS="$CFLAGS -Wno-format-zero-length"]
check_cc_flag([-Wformat-truncation], [CFLAGS="$CFLAGS -Wno-format-truncation"])
check_cc_flag([-Wformat-overflow], [CFLAGS="$CFLAGS -Wno-format-overflow"])
dnl While it can be useful to occasionally to compile with warnings about
dnl unused variables and parameters, we often 'think ahead' when coding things
dnl so they may be useless now but not later. Similarly, for variables, we

View File

@ -7,7 +7,7 @@
\___/|_| |_|_| \___|\__,_|_|\___/\_| \_| \____/\__,_|
Configuration Program
for UnrealIRCd 6.0.3
for UnrealIRCd 6.0.4.2
This program will help you to compile your IRC server, and ask you
questions regarding the compile-time settings of it during the process.

View File

@ -1,15 +1,183 @@
UnrealIRCd 6.0.3
=================
UnrealIRCd 6.0.4.2
===================
Another small update to 6.0.4.x:
A number of serious issues were discovered in UnrealIRCd 6. Among these is
an issue which will likely crash the IRCd sooner or later if you /REHASH
with any active clients connected.
We suggest everyone who is running UnrealIRCd 6 to upgrade to 6.0.3.
* Fix crash when linking. This requires a certain sequence of events: first
a server is linked in successfully, then we need to REHASH, and then a new
link attempt has to come in with the same server name (for example because
there is a network issue and the old link has not timed out yet).
If all that happens, then an UnreaIRCd 6 server may crash, but not always.
* Two IRCv3 specifications were ratified which we already supported as drafts:
* Change CAP `draft/extended-monitor` to `extended-monitor`
* Add message-tag `bot` next to existing (for now) `draft/bot`
* Update Turkish translations
UnrealIRCd 6.0.4.1
===================
This is a small update to 6.0.4. It fixes the following issues that were
present in all 6.0.x versions:
* Fix sporadic crash when linking a server (after successful authentication).
This feels like a compiler bug. It affected only some people with GCC and
only in some situations. When compiled with clang there was no problem.
Hopefully we can work around it this way.
* Make /INVITE bypass (nearly) all channel mode restrictions, as it used to
be in UnrealIRCd 5.x. Both for invites by channel ops and for OperOverride.
This also fixes a bug where an IRCOp with OperOverride could not bypass +l
(limit) and other restrictions and would have to resort back to using
MODE or SAMODE. Only +b and +i could be bypassed via INVITE OperOverride.
UnrealIRCd 6.0.4
-----------------
This release comes with lots of features and enhancements. In particular,
security groups and mask items now allow you to write cleaner and more
flexible configuration files. There are also JSON logging enhancements and
several bug fixes. Thanks a lot to everyone who tested the release candidates!
If you are already running UnrealIRCd 6 then read below. Otherwise, jump
straight to the [summary about UnrealIRCd 6](#Summary) to learn more
about UnrealIRCd 6.
### Enhancements:
* Show security groups in `WHOIS`
* The [security-group block](https://www.unrealircd.org/docs/Security-group_block)
has been expanded and the same functionality is now available in
[mask items](https://www.unrealircd.org/docs/Mask_item) too:
* This means the existing options like `identified`, `webirc`, `tls` and
`reputation-score` can be used in `allow::mask` etc.
* New options (in both security-group and mask) are:
* `connect-time`: time a user is connected to IRC
* `security-group`: to check another security group
* `account`: services account name
* `country`: country code, as found by GeoIP
* `realname`: realname (gecos) of the user
* `certfp`: certificate fingerprint
* Every option also has an exclude- variant, eg. `exclude-country`.
If a user matches any `exclude-` option then it is considered not a match.
* The modules [connthrottle](https://www.unrealircd.org/docs/Connthrottle),
[restrict-commands](https://www.unrealircd.org/docs/Set_block#set::restrict-commands)
and [antirandom](https://www.unrealircd.org/docs/Set_block#set::antirandom)
now use the new `except` sub-block which is a mask item. The old syntax
(eg <code>set::antirandom::except-webirc</code>) is still accepted by UnrealIRCd
and converted to the appropriate new setting behind the scenes
(<code>set::antirandom::except::webirc</code>).
* The modules [blacklist](https://www.unrealircd.org/docs/Blacklist_block)
and [antimixedutf8](https://www.unrealircd.org/docs/Set_block#set::antimixedutf8)
now also support the `except` block (a mask item).
* Other than that the extended functionality is available in these blocks:
`allow`, `oper`, `tld`, `vhost`, `deny channel`, `allow channel`.
* Example of direct use in a ::mask item:
```
/* Spanish MOTD for Spanish speaking countries */
tld {
mask { country { ES; AR; BO; CL; CO; CR; DO; EC; SV; GT; HN; MX; NI; PA; PY; PE; PR; UY; VE; } }
motd "motd.es.txt";
rules "rules.es.txt";
}
```
* Example of defining a security group and using it in a mask item later:
```
security-group irccloud {
mask { ip1; ip2; ip3; ip4; }
}
allow {
mask { security-group irccloud; }
class clients;
maxperip 128;
}
except ban {
mask { security-group irccloud; }
type { blacklist; connect-flood; handshake-data-flood; }
}
```
* Because the mask item is so powerful now, the `password` in the
[oper block](https://www.unrealircd.org/docs/Oper_block) is optional now.
* We now support oper::auto-login, which means the user will become IRCOp
automatically if they match the conditions on-connect. This can be used
in combination with
[certificate fingerprint](https://www.unrealircd.org/docs/Certificate_fingerprint)
authentication for example:
```
security-group Syzop { certfp "1234etc."; }
oper Syzop {
auto-login yes;
mask { security-group Syzop; }
operclass netadmin-with-override;
class opers;
}
except ban {
mask { security-group Syzop; }
type all;
}
```
* For [JSON logging](https://www.unrealircd.org/docs/JSON_logging) a number
of fields were added when a client is expanded:
* `geoip`: with subitem `country_code` (eg. `NL`)
* `tls`: with subitems `cipher` and `certfp`
* Under subitem `users`:
* `vhost`: if the visible host differs from the realhost then this is
set (thus for both vhost and cloaked host)
* `cloakedhost`: this is always set (except for eg. services users), even
if the user is not cloaked so you can easily search on a cloaked host.
* `idle_since`: last time the user has spoken (local clients only)
* `channels`: list of channels (array), with a maximum of 384 chars.
* The JSON logging now also strips ASCII below 32, so color- and
control codes.
* Support IRCv3 `+draft/channel-context`
* Add `example.es.conf` (Spanish example configuration file)
* The country of users is now communicated in the
[message-tag](https://www.unrealircd.org/docs/Message_tags)
`unrealircd.org/geoip` (only to IRCOps).
* Add support for linking servers via UNIX domain sockets
(`link::outgoing::file`).
### Fixes:
* Crash in `except ban` with `~security-group:xyz`
* Crash if hideserver module was loaded but `LINKS` was not blocked.
* Crash on Windows when using the "Rehash" GUI option.
* Infinite loop if one security-group referred to another.
* Duplicate entries in the `+beI` lists of `+P` channels.
* Regular users were able to -o a service bot (that has umode +S)
* Module manager did not stop on compile error
* [`set::modes-on-join`](https://www.unrealircd.org/docs/Set_block#set::modes-on-join)
did not work with `+f` + timed bans properly, eg `[3t#b1]:10`
* Several log messages were missing some information.
* Reputation syncing across servers had a small glitch. Fix is mostly
useful for servers that were not linked to the network for days or weeks.
### Changes:
* Clarified that UnrealIRCd is licensed as "GPLv2 or later"
* Fix use of variables in
[`set::reject-message](https://www.unrealircd.org/docs/Set_block#set::reject-message)
and in [`blacklist::reason](https://www.unrealircd.org/docs/Blacklist_block):
previously short forms of variables were (unintentionally) expanded
as well, such as `$serv` for `$server`. This is no longer supported, you need
to use the correct full variable names.
### Developers and protocol:
* The `creationtime` is now communicated of users. Until now this
information was only known locally (the thing that was communicated
that came close was "last nick change" but that is not the same).
This is synced via (early) moddata across servers.
Module coders can use `get_connected_time()`.
* The `RPL_HOSTHIDDEN` is now sent from `userhost_changed()` so you
don't explicitly send it yourself anymore.
* The `SVSO` command is back, so services can make people IRCOp again.
See `HELPOP SVSO` or [the commit](https://github.com/unrealircd/unrealircd/commit/50e5d91c798e7d07ca0c68d9fca302a6b6610786)
for more information.
* Due to last change the `HOOKTYPE_LOCAL_OPER` parameters were changed.
* Module coders can enhance the
[JSON logging](https://www.unrealircd.org/docs/JSON_logging)
expansion items for clients and channels via new hooks like
`HOOKTYPE_JSON_EXPAND_CLIENT`. This is used by the geoip and tls modules.
UnrealIRCd 6.0.3
-----------------
A number of serious issues were discovered in UnrealIRCd 6. Among these is
an issue which will likely crash the IRCd sooner or later if you /REHASH
with any active clients connected.
We suggest everyone who is running UnrealIRCd 6 to upgrade to 6.0.3.
Fixes:
* Crash in `WATCH` if the IRCd has been rehashed at least once. After doing
a `REHASH` with active clients it will likely corrupt memory. It may take

View File

@ -74,11 +74,13 @@ loadmodule "setident";
loadmodule "squit";
loadmodule "stats";
loadmodule "tkl";
loadmodule "tline";
loadmodule "trace";
loadmodule "tsctl";
loadmodule "unsqline";
// Server-2-Server Commands
loadmodule "creationtime";
loadmodule "eos";
loadmodule "md";
loadmodule "netinfo";
@ -97,6 +99,7 @@ loadmodule "sendsno";
loadmodule "sendumode";
loadmodule "svsjoin";
loadmodule "svskill";
loadmodule "svslogin";
loadmodule "svslusers";
loadmodule "svsmode";
loadmodule "svsmotd";
@ -172,12 +175,13 @@ loadmodule "extbans/quiet"; /* +b ~quiet */
loadmodule "extbans/textban"; /* +b ~text */
loadmodule "extbans/timedban"; /* +b ~time */
loadmodule "extbans/securitygroup"; /* +b ~security-group */
e
// IRCv3 Extensions
loadmodule "account-notify";
loadmodule "account-tag";
loadmodule "batch";
loadmodule "bot-tag";
loadmodule "channel-context";
loadmodule "chathistory";
loadmodule "clienttagdeny";
loadmodule "echo-message";
@ -200,6 +204,7 @@ loadmodule "blacklist";
loadmodule "certfp";
loadmodule "channeldb";
loadmodule "charsys";
loadmodule "connect-flood";
loadmodule "connthrottle";
#loadmodule "geoip_base";
#loadmodule "geoip_classic";
@ -209,6 +214,7 @@ loadmodule "history_backend_mem";
loadmodule "ident_lookup";
loadmodule "jointhrottle";
loadmodule "json-log-tag";
loadmodule "max-unknown-connections-per-ip";
loadmodule "targetfloodprot";
loadmodule "tkldb";
loadmodule "tls_antidos";
@ -220,4 +226,5 @@ loadmodule "restrict-commands";
loadmodule "rmtkl";
loadmodule "watch-backend";
#loadmodule "webirc";
#loadmodule "websocket";
#loadmodule "webserver";
#loadmodule "websocket";

View File

@ -1,4 +1,5 @@
oper acidvegas {
auto-login yes;
mask localhost;
password "REDACTED" { sslclientcertfp; }
class clients;

View File

@ -1,4 +1,12 @@
admin { ""; }
@define $VOID "8,4 E N T E R T H E V O I D ";
admin {
"4Administrator: Brandon Brown 14(aka MRCHATS) 6branbran89@supernets.org";
" 4Moderator: Bristopher Manning 14(aka delorean) 6simpsonsfan95@supernets.org";
" 4Sales: Branthony Bronson 14(aka pyrex) 6showercaphandgun@supernets.org";
"";
"Feel free to chat with us in #5000 for network help & support!";
}
alias botserv { type services; }
alias bs { target botserv; type services; }
@ -60,7 +68,7 @@ blacklist dronebl {
}
action gzline;
ban-time 30d;
reason "8,4 E N T E R T H E V O I D ";
reason "$VOID";
}
blacklist efnetrbl {
@ -71,7 +79,7 @@ blacklist efnetrbl {
}
action gzline;
ban-time 30d;
reason "8,4 E N T E R T H E V O I D ";
reason "$VOID";
}
blacklist torbl {
@ -82,7 +90,7 @@ blacklist torbl {
}
action gzline;
ban-time 30d;
reason "8,4 E N T E R T H E V O I D ";
reason "$VOID";
}
set {
@ -96,13 +104,13 @@ set {
restrict-usermodes "ips";
restrict-channelmodes "nLpPs";
restrict-commands {
channel-message { connect-delay 60; exempt-identified yes; exempt-reputation-score 100; }
channel-notice { connect-delay 60; exempt-identified yes; exempt-reputation-score 100; }
invite { connect-delay 300; exempt-identified yes; exempt-reputation-score 100; }
join { connect-delay 15; exempt-identified yes; exempt-reputation-score 100; }
list { connect-delay 30; exempt-identified yes; exempt-reputation-score 100; }
private-message { connect-delay 300; exempt-identified yes; exempt-reputation-score 100; }
private-notice { connect-delay 300; exempt-identified yes; exempt-reputation-score 100; }
channel-message { connect-time 60; identified yes; reputation-score 100; }
channel-notice { connect-time 60; identified yes; reputation-score 100; }
invite { connect-time 300; identified yes; reputation-score 100; }
join { connect-time 15; identified yes; reputation-score 100; }
list { connect-time 30; identified yes; reputation-score 100; }
private-message { connect-time 300; identified yes; reputation-score 100; }
private-notice { connect-time 300; identified yes; reputation-score 100; }
}
auto-join "#superbowl";
static-quit "EMO-QUIT";
@ -145,14 +153,14 @@ set {
ban-action gzline;
ban-time 1h;
}
#target-flood {
# channel-notice 15:5;
# channel-privmsg 45:5;
# channel-tagmsg 15:5;
# private-notice 10:5;
# private-privmsg 30:5;
# private-tagmsg 10:5;
#}
target-flood {
channel-notice 15:5;
channel-privmsg 45:5;
channel-tagmsg 15:5;
private-notice 10:5;
private-privmsg 30:5;
private-tagmsg 10:5;
}
}
known-users {
away-flood 3:300;
@ -185,29 +193,29 @@ set {
modef-default-unsettime 5;
spamfilter {
ban-time 1d;
ban-reason "8,4 E N T E R T H E V O I D ";
ban-reason "$VOID";
except "#anythinggoes";
}
max-targets-per-command { kick 1; part 1; privmsg 1; }
hide-ban-reason yes;
reject-message {
gline "8,4 E N T E R T H E V O I D ";
kline "8,4 E N T E R T H E V O I D ";
password-mismatch "8,4 E N T E R T H E V O I D ";
server-full "8,4 E N T E R T H E V O I D ";
too-many-connections "8,4 E N T E R T H E V O I D ";
unauthorized "8,4 E N T E R T H E V O I D ";
gline "$VOID";
kline "$VOID";
password-mismatch "$VOID";
server-full "$VOID";
too-many-connections "$VOID";
unauthorized "$VOID";
}
antimixedutf8 {
score 8;
ban-action block;
ban-reason "8,4 E N T E R T H E V O I D ";
ban-reason "$VOID";
}
connthrottle {
known-users { minimum-reputation-score 100; sasl-bypass yes; }
new-users { local-throttle 20:60; global-throttle 30:60; }
disabled-when { reputation-gathering 1w; start-delay 3m; }
reason "8,4 E N T E R T H E V O I D ";
except { reputation-score 100; identified yes; webirc yes; }
new-users { local-throttle 20:60; global-throttle 30:60; }
disabled-when { reputation-gathering 1w; start-delay 3m; }
reason "$VOID";
}
history {
channel {
@ -220,32 +228,32 @@ set {
}
hide-idle-time { policy always; }
whois-details {
account { everyone full; }
away { everyone full; }
basic { everyone full; }
bot { everyone full; }
certfp { everyone full; }
channels { everyone none; self full; oper full; }
geo { everyone none; }
idle { everyone none; }
modes { everyone none; self full; oper full; }
account { everyone full; }
away { everyone full; }
basic { everyone full; }
bot { everyone full; }
certfp { everyone full; }
channels { everyone none; self full; oper full; }
geo { everyone none; }
idle { everyone none; }
modes { everyone none; self full; oper full; }
oper { everyone limited; self full; oper full; }
realhost { everyone none; self full; oper full; }
registered-nick { everyone full; }
reputation { everyone full; }
realhost { everyone none; self full; oper full; }
registered-nick { everyone full; }
reputation { everyone full; }
secure { everyone limited; self full; oper full; }
server { everyone full; }
services { everyone full; }
shunned { everyone none; self none; oper full; }
swhois { everyone full; }
server { everyone full; }
services { everyone full; }
shunned { everyone none; self none; oper full; }
swhois { everyone full; }
}
}
hideserver {
disable-map yes;
disable-links yes;
map-deny-message "8,4 E N T E R T H E V O I D ";
links-deny-message "8,4 E N T E R T H E V O I D ";
map-deny-message "$VOID";
links-deny-message "$VOID";
}
security-group known-users {

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "UnrealIRCd"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 6.0.3
PROJECT_NUMBER = 6.0.4.2
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@ -119,5 +119,5 @@ fi
chmod +x unrealircd-upgrade-script.stage2
./unrealircd-upgrade-script.stage2 $*
SAVERET="$?"
rm -f unrealircd-upgrade-script.stage2 unrealircd-upgrade-script.stage2
rm -f unrealircd-upgrade-script.stage2 unrealircd-upgrade-script.stage2.asc
exit $SAVERET

View File

@ -123,7 +123,7 @@
* Common usage for this are: a trusted bot ran by an IRCOp, that you only
* want to give "flood access" and nothing else, and other such things.
*/
#define FAKELAG_CONFIGURABLE
//#undef FAKELAG_CONFIGURABLE
/* The default value for class::sendq */
#define DEFAULT_SENDQ 3000000

View File

@ -111,7 +111,6 @@ extern MODVAR ConfigItem_deny_version *conf_deny_version;
extern MODVAR ConfigItem_alias *conf_alias;
extern MODVAR ConfigItem_help *conf_help;
extern MODVAR ConfigItem_offchans *conf_offchans;
extern MODVAR SecurityGroup *securitygroups;
extern void completed_connection(int, int, void *);
extern void clear_unknown();
extern EVENT(e_unload_module_delayed);
@ -677,6 +676,7 @@ extern void unreal_setfilemodtime(const char *filename, time_t mtime);
extern void DeleteTempModules(void);
extern MODVAR Extban *extbaninfo;
extern Extban *findmod_by_bantype(const char *str, const char **remainder);
extern Extban *findmod_by_bantype_raw(const char *str, int ban_name_length);
extern Extban *ExtbanAdd(Module *reserved, ExtbanInfo req);
extern void ExtbanDel(Extban *);
extern void extban_init(void);
@ -749,8 +749,9 @@ extern MODVAR const char *(*tkl_type_string)(TKL *tk);
extern MODVAR const char *(*tkl_type_config_string)(TKL *tk);
extern MODVAR TKL *(*tkl_add_serverban)(int type, const char *usermask, const char *hostmask, const char *reason, const char *setby,
time_t expire_at, time_t set_at, int soft, int flags);
extern MODVAR TKL *(*tkl_add_banexception)(int type, const char *usermask, const char *hostmask, const char *reason, const char *set_by,
time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags);
extern MODVAR TKL *(*tkl_add_banexception)(int type, const char *usermask, const char *hostmask, SecurityGroup *match,
const char *reason, const char *set_by,
time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags);
extern MODVAR TKL *(*tkl_add_nameban)(int type, const char *name, int hold, const char *reason, const char *setby,
time_t expire_at, time_t set_at, int flags);
extern MODVAR TKL *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, Match *match, const char *setby,
@ -779,10 +780,9 @@ extern MODVAR int (*match_spamfilter)(Client *client, const char *str_in, int ty
extern MODVAR int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, const char *cmd);
extern MODVAR int (*join_viruschan)(Client *client, TKL *tk, int type);
extern MODVAR const char *(*StripColors)(const char *text);
extern MODVAR const char *(*StripControlCodes)(const char *text);
extern MODVAR void (*spamfilter_build_user_string)(char *buf, const char *nick, Client *acptr);
extern MODVAR void (*send_protoctl_servers)(Client *client, int response);
extern MODVAR int (*verify_link)(Client *client, ConfigItem_link **link_out);
extern MODVAR ConfigItem_link *(*verify_link)(Client *client);
extern MODVAR void (*send_server_message)(Client *client);
extern MODVAR void (*broadcast_md_client)(ModDataInfo *mdi, Client *acptr, ModData *md);
extern MODVAR void (*broadcast_md_channel)(ModDataInfo *mdi, Channel *channel, ModData *md);
@ -834,6 +834,8 @@ extern MODVAR char *(*tkl_uhost)(TKL *tkl, char *buf, size_t buflen, int options
extern MODVAR void (*do_unreal_log_remote_deliver)(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized);
extern MODVAR char *(*get_chmodes_for_user)(Client *client, const char *flags);
extern MODVAR WhoisConfigDetails (*whois_get_policy)(Client *client, Client *target, const char *name);
extern MODVAR int (*make_oper)(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost);
extern MODVAR int (*unreal_match_iplist)(Client *client, NameList *l);
/* /Efuncs */
/* TLS functions */
@ -859,6 +861,7 @@ extern MODVAR EVP_MD *sha1_function;
extern MODVAR EVP_MD *md5_function;
/* End of TLS functions */
/* Default handlers for efunctions */
extern void parse_message_tags_default_handler(Client *client, char **str, MessageTag **mtag_list);
extern const char *mtags_to_string_default_handler(MessageTag *m, Client *client);
extern void *labeled_response_save_context_default_handler(void);
@ -868,6 +871,8 @@ extern int add_silence_default_handler(Client *client, const char *mask, int sen
extern int del_silence_default_handler(Client *client, const char *mask);
extern int is_silenced_default_handler(Client *client, Client *acptr);
extern void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized);
extern int make_oper_default_handler(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost);
/* End of default handlers for efunctions */
extern MODVAR MOTDFile opermotd, svsmotd, motd, botmotd, smotd, rules;
extern MODVAR int max_connection_count;
@ -925,10 +930,6 @@ extern void unreal_delete_match(Match *m);
extern int unreal_match(Match *m, const char *str);
extern int unreal_match_method_strtoval(const char *str);
extern char *unreal_match_method_valtostr(int val);
extern void unreal_delete_masks(ConfigItem_mask *m);
extern void unreal_add_masks(ConfigItem_mask **head, ConfigEntry *ce);
extern int unreal_mask_match(Client *acptr, ConfigItem_mask *m);
extern int unreal_mask_match_string(const char *name, ConfigItem_mask *m);
#ifdef _WIN32
extern MODVAR BOOL IsService;
#endif
@ -946,7 +947,7 @@ extern void modulemanager(int argc, char *argv[]);
extern int inet_pton4(const char *src, unsigned char *dst);
extern int inet_pton6(const char *src, unsigned char *dst);
extern int unreal_bind(int fd, const char *ip, int port, SocketType socket_type);
extern int unreal_connect(int fd, const char *ip, int port, int ipv6);
extern int unreal_connect(int fd, const char *ip, int port, SocketType socket_type);
extern int is_valid_ip(const char *str);
extern int ipv6_capable(void);
extern int unix_sockets_capable(void);
@ -1092,14 +1093,6 @@ extern int hide_idle_time(Client *client, Client *target);
extern void lost_server_link(Client *serv, const char *tls_error_string);
extern const char *sendtype_to_cmd(SendType sendtype);
extern MODVAR MessageTagHandler *mtaghandlers;
extern int security_group_valid_name(const char *name);
extern int security_group_exists(const char *name);
extern SecurityGroup *add_security_group(const char *name, int order);
extern SecurityGroup *find_security_group(const char *name);
extern void free_security_group(SecurityGroup *s);
extern void set_security_group_defaults(void);
extern int user_allowed_by_security_group(Client *client, SecurityGroup *s);
extern int user_allowed_by_security_group_name(Client *client, const char *secgroupname);
#define nv_find_by_name(stru, name) do_nv_find_by_name(stru, name, ARRAY_SIZEOF((stru)))
extern long do_nv_find_by_name(NameValue *table, const char *cmd, int numelements);
#define nv_find_by_value(stru, value) do_nv_find_by_value(stru, value, ARRAY_SIZEOF((stru)))
@ -1119,6 +1112,9 @@ extern void add_fmt_nvplist(NameValuePrioList **lst, int priority, const char *n
extern void add_nvplist_numeric_fmt(NameValuePrioList **lst, int priority, const char *name, Client *to, int numeric, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,6,7)));
extern NameValuePrioList *find_nvplist(NameValuePrioList *list, const char *name);
extern void free_nvplist(NameValuePrioList *lst);
extern void unreal_add_name_values(NameValuePrioList **n, const char *name, ConfigEntry *ce);
extern const char *namevalue(NameValuePrioList *n);
extern const char *namevalue_nospaces(NameValuePrioList *n);
extern const char *get_connect_extinfo(Client *client);
extern char *unreal_strftime(const char *str);
extern void strtolower(char *str);
@ -1132,6 +1128,30 @@ extern void read_until(char **p, char *stopchars);
extern int is_ip_valid(const char *ip);
extern int is_file_readable(const char *file, const char *dir);
json_t *json_string_unreal(const char *s);
/* securitygroup.c start */
extern MODVAR SecurityGroup *securitygroups;
extern void unreal_delete_masks(ConfigItem_mask *m);
extern void unreal_add_masks(ConfigItem_mask **head, ConfigEntry *ce);
extern int unreal_mask_match(Client *acptr, ConfigItem_mask *m);
extern int unreal_mask_match_string(const char *name, ConfigItem_mask *m);
extern int test_match_item(ConfigFile *conf, ConfigEntry *cep, int *errors);
extern int test_match_block(ConfigFile *conf, ConfigEntry *ce, int *errors_out);
extern int test_match_block_too_broad(ConfigFile *conf, ConfigEntry *ce);
extern int security_group_valid_name(const char *name);
extern int security_group_exists(const char *name);
extern SecurityGroup *add_security_group(const char *name, int order);
extern SecurityGroup *find_security_group(const char *name);
extern void free_security_group(SecurityGroup *s);
extern void set_security_group_defaults(void);
extern int user_allowed_by_security_group(Client *client, SecurityGroup *s);
extern int user_allowed_by_security_group_name(Client *client, const char *secgroupname);
extern const char *get_security_groups(Client *client);
extern int test_match_item(ConfigFile *conf, ConfigEntry *cep, int *errors);
extern int conf_match_item(ConfigFile *conf, ConfigEntry *cep, SecurityGroup **block);
extern int test_match_block(ConfigFile *conf, ConfigEntry *ce, int *errors_out);
extern int conf_match_block(ConfigFile *conf, ConfigEntry *ce, SecurityGroup **block);
extern int test_extended_list(Extban *extban, ConfigEntry *cep, int *errors);
/* securitygroup.c end */
/* src/unrealdb.c start */
extern UnrealDB *unrealdb_open(const char *filename, UnrealDBMode mode, char *secret_block);
extern int unrealdb_close(UnrealDB *c);
@ -1249,3 +1269,6 @@ extern void add_proc_io_server(void);
extern void procio_post_rehash(int failure);
/* end of proc i/o */
extern int minimum_msec_since_last_run(struct timeval *tv_old, long minimum);
extern long get_connected_time(Client *client);
extern const char *StripControlCodes(const char *text);
extern const char *StripControlCodesEx(const char *text, char *output, size_t outputlen, int strip_flags);

View File

@ -27,10 +27,10 @@
char *gnulicense[] = {
" \2UnrealIRCd License\2",
"This program is free software; you can redistribute it and/or modify",
"it under the terms of the GNU General Public License as published by",
"the Free Software Foundation; either version 1, or (at your option)",
"any later version.",
"This program is free software; you can redistribute it and/or",
"modify it under the terms of the GNU General Public License",
"as published by the Free Software Foundation; either version 2",
"of the License, or (at your option) any later version.",
"",
"This program is distributed in the hope that it will be useful,",
"but WITHOUT ANY WARRANTY; without even the implied warranty of",
@ -39,10 +39,11 @@ char *gnulicense[] = {
"",
"You should have received a copy of the GNU General Public License",
"along with this program; if not, write to the Free Software",
"Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.",
"Foundation, Inc., 51 Franklin Street, Fifth Floor,",
"Boston, MA 02110-1301, USA.",
"",
"To see the UnrealIRCd License, please point your browser",
"at http://www.gnu.org/copyleft/gpl.html or look in the",
"file LICENSE in the UnrealIRCd dist",
0
"to https://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
"or look at the LICENSE file in the UnrealIRCd distribution.",
NULL
};

View File

@ -450,8 +450,11 @@ struct Extban {
/** extbans module */
Module *owner;
/* Set to 1 during rehash when module is unloading (which may be re-used, and then set to 0) */
/** Set to 1 during rehash when module is unloading (which may be re-used, and then set to 0) */
char unloaded;
/** Set to 1 when it is preregistered in MOD_TEST already */
char preregistered;
};
typedef struct {
@ -1097,8 +1100,8 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, const char *varshortname, l
#define HOOKTYPE_SEE_CHANNEL_IN_WHOIS 77
/** See hooktype_join_data() */
#define HOOKTYPE_JOIN_DATA 78
/** See hooktype_oper_invite_ban() */
#define HOOKTYPE_OPER_INVITE_BAN 79
/** See hooktype_invite_bypass() */
#define HOOKTYPE_INVITE_BYPASS 79
/** See hooktype_view_topic_outside_channel() */
#define HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL 80
/** See hooktype_chan_permit_nick_change() */
@ -1159,6 +1162,15 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, const char *varshortname, l
#define HOOKTYPE_CAN_SET_TOPIC 110
/** See hooktype_ip_change() */
#define HOOKTYPE_IP_CHANGE 111
/** See hooktype_json_expand_client() */
#define HOOKTYPE_JSON_EXPAND_CLIENT 112
/** See hooktype_json_expand_client() */
#define HOOKTYPE_JSON_EXPAND_CLIENT_USER 113
/** See hooktype_json_expand_client() */
#define HOOKTYPE_JSON_EXPAND_CLIENT_SERVER 114
/** See hooktype_json_expand_channel() */
#define HOOKTYPE_JSON_EXPAND_CHANNEL 115
/* Adding a new hook here?
* 1) Add the #define HOOKTYPE_.... with a new number
* 2) Add a hook prototype (see below)
@ -1683,9 +1695,10 @@ int hooktype_stats(Client *client, const char *str);
* @param client The client
* @param add 1 if the user becomes IRCOp, 0 if the user is no longer IRCOp
* @param oper_block The name of the oper block used to oper up
* @param operclass The name of the operclass
* @return The return value is ignored (use return 0)
*/
int hooktype_local_oper(Client *client, int add, ConfigItem_oper *oper_block);
int hooktype_local_oper(Client *client, int add, const char *oper_block, const char *operclass);
/** Called when a client sends a PASS command (function prototype for HOOKTYPE_LOCAL_PASS).
* @param client The client
@ -1858,14 +1871,16 @@ int hooktype_see_channel_in_whois(Client *client, Client *target, Channel *chann
*/
int hooktype_join_data(Client *who, Channel *channel);
/** Should the user be able to bypass bans? (function prototype for HOOKTYPE_OPER_INVITE_BAN).
/** Should the user be able to bypass channel restrictions because they are invited? (function prototype for HOOKTYPE_INVITE_BYPASS).
* @param client The client
* @param channel The channel
* @note The actual meaning of this hook is more complex, you are unlikely to use it, anyway.
* @retval HOOK_DENY Deny the join if the user is also banned
* @retval HOOK_DENY Don't allow the user to bypass channel restrictions when they are invited
* @retval HOOK_CONTINUE Obey the normal rules
* @note Usually you want a user to be able to bypass channel restrictions such as +l or +b when they are /INVITEd by another user
* or have invited themselves (OperOverride). But, there may be special cases where you don't want this.
* For example, this hook is used by +O to still not allow ircops to join +O channels even if they have OperOverride capability.
*/
int hooktype_oper_invite_ban(Client *client, Channel *channel);
int hooktype_invite_bypass(Client *client, Channel *channel);
/** Should a user be able to view the topic when not in the channel? (function prototype for HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL).
* @param client The client requesting the topic
@ -2145,6 +2160,46 @@ int hooktype_realname_change(Client *client, const char *oldinfo);
*/
int hooktype_ip_change(Client *client, const char *oldip);
/** Called when json_expand_client() is called.
* Used for expanding information about 'client' in logging routines.
* @param client The client that should be expanded
* @param detail The amount of detail to provide (always 0 at the moment)
* @param j The JSON object
* @return The return value is ignored (use return 0)
*/
int hooktype_json_expand_client(Client *client, int detail, json_t *j);
/** Called when json_expand_client_user() is called.
* Used for expanding information about 'client' in logging routines
* when the client is a USER.
* @param client The client that should be expanded
* @param detail The amount of detail to provide (always 0 at the moment)
* @param j The JSON object - root
* @param child The JSON object - "user" child item
* @return The return value is ignored (use return 0)
*/
int hooktype_json_expand_client_user(Client *client, int detail, json_t *j, json_t *child);
/** Called when json_expand_client_server() is called.
* Used for expanding information about 'client' in logging routines
* when the client is a SERVER.
* @param client The client that should be expanded
* @param detail The amount of detail to provide (always 0 at the moment)
* @param j The JSON object - root
* @param child The JSON object - "server" child item
* @return The return value is ignored (use return 0)
*/
int hooktype_json_expand_client_server(Client *client, int detail, json_t *j, json_t *child);
/** Called when json_expand_channel() is called.
* Used for expanding information about 'channel' in logging routines.
* @param channel The channel that should be expanded
* @param detail The amount of detail to provide (always 0 at the moment)
* @param j The JSON object
* @return The return value is ignored (use return 0)
*/
int hooktype_json_expand_channel(Channel *channel, int detail, json_t *j);
/** @} */
#ifdef GCC_TYPECHECKING
@ -2223,7 +2278,7 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
((hooktype == HOOKTYPE_JOIN_DATA) && !ValidateHook(hooktype_join_data, func)) || \
((hooktype == HOOKTYPE_PRE_KNOCK) && !ValidateHook(hooktype_pre_knock, func)) || \
((hooktype == HOOKTYPE_PRE_INVITE) && !ValidateHook(hooktype_pre_invite, func)) || \
((hooktype == HOOKTYPE_OPER_INVITE_BAN) && !ValidateHook(hooktype_oper_invite_ban, func)) || \
((hooktype == HOOKTYPE_INVITE_BYPASS) && !ValidateHook(hooktype_invite_bypass, func)) || \
((hooktype == HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL) && !ValidateHook(hooktype_view_topic_outside_channel, func)) || \
((hooktype == HOOKTYPE_CHAN_PERMIT_NICK_CHANGE) && !ValidateHook(hooktype_chan_permit_nick_change, func)) || \
((hooktype == HOOKTYPE_IS_CHANNEL_SECURE) && !ValidateHook(hooktype_is_channel_secure, func)) || \
@ -2259,7 +2314,11 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
((hooktype == HOOKTYPE_POST_REMOTE_NICKCHANGE) && !ValidateHook(hooktype_post_remote_nickchange, func)) || \
((hooktype == HOOKTYPE_USERHOST_CHANGE) && !ValidateHook(hooktype_userhost_change, func)) || \
((hooktype == HOOKTYPE_REALNAME_CHANGE) && !ValidateHook(hooktype_realname_change, func)) || \
((hooktype == HOOKTYPE_IP_CHANGE) && !ValidateHook(hooktype_ip_change, func)) ) \
((hooktype == HOOKTYPE_IP_CHANGE) && !ValidateHook(hooktype_ip_change, func)) || \
((hooktype == HOOKTYPE_JSON_EXPAND_CLIENT) && !ValidateHook(hooktype_json_expand_client, func)) || \
((hooktype == HOOKTYPE_JSON_EXPAND_CLIENT_USER) && !ValidateHook(hooktype_json_expand_client_user, func)) || \
((hooktype == HOOKTYPE_JSON_EXPAND_CLIENT_SERVER) && !ValidateHook(hooktype_json_expand_client_server, func)) || \
((hooktype == HOOKTYPE_JSON_EXPAND_CHANNEL) && !ValidateHook(hooktype_json_expand_channel, func)) ) \
_hook_error_incompatible();
#endif /* GCC_TYPECHECKING */
@ -2315,7 +2374,6 @@ enum EfunctionType {
EFUNC_FIND_TKLINE_MATCH_ZAP_EX,
EFUNC_SEND_LIST,
EFUNC_STRIPCOLORS,
EFUNC_STRIPCONTROLCODES,
EFUNC_SPAMFILTER_BUILD_USER_STRING,
EFUNC_SEND_PROTOCTL_SERVERS,
EFUNC_VERIFY_LINK,
@ -2384,6 +2442,8 @@ enum EfunctionType {
EFUNC_DO_UNREAL_LOG_REMOTE_DELIVER,
EFUNC_GET_CHMODES_FOR_USER,
EFUNC_WHOIS_GET_POLICY,
EFUNC_MAKE_OPER,
EFUNC_UNREAL_MATCH_IPLIST,
};
/* Module flags */

View File

@ -559,6 +559,7 @@ typedef enum ClientStatus {
#define IsIPV6(x) ((x)->local->socket_type == SOCKET_TYPE_IPV6)
#define IsUnixSocket(x) ((x)->local->socket_type == SOCKET_TYPE_UNIX)
#define SetIPV6(x) do { (x)->local->socket_type = SOCKET_TYPE_IPV6; } while(0)
#define SetUnixSocket(x) do { (x)->local->socket_type = SOCKET_TYPE_UNIX; } while(0)
/** @} */
@ -608,6 +609,7 @@ union ModData
{
int i;
long l;
long long ll;
char *str;
void *ptr;
};
@ -762,6 +764,8 @@ struct NameList {
/** Delete an entry from a NameList - AND free it */
#define del_name_list(list, str) _del_name_list(&list, str)
extern void unreal_add_names(NameList **n, ConfigEntry *ce);
/** @} */
typedef struct MultiLine MultiLine;
@ -1102,6 +1106,7 @@ struct Spamfilter {
struct BanException {
char *usermask; /**< User mask */
char *hostmask; /**< Host mask */
SecurityGroup *match; /**< Security group (for config file items only) */
unsigned short subtype; /**< See TKL_SUBTYPE_* */
char *bantypes; /**< Exception types */
char *reason; /**< Reason */
@ -1557,7 +1562,7 @@ struct ConfigFlag_allow {
struct ConfigItem_allow {
ConfigItem_allow *prev, *next;
ConfigFlag flag;
ConfigItem_mask *mask;
SecurityGroup *match;
char *server;
AuthConfig *auth;
int maxperip; /**< Maximum connections permitted per IP address (locally) */
@ -1624,12 +1629,13 @@ struct ConfigItem_oper {
AuthConfig *auth;
char *operclass;
ConfigItem_class *class;
ConfigItem_mask *mask;
SecurityGroup *match;
unsigned long modes, require_modes;
char *vhost;
int maxlogins;
int server_notice_colors;
int server_notice_show_event;
int auto_login;
};
/** The TLS options that are used in set::tls and otherblocks::tls-options.
@ -1679,7 +1685,7 @@ struct ConfigItem_ulines {
struct ConfigItem_tld {
ConfigItem_tld *prev, *next;
ConfigFlag_tld flag;
ConfigItem_mask *mask;
SecurityGroup *match;
char *channel;
char *motd_file, *rules_file, *smotd_file;
char *botmotd_file, *opermotd_file;
@ -1713,7 +1719,7 @@ struct ConfigItem_sni {
struct ConfigItem_vhost {
ConfigItem_vhost *prev, *next;
ConfigFlag flag;
ConfigItem_mask *mask;
SecurityGroup *match;
char *login, *virthost, *virtuser;
SWhois *swhois;
AuthConfig *auth;
@ -1725,9 +1731,10 @@ struct ConfigItem_link {
/* config options: */
char *servername; /**< Name of the server ('link <servername> { }') */
struct {
ConfigItem_mask *mask; /**< incoming mask(s) to accept */
SecurityGroup *match; /**< incoming mask(s) to accept */
} incoming;
struct {
char *file; /**< UNIX domain socket to connect to */
char *bind_ip; /**< Our IP to bind to when doing the connect */
char *hostname; /**< Hostname or IP to connect to */
int port; /**< Port to connect to */
@ -1779,14 +1786,14 @@ struct ConfigItem_deny_channel {
ConfigFlag flag;
char *channel, *reason, *redirect, *class;
unsigned char warn;
ConfigItem_mask *mask;
SecurityGroup *match;
};
struct ConfigItem_allow_channel {
ConfigItem_allow_channel *prev, *next;
ConfigFlag flag;
char *channel, *class;
ConfigItem_mask *mask;
SecurityGroup *match;
};
struct ConfigItem_allow_dcc {
@ -1859,11 +1866,28 @@ struct SecurityGroup {
SecurityGroup *prev, *next;
int priority;
char name[SECURITYGROUPLEN+1];
NameValuePrioList *printable_list;
int printable_list_counter;
/* Include */
int identified;
int reputation_score;
long connect_time;
int webirc;
int tls;
ConfigItem_mask *include_mask;
NameList *ip;
ConfigItem_mask *mask;
NameList *security_group;
NameValuePrioList *extended;
/* Exclude */
int exclude_identified;
int exclude_reputation_score;
long exclude_connect_time;
int exclude_webirc;
int exclude_tls;
NameList *exclude_ip;
ConfigItem_mask *exclude_mask;
NameList *exclude_security_group;
NameValuePrioList *exclude_extended;
};
#define HM_HOST 1
@ -2242,6 +2266,10 @@ typedef enum WhoisConfigDetails {
WHOIS_CONFIG_DETAILS_FULL = 3,
} WhoisConfigDetails;
/* Options for StripControlCodesEx() */
#define UNRL_STRIP_LOW_ASCII 0x1 /**< Strip all ASCII < 32 (control codes) */
#define UNRL_STRIP_KEEP_LF 0x2 /**< Do not strip LF (line feed, \n) */
#endif /* __struct_include__ */
#include "dynconf.h"

View File

@ -62,10 +62,10 @@
#define UNREAL_VERSION_MAJOR 0
/* Minor version number (e.g.: 1 for Unreal3.2.1) */
#define UNREAL_VERSION_MINOR 3
#define UNREAL_VERSION_MINOR 4
/* Version suffix such as a beta marker or release candidate marker. (e.g.:
-rcX for unrealircd-3.2.9-rcX) */
#define UNREAL_VERSION_SUFFIX ""
#define UNREAL_VERSION_SUFFIX ".2"
#endif

View File

@ -25,7 +25,7 @@ OBJS=ircd_vars.o dns.o auth.o channel.o crule.o dbuf.o \
fdlist.o hash.o ircsprintf.o list.o \
match.o modules.o parse.o mempool.o operclass.o \
conf_preprocessor.o conf.o proc_io_server.o debug.o dispatch.o \
misc.o serv.o aliases.o socket.o \
securitygroup.o misc.o serv.o aliases.o socket.o \
tls.o user.o scache.o send.o support.o \
version.o whowas.o random.o api-usermode.o api-channelmode.o \
api-moddata.o api-extban.o api-isupport.o api-command.o \

View File

@ -57,8 +57,9 @@ TKL *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short actio
time_t expire_at, time_t set_at,
time_t spamf_tkl_duration, const char *spamf_tkl_reason,
int flags);
TKL *(*tkl_add_banexception)(int type, const char *usermask, const char *hostmask, const char *reason, const char *set_by,
time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags);
TKL *(*tkl_add_banexception)(int type, const char *usermask, const char *hostmask, SecurityGroup *match,
const char *reason, const char *set_by,
time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags);
TKL *(*tkl_del_line)(TKL *tkl);
void (*tkl_check_local_remove_shun)(TKL *tmp);
int (*find_tkline_match)(Client *client, int skip_soft);
@ -74,10 +75,9 @@ int (*match_spamfilter)(Client *client, const char *str_in, int type, const char
int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, const char *cmd);
int (*join_viruschan)(Client *client, TKL *tk, int type);
const char *(*StripColors)(const char *text);
const char *(*StripControlCodes)(const char *text);
void (*spamfilter_build_user_string)(char *buf, const char *nick, Client *client);
void (*send_protoctl_servers)(Client *client, int response);
int (*verify_link)(Client *client, ConfigItem_link **link_out);
ConfigItem_link *(*verify_link)(Client *client);
void (*introduce_user)(Client *to, Client *client);
void (*send_server_message)(Client *client);
void (*broadcast_md_client)(ModDataInfo *mdi, Client *client, ModData *md);
@ -135,6 +135,8 @@ int (*watch_check)(Client *client, int reply, int (*watch_notify)(Client *client
void (*do_unreal_log_remote_deliver)(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized);
char *(*get_chmodes_for_user)(Client *client, const char *flags);
WhoisConfigDetails (*whois_get_policy)(Client *client, Client *target, const char *name);
int (*make_oper)(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost);
int (*unreal_match_iplist)(Client *client, NameList *l);
Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)())
{
@ -339,7 +341,6 @@ void efunctions_init(void)
efunc_init_function(EFUNC_MATCH_SPAMFILTER_MTAGS, match_spamfilter_mtags, NULL);
efunc_init_function(EFUNC_JOIN_VIRUSCHAN, join_viruschan, NULL);
efunc_init_function(EFUNC_STRIPCOLORS, StripColors, NULL);
efunc_init_function(EFUNC_STRIPCONTROLCODES, StripControlCodes, NULL);
efunc_init_function(EFUNC_SPAMFILTER_BUILD_USER_STRING, spamfilter_build_user_string, NULL);
efunc_init_function(EFUNC_SEND_PROTOCTL_SERVERS, send_protoctl_servers, NULL);
efunc_init_function(EFUNC_VERIFY_LINK, verify_link, NULL);
@ -406,4 +407,6 @@ void efunctions_init(void)
efunc_init_function(EFUNC_DO_UNREAL_LOG_REMOTE_DELIVER, do_unreal_log_remote_deliver, do_unreal_log_remote_deliver_default_handler);
efunc_init_function(EFUNC_GET_CHMODES_FOR_USER, get_chmodes_for_user, NULL);
efunc_init_function(EFUNC_WHOIS_GET_POLICY, whois_get_policy, NULL);
efunc_init_function(EFUNC_MAKE_OPER, make_oper, make_oper_default_handler);
efunc_init_function(EFUNC_UNREAL_MATCH_IPLIST, unreal_match_iplist, NULL);
}

View File

@ -38,9 +38,27 @@ void set_isupport_extban(void)
ISupportSetFmt(NULL, "EXTBAN", "~,%s", extbanstr);
}
Extban *findmod_by_bantype(const char *str, const char **remainder)
Extban *findmod_by_bantype_raw(const char *str, int ban_name_length)
{
Extban *e;
for (e=extbans; e; e = e->next)
{
if ((ban_name_length == 1) && (e->letter == str[0]))
return e;
if (e->name)
{
int namelen = strlen(e->name);
if ((namelen == ban_name_length) && !strncmp(e->name, str, namelen))
return e;
}
}
return NULL;
}
Extban *findmod_by_bantype(const char *str, const char **remainder)
{
int ban_name_length;
const char *p = strchr(str, ':');
@ -54,20 +72,7 @@ Extban *findmod_by_bantype(const char *str, const char **remainder)
*remainder = p+1;
ban_name_length = p - str - 1;
for (e=extbans; e; e = e->next)
{
if ((ban_name_length == 1) && (e->letter == str[1]))
return e;
if (e->name)
{
int namelen = strlen(e->name);
if ((namelen == ban_name_length) && !strncmp(e->name, str+1, namelen))
return e;
}
}
return NULL;
return findmod_by_bantype_raw(str+1, ban_name_length);
}
/* Check if this is a valid extended ban name */
@ -121,6 +126,7 @@ static void extban_add_sorted(Extban *n)
Extban *ExtbanAdd(Module *module, ExtbanInfo req)
{
Extban *e;
ModuleObject *banobj;
int existing = 0;
if (!req.name)
@ -167,14 +173,35 @@ Extban *ExtbanAdd(Module *module, ExtbanInfo req)
{
if (e->letter == req.letter)
{
/* Extban already exists in our list, let's see... */
if (e->unloaded)
{
e->unloaded = 0;
existing = 1;
break;
} else {
if (module)
module->errorcode = MODERR_EXISTS;
} else
if ((module->flags == MODFLAG_TESTING) && e->preregistered)
{
/* We are in MOD_INIT (yeah confusing, isn't it?)
* and the extban already exists and it was preregistered.
* Then go ahead with really registering it.
*/
e->preregistered = 0;
existing = 1;
} else
if (module->flags == MODFLAG_NONE)
{
/* Better don't touch it, as we may still fail at this stage
* and if we would set .conv_param etc to this and the new module
* gets unloaded because of a config typo then we would be screwed
* (now we are not).
* NOTE: this does mean that if you hot-load an extban module
* then it may only be available for config stuff the 2nd rehash.
*/
return e;
} else
{
module->errorcode = MODERR_EXISTS;
return NULL;
}
}
@ -195,14 +222,16 @@ Extban *ExtbanAdd(Module *module, ExtbanInfo req)
e->is_banned_events = req.is_banned_events;
e->owner = module;
e->options = req.options;
if (module)
{
ModuleObject *banobj = safe_alloc(sizeof(ModuleObject));
banobj->object.extban = e;
banobj->type = MOBJ_EXTBAN;
AddListItem(banobj, module->objects);
module->errorcode = MODERR_NOERROR;
}
if (module->flags == MODFLAG_NONE)
e->preregistered = 1;
banobj = safe_alloc(sizeof(ModuleObject));
banobj->object.extban = e;
banobj->type = MOBJ_EXTBAN;
AddListItem(banobj, module->objects);
module->errorcode = MODERR_NOERROR;
set_isupport_extban();
return e;
}
@ -217,10 +246,27 @@ static void unload_extban_commit(Extban *e)
/* Then unload the extban */
DelListItem(e, extbans);
safe_free(e->name);
safe_free(e);
set_isupport_extban();
}
/** Unload all unused extended bans after a REHASH */
void unload_all_unused_extbans(void)
{
Extban *e, *e_next;
for (e=extbans; e; e = e_next)
{
e_next = e->next;
if (e->letter && e->unloaded)
{
unload_extban_commit(e);
}
}
}
void ExtbanDel(Extban *e)
{
/* Always free the module object */

View File

@ -40,9 +40,6 @@ long sajoinmode = 0;
*/
Channel *channels = NULL;
/* A buffer for rebuilding channel/nick lists with comma's */
static char buf[BUFSIZE];
static mp_pool_t *channel_pool = NULL;
/** This describes the letters, modes and options for core channel modes.

View File

@ -62,7 +62,7 @@ static int _conf_alias (ConfigFile *conf, ConfigEntry *ce);
static int _conf_help (ConfigFile *conf, ConfigEntry *ce);
static int _conf_offchans (ConfigFile *conf, ConfigEntry *ce);
static int _conf_sni (ConfigFile *conf, ConfigEntry *ce);
static int _conf_security_group (ConfigFile *conf, ConfigEntry *ce);
extern int _conf_security_group (ConfigFile *conf, ConfigEntry *ce);
static int _conf_secret (ConfigFile *conf, ConfigEntry *ce);
/*
@ -95,7 +95,7 @@ static int _test_alias (ConfigFile *conf, ConfigEntry *ce);
static int _test_help (ConfigFile *conf, ConfigEntry *ce);
static int _test_offchans (ConfigFile *conf, ConfigEntry *ce);
static int _test_sni (ConfigFile *conf, ConfigEntry *ce);
static int _test_security_group (ConfigFile *conf, ConfigEntry *ce);
extern int _test_security_group (ConfigFile *conf, ConfigEntry *ce);
static int _test_secret (ConfigFile *conf, ConfigEntry *ce);
/* This MUST be alphabetized */
@ -185,6 +185,7 @@ ConfigEntry *config_find_entry(ConfigEntry *ce, const char *name);
extern void add_entropy_configfile(struct stat *st, const char *buf);
extern void unload_all_unused_umodes(void);
extern void unload_all_unused_extcmodes(void);
extern void unload_all_unused_extbans(void);
extern void unload_all_unused_caps(void);
extern void unload_all_unused_history_backends(void);
int reloadable_perm_module_unloaded(void);
@ -232,7 +233,6 @@ ConfigResource *config_resources = NULL;
ConfigItem_blacklist_module *conf_blacklist_module = NULL;
ConfigItem_help *conf_help = NULL;
ConfigItem_offchans *conf_offchans = NULL;
SecurityGroup *securitygroups = NULL;
Secret *secrets = NULL;
MODVAR Configuration iConf;
@ -577,6 +577,11 @@ long config_checkval(const char *orig, unsigned short flags)
/** Free configuration setting for set::modes-on-join */
void free_conf_channelmodes(struct ChMode *store)
{
int i;
for (i=0; i < 255; i++)
safe_free(store->extparams[i]);
memset(store, 0, sizeof(struct ChMode));
}
@ -2353,7 +2358,7 @@ void config_rehash()
safe_free(oper_ptr->operclass);
safe_free(oper_ptr->vhost);
Auth_FreeAuthConfig(oper_ptr->auth);
unreal_delete_masks(oper_ptr->mask);
free_security_group(oper_ptr->match);
DelListItem(oper_ptr, conf_oper);
for (s = oper_ptr->swhois; s; s = s_next)
{
@ -2400,7 +2405,7 @@ void config_rehash()
for (allow_ptr = conf_allow; allow_ptr; allow_ptr = (ConfigItem_allow *) next)
{
next = (ListStruct *)allow_ptr->next;
unreal_delete_masks(allow_ptr->mask);
free_security_group(allow_ptr->match);
Auth_FreeAuthConfig(allow_ptr->auth);
DelListItem(allow_ptr, conf_allow);
safe_free(allow_ptr);
@ -2437,6 +2442,8 @@ void config_rehash()
free_motd(&tld_ptr->opermotd);
free_motd(&tld_ptr->botmotd);
free_security_group(tld_ptr->match);
DelListItem(tld_ptr, conf_tld);
safe_free(tld_ptr);
}
@ -2450,7 +2457,7 @@ void config_rehash()
Auth_FreeAuthConfig(vhost_ptr->auth);
safe_free(vhost_ptr->virthost);
safe_free(vhost_ptr->virtuser);
unreal_delete_masks(vhost_ptr->mask);
free_security_group(vhost_ptr->match);
for (s = vhost_ptr->swhois; s; s = s_next)
{
s_next = s->next;
@ -2488,7 +2495,7 @@ void config_rehash()
safe_free(deny_channel_ptr->reason);
safe_free(deny_channel_ptr->class);
DelListItem(deny_channel_ptr, conf_deny_channel);
unreal_delete_masks(deny_channel_ptr->mask);
free_security_group(deny_channel_ptr->match);
safe_free(deny_channel_ptr);
}
@ -2498,7 +2505,7 @@ void config_rehash()
safe_free(allow_channel_ptr->channel);
safe_free(allow_channel_ptr->class);
DelListItem(allow_channel_ptr, conf_allow_channel);
unreal_delete_masks(allow_channel_ptr->mask);
free_security_group(allow_channel_ptr->match);
safe_free(allow_channel_ptr);
}
@ -3036,7 +3043,7 @@ ConfigItem_tld *find_tld(Client *client)
for (tld = conf_tld; tld; tld = tld->next)
{
if (unreal_mask_match(client, tld->mask))
if (user_allowed_by_security_group(client, tld->match))
{
if ((tld->options & TLD_TLS) && !IsSecureConnect(client))
continue;
@ -3056,7 +3063,8 @@ ConfigItem_link *find_link(const char *servername, Client *client)
for (link = conf_link; link; link = link->next)
{
if (match_simple(link->servername, servername) && unreal_mask_match(client, link->incoming.mask))
if (match_simple(link->servername, servername) &&
user_allowed_by_security_group(client, link->incoming.match))
{
return link;
}
@ -3137,7 +3145,7 @@ ConfigItem_deny_channel *find_channel_allowed(Client *client, const char *name)
{
if (dchannel->class && strcmp(client->local->class->name, dchannel->class))
continue;
if (dchannel->mask && !unreal_mask_match(client, dchannel->mask))
if (dchannel->match && !user_allowed_by_security_group(client, dchannel->match))
continue;
break; /* MATCH deny channel { } */
}
@ -3152,7 +3160,7 @@ ConfigItem_deny_channel *find_channel_allowed(Client *client, const char *name)
{
if (achannel->class && strcmp(client->local->class->name, achannel->class))
continue;
if (achannel->mask && !unreal_mask_match(client, achannel->mask))
if (achannel->match && !user_allowed_by_security_group(client, achannel->match))
continue;
break; /* MATCH allow channel { } */
}
@ -3935,6 +3943,7 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce)
oper = safe_alloc(sizeof(ConfigItem_oper));
safe_strdup(oper->name, ce->value);
oper->match = safe_alloc(sizeof(SecurityGroup));
/* Inherit some defaults: */
oper->server_notice_colors = tempiConf.server_notice_colors;
@ -3990,6 +3999,10 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce)
{
oper->server_notice_show_event = config_checkval(cep->value, CFG_YESNO);
}
else if (!strcmp(cep->name, "auto-login"))
{
oper->auto_login = config_checkval(cep->value, CFG_YESNO);
}
else if (!strcmp(cep->name, "modes"))
{
oper->modes = set_usermode(cep->value);
@ -4002,9 +4015,9 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce)
{
oper->maxlogins = atoi(cep->value);
}
else if (!strcmp(cep->name, "mask"))
else if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "match"))
{
unreal_add_masks(&oper->mask, cep);
conf_match_block(conf, cep, &oper->match);
}
else if (!strcmp(cep->name, "vhost"))
{
@ -4018,8 +4031,8 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce)
int _test_oper(ConfigFile *conf, ConfigEntry *ce)
{
char has_class = 0, has_password = 0, has_snomask = 0;
char has_modes = 0, has_require_modes = 0, has_mask = 0, has_maxlogins = 0;
char has_operclass = 0, has_vhost = 0;
char has_modes = 0, has_require_modes = 0, has_mask = 0, has_match = 0, has_broad_match = 0;
char has_maxlogins = 0, has_operclass = 0, has_vhost = 0, has_auto_login = 0;
ConfigEntry *cep;
int errors = 0;
@ -4125,6 +4138,10 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "server-notice-show-event"))
{
}
else if (!strcmp(cep->name, "auto-login"))
{
has_auto_login = config_checkval(cep->value, CFG_YESNO);
}
/* oper::modes */
else if (!strcmp(cep->name, "modes"))
{
@ -4187,7 +4204,22 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "mask"))
{
if (cep->value || cep->items)
{
has_mask = 1;
test_match_block(conf, cep, &errors);
if (test_match_block_too_broad(conf, cep))
has_broad_match = 1;
}
}
else if (!strcmp(cep->name, "match"))
{
if (cep->value || cep->items)
{
has_match = 1;
test_match_block(conf, cep, &errors);
if (test_match_block_too_broad(conf, cep))
has_broad_match = 1;
}
}
else
{
@ -4207,7 +4239,22 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "mask"))
{
if (cep->value || cep->items)
{
has_mask = 1;
test_match_block(conf, cep, &errors);
if (test_match_block_too_broad(conf, cep))
has_broad_match = 1;
}
}
else if (!strcmp(cep->name, "match"))
{
if (cep->value || cep->items)
{
has_match = 1;
test_match_block(conf, cep, &errors);
if (test_match_block_too_broad(conf, cep))
has_broad_match = 1;
}
}
else if (!strcmp(cep->name, "password"))
{
@ -4230,16 +4277,39 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce)
}
}
}
if (!has_password)
if (has_auto_login && has_broad_match)
{
config_error_missing(ce->file->filename, ce->line_number,
"oper::password");
config_error("%s:%i: your oper block for '%s' has auto-login but is completely unrestricted (mask *@*)!",
ce->file->filename, ce->line_number, ce->value);
errors++;
} else
if (!has_password && has_broad_match)
{
config_error("%s:%i: your oper block for '%s' has no password and is completely unrestricted (mask *@*)!",
ce->file->filename, ce->line_number, ce->value);
errors++;
}
if (!has_mask)
/* The rest should NOT be in an 'else'... */
if (has_password && has_auto_login)
{
config_error("%s:%i: You have auto-login enabled for your oper block '%s' but you also have a password set. "
"Remove the password if you want to use auto-login.",
ce->file->filename, ce->line_number, ce->value);
errors++;
}
if (!has_mask && !has_match)
{
config_error_missing(ce->file->filename, ce->line_number,
"oper::mask");
"oper::match");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use oper::match.",
ce->file->filename, ce->line_number);
errors++;
}
if (!has_class)
@ -4590,8 +4660,8 @@ int _conf_tld(ConfigFile *conf, ConfigEntry *ce)
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "mask"))
unreal_add_masks(&ca->mask, cep);
if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
conf_match_block(conf, cep, &ca->match);
else if (!strcmp(cep->name, "motd"))
{
safe_strdup(ca->motd_file, cep->value);
@ -4640,12 +4710,12 @@ int _test_tld(ConfigFile *conf, ConfigEntry *ce)
ConfigEntry *cep;
int errors = 0;
int fd = -1;
char has_mask = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0, has_channel = 0;
char has_opermotd = 0, has_botmotd = 0, has_options = 0;
char has_mask = 0, has_match = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0;
char has_channel = 0, has_opermotd = 0, has_botmotd = 0, has_options = 0;
for (cep = ce->items; cep; cep = cep->next)
{
if (!cep->value && strcmp(cep->name, "options"))
if (!cep->value && strcmp(cep->name, "options") && strcmp(cep->name, "mask") && strcmp(cep->name, "match"))
{
config_error_empty(cep->file->filename, cep->line_number,
"tld", cep->name);
@ -4656,7 +4726,18 @@ int _test_tld(ConfigFile *conf, ConfigEntry *ce)
if (!strcmp(cep->name, "mask"))
{
if (cep->value || cep->items)
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
}
else if (!strcmp(cep->name, "match"))
{
if (cep->value || cep->items)
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
}
/* tld::motd */
else if (!strcmp(cep->name, "motd"))
@ -4806,10 +4887,17 @@ int _test_tld(ConfigFile *conf, ConfigEntry *ce)
continue;
}
}
if (!has_mask)
if (!has_mask && !has_match)
{
config_error_missing(ce->file->filename, ce->line_number,
"tld::mask");
"tld::match");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use %s::match.",
ce->file->filename, ce->line_number, ce->name);
errors++;
}
if (!has_motd)
@ -5342,12 +5430,13 @@ int _conf_allow(ConfigFile *conf, ConfigEntry *ce)
}
allow = safe_alloc(sizeof(ConfigItem_allow));
allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask;
allow->match = safe_alloc(sizeof(SecurityGroup));
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "ip") || !strcmp(cep->name, "hostname"))
if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask") || !strcmp(cep->name, "ip") || !strcmp(cep->name, "hostname"))
{
unreal_add_masks(&allow->mask, cep);
conf_match_block(conf, cep, &allow->match);
}
else if (!strcmp(cep->name, "password"))
allow->auth = AuthBlockToAuthConfig(cep);
@ -5413,7 +5502,7 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce)
ConfigEntry *cep, *cepp;
int errors = 0;
Hook *h;
char has_ip = 0, has_hostname = 0, has_mask = 0;
char has_ip = 0, has_hostname = 0, has_mask = 0, has_match = 0;
char has_maxperip = 0, has_global_maxperip = 0, has_password = 0, has_class = 0;
char has_redirectserver = 0, has_redirectport = 0, has_options = 0;
int hostname_possible_silliness = 0;
@ -5463,6 +5552,7 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce)
for (cep = ce->items; cep; cep = cep->next)
{
if (strcmp(cep->name, "options") &&
strcmp(cep->name, "match") &&
strcmp(cep->name, "mask") &&
config_is_blankorempty(cep, "allow"))
{
@ -5494,6 +5584,12 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "mask"))
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "match"))
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "maxperip"))
{
@ -5641,27 +5737,33 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce)
}
}
if (has_mask && (has_ip || has_hostname))
if ((has_mask || has_match) && (has_ip || has_hostname))
{
config_error("%s:%d: The allow block uses allow::mask, but you also have an allow::ip and allow::hostname.",
config_error("%s:%d: The allow block uses allow::match, but you also have an allow::ip and allow::hostname.",
ce->file->filename, ce->line_number);
config_error("Please delete your allow::ip and allow::hostname entries and/or integrate them into allow::mask");
config_error("Please delete your allow::ip and allow::hostname entries and/or integrate them into allow::match");
} else
if (has_ip)
{
config_warn("%s:%d: The allow block uses allow::mask nowadays. Rename your allow::ip item to allow::mask.",
config_warn("%s:%d: The allow block uses allow::match nowadays. Rename your allow::ip item to allow::match.",
ce->file->filename, ce->line_number);
config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information");
} else
if (has_hostname)
{
config_warn("%s:%d: The allow block uses allow::mask nowadays. Rename your allow::hostname item to allow::mask.",
config_warn("%s:%d: The allow block uses allow::match nowadays. Rename your allow::hostname item to allow::match.",
ce->file->filename, ce->line_number);
config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information");
} else
if (!has_mask)
if (has_mask && has_match)
{
config_error("%s:%d: allow block needs an allow::mask",
config_error("%s:%d: You cannot have both ::mask and ::match. You should only use allow::match.",
ce->file->filename, ce->line_number);
errors++;
} else
if (!has_match && !has_mask)
{
config_error("%s:%d: allow block needs an allow::match",
ce->file->filename, ce->line_number);
errors++;
}
@ -5702,15 +5804,15 @@ int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce)
ConfigItem_allow_channel *allow = NULL;
ConfigEntry *cep;
char *class = NULL;
ConfigEntry *mask = NULL;
ConfigEntry *match = NULL;
/* First, search for ::class, if any */
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "class"))
class = cep->value;
else if (!strcmp(cep->name, "mask"))
mask = cep;
else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
match = cep;
}
for (cep = ce->items; cep; cep = cep->next)
@ -5722,8 +5824,8 @@ int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce)
safe_strdup(allow->channel, cep->value);
if (class)
safe_strdup(allow->class, class);
if (mask)
unreal_add_masks(&allow->mask, mask);
if (match)
conf_match_block(conf, match, &allow->match);
AddListItem(allow, conf_allow_channel);
}
}
@ -5732,9 +5834,10 @@ int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce)
int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce)
{
ConfigEntry *cep;
int errors = 0;
char has_channel = 0, has_class = 0;
ConfigEntry *cep;
int errors = 0;
char has_match = 0, has_mask = 0, has_channel = 0, has_class = 0;
for (cep = ce->items; cep; cep = cep->next)
{
if (config_is_blankorempty(cep, "allow channel"))
@ -5758,8 +5861,15 @@ int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce)
}
has_class = 1;
}
else if (!strcmp(cep->name, "match"))
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "mask"))
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
else
{
@ -5768,6 +5878,13 @@ int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce)
errors++;
}
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use %s::match.",
ce->file->filename, ce->line_number, ce->name);
errors++;
}
if (!has_channel)
{
config_error_missing(ce->file->filename, ce->line_number,
@ -5860,6 +5977,7 @@ int _conf_vhost(ConfigFile *conf, ConfigEntry *ce)
ConfigItem_vhost *vhost;
ConfigEntry *cep, *cepp;
vhost = safe_alloc(sizeof(ConfigItem_vhost));
vhost->match = safe_alloc(sizeof(SecurityGroup));
for (cep = ce->items; cep; cep = cep->next)
{
@ -5880,9 +5998,9 @@ int _conf_vhost(ConfigFile *conf, ConfigEntry *ce)
safe_strdup(vhost->login, cep->value);
else if (!strcmp(cep->name, "password"))
vhost->auth = AuthBlockToAuthConfig(cep);
else if (!strcmp(cep->name, "mask"))
else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
{
unreal_add_masks(&vhost->mask, cep);
conf_match_block(conf, cep, &vhost->match);
}
else if (!strcmp(cep->name, "swhois"))
{
@ -5914,7 +6032,7 @@ int _test_vhost(ConfigFile *conf, ConfigEntry *ce)
{
int errors = 0;
ConfigEntry *cep;
char has_vhost = 0, has_login = 0, has_password = 0, has_mask = 0;
char has_vhost = 0, has_login = 0, has_password = 0, has_mask = 0, has_match = 0;
for (cep = ce->items; cep; cep = cep->next)
{
@ -6007,6 +6125,12 @@ int _test_vhost(ConfigFile *conf, ConfigEntry *ce)
else if (!strcmp(cep->name, "mask"))
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "match"))
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "swhois"))
{
@ -6038,10 +6162,17 @@ int _test_vhost(ConfigFile *conf, ConfigEntry *ce)
"vhost::password");
errors++;
}
if (!has_mask)
if (!has_mask && !has_match)
{
config_error_missing(ce->file->filename, ce->line_number,
"vhost::mask");
"vhost::match");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use %s::match.",
ce->file->filename, ce->line_number, ce->name);
errors++;
}
return errors;
@ -6172,9 +6303,9 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce)
{
for (cepp = cep->items; cepp; cepp = cepp->next)
{
if (!strcmp(cepp->name, "mask"))
if (!strcmp(cepp->name, "match") || !strcmp(cepp->name, "mask"))
{
unreal_add_masks(&link->incoming.mask, cepp);
conf_match_block(conf, cepp, &link->incoming.match);
}
}
}
@ -6184,6 +6315,8 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce)
{
if (!strcmp(cepp->name, "bind-ip"))
safe_strdup(link->outgoing.bind_ip, cepp->value);
else if (!strcmp(cepp->name, "file"))
safe_strdup(link->outgoing.file, cepp->value);
else if (!strcmp(cepp->name, "hostname"))
safe_strdup(link->outgoing.hostname, cepp->value);
else if (!strcmp(cepp->name, "port"))
@ -6273,7 +6406,7 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce)
ConfigEntry *cep, *cepp, *ceppp;
int errors = 0;
int has_incoming = 0, has_incoming_mask = 0, has_outgoing = 0;
int has_incoming = 0, has_incoming_mask = 0, has_incoming_match = 0, has_outgoing = 0, has_outgoing_file = 0;
int has_outgoing_bind_ip = 0, has_outgoing_hostname = 0, has_outgoing_port = 0;
int has_outgoing_options = 0, has_hub = 0, has_leaf = 0, has_leaf_depth = 0;
int has_password = 0, has_class = 0, has_options = 0;
@ -6300,11 +6433,26 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce)
config_detect_duplicate(&has_incoming, cep, &errors);
for (cepp = cep->items; cepp; cepp = cepp->next)
{
if (!strcmp(cepp->name, "match"))
{
if (cepp->value || cepp->items)
{
has_incoming_match = 1;
test_match_block(conf, cepp, &errors);
} else
if (config_is_blankorempty(cepp, "link::incoming"))
{
errors++;
continue;
}
} else
if (!strcmp(cepp->name, "mask"))
{
if (cepp->value || cepp->items)
{
has_incoming_mask = 1;
else
test_match_block(conf, cepp, &errors);
} else
if (config_is_blankorempty(cepp, "link::incoming"))
{
errors++;
@ -6328,6 +6476,15 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce)
config_detect_duplicate(&has_outgoing_bind_ip, cepp, &errors);
// todo: ipv4 vs ipv6
}
else if (!strcmp(cepp->name, "file"))
{
if (config_is_blankorempty(cepp, "link::outgoing"))
{
errors++;
continue;
}
config_detect_duplicate(&has_outgoing_file, cepp, &errors);
}
else if (!strcmp(cepp->name, "hostname"))
{
if (config_is_blankorempty(cepp, "link::outgoing"))
@ -6491,24 +6648,41 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce)
if (has_incoming)
{
/* If we have an incoming sub-block then we need at least 'mask' and 'password' */
if (!has_incoming_mask)
if (!has_incoming_mask && !has_incoming_match)
{
config_error_missing(ce->file->filename, ce->line_number, "link::incoming::mask");
config_error_missing(ce->file->filename, ce->line_number, "link::incoming::match");
errors++;
}
if (has_incoming_mask && has_incoming_match)
{
config_error("%s:%d: You cannot have both link::incoming::mask and link::incoming::match. "
"You should only use link::incoming::match.",
ce->file->filename, ce->line_number);
errors++;
}
}
if (has_outgoing)
{
/* If we have an outgoing sub-block then we need at least a hostname and port */
if (!has_outgoing_hostname)
/* If we have an outgoing sub-block then we need at least a hostname and port or a file */
if (!has_outgoing_file)
{
config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::hostname");
errors++;
if (!has_outgoing_hostname)
{
config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::hostname");
errors++;
}
if (!has_outgoing_port)
{
config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::port");
errors++;
}
}
if (!has_outgoing_port)
else if (has_outgoing_file && (has_outgoing_hostname || has_outgoing_port))
{
config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::port");
config_error("%s:%d: link block should either have a 'file' (for *NIX domain socket), "
"OR have a 'hostname' and 'port' (for IPv4/IPv6). You cannot combine both in one link block.",
ce->file->filename, ce->line_number);
errors++;
}
}
@ -8965,6 +9139,7 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce)
{
/* keep this in sync with _test_allow() */
int ipv6mask;
CheckNull(cep);
ipv6mask = atoi(cep->value);
if (ipv6mask == 0)
{
@ -9818,9 +9993,9 @@ int _conf_deny_channel(ConfigFile *conf, ConfigEntry *ce)
{
safe_strdup(deny->class, cep->value);
}
else if (!strcmp(cep->name, "mask"))
else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
{
unreal_add_masks(&deny->mask, cep);
conf_match_block(conf, cep, &deny->match);
}
}
AddListItem(deny, conf_deny_channel);
@ -9894,6 +10069,7 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce)
if (!strcmp(ce->value, "channel"))
{
char has_channel = 0, has_warn = 0, has_reason = 0, has_redirect = 0, has_class = 0;
char has_mask = 0, has_match = 0;
for (cep = ce->items; cep; cep = cep->next)
{
if (config_is_blankorempty(cep, "deny channel"))
@ -9951,8 +10127,15 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce)
}
has_class = 1;
}
else if (!strcmp(cep->name, "match"))
{
has_match = 1;
test_match_block(conf, cep, &errors);
}
else if (!strcmp(cep->name, "mask"))
{
has_mask = 1;
test_match_block(conf, cep, &errors);
}
else
{
@ -9973,6 +10156,13 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce)
"deny channel::reason");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use %s %s::match.",
ce->file->filename, ce->line_number, ce->name, ce->value);
errors++;
}
}
else if (!strcmp(ce->value, "link"))
{
@ -10177,104 +10367,6 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce)
return errors;
}
int _test_security_group(ConfigFile *conf, ConfigEntry *ce)
{
int errors = 0;
ConfigEntry *cep;
if (!ce->value)
{
config_error("%s:%i: security-group block needs a name, eg: security-group web-users {",
ce->file->filename, ce->line_number);
errors++;
} else {
if (!strcasecmp(ce->value, "unknown-users"))
{
config_error("%s:%i: The 'unknown-users' group is a special group that is the "
"inverse of 'known-users', you cannot create or adjust it in the "
"config file, as it is created automatically by UnrealIRCd.",
ce->file->filename, ce->line_number);
errors++;
return errors;
}
if (!security_group_valid_name(ce->value))
{
config_error("%s:%i: security-group block name '%s' contains invalid characters or is too long. "
"Only letters, numbers, underscore and hyphen are allowed.",
ce->file->filename, ce->line_number, ce->value);
errors++;
}
}
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "webirc"))
{
CheckNull(cep);
} else
if (!strcmp(cep->name, "identified"))
{
CheckNull(cep);
} else
if (!strcmp(cep->name, "tls"))
{
CheckNull(cep);
} else
if (!strcmp(cep->name, "reputation-score"))
{
int v;
CheckNull(cep);
v = atoi(cep->value);
if ((v < 1) || (v > 10000))
{
config_error("%s:%i: security-group::reputation-score needs to be a value of 1-10000",
cep->file->filename, cep->line_number);
errors++;
}
} else
if (!strcmp(cep->name, "include-mask"))
{
} else
{
config_error_unknown(cep->file->filename, cep->line_number,
"security-group", cep->name);
errors++;
continue;
}
}
return errors;
}
int _conf_security_group(ConfigFile *conf, ConfigEntry *ce)
{
ConfigEntry *cep;
SecurityGroup *s = add_security_group(ce->value, 1);
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "webirc"))
s->webirc = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "identified"))
s->identified = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "tls"))
s->tls = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "reputation-score"))
s->reputation_score = atoi(cep->value);
else if (!strcmp(cep->name, "priority"))
{
s->priority = atoi(cep->value);
DelListItem(s, securitygroups);
AddListItemPrio(s, securitygroups, s->priority);
}
else if (!strcmp(cep->name, "include-mask"))
{
unreal_add_masks(&s->include_mask, cep);
}
}
return 1;
}
Secret *find_secret(const char *secret_name)
{
Secret *s;
@ -10687,6 +10779,7 @@ int rehash_internal(Client *client)
reread_motdsandrules();
unload_all_unused_umodes();
unload_all_unused_extcmodes();
unload_all_unused_extbans();
unload_all_unused_caps();
unload_all_unused_history_backends();
// unload_all_unused_moddata(); -- this will crash
@ -10703,8 +10796,9 @@ int rehash_internal(Client *client)
void link_cleanup(ConfigItem_link *link_ptr)
{
safe_free(link_ptr->servername);
unreal_delete_masks(link_ptr->incoming.mask);
free_security_group(link_ptr->incoming.match);
Auth_FreeAuthConfig(link_ptr->auth);
safe_free(link_ptr->outgoing.file);
safe_free(link_ptr->outgoing.bind_ip);
safe_free(link_ptr->outgoing.hostname);
safe_free(link_ptr->hub);

View File

@ -1,6 +1,6 @@
/* UnrealIRCd configuration preprocessor
* (C) Copyright 2019 Bram Matthys ("Syzop") and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*
* Technically this isn't a 100% true preprocessor, but to the end user
* it will certainly look like it, hence the name.

View File

@ -1,6 +1,6 @@
/* UnrealIRCd crash reporter code.
* (C) Copyright 2015-2019 Bram Matthys ("Syzop") and the UnrealIRCd Team.
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"
@ -306,7 +306,7 @@ int crash_report_asan_log(FILE *reportfd, char *coredump)
stripcrlf(buf);
fprintf(reportfd, " %s\n", buf);
}
n = pclose(fd);
n = fclose(fd);
fprintf(reportfd, "END OF ASAN LOG\n");
if (WEXITSTATUS(n) == 127)

View File

@ -57,7 +57,7 @@ EVENT(garbage_collect)
if (loop.do_garbage_collect == 1) {
loop.do_garbage_collect = 0;
unreal_log(ULOG_INFO, "main", "GARBAGE_COLLECT_STARTED", NULL, "Cleaned up $count garbage blocks",
(ii - freelinks));
log_data_integer("count", (ii - freelinks)));
}
}
if (loop.do_garbage_collect == 1)
@ -83,7 +83,7 @@ int match_tkls(Client *client)
{
unreal_log(ULOG_INFO, "tkl", "BAN_REALNAME", client,
"Banned client $client.details due to realname ban: $reason",
bconf->reason ? bconf->reason : "no reason");
log_data_string("reason", bconf->reason ? bconf->reason : "no reason"));
if (bconf->reason) {
if (IsUser(client))

View File

@ -1,7 +1,7 @@
/************************************************************************
* UnrealIRCd - Unreal Internet Relay Chat Daemon - src/ircd_vars.c
* (c) 2021- Bram Matthys and The UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -3,7 +3,7 @@
* (C) 2021 Bram Matthys (Syzop) and the UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -43,6 +43,26 @@ int log_sources_match(LogSource *logsource, LogLevel loglevel, const char *subsy
void do_unreal_log_internal(LogLevel loglevel, const char *subsystem, const char *event_id, Client *client, int expand_msg, const char *msg, va_list vl);
void log_blocks_switchover(void);
/** Calculate expansion of a JSON string thanks to double escaping.
* orig => JSON => IRC
* " => \" => \\"
* \ => \\ => \\\\
*/
int json_dump_string_length(const char *s)
{
int len = 0;
for (; *s; s++)
{
if (*s == '\\')
len += 4;
else if (*s == '"')
len += 3;
else
len++;
}
return len;
}
/** Convert a regular string value to a JSON string.
* In UnrealIRCd, this must be used instead of json_string()
* as we may use non-UTF8 sequences. Also, this takes care
@ -53,13 +73,16 @@ void log_blocks_switchover(void);
*/
json_t *json_string_unreal(const char *s)
{
static char buf[8192];
char buf1[512], buf2[512];
char *verified_s;
const char *stripped;
if (s == NULL)
return json_null();
verified_s = unrl_utf8_make_valid(s, buf, sizeof(buf), 0);
stripped = StripControlCodesEx(s, buf1, sizeof(buf1), UNRL_STRIP_LOW_ASCII|UNRL_STRIP_KEEP_LF);
verified_s = unrl_utf8_make_valid(buf1, buf2, sizeof(buf2), 0);
return json_string(verified_s);
}
@ -561,6 +584,9 @@ void json_expand_client(json_t *j, const char *key, Client *client, int detail)
if (client->local && client->local->creationtime)
json_object_set_new(child, "connected_since", json_timestamp(client->local->creationtime));
if (client->local && client->local->idle_since)
json_object_set_new(child, "idle_since", json_timestamp(client->local->idle_since));
if (client->user)
{
char buf[512];
@ -572,6 +598,10 @@ void json_expand_client(json_t *j, const char *key, Client *client, int detail)
json_object_set_new(user, "username", json_string_unreal(client->user->username));
if (!BadPtr(client->info))
json_object_set_new(user, "realname", json_string_unreal(client->info));
if (has_user_mode(client, 'x') && client->user->virthost && strcmp(client->user->virthost, client->user->realhost))
json_object_set_new(user, "vhost", json_string_unreal(client->user->virthost));
if (*client->user->cloakedhost)
json_object_set_new(user, "cloakedhost", json_string_unreal(client->user->cloakedhost));
if (client->uplink)
json_object_set_new(user, "servername", json_string_unreal(client->uplink->name));
if (IsLoggedIn(client))
@ -592,6 +622,26 @@ void json_expand_client(json_t *j, const char *key, Client *client, int detail)
str = get_operclass(client);
if (str)
json_object_set_new(user, "operclass", json_string_unreal(str));
if (client->user->channel)
{
Membership *m;
int cnt = 0;
int len = 0;
json_t *channels = json_array();
json_object_set_new(user, "channels", channels);
for (m = client->user->channel; m; m = m->next)
{
len += json_dump_string_length(m->channel->name);
if (len > 384)
{
/* Truncated */
json_array_append_new(channels, json_string_unreal("..."));
break;
}
json_array_append_new(channels, json_string_unreal(m->channel->name));
}
}
RunHook(HOOKTYPE_JSON_EXPAND_CLIENT_USER, client, detail, child, user);
} else
if (IsMe(client))
{
@ -645,7 +695,9 @@ void json_expand_client(json_t *j, const char *key, Client *client, int detail)
}
if (!BadPtr(client->server->features.nickchars))
json_object_set_new(features, "nick_character_sets", json_string_unreal(client->server->features.nickchars));
RunHook(HOOKTYPE_JSON_EXPAND_CLIENT_SERVER, client, detail, child, server);
}
RunHook(HOOKTYPE_JSON_EXPAND_CLIENT, client, detail, child);
}
void json_expand_channel(json_t *j, const char *key, Channel *channel, int detail)
@ -675,6 +727,7 @@ void json_expand_channel(json_t *j, const char *key, Channel *channel, int detai
}
// Possibly later: If detail is set to 1 then expand more...
RunHook(HOOKTYPE_JSON_EXPAND_CHANNEL, channel, detail, child);
}
const char *timestamp_iso8601_now(void)
@ -883,9 +936,11 @@ LogData *log_data_link_block(ConfigItem_link *link)
safe_strdup(d->key, "link_block");
d->value.object = j = json_object();
json_object_set_new(j, "name", json_string_unreal(link->servername));
json_object_set_new(j, "file", json_string_unreal(link->outgoing.file));
json_object_set_new(j, "hostname", json_string_unreal(link->outgoing.hostname));
json_object_set_new(j, "ip", json_string_unreal(link->connect_ip));
json_object_set_new(j, "port", json_integer(link->outgoing.port));
json_object_set_new(j, "class", json_string_unreal(link->class->name));
if (!link->outgoing.bind_ip && iConf.link_bindip)
bind_ip = iConf.link_bindip;

View File

@ -916,145 +916,74 @@ int is_autojoin_chan(const char *chname)
return 0;
}
/** Free all masks in the mask list */
void unreal_delete_masks(ConfigItem_mask *m)
{
ConfigItem_mask *m_next;
for (; m; m = m_next)
{
m_next = m->next;
safe_free(m->mask);
safe_free(m);
}
}
/** Internal function to add one individual mask to the list */
static void unreal_add_mask(ConfigItem_mask **head, ConfigEntry *ce)
{
ConfigItem_mask *m = safe_alloc(sizeof(ConfigItem_mask));
/* Since we allow both mask "xyz"; and mask { abc; def; };... */
if (ce->value)
safe_strdup(m->mask, ce->value);
else
safe_strdup(m->mask, ce->name);
add_ListItem((ListStruct *)m, (ListStruct **)head);
}
/** Add mask entries from config */
void unreal_add_masks(ConfigItem_mask **head, ConfigEntry *ce)
/** Add name entries from config */
void unreal_add_names(NameList **n, ConfigEntry *ce)
{
if (ce->items)
{
ConfigEntry *cep;
for (cep = ce->items; cep; cep = cep->next)
unreal_add_mask(head, cep);
_add_name_list(n, cep->value ? cep->value : cep->name);
} else
if (ce->value)
{
unreal_add_mask(head, ce);
_add_name_list(n, ce->value);
}
}
/** Check if a client matches any of the masks in the mask list.
* The following rules apply:
* - If you have only negating entries, like '!abc' and '!def', then
* we assume an implicit * rule first, since that is clearly what
* the user wants.
* - If you have a mix, like '*.com', '!irc1*', '!irc2*' then the
* implicit * is dropped and we assume you only want to match *.com,
* with the exception of irc1*.com and irc2*.com.
* - If you only have normal entries without ! then things are
* as they always are.
* @param client The client to run the mask match against
* @param mask The mask entry from the config file
* @returns 1 on match, 0 on non-match.
*/
int unreal_mask_match(Client *client, ConfigItem_mask *mask)
/** Add name/value entries from config */
void unreal_add_name_values(NameValuePrioList **n, const char *name, ConfigEntry *ce)
{
int retval = 1;
ConfigItem_mask *m;
if (!mask)
return 0; /* Empty mask block is no match */
/* First check normal matches (without ! prefix) */
for (m = mask; m; m = m->next)
if (ce->items)
{
if (m->mask[0] != '!')
{
retval = 0; /* no implicit * */
if (match_user(m->mask, client, MATCH_CHECK_REAL|MATCH_CHECK_EXTENDED))
{
retval = 1;
break;
}
}
}
if (retval)
ConfigEntry *cep;
for (cep = ce->items; cep; cep = cep->next)
add_nvplist(n, 0, name, cep->value ? cep->value : cep->name);
} else
if (ce->value)
{
/* We matched. Check for exceptions (with ! prefix) */
for (m = mask; m; m = m->next)
{
if ((m->mask[0] == '!') && match_user(m->mask+1, client, MATCH_CHECK_REAL|MATCH_CHECK_EXTENDED))
return 0;
}
add_nvplist(n, 0, name, ce->value);
}
return retval;
}
/** Check if a string matches any of the masks in the mask list.
* The following rules apply:
* - If you have only negating entries, like '!abc' and '!def', then
* we assume an implicit * rule first, since that is clearly what
* the user wants.
* - If you have a mix, like '*.com', '!irc1*', '!irc2*' then the
* implicit * is dropped and we assume you only want to match *.com,
* with the exception of irc1*.com and irc2*.com.
* - If you only have normal entries without ! then things are
* as they always are.
* @param name The name to run the mask matching on
* @param mask The mask entry from the config file
* @returns 1 on match, 0 on non-match.
*/
int unreal_mask_match_string(const char *name, ConfigItem_mask *mask)
/** Prints the name:value pair of a NameValuePrioList */
const char *namevalue(NameValuePrioList *n)
{
int retval = 1;
ConfigItem_mask *m;
static char buf[512];
if (!mask)
return 0; /* Empty mask block is no match */
if (!n->name)
return "";
/* First check normal matches (without ! prefix) */
for (m = mask; m; m = m->next)
{
if (m->mask[0] != '!')
{
retval = 0; /* no implicit * */
if (match_simple(m->mask, name))
{
retval = 1;
break;
}
}
}
if (!n->value)
return n->name;
if (retval)
{
/* We matched. Check for exceptions (with ! prefix) */
for (m = mask; m; m = m->next)
{
if ((m->mask[0] == '!') && match_simple(m->mask+1, name))
return 0;
}
}
snprintf(buf, sizeof(buf), "%s:%s", n->name, n->value);
return buf;
}
return retval;
/** Version of namevalue() but replaces spaces with underscores.
* Used in for example numeric sending routines where a field
* may not contain any spaces.
*/
const char *namevalue_nospaces(NameValuePrioList *n)
{
static char buf[512];
char *p;
if (!n->name)
return "";
if (!n->value)
strlcpy(buf, n->name, sizeof(buf));
snprintf(buf, sizeof(buf), "%s:%s", n->name, n->value);
/* Replace spaces with underscores */
for (p=buf; *p; p++)
if (*p == ' ')
*p = '_';
return buf;
}
/** Our own strcasestr implementation because strcasestr is
@ -1461,6 +1390,11 @@ void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, const char
{
}
int make_oper_default_handler(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost)
{
return 0;
}
/** my_timegm: mktime()-like function which will use GMT/UTC.
* Strangely enough there is no standard function for this.
* On some *NIX OS's timegm() may be available, sometimes only
@ -2539,3 +2473,141 @@ int minimum_msec_since_last_run(struct timeval *tv_old, long minimum)
}
return 0;
}
/** Strip color, bold, underline, and reverse codes from a string.
* @param text The input text
* @param output The buffer for the output text
* @param outputlen The length of the output buffer
* @param strip_flags Zero or (a combination of) UNRL_STRIP_LOW_ASCII / UNRL_STRIP_KEEP_LF.
* @returns The new string, which will be 'output', or in unusual cases (outputlen==0) will be NULL.
*/
const char *StripControlCodesEx(const char *text, char *output, size_t outputlen, int strip_flags)
{
int i = 0, len = strlen(text), save_len=0;
char nc = 0, col = 0, rgb = 0;
char *o = output;
const char *save_text=NULL;
/* Handle special cases first.. */
if (outputlen == 0)
return NULL;
if (outputlen == 1)
{
*output = '\0';
return output;
}
/* Reserve room for the NUL byte */
outputlen--;
while (len > 0)
{
if ( col && ((isdigit(*text) && nc < 2) || (*text == ',' && nc < 3)))
{
nc++;
if (*text == ',')
nc = 0;
}
/* Syntax for RGB is ^DHHHHHH where H is a hex digit.
* If < 6 hex digits are specified, the code is displayed
* as text
*/
else if ((rgb && isxdigit(*text) && nc < 6) || (rgb && *text == ',' && nc < 7))
{
nc++;
if (*text == ',')
nc = 0;
}
else
{
if (col)
col = 0;
if (rgb)
{
if (nc != 6)
{
text = save_text+1;
len = save_len-1;
rgb = 0;
continue;
}
rgb = 0;
}
switch (*text)
{
case 3:
/* color */
col = 1;
nc = 0;
break;
case 4:
/* RGB */
save_text = text;
save_len = len;
rgb = 1;
nc = 0;
break;
case 2:
/* bold */
break;
case 31:
/* underline */
break;
case 22:
/* reverse */
break;
case 15:
/* plain */
break;
case 29:
/* italic */
break;
case 30:
/* strikethrough */
break;
case 17:
/* monospace */
break;
case 0xe2:
if (!strncmp(text+1, "\x80\x8b", 2))
{
/* +2 means we skip 3 */
text += 2;
len -= 2;
break;
}
/*fallthrough*/
default:
if ((*text >= ' ') ||
!(strip_flags & UNRL_STRIP_LOW_ASCII) ||
((strip_flags & UNRL_STRIP_KEEP_LF) && (*text == '\n'))
)
{
*o++ = *text;
outputlen--;
if (outputlen == 0)
{
*o = '\0';
return output;
}
}
break;
}
}
text++;
len--;
}
*o = '\0';
return output;
}
/* strip color, bold, underline, and reverse codes from a string */
const char *StripControlCodes(const char *text)
{
static unsigned char new_str[4096];
return StripControlCodesEx(text, new_str, sizeof(new_str), 0);
}

View File

@ -1,6 +1,6 @@
/* UnrealIRCd module manager.
* (C) Copyright 2019 Bram Matthys ("Syzop") and the UnrealIRCd Team.
* License: GPLv2
* License: GPLv2 or later
* See https://www.unrealircd.org/docs/Module_manager for user documentation.
*/
@ -1206,7 +1206,7 @@ int mm_compile(ManagedModule *m, char *tmpfile, int test)
fprintf(stderr, "ERROR: Compile errors encountered while compiling module '%s'\n"
"You are suggested to contact the author (%s) of this module:\n%s\n",
m->name, m->author, m->troubleshooting);
return 1;
return 0;
}
/** Actually download and install the module.
@ -1222,7 +1222,7 @@ void mm_install_module(ManagedModule *m)
basename = "mod.c";
tmpfile = unreal_mktemp(TMPDIR, basename);
printf("ConfigResourceing %s from %s...\n", m->name, m->source);
printf("Downloading %s from %s...\n", m->name, m->source);
if (!mm_http_request(m->source, tmpfile, 1))
{
fprintf(stderr, "Repository %s seems to list a module file that cannot be retrieved (%s).\n", m->repo_url, m->source);
@ -1655,21 +1655,23 @@ void mm_parse_c_file(int argc, char *args[])
void mm_self_test(void)
{
char name[512];
snprintf(name, sizeof(name), "%s/src/modules/third", BUILDDIR);
if (file_exists(name))
return;
if (!file_exists(BUILDDIR))
{
fprintf(stderr, "ERROR: Directory %s does not exist.\n"
"The UnrealIRCd source is required for the module manager to work!\n",
BUILDDIR);
exit(-1);
} else {
fprintf(stderr, "ERROR: Directory %s exists, but %s does not.\n"
"The UnrealIRCd source is required for the module manager to work.\n"
"It seems you only have a partial build directory??\n",
BUILDDIR, name);
snprintf(name, sizeof(name), "%s/src/modules/third/Makefile", BUILDDIR);
if (!file_exists(name))
{
fprintf(stderr, "ERROR: Directory %s exists, but your UnrealIRCd is not compiled yet.\n"
"You must compile your UnrealIRCd first (run './Config', then 'make install')\n",
BUILDDIR);
exit(-1);
}
}
exit(-1);
}
void modulemanager(int argc, char *args[])

View File

@ -39,7 +39,7 @@ MODULES= \
sethost.so chghost.so chgident.so setname.so \
setident.so sdesc.so svsmode.so swhois.so\
svsmotd.so svsnline.so who_old.so whox.so mkpasswd.so \
away.so svsnoop.so svsnick.so \
away.so svsnoop.so svsnick.so svso.so \
chgname.so kill.so \
lag.so message.so oper.so pingpong.so \
quit.so sendumode.so sqline.so \
@ -71,12 +71,12 @@ MODULES= \
message-tags.so batch.so \
account-tag.so labeled-response.so link-security.so \
message-ids.so plaintext-policy.so server-time.so sts.so \
echo-message.so userip-tag.so userhost-tag.so \
echo-message.so userip-tag.so userhost-tag.so geoip-tag.so \
bot-tag.so reply-tag.so json-log-tag.so \
typing-indicator.so \
typing-indicator.so channel-context.so \
ident_lookup.so history.so chathistory.so \
targetfloodprot.so clienttagdeny.so watch-backend.so \
monitor.so slog.so tls_cipher.so operinfo.so \
monitor.so slog.so tls_cipher.so operinfo.so creationtime.so \
unreal_server_compat.so \
extended-monitor.so geoip_csv.so \
geoip_base.so extjwt.so \

View File

@ -18,6 +18,8 @@
* ban-action block;
* ban-reason "Possible mixed character spam";
* ban-time 4h; // For other types
* except {
* }
* };
* };
*
@ -56,6 +58,7 @@ struct {
BanAction ban_action;
char *ban_reason;
long ban_time;
SecurityGroup *except;
} cfg;
static void free_config(void);
@ -196,9 +199,12 @@ CMD_OVERRIDE_FUNC(override_msg)
{
int score, ret;
if (!MyUser(client) || (parc < 3) || BadPtr(parv[2]))
if (!MyUser(client) || (parc < 3) || BadPtr(parv[2]) ||
user_allowed_by_security_group(client, cfg.except))
{
/* Short circuit for: remote clients or insufficient parameters */
/* Short circuit for: remote clients, insufficient parameters,
* antimixedutf8::except.
*/
CallCommandOverride(ovr, client, recv_mtags, parc, parv);
return;
}
@ -271,6 +277,7 @@ static void init_config(void)
static void free_config(void)
{
safe_free(cfg.ban_reason);
free_security_group(cfg.except);
memset(&cfg, 0, sizeof(cfg)); /* needed! */
}
@ -319,6 +326,10 @@ int antimixedutf8_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *er
if (!strcmp(cep->name, "ban-time"))
{
} else
if (!strcmp(cep->name, "except"))
{
test_match_block(cf, cep, &errors);
} else
{
config_error("%s:%i: unknown directive set::antimixedutf8::%s",
cep->file->filename, cep->line_number, cep->name);
@ -357,6 +368,10 @@ int antimixedutf8_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
if (!strcmp(cep->name, "ban-time"))
{
cfg.ban_time = config_checkval(cep->value, CFG_TIME);
} else
if (!strcmp(cep->name, "except"))
{
conf_match_block(cf, cep, &cfg.except);
}
}
return 1;

View File

@ -25,7 +25,7 @@
ModuleHeader MOD_HEADER
= {
"antirandom",
"1.4",
"1.5",
"Detect and ban users with random names",
"UnrealIRCd Team",
"unrealircd-6",
@ -504,8 +504,7 @@ struct {
long ban_time;
int convert_to_lowercase;
int show_failedconnects;
ConfigItem_mask *except_hosts;
int except_webirc;
SecurityGroup *except;
} cfg;
/* Forward declarations */
@ -542,7 +541,8 @@ MOD_INIT()
/* Some default values: */
cfg.convert_to_lowercase = 1;
cfg.except_webirc = 1;
cfg.except = safe_alloc(sizeof(SecurityGroup));
cfg.except->webirc = 1;
return MOD_SUCCESS;
}
@ -562,7 +562,7 @@ MOD_UNLOAD()
static void free_config(void)
{
safe_free(cfg.ban_reason);
unreal_delete_masks(cfg.except_hosts);
free_security_group(cfg.except);
memset(&cfg, 0, sizeof(cfg)); /* needed! */
}
@ -580,6 +580,10 @@ int antirandom_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "except"))
{
test_match_block(cf, cep, &errors);
} else
if (!strcmp(cep->name, "except-hosts"))
{
} else
@ -653,14 +657,20 @@ int antirandom_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "except"))
{
conf_match_block(cf, cep, &cfg.except);
} else
if (!strcmp(cep->name, "except-hosts"))
{
/* backwards compatible with set::antirandom::except */
for (cep2 = cep->items; cep2; cep2 = cep2->next)
unreal_add_masks(&cfg.except_hosts, cep2);
unreal_add_masks(&cfg.except->mask, cep2);
} else
if (!strcmp(cep->name, "except-webirc"))
{
cfg.except_webirc = config_checkval(cep->value, CFG_YESNO);
/* backwards compatible with set::antirandom::except */
cfg.except->webirc = config_checkval(cep->value, CFG_YESNO);
} else
if (!strcmp(cep->name, "threshold"))
{
@ -863,14 +873,14 @@ int antirandom_preconnect(Client *client)
if (cfg.ban_action == BAN_ACT_WARN)
{
unreal_log(ULOG_INFO, "antirandom", "ANTIRANDOM_DENIED_USER", client,
"[antirandom] would have denied access to user with score $score: $client:$client.info",
"[antirandom] would have denied access to user with score $score: $client.details:$client.user.realname",
log_data_integer("score", score));
return HOOK_CONTINUE;
}
if (cfg.show_failedconnects)
{
unreal_log(ULOG_INFO, "antirandom", "ANTIRANDOM_DENIED_USER", client,
"[antirandom] denied access to user with score $score: $client:$client.info",
"[antirandom] denied access to user with score $score: $client.details:$client.user.realname",
log_data_integer("score", score));
}
place_host_ban(client, cfg.ban_action, cfg.ban_reason, cfg.ban_time);
@ -894,13 +904,8 @@ static void free_stuff(void)
/** Is this user exempt from antirandom interventions? */
static int is_exempt(Client *client)
{
/* WEBIRC gateway and exempt? */
if (cfg.except_webirc)
{
const char *val = moddata_client_get(client, "webirc");
if (val && (atoi(val)>0))
return 1;
}
if (user_allowed_by_security_group(client, cfg.except))
return 1;
if (find_tkl_exception(TKL_ANTIRANDOM, client))
return 1;
@ -909,6 +914,5 @@ static int is_exempt(Client *client)
if (IsSoftBanAction(cfg.ban_action) && IsLoggedIn(client))
return 1;
/* On except host? */
return unreal_mask_match(client, cfg.except_hosts);
return 0;
}

View File

@ -67,6 +67,7 @@ struct Blacklist {
int action;
long ban_time;
char *reason;
SecurityGroup *except;
};
/* Blacklist user struct. In the c-ares DNS reply callback we need to pass
@ -236,6 +237,8 @@ void delete_blacklist_block(Blacklist *e)
safe_free(e->name);
safe_free(e->reason);
free_security_group(e->except);
safe_free(e);
}
@ -356,6 +359,10 @@ int blacklist_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
}
}
} else
if (!strcmp(cep->name, "except"))
{
test_match_block(cf, cep, &errors);
} else
if (!cep->value)
{
config_error_empty(cep->file->filename, cep->line_number,
@ -388,8 +395,8 @@ int blacklist_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
continue;
}
has_ban_time = 1;
} else
if (!strcmp(cep->name, "reason"))
}
else if (!strcmp(cep->name, "reason"))
{
if (has_reason)
{
@ -524,13 +531,17 @@ int blacklist_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{
d->action = banact_stringtoval(cep->value);
}
else if (!strcmp(cep->name, "ban-time"))
{
d->ban_time = config_checkval(cep->value, CFG_TIME);
}
else if (!strcmp(cep->name, "reason"))
{
safe_strdup(d->reason, cep->value);
}
else if (!strcmp(cep->name, "ban-time"))
else if (!strcmp(cep->name, "except"))
{
d->ban_time = config_checkval(cep->value, CFG_TIME);
conf_match_block(cf, cep, &d->except);
}
}
@ -574,7 +585,7 @@ int blacklist_start_check(Client *client)
SetNoHandshakeDelay(client);
return 0;
}
if (!BLUSER(client))
{
SetBLUser(client, safe_alloc(sizeof(BLUser)));
@ -587,6 +598,10 @@ int blacklist_start_check(Client *client)
if (!BLUSER(client))
break;
/* Check if user is exempt (then don't bother checking) */
if (user_allowed_by_security_group(client, bl->except))
continue;
/* Initiate blacklist requests */
if (bl->backend_type == BLACKLIST_BACKEND_DNS)
blacklist_dns_request(client, bl);

View File

@ -45,6 +45,12 @@ MOD_INIT()
MARK_AS_OFFICIAL_MODULE(modinfo);
memset(&mtag, 0, sizeof(mtag));
mtag.name = "bot";
mtag.is_ok = bottag_mtag_is_ok;
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
MessageTagHandlerAdd(modinfo->handle, &mtag);
memset(&mtag, 0, sizeof(mtag));
mtag.name = "draft/bot";
mtag.is_ok = bottag_mtag_is_ok;
@ -82,7 +88,14 @@ void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_l
if (IsUser(client) && has_user_mode(client, 'B'))
{
MessageTag *m = safe_alloc(sizeof(MessageTag));
MessageTag *m;
m = safe_alloc(sizeof(MessageTag));
safe_strdup(m->name, "bot");
m->value = NULL;
AddListItem(m, *mtag_list);
m = safe_alloc(sizeof(MessageTag));
safe_strdup(m->name, "draft/bot");
m->value = NULL;
AddListItem(m, *mtag_list);

View File

@ -6,7 +6,7 @@
*
* (C) Copyright 2014-2015 The UnrealIRCd team (Syzop, DBoyz, Nath and others)
*
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"
@ -27,6 +27,7 @@ void certfp_unserialize(const char *str, ModData *m);
int certfp_handshake(Client *client);
int certfp_connect(Client *client);
int certfp_whois(Client *client, Client *target, NameValuePrioList **list);
int certfp_log(Client *client, int detail, json_t *j);
ModDataInfo *certfp_md; /* Module Data structure which we acquire */
@ -51,6 +52,7 @@ MOD_INIT()
HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, certfp_handshake);
HookAdd(modinfo->handle, HOOKTYPE_SERVER_HANDSHAKE_OUT, 0, certfp_handshake);
HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, certfp_whois);
HookAdd(modinfo->handle, HOOKTYPE_JSON_EXPAND_CLIENT, 0, certfp_log);
return MOD_SUCCESS;
}
@ -158,3 +160,24 @@ void certfp_unserialize(const char *str, ModData *m)
{
safe_strdup(m->str, str);
}
int certfp_log(Client *client, int detail, json_t *j)
{
json_t *tls;
const char *str;
str = moddata_client_get(client, "certfp");
if (!str)
return 0;
tls = json_object_get(j, "tls");
if (!tls)
{
tls = json_object();
json_object_set_new(j, "tls", tls);
}
json_object_set_new(tls, "certfp", json_string_unreal(str));
return 0;
}

View File

@ -110,7 +110,7 @@ struct ChannelFloodProtection {
/* Global variables */
ModDataInfo *mdflood = NULL;
Cmode_t EXTMODE_FLOODLIMIT = 0L;
static int timedban_available = 0; /**< Set to 1 if extbans/timedban module is loaded. */
static int timedban_available = 1; /**< Set to 1 if extbans/timedban module is loaded. Assumed 1 during config load due to set::modes-on-join race. */
RemoveChannelModeTimer *removechannelmodetimer_list = NULL;
char *floodprot_msghash_key = NULL;

View File

@ -1,7 +1,7 @@
/*
* modules/chanmodes/history - Channel History
* (C) Copyright 2009-2019 Bram Matthys (Syzop) and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -35,7 +35,7 @@ Cmode_t EXTCMODE_OPERONLY;
int operonly_require_oper(Client *client, Channel *channel, char mode, const char *para, int checkt, int what);
int operonly_can_join(Client *client, Channel *channel, const char *key, char **errmsg);
int operonly_view_topic_outside_channel(Client *client, Channel *channel);
int operonly_oper_invite_ban(Client *client, Channel *channel);
int operonly_invite_bypass(Client *client, Channel *channel);
MOD_TEST()
{
@ -53,7 +53,7 @@ CmodeInfo req;
CmodeAdd(modinfo->handle, req, &EXTCMODE_OPERONLY);
HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, operonly_can_join);
HookAdd(modinfo->handle, HOOKTYPE_OPER_INVITE_BAN, 0, operonly_oper_invite_ban);
HookAdd(modinfo->handle, HOOKTYPE_INVITE_BYPASS, 0, operonly_invite_bypass);
HookAdd(modinfo->handle, HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL, 0, operonly_view_topic_outside_channel);
@ -81,10 +81,9 @@ int operonly_can_join(Client *client, Channel *channel, const char *key, char **
return 0;
}
int operonly_oper_invite_ban(Client *client, Channel *channel)
int operonly_invite_bypass(Client *client, Channel *channel)
{
if ((channel->mode.mode & EXTCMODE_OPERONLY) &&
!ValidatePermissionsForPath("channel:operonly:ban",client,NULL,NULL,NULL))
if ((channel->mode.mode & EXTCMODE_OPERONLY) && !ValidatePermissionsForPath("channel:operonly:ban",client,NULL,NULL,NULL))
return HOOK_DENY;
return HOOK_CONTINUE;

View File

@ -0,0 +1,95 @@
/*
* IRC - Internet Relay Chat, src/modules/channel-context.c
* (C) 2022 Valware & The UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"channel-context",
"1.0",
"Channel Context (IRCv3)",
"UnrealIRCd team",
"unrealircd-6",
};
int chancontext_mtag_is_ok(Client *client, const char *name, const char *value);
void mtag_add_chancontext(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature);
MOD_INIT()
{
MessageTagHandlerInfo mtag;
MARK_AS_OFFICIAL_MODULE(modinfo);
memset(&mtag, 0, sizeof(mtag));
mtag.is_ok = chancontext_mtag_is_ok;
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
mtag.name = "+draft/channel-context";
MessageTagHandlerAdd(modinfo->handle, &mtag);
HookAddVoid(modinfo->handle, HOOKTYPE_NEW_MESSAGE, 0, mtag_add_chancontext);
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
int chancontext_mtag_is_ok(Client *client, const char *name, const char *value)
{
if (BadPtr(value))
return 0;
/* Validate a bit further, but only for local users.. */
if (MyUser(client))
{
Channel *channel = find_channel(value);
if (!channel)
return 0;
if (!IsMember(client, channel))
return 0;
}
return 1;
}
void mtag_add_chancontext(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature)
{
MessageTag *m;
if (IsUser(client))
{
m = find_mtag(recv_mtags, "+draft/channel-context");
if (m)
{
m = duplicate_mtag(m);
AddListItem(m, *mtag_list);
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Stores channel settings for +P channels in a .db file
* (C) Copyright 2019 Syzop, Gottem and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"
@ -359,6 +359,14 @@ int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel)
return 1;
}
int ban_exists(Ban *lst, Ban *e)
{
for (; lst; lst = lst->next)
if (!mycmp(lst->banstr, e->banstr))
return 1;
return 0;
}
#define R_SAFE(x) \
do { \
if (!(x)) { \
@ -401,10 +409,18 @@ int read_listmode(UnrealDB *db, Ban **lst)
}
safe_strdup(e->banstr, str);
/* Add to list */
e->when = when;
e->next = *lst;
*lst = e;
if (ban_exists(*lst, e))
{
/* Free again - duplicate item */
safe_free(e->banstr);
safe_free(e->who);
safe_free(e);
} else {
/* Add to list */
e->when = when;
e->next = *lst;
*lst = e;
}
}
return 1;

View File

@ -1,6 +1,6 @@
/* src/modules/chathistory.c - IRCv3 CHATHISTORY command.
* (C) Copyright 2021 Bram Matthys (Syzop) and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*
* This implements the "CHATHISTORY" command, the CAP and 005 token.
* https://ircv3.net/specs/extensions/chathistory

View File

@ -231,6 +231,9 @@ void _userhost_changed(Client *client)
if (HasCapabilityFast(client, CAP_CHGHOST))
sendto_one(client, NULL, "%s", buf);
if (MyUser(client))
sendnumeric(client, RPL_HOSTHIDDEN, GetHost(client));
/* A userhost change always generates the following network traffic:
* server to server traffic, CAP "chghost" notifications, and
* possibly PART+JOIN+MODE if force-rejoin had work to do.
@ -338,7 +341,4 @@ CMD_FUNC(cmd_chghost)
safe_strdup(target->user->virthost, parv[2]);
userhost_changed(target);
if (MyUser(target))
sendnumeric(target, RPL_HOSTHIDDEN, parv[2]);
}

View File

@ -106,10 +106,10 @@ CMD_FUNC(cmd_connect)
return;
}
if (!aconf->outgoing.hostname)
if (!aconf->outgoing.hostname && !aconf->outgoing.file)
{
sendnotice(client,
"*** Connect: Server %s is not configured to be an outgoing link (has a link block, but no link::outgoing::hostname)",
"*** Connect: Server %s is not configured to be an outgoing link (has a link block, but no link::outgoing::hostname or link::outgoing::file)",
parv[1]);
return;
}

View File

@ -1,7 +1,7 @@
/*
* connthrottle - Connection throttler
* (C) Copyright 2004-2020 Bram Matthys (Syzop) and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
* See https://www.unrealircd.org/docs/Connthrottle
*/
@ -31,10 +31,8 @@ struct cfgstruct {
/* set::connthrottle::known-users: */
ThrottleSetting local;
ThrottleSetting global;
/* set::connthrottle::new-users: */
int minimum_reputation_score;
int sasl_bypass;
int webirc_bypass;
/* set::connthrottle::except: */
SecurityGroup *except;
/* set::connthrottle::disabled-when: */
long reputation_gathering;
int start_delay;
@ -53,10 +51,8 @@ struct UCounter {
ThrottleCounter local; /**< Local counter */
ThrottleCounter global; /**< Global counter */
int rejected_clients; /**< Number of rejected clients this minute */
int allowed_score; /**< Number of allowed clients of type known-user */
int allowed_sasl; /**< Number of allowed clients of type SASL */
int allowed_webirc; /**< Number of allowed clients of type WEBIRC */
int allowed_other; /**< Number of allowed clients of type other (new) */
int allowed_except; /**< Number of allowed clients - on except list */
int allowed_unknown_users; /**< Number of allowed clients - not on except list */
char disabled; /**< Module disabled by oper? */
int throttling_this_minute; /**< Did we do any throttling this minute? */
int throttling_previous_minute; /**< Did we do any throttling previous minute? */
@ -87,9 +83,10 @@ MOD_TEST()
cfg.global.count = 30; cfg.global.period = 60;
cfg.start_delay = 180; /* 3 minutes */
safe_strdup(cfg.reason, "Throttled: Too many users trying to connect, please wait a while and try again");
cfg.minimum_reputation_score = 24;
cfg.sasl_bypass = 1;
cfg.webirc_bypass = 0;
cfg.except = safe_alloc(sizeof(SecurityGroup));
cfg.except->reputation_score = 24;
cfg.except->identified = 1;
cfg.except->webirc = 0;
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, ct_config_test);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, ct_config_posttest);
@ -120,6 +117,7 @@ MOD_UNLOAD()
{
SavePersistentPointer(modinfo, ucounter);
safe_free(cfg.reason);
free_security_group(cfg.except);
return MOD_SUCCESS;
}
@ -161,6 +159,10 @@ int ct_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "except"))
{
test_match_block(cf, cep, &errors);
} else
if (!strcmp(cep->name, "known-users"))
{
for (cepp = cep->items; cepp; cepp = cepp->next)
@ -286,16 +288,20 @@ int ct_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "except"))
{
conf_match_block(cf, cep, &cfg.except);
} else
if (!strcmp(cep->name, "known-users"))
{
for (cepp = cep->items; cepp; cepp = cepp->next)
{
if (!strcmp(cepp->name, "minimum-reputation-score"))
cfg.minimum_reputation_score = atoi(cepp->value);
cfg.except->reputation_score = atoi(cepp->value);
else if (!strcmp(cepp->name, "sasl-bypass"))
cfg.sasl_bypass = config_checkval(cepp->value, CFG_YESNO);
cfg.except->identified = config_checkval(cepp->value, CFG_YESNO);
else if (!strcmp(cepp->name, "webirc-bypass"))
cfg.webirc_bypass = config_checkval(cepp->value, CFG_YESNO);
cfg.except->webirc = config_checkval(cepp->value, CFG_YESNO);
}
} else
if (!strcmp(cep->name, "new-users"))
@ -360,23 +366,17 @@ EVENT(connthrottle_evt)
unreal_log(ULOG_INFO, "connthrottle", "CONNTHROTLE_REPORT", NULL,
"ConnThrottle] Stats for this server past 60 secs: "
"Connections rejected: $num_rejected. "
"Accepted: $num_accepted_known_users known user(s), "
"$num_accepted_sasl SASL, "
"$num_accepted_webirc WEBIRC and "
"Accepted: $num_accepted_except except user(s) and "
"$num_accepted_unknown_users new user(s).",
log_data_integer("num_rejected", ucounter->rejected_clients),
log_data_integer("num_accepted_known_users", ucounter->allowed_score),
log_data_integer("num_accepted_sasl", ucounter->allowed_sasl),
log_data_integer("num_accepted_webirc", ucounter->allowed_webirc),
log_data_integer("num_accepted_unknown_users", ucounter->allowed_other));
log_data_integer("num_accepted_except", ucounter->allowed_except),
log_data_integer("num_accepted_unknown_users", ucounter->allowed_unknown_users));
}
/* Reset stats for next message */
ucounter->rejected_clients = 0;
ucounter->allowed_score = 0;
ucounter->allowed_sasl = 0;
ucounter->allowed_webirc = 0;
ucounter->allowed_other = 0;
ucounter->allowed_except = 0;
ucounter->allowed_unknown_users = 0;
ucounter->throttling_previous_minute = ucounter->throttling_this_minute;
ucounter->throttling_this_minute = 0; /* reset */
@ -399,24 +399,8 @@ int ct_pre_lconnect(Client *client)
if (still_reputation_gathering())
return HOOK_CONTINUE; /* still gathering reputation data */
if (cfg.sasl_bypass && IsLoggedIn(client))
{
/* Allowed in: user authenticated using SASL */
return HOOK_CONTINUE;
}
if (cfg.webirc_bypass && moddata_client_get(client, "webirc"))
{
/* Allowed in: user using WEBIRC */
return HOOK_CONTINUE;
}
score = GetReputation(client);
if (score >= cfg.minimum_reputation_score)
{
/* Allowed in: IP has enough reputation ("known user") */
return HOOK_CONTINUE;
}
if (user_allowed_by_security_group(client, cfg.except))
return HOOK_CONTINUE; /* allowed: user is exempt (known user or otherwise) */
/* If we reach this then the user is NEW */
@ -486,30 +470,14 @@ int ct_lconnect(Client *client)
if (still_reputation_gathering())
return 0; /* still gathering reputation data */
if (cfg.sasl_bypass && IsLoggedIn(client))
if (user_allowed_by_security_group(client, cfg.except))
{
/* Allowed in: user authenticated using SASL */
ucounter->allowed_sasl++;
return 0;
}
if (cfg.webirc_bypass && moddata_client_get(client, "webirc"))
{
/* Allowed in: user using WEBIRC */
ucounter->allowed_webirc++;
return 0;
}
score = GetReputation(client);
if (score >= cfg.minimum_reputation_score)
{
/* Allowed in: IP has enough reputation ("known user") */
ucounter->allowed_score++;
return 0;
ucounter->allowed_except++;
return HOOK_CONTINUE; /* allowed: user is exempt (known user or otherwise) */
}
/* Allowed NEW user */
ucounter->allowed_other++;
ucounter->allowed_unknown_users++;
bump_connect_counter(1);
@ -539,9 +507,8 @@ int ct_rconnect(Client *client)
}
#endif
score = GetReputation(client);
if (score >= cfg.minimum_reputation_score)
return 0; /* sufficient reputation: "known-user" */
if (user_allowed_by_security_group(client, cfg.except))
return 0; /* user is on except list (known user or otherwise) */
bump_connect_counter(0);

View File

@ -0,0 +1,99 @@
/*
* Store creationtime in ModData
* (C) Copyright 2022-.. Syzop and The UnrealIRCd Team
* License: GPLv2 or later
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"creationtime",
"6.0",
"Store and retrieve creation time of clients",
"UnrealIRCd Team",
"unrealircd-6",
};
/* Forward declarations */
void creationtime_free(ModData *m);
const char *creationtime_serialize(ModData *m);
void creationtime_unserialize(const char *str, ModData *m);
int creationtime_handshake(Client *client);
int creationtime_welcome_user(Client *client, int numeric);
int creationtime_whois(Client *client, Client *target);
ModDataInfo *creationtime_md; /* Module Data structure which we acquire */
#define SetCreationTime(x,y) do { moddata_client(x, creationtime_md).ll = y; } while(0)
MOD_INIT()
{
ModDataInfo mreq;
MARK_AS_OFFICIAL_MODULE(modinfo);
memset(&mreq, 0, sizeof(mreq));
mreq.name = "creationtime";
mreq.free = creationtime_free;
mreq.serialize = creationtime_serialize;
mreq.unserialize = creationtime_unserialize;
mreq.sync = MODDATA_SYNC_EARLY;
mreq.type = MODDATATYPE_CLIENT;
creationtime_md = ModDataAdd(modinfo->handle, mreq);
if (!creationtime_md)
abort();
/* This event sets creationtime very early: on handshake in and out */
HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, creationtime_handshake);
HookAdd(modinfo->handle, HOOKTYPE_SERVER_HANDSHAKE_OUT, 0, creationtime_handshake);
/* And this event (re)sets it because that also happens in
* welcome_user() in nick.c regarding #2174
*/
HookAdd(modinfo->handle, HOOKTYPE_WELCOME, 0, creationtime_welcome_user);
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
int creationtime_handshake(Client *client)
{
SetCreationTime(client, client->local->creationtime);
return 0;
}
int creationtime_welcome_user(Client *client, int numeric)
{
if (numeric == 0)
SetCreationTime(client, client->local->creationtime);
return 0;
}
void creationtime_free(ModData *m)
{
m->ll = 0;
}
const char *creationtime_serialize(ModData *m)
{
static char buf[64];
snprintf(buf, sizeof(buf), "%lld", (long long)m->ll);
return buf;
}
void creationtime_unserialize(const char *str, ModData *m)
{
m->ll = atoll(str);
}

View File

@ -31,11 +31,10 @@ ModuleHeader MOD_HEADER
const char *extban_account_conv_param(BanContext *b, Extban *extban);
int extban_account_is_banned(BanContext *b);
/** Called upon module init */
MOD_INIT()
Extban *register_account_extban(ModuleInfo *modinfo)
{
ExtbanInfo req;
memset(&req, 0, sizeof(req));
req.letter = 'a';
req.name = "account";
@ -44,14 +43,30 @@ MOD_INIT()
req.is_banned = extban_account_is_banned;
req.is_banned_events = BANCHK_ALL|BANCHK_TKL;
req.options = EXTBOPT_INVEX|EXTBOPT_TKL;
if (!ExtbanAdd(modinfo->handle, req))
return ExtbanAdd(modinfo->handle, req);
}
/** Called upon module test */
MOD_TEST()
{
if (!register_account_extban(modinfo))
{
config_error("could not register extended ban type");
return MOD_FAILED;
}
return MOD_SUCCESS;
}
/** Called upon module init */
MOD_INIT()
{
if (!register_account_extban(modinfo))
{
config_error("could not register extended ban type");
return MOD_FAILED;
}
MARK_AS_OFFICIAL_MODULE(modinfo);
return MOD_SUCCESS;
}
@ -95,7 +110,7 @@ int extban_account_is_banned(BanContext *b)
if (!strcmp(b->banstr, "*") && IsLoggedIn(b->client))
return 1;
if (match_simple(b->banstr, b->client->user->account))
if (b->client->user && match_simple(b->banstr, b->client->user->account))
return 1;
return 0;

View File

@ -32,11 +32,10 @@ int extban_certfp_is_ok(BanContext *b);
const char *extban_certfp_conv_param(BanContext *b, Extban *extban);
int extban_certfp_is_banned(BanContext *b);
/* Called upon module init */
MOD_INIT()
Extban *register_certfp_extban(ModuleInfo *modinfo)
{
ExtbanInfo req;
memset(&req, 0, sizeof(req));
req.letter = 'S';
req.name = "certfp";
@ -45,14 +44,32 @@ MOD_INIT()
req.is_banned = extban_certfp_is_banned;
req.is_banned_events = BANCHK_ALL|BANCHK_TKL;
req.options = EXTBOPT_INVEX|EXTBOPT_TKL;
if (!ExtbanAdd(modinfo->handle, req))
return ExtbanAdd(modinfo->handle, req);
}
/* Called upon module test */
MOD_TEST()
{
if (!register_certfp_extban(modinfo))
{
config_error("could not register extended ban type");
return MOD_FAILED;
}
return MOD_SUCCESS;
}
/* Called upon module init */
MOD_INIT()
{
if (!register_certfp_extban(modinfo))
{
config_error("could not register extended ban type");
return MOD_FAILED;
}
MARK_AS_OFFICIAL_MODULE(modinfo);
return MOD_SUCCESS;
}
@ -82,10 +99,10 @@ int extban_certfp_is_ok(BanContext *b)
if (b->is_ok_check == EXCHK_PARAM)
{
const char *p;
if (strlen(b->banstr) != CERT_FP_LEN)
return extban_certfp_usage(b->client);
for (p = b->banstr; *p; p++)
if (!isxdigit(*p))
return extban_certfp_usage(b->client);
@ -100,9 +117,9 @@ const char *extban_certfp_conv_param(BanContext *b, Extban *extban)
{
static char retbuf[EVP_MAX_MD_SIZE * 2 + 1];
char *p;
strlcpy(retbuf, b->banstr, sizeof(retbuf));
for (p = retbuf; *p; p++)
{
*p = tolower(*p);

View File

@ -32,8 +32,7 @@ int extban_country_is_ok(BanContext *b);
const char *extban_country_conv_param(BanContext *b, Extban *extban);
int extban_country_is_banned(BanContext *b);
/* Called upon module init */
MOD_INIT()
Extban *register_country_extban(ModuleInfo *modinfo)
{
ExtbanInfo req;
@ -45,7 +44,25 @@ MOD_INIT()
req.is_banned = extban_country_is_banned;
req.is_banned_events = BANCHK_ALL|BANCHK_TKL;
req.options = EXTBOPT_INVEX|EXTBOPT_TKL;
if (!ExtbanAdd(modinfo->handle, req))
return ExtbanAdd(modinfo->handle, req);
}
/* Called upon module test */
MOD_TEST()
{
if (!register_country_extban(modinfo))
{
config_error("could not register extended ban type");
return MOD_FAILED;
}
return MOD_SUCCESS;
}
/* Called upon module init */
MOD_INIT()
{
if (!register_country_extban(modinfo))
{
config_error("could not register extended ban type");
return MOD_FAILED;

View File

@ -31,11 +31,10 @@ ModuleHeader MOD_HEADER
const char *extban_realname_conv_param(BanContext *b, Extban *extban);
int extban_realname_is_banned(BanContext *b);
/** Called upon module init */
MOD_INIT()
Extban *register_realname_extban(ModuleInfo *modinfo)
{
ExtbanInfo req;
memset(&req, 0, sizeof(req));
req.letter = 'r';
req.name = "realname";
@ -44,14 +43,30 @@ MOD_INIT()
req.is_banned = extban_realname_is_banned;
req.is_banned_events = BANCHK_ALL|BANCHK_TKL;
req.options = EXTBOPT_INVEX|EXTBOPT_TKL;
if (!ExtbanAdd(modinfo->handle, req))
return ExtbanAdd(modinfo->handle, req);
}
/** Called upon module test */
MOD_TEST()
{
if (!register_realname_extban(modinfo))
{
config_error("could not register extended ban type");
return MOD_FAILED;
}
return MOD_SUCCESS;
}
/** Called upon module init */
MOD_INIT()
{
if (!register_realname_extban(modinfo))
{
config_error("could not register extended ban type");
return MOD_FAILED;
}
MARK_AS_OFFICIAL_MODULE(modinfo);
return MOD_SUCCESS;
}

View File

@ -32,8 +32,7 @@ const char *extban_securitygroup_conv_param(BanContext *b, Extban *extban);
int extban_securitygroup_is_ok(BanContext *b);
int extban_securitygroup_is_banned(BanContext *b);
/** Called upon module init */
MOD_INIT()
Extban *register_securitygroup_extban(ModuleInfo *modinfo)
{
ExtbanInfo req;
@ -45,7 +44,25 @@ MOD_INIT()
req.is_banned = extban_securitygroup_is_banned;
req.is_banned_events = BANCHK_ALL|BANCHK_TKL;
req.options = EXTBOPT_INVEX|EXTBOPT_TKL;
if (!ExtbanAdd(modinfo->handle, req))
return ExtbanAdd(modinfo->handle, req);
}
/** Called upon module test */
MOD_TEST()
{
if (!register_securitygroup_extban(modinfo))
{
config_error("could not register extended ban type ~G");
return MOD_FAILED;
}
return MOD_SUCCESS;
}
/** Called upon module init */
MOD_INIT()
{
if (!register_securitygroup_extban(modinfo))
{
config_error("could not register extended ban type ~G");
return MOD_FAILED;

View File

@ -1,7 +1,7 @@
/*
* timedban - Timed bans that are automatically unset.
* (C) Copyright 2009-2017 Bram Matthys (Syzop) and the UnrealIRCd team.
* License: GPLv2
* License: GPLv2 or later
*
* This module adds an extended ban ~t:time:mask
* Where 'time' is the time in minutes after which the ban will be removed.

View File

@ -49,7 +49,7 @@ MOD_INIT()
ModDataInfo mreq;
memset(&cap, 0, sizeof(cap));
cap.name = "draft/extended-monitor";
cap.name = "extended-monitor";
c = ClientCapabilityAdd(modinfo->handle, &cap, &CAP_EXTENDED_MONITOR);
if (!c)
{

108
src/modules/geoip-tag.c Normal file
View File

@ -0,0 +1,108 @@
/*
* IRC - Internet Relay Chat, src/modules/geoip-tag.c
* (C) 2022 westor, Syzop and The UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"geoip-tag",
"6.0",
"geoip message tag",
"UnrealIRCd Team",
"unrealircd-6",
};
/* Forward declarations */
int geoip_mtag_is_ok(Client *client, const char *name, const char *value);
int geoip_mtag_should_send_to_client(Client *target);
void mtag_add_geoip(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature);
MOD_INIT()
{
MessageTagHandlerInfo mtag;
MARK_AS_OFFICIAL_MODULE(modinfo);
memset(&mtag, 0, sizeof(mtag));
mtag.name = "unrealircd.org/geoip";
mtag.is_ok = geoip_mtag_is_ok;
mtag.should_send_to_client = geoip_mtag_should_send_to_client;
mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED;
MessageTagHandlerAdd(modinfo->handle, &mtag);
HookAddVoid(modinfo->handle, HOOKTYPE_NEW_MESSAGE, 0, mtag_add_geoip);
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
/** This function verifies if the client sending
* 'geoip-tag' is permitted to do so and uses a permitted
* syntax.
* We simply allow geoip-tag ONLY from servers and with any syntax.
*/
int geoip_mtag_is_ok(Client *client, const char *name, const char *value)
{
if (IsServer(client))
return 1;
return 0;
}
void mtag_add_geoip(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature)
{
MessageTag *m;
GeoIPResult *geoip;
if (IsUser(client) && ((geoip = geoip_client(client))))
{
MessageTag *m = find_mtag(recv_mtags, "unrealircd.org/geoip");
if (m)
{
m = duplicate_mtag(m);
} else {
m = safe_alloc(sizeof(MessageTag));
safe_strdup(m->name, "unrealircd.org/geoip");
safe_strdup(m->value, geoip->country_code);
}
AddListItem(m, *mtag_list);
}
}
/** Outgoing filter for this message tag */
int geoip_mtag_should_send_to_client(Client *target)
{
if (IsServer(target) || IsOper(target))
return 1;
return 0;
}

View File

@ -2,7 +2,7 @@
* GEOIP Base module, needed for all geoip functions
* as this stores the geo information in ModData.
* (C) Copyright 2021-.. Syzop and The UnrealIRCd Team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"
@ -28,6 +28,7 @@ int geoip_base_handshake(Client *client);
int geoip_base_ip_change(Client *client, const char *oldip);
int geoip_base_whois(Client *client, Client *target, NameValuePrioList **list);
int geoip_connect_extinfo(Client *client, NameValuePrioList **list);
int geoip_log(Client *client, int detail, json_t *j);
int geoip_base_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
int geoip_base_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
EVENT(geoip_base_set_existing_users_evt);
@ -125,6 +126,7 @@ MOD_INIT()
HookAdd(modinfo->handle, HOOKTYPE_CONNECT_EXTINFO, 1, geoip_connect_extinfo); /* (prio: near-first) */
HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0,geoip_base_handshake); /* in case the IP changed in registration phase (WEBIRC, HTTP Forwarded) */
HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, geoip_base_whois);
HookAdd(modinfo->handle, HOOKTYPE_JSON_EXPAND_CLIENT, 0, geoip_log);
CommandAdd(modinfo->handle, "GEOIP", cmd_geoip, MAXPARA, CMD_USER);
@ -206,7 +208,7 @@ void geoip_base_unserialize(const char *str, ModData *m)
char *country_code = NULL;
GeoIPResult *res;
if (m->ptr == NULL)
if (m->ptr)
{
free_geoip_result((GeoIPResult *)m->ptr);
m->ptr = NULL;
@ -254,6 +256,21 @@ int geoip_connect_extinfo(Client *client, NameValuePrioList **list)
return 0;
}
int geoip_log(Client *client, int detail, json_t *j)
{
GeoIPResult *geo = GEOIPDATA(client);
json_t *geoip;
if (!geo)
return 0;
geoip = json_object();
json_object_set_new(j, "geoip", geoip);
json_object_set_new(geoip, "country_code", json_string_unreal(geo->country_code));
return 0;
}
int geoip_base_whois(Client *client, Client *target, NameValuePrioList **list)
{
GeoIPResult *geo;

View File

@ -1,6 +1,6 @@
/* GEOIP Classic module
* (C) Copyright 2021 Bram Matthys and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -1,6 +1,6 @@
/* GEOIP maxmind module
* (C) Copyright 2021 Bram Matthys and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -459,7 +459,7 @@ CMD_OVERRIDE_FUNC(override_links)
sendnumeric(client, RPL_LINKS, acptr->name, me.name,
1, (acptr->info[0] ? acptr->info : "(Unknown Location)"));
else
sendnumeric(client, RPL_LINKS, acptr->name, acptr->uplink->name,
sendnumeric(client, RPL_LINKS, acptr->name, acptr->uplink ? acptr->uplink->name : me.name,
acptr->hopcount, (acptr->info[0] ? acptr->info : "(Unknown Location)"));
}

View File

@ -1,6 +1,6 @@
/* src/modules/history_backend_mem.c - History Backend: memory
* (C) Copyright 2019-2021 Bram Matthys (Syzop) and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -1,6 +1,6 @@
/* src/modules/history_backend_null.c - History Backend: null / none
* (C) Copyright 2019 Bram Matthys (Syzop) and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -1,6 +1,6 @@
/* src/modules/ident_lookup.c - Ident lookups (RFC1413)
* (C) Copyright 2019 Bram Matthys (Syzop) and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -62,10 +62,9 @@ MOD_UNLOAD()
* ISON :nicklist
*/
static char buf[BUFSIZE];
CMD_FUNC(cmd_ison)
{
char buf[BUFSIZE];
char request[BUFSIZE];
char namebuf[USERLEN + HOSTLEN + 4];
Client *acptr;

View File

@ -94,37 +94,32 @@ MOD_UNLOAD()
*/
int _can_join(Client *client, Channel *channel, const char *key, char **errmsg)
{
Link *lp;
Ban *banned;
Hook *h;
int i=0, j=0;
/* An /INVITE lets you bypass all restrictions */
if (is_invited(client, channel))
{
int j = 0;
for (h = Hooks[HOOKTYPE_INVITE_BYPASS]; h; h = h->next)
{
j = (*(h->func.intfunc))(client,channel);
if (j != 0)
break;
}
/* Bypass is OK, unless a HOOKTYPE_INVITE_BYPASS hook returns HOOK_DENY */
if (j != HOOK_DENY)
return 0;
}
for (h = Hooks[HOOKTYPE_CAN_JOIN]; h; h = h->next)
{
i = (*(h->func.intfunc))(client,channel,key, errmsg);
int i = (*(h->func.intfunc))(client,channel,key, errmsg);
if (i != 0)
return i;
}
for (h = Hooks[HOOKTYPE_OPER_INVITE_BAN]; h; h = h->next)
{
j = (*(h->func.intfunc))(client,channel);
if (j != 0)
break;
}
/* See if we can evade this ban */
banned = is_banned(client, channel, BANCHK_JOIN, NULL, NULL);
if (banned && j == HOOK_DENY)
{
*errmsg = STR_ERR_BANNEDFROMCHAN;
return ERR_BANNEDFROMCHAN;
}
if (is_invited(client, channel))
return 0; /* allowed to walk through all the other modes */
if (banned)
if (is_banned(client, channel, BANCHK_JOIN, NULL, NULL))
{
*errmsg = STR_ERR_BANNEDFROMCHAN;
return ERR_BANNEDFROMCHAN;
@ -262,6 +257,7 @@ void _join_channel(Channel *channel, Client *client, MessageTag *recv_mtags, con
MessageTag *mtags_mode = NULL;
Cmode *cm;
char modebuf[BUFSIZE], parabuf[BUFSIZE];
int should_destroy = 0;
channel->mode.mode = MODES_ON_JOIN;
@ -281,6 +277,7 @@ void _join_channel(Channel *channel, Client *client, MessageTag *recv_mtags, con
sendto_server(NULL, 0, 0, mtags_mode, ":%s MODE %s %s %s %lld",
me.id, channel->name, modebuf, parabuf, (long long)channel->creationtime);
sendto_one(client, mtags_mode, ":%s MODE %s %s %s", me.name, channel->name, modebuf, parabuf);
RunHook(HOOKTYPE_LOCAL_CHANMODE, &me, channel, mtags_mode, modebuf, parabuf, 0, 0, &should_destroy);
free_message_tags(mtags_mode);
}

View File

@ -21,7 +21,6 @@
#include "unrealircd.h"
CMD_FUNC(cmd_kill);
static char buf[BUFSIZE], buf2[BUFSIZE];
ModuleHeader MOD_HEADER
= {
@ -58,6 +57,7 @@ CMD_FUNC(cmd_kill)
{
char targetlist[BUFSIZE];
char reason[BUFSIZE];
char buf2[BUFSIZE];
char *str;
char *nick, *save = NULL;
Client *target;

View File

@ -197,11 +197,15 @@ int lr_post_command(Client *from, MessageTag *mtags, const char *buf)
int more_tags = currentcmd.firstbuf[0] == '@';
currentcmd.client = NULL; /* prevent lr_packet from interfering */
snprintf(packet, sizeof(packet)-3,
"@label=%s%s%s\r\n",
"@label=%s%s%s",
currentcmd.label,
more_tags ? ";" : " ",
more_tags ? currentcmd.firstbuf+1 : currentcmd.firstbuf);
sendbufto_one(from, packet, 0);
/* Format the IRC message correctly here, so we can take the
* quick path through sendbufto_one().
*/
strlcat(packet, "\r\n", sizeof(packet));
sendbufto_one(from, packet, strlen(packet));
goto done;
}

View File

@ -22,7 +22,6 @@
/* Forward declarations */
const char *_StripColors(const char *text);
const char *_StripControlCodes(const char *text);
int ban_version(Client *client, const char *text);
CMD_FUNC(cmd_private);
CMD_FUNC(cmd_notice);
@ -47,7 +46,6 @@ MOD_TEST()
{
MARK_AS_OFFICIAL_MODULE(modinfo);
EfunctionAddConstString(modinfo->handle, EFUNC_STRIPCOLORS, _StripColors);
EfunctionAddConstString(modinfo->handle, EFUNC_STRIPCONTROLCODES, _StripControlCodes);
EfunctionAdd(modinfo->handle, EFUNC_CAN_SEND_TO_CHANNEL, _can_send_to_channel);
return MOD_SUCCESS;
}
@ -526,8 +524,10 @@ CMD_FUNC(cmd_tagmsg)
/* Taken from xchat by Peter Zelezny
* changed very slightly by codemastr
* RGB color stripping support added -- codemastr
*
* NOTE: if you change/update/enhance StripColors() then consider changing
* the StripControlCodes() function as well (in misc.c) !!
*/
const char *_StripColors(const char *text)
{
int i = 0, len = strlen(text), save_len=0;
@ -595,103 +595,6 @@ const char *_StripColors(const char *text)
return new_str;
}
/* strip color, bold, underline, and reverse codes from a string */
const char *_StripControlCodes(const char *text)
{
int i = 0, len = strlen(text), save_len=0;
char nc = 0, col = 0, rgb = 0;
const char *save_text=NULL;
static unsigned char new_str[4096];
while (len > 0)
{
if ( col && ((isdigit(*text) && nc < 2) || (*text == ',' && nc < 3)))
{
nc++;
if (*text == ',')
nc = 0;
}
/* Syntax for RGB is ^DHHHHHH where H is a hex digit.
* If < 6 hex digits are specified, the code is displayed
* as text
*/
else if ((rgb && isxdigit(*text) && nc < 6) || (rgb && *text == ',' && nc < 7))
{
nc++;
if (*text == ',')
nc = 0;
}
else
{
if (col)
col = 0;
if (rgb)
{
if (nc != 6)
{
text = save_text+1;
len = save_len-1;
rgb = 0;
continue;
}
rgb = 0;
}
switch (*text)
{
case 3:
/* color */
col = 1;
nc = 0;
break;
case 4:
/* RGB */
save_text = text;
save_len = len;
rgb = 1;
nc = 0;
break;
case 2:
/* bold */
break;
case 31:
/* underline */
break;
case 22:
/* reverse */
break;
case 15:
/* plain */
break;
case 29:
/* italic */
break;
case 30:
/* strikethrough */
break;
case 17:
/* monospace */
break;
case 0xe2:
if (!strncmp(text+1, "\x80\x8b", 2))
{
/* +2 means we skip 3 */
text += 2;
len -= 2;
break;
}
/*fallthrough*/
default:
new_str[i] = *text;
i++;
break;
}
}
text++;
len--;
}
new_str[i] = 0;
return new_str;
}
/** Check ban version { } blocks, returns 1 if banned and 0 if not. */
int ban_version(Client *client, const char *text)
{

View File

@ -472,8 +472,8 @@ MultiLineMode *make_mode_str(Client *client, Channel *channel, Cmode_t oldem, in
if (curr == MAXMULTILINEMODES)
{
/* Should be impossible.. */
unreal_log(ULOG_ERROR, "mode", "MODE_MULTINE_EXCEEDED", client,
"A mode string caused an avalanche effect of more than $max_multiline modes "
unreal_log(ULOG_ERROR, "mode", "MODE_MULTILINE_EXCEEDED", client,
"A mode string caused an avalanche effect of more than $max_multiline_modes modes "
"in channel $channel. Caused by client $client. Expect a desync.",
log_data_integer("max_multiline_modes", MAXMULTILINEMODES),
log_data_channel("channel", channel));
@ -807,6 +807,7 @@ void do_mode_char_member_mode_new(Channel *channel, Cmode *handler, const char *
"[BUG] Client $target.details on channel $channel: "
"found via find_membership_link() but NOT found via find_member_link(). "
"This should never happen! Please report on https://bugs.unrealircd.org/",
log_data_client("target", target),
log_data_channel("channel", channel));
return;
}
@ -816,6 +817,58 @@ void do_mode_char_member_mode_new(Channel *channel, Cmode *handler, const char *
if ((what == MODE_DEL) && !strchr(member->member_modes, modechar))
return; /* already unset */
/* HOOKTYPE_MODE_DEOP code */
if (what == MODE_DEL)
{
int ret = EX_ALLOW;
const char *badmode = NULL;
Hook *h;
const char *my_access;
Membership *my_membership;
/* Set "my_access" to access flags of the requestor */
if (IsUser(client) && (my_membership = find_membership_link(client->user->channel, channel)))
my_access = my_membership->member_modes; /* client */
else
my_access = ""; /* server */
for (h = Hooks[HOOKTYPE_MODE_DEOP]; h; h = h->next)
{
int n = (*(h->func.intfunc))(client, target, channel, what, modechar, my_access, member->member_modes, &badmode);
if (n == EX_DENY)
{
ret = n;
} else
if (n == EX_ALWAYS_DENY)
{
ret = n;
break;
}
}
if (ret == EX_ALWAYS_DENY)
{
if (MyUser(client) && badmode)
sendto_one(client, NULL, "%s", badmode); /* send error message, if any */
if (MyUser(client))
return; /* stop processing this mode */
}
/* This probably should work but is completely untested (the operoverride stuff, I mean): */
if (ret == EX_DENY)
{
if (!op_can_override("channel:override:mode:del",client,channel,handler))
{
if (badmode)
sendto_one(client, NULL, "%s", badmode); /* send error message, if any */
return; /* stop processing this mode */
} else {
opermode = 1;
}
}
}
if (what == MODE_ADD)
{
if (strchr(member->member_modes, modechar))
@ -1306,8 +1359,6 @@ CMD_FUNC(_cmd_umode)
/* Notify */
userhost_changed(client);
if (MyUser(client))
sendnumeric(client, RPL_HOSTHIDDEN, client->user->virthost);
}
/* -x */
@ -1321,8 +1372,6 @@ CMD_FUNC(_cmd_umode)
/* Notify */
userhost_changed(client);
if (MyUser(client))
sendnumeric(client, RPL_HOSTHIDDEN, client->user->realhost);
}
/*
* If I understand what this code is doing correctly...
@ -1337,7 +1386,7 @@ CMD_FUNC(_cmd_umode)
{
list_del(&client->special_node);
if (MyUser(client))
RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL);
RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL, NULL);
remove_oper_privileges(client, 0);
}

View File

@ -68,8 +68,6 @@ MOD_UNLOAD()
* 12 Feb 2000 - geesh, time for a rewrite -lucas
************************************************************************/
static char buf[BUFSIZE];
/*
** cmd_names
** parv[1] = channel
@ -88,6 +86,7 @@ CMD_FUNC(cmd_names)
int idx, flag = 1, spos;
const char *para = parv[1], *s;
char nuhBuffer[NICKLEN+USERLEN+HOSTLEN+3];
char buf[BUFSIZE];
if (parc < 2 || !MyConnect(client))
{

View File

@ -46,7 +46,6 @@ ModuleHeader MOD_HEADER
#define ASSUME_NICK_IN_FLIGHT
/* Variables */
static char buf[BUFSIZE];
static char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64];
/* Forward declarations */
@ -489,6 +488,7 @@ CMD_FUNC(cmd_uid)
Client *acptr, *serv = NULL;
Client *acptrs;
char nick[NICKLEN + 1];
char buf[BUFSIZE];
long lastnick = 0;
int differ = 1;
const char *hostname, *username, *sstamp, *umodes, *virthost, *ip_raw, *realname;
@ -758,10 +758,21 @@ CMD_FUNC(cmd_nick)
}
}
/** Welcome the user on IRC.
* Send the RPL_WELCOME, LUSERS, MOTD, auto join channels, everything...
*/
void welcome_user(Client *client, TKL *viruschan_tkl)
{
int i;
ConfigItem_tld *tlds;
char buf[BUFSIZE];
/* Make creation time the real 'online since' time, excluding registration time.
* Otherwise things like set::anti-spam-quit-messagetime 10s could mean
* 1 second in practice (#2174).
*/
client->local->creationtime = TStime();
client->local->idle_since = TStime();
RunHook(HOOKTYPE_WELCOME, client, 0);
sendnumeric(client, RPL_WELCOME, NETWORK_NAME, client->name, client->user->username, client->user->realhost);
@ -826,10 +837,11 @@ void welcome_user(Client *client, TKL *viruschan_tkl)
sendto_serv_butone_nickcmd(client->direction, NULL, client, (*buf == '\0' ? "+" : buf));
broadcast_moddata_client(client);
RunHook(HOOKTYPE_LOCAL_CONNECT, client);
if (buf[0] != '\0' && buf[1] != '\0')
sendto_one(client, NULL, ":%s MODE %s :%s", client->name,
client->name, buf);
if (client->user->snomask)
sendnumeric(client, RPL_SNOMASK, client->user->snomask);
@ -839,12 +851,7 @@ void welcome_user(Client *client, TKL *viruschan_tkl)
if (IsSecure(client) && (iConf.outdated_tls_policy_user == POLICY_WARN) && outdated_tls_client(client))
sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_user_message, client));
/* Make creation time the real 'online since' time, excluding registration time.
* Otherwise things like set::anti-spam-quit-messagetime 10s could mean
* 1 second in practice (#2174).
*/
client->local->creationtime = TStime();
client->local->idle_since = TStime();
RunHook(HOOKTYPE_LOCAL_CONNECT, client);
/* Give the user a fresh start as far as fake-lag is concerned.
* Otherwise the user could be lagged up already due to all the CAP stuff.
@ -1261,7 +1268,7 @@ int AllowClient(Client *client)
if (aconf->flags.tls && !IsSecure(client))
continue;
if (!unreal_mask_match(client, aconf->mask))
if (!user_allowed_by_security_group(client, aconf->match))
continue;
/* Check authentication */

View File

@ -20,10 +20,6 @@
#include "unrealircd.h"
CMD_FUNC(cmd_oper);
/* Place includes here */
#define MSG_OPER "OPER" /* OPER */
ModuleHeader MOD_HEADER
@ -35,27 +31,37 @@ ModuleHeader MOD_HEADER
"unrealircd-6",
};
/* This is called on module init, before Server Ready */
MOD_INIT()
/* Forward declarations */
CMD_FUNC(cmd_oper);
int _make_oper(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost);
int oper_connect(Client *client);
MOD_TEST()
{
CommandAdd(modinfo->handle, MSG_OPER, cmd_oper, MAXPARA, CMD_USER);
MARK_AS_OFFICIAL_MODULE(modinfo);
EfunctionAdd(modinfo->handle, EFUNC_MAKE_OPER, _make_oper);
return MOD_SUCCESS;
}
MOD_INIT()
{
MARK_AS_OFFICIAL_MODULE(modinfo);
CommandAdd(modinfo->handle, MSG_OPER, cmd_oper, MAXPARA, CMD_USER);
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, oper_connect);
return MOD_SUCCESS;
}
/* Is first run when server is 100% ready */
MOD_LOAD()
{
return MOD_SUCCESS;
}
/* Called when module is unloaded */
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
void set_oper_host(Client *client, char *host)
void set_oper_host(Client *client, const char *host)
{
char uhost[HOSTLEN + USERLEN + 1];
char *p;
@ -70,8 +76,96 @@ void set_oper_host(Client *client, char *host)
client->id, client->user->username);
host = p;
}
iNAH_host(client, host);
SetHidden(client);
safe_strdup(client->user->virthost, host);
if (MyConnect(client))
sendto_server(NULL, 0, 0, NULL, ":%s SETHOST :%s", client->id, client->user->virthost);
client->umodes |= UMODE_SETHOST|UMODE_HIDE;
}
int _make_oper(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost)
{
long old_umodes = client->umodes & ALL_UMODES;
userhost_save_current(client);
/* Put in the right class (if any) */
if (clientclass)
{
if (client->local->class)
client->local->class->clients--;
client->local->class = clientclass;
client->local->class->clients++;
}
/* set oper user modes */
client->umodes |= UMODE_OPER;
if (modes)
client->umodes |= modes; /* oper::modes */
else
client->umodes |= OPER_MODES; /* set::modes-on-oper */
/* oper::vhost */
if (vhost)
{
set_oper_host(client, vhost);
} else
if (IsHidden(client) && !client->user->virthost)
{
/* +x has just been set by modes-on-oper and no vhost. cloak the oper! */
safe_strdup(client->user->virthost, client->user->cloakedhost);
}
userhost_changed(client);
unreal_log(ULOG_INFO, "oper", "OPER_SUCCESS", client,
"$client.details is now an IRC Operator [oper-block: $oper_block] [operclass: $operclass]",
log_data_string("oper_block", operblock_name),
log_data_string("operclass", operclass));
/* set oper snomasks */
if (snomask)
set_snomask(client, snomask); /* oper::snomask */
else
set_snomask(client, OPER_SNOMASK); /* set::snomask-on-oper */
send_umode_out(client, 1, old_umodes);
if (client->user->snomask)
sendnumeric(client, RPL_SNOMASK, client->user->snomask);
list_add(&client->special_node, &oper_list);
RunHook(HOOKTYPE_LOCAL_OPER, client, 1, operblock_name, operclass);
sendnumeric(client, RPL_YOUREOPER);
/* Update statistics */
if (IsInvisible(client) && !(old_umodes & UMODE_INVISIBLE))
irccounts.invisible++;
if (IsOper(client) && !IsHideOper(client))
irccounts.operators++;
if (SHOWOPERMOTD == 1)
{
const char *args[1] = { NULL };
do_cmd(client, NULL, "OPERMOTD", 1, args);
}
if (!BadPtr(OPER_AUTO_JOIN_CHANS) && strcmp(OPER_AUTO_JOIN_CHANS, "0"))
{
char *chans = strdup(OPER_AUTO_JOIN_CHANS);
const char *args[3] = {
client->name,
chans,
NULL
};
do_cmd(client, NULL, "JOIN", 3, args);
safe_free(chans);
/* Theoretically the oper may be killed on join. Would be fun, though */
if (IsDead(client))
return 0;
}
return 1;
}
/*
@ -83,7 +177,6 @@ CMD_FUNC(cmd_oper)
{
ConfigItem_oper *operblock;
const char *operblock_name, *password;
long old_umodes = client->umodes & ALL_UMODES;
if (!MyUser(client))
return;
@ -152,7 +245,7 @@ CMD_FUNC(cmd_oper)
* more seriously, they are logged as errors instead of warnings.
*/
if (!unreal_mask_match(client, operblock->mask))
if (!user_allowed_by_security_group(client, operblock->match))
{
sendnumeric(client, ERR_NOOPERHOST);
unreal_log(ULOG_ERROR, "oper", "OPER_FAILED", client,
@ -164,7 +257,7 @@ CMD_FUNC(cmd_oper)
return;
}
if (!Auth_Check(client, operblock->auth, password))
if (operblock->auth && !Auth_Check(client, operblock->auth, password))
{
sendnumeric(client, ERR_PASSWDMISMATCH);
if (FAILOPER_WARN)
@ -227,12 +320,6 @@ CMD_FUNC(cmd_oper)
/* Store which oper block was used to become IRCOp (for maxlogins and whois) */
safe_strdup(client->user->operlogin, operblock->name);
/* Put in the right class */
if (client->local->class)
client->local->class->clients--;
client->local->class = operblock->class;
client->local->class->clients++;
/* oper::swhois */
if (operblock->swhois)
{
@ -241,67 +328,7 @@ CMD_FUNC(cmd_oper)
swhois_add(client, "oper", -100, s->line, &me, NULL);
}
/* set oper user modes */
client->umodes |= UMODE_OPER;
if (operblock->modes)
client->umodes |= operblock->modes; /* oper::modes */
else
client->umodes |= OPER_MODES; /* set::modes-on-oper */
/* oper::vhost */
if (operblock->vhost)
{
set_oper_host(client, operblock->vhost);
} else
if (IsHidden(client) && !client->user->virthost)
{
/* +x has just been set by modes-on-oper and no vhost. cloak the oper! */
safe_strdup(client->user->virthost, client->user->cloakedhost);
}
unreal_log(ULOG_INFO, "oper", "OPER_SUCCESS", client,
"$client.details is now an IRC Operator [oper-block: $oper_block]",
log_data_string("oper_block", parv[1]));
/* set oper snomasks */
if (operblock->snomask)
set_snomask(client, operblock->snomask); /* oper::snomask */
else
set_snomask(client, OPER_SNOMASK); /* set::snomask-on-oper */
send_umode_out(client, 1, old_umodes);
if (client->user->snomask)
sendnumeric(client, RPL_SNOMASK, client->user->snomask);
list_add(&client->special_node, &oper_list);
RunHook(HOOKTYPE_LOCAL_OPER, client, 1, operblock);
sendnumeric(client, RPL_YOUREOPER);
/* Update statistics */
if (IsInvisible(client) && !(old_umodes & UMODE_INVISIBLE))
irccounts.invisible++;
if (IsOper(client) && !IsHideOper(client))
irccounts.operators++;
if (SHOWOPERMOTD == 1)
do_cmd(client, NULL, "OPERMOTD", parc, parv);
if (!BadPtr(OPER_AUTO_JOIN_CHANS) && strcmp(OPER_AUTO_JOIN_CHANS, "0"))
{
char *chans = strdup(OPER_AUTO_JOIN_CHANS);
const char *args[3] = {
client->name,
chans,
NULL
};
do_cmd(client, NULL, "JOIN", 3, args);
safe_free(chans);
/* Theoretically the oper may be killed on join. Would be fun, though */
if (IsDead(client))
return;
}
make_oper(client, operblock->name, operblock->operclass, operblock->class, operblock->modes, operblock->snomask, operblock->vhost);
/* set::plaintext-policy::oper 'warn' */
if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_oper == POLICY_WARN))
@ -323,3 +350,29 @@ CMD_FUNC(cmd_oper)
log_data_string("warn_type", "OUTDATED_TLS_PROTOCOL_OR_CIPHER"));
}
}
int oper_connect(Client *client)
{
ConfigItem_oper *e;
if (IsOper(client))
return 0;
for (e = conf_oper; e; e = e->next)
{
if (e->auto_login && user_allowed_by_security_group(client, e->match))
{
/* Ideally we would check all the criteria that cmd_oper does.
* I'm taking a shortcut for now that is not ideal...
*/
const char *parx[3];
parx[0] = NULL;
parx[1] = e->name;
parx[2] = NULL;
do_cmd(client, NULL, "OPER", 3, parx);
return 0;
}
}
return 0;
}

View File

@ -1,7 +1,7 @@
/*
* Store oper login in ModData, used by WHOIS and for auditting purposes.
* (C) Copyright 2021-.. Syzop and The UnrealIRCd Team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"
@ -16,7 +16,7 @@ ModuleHeader MOD_HEADER
};
/* Forward declarations */
int operinfo_local_oper(Client *client, int up, ConfigItem_oper *oper_block);
int operinfo_local_oper(Client *client, int up, const char *oper_block, const char *operclass);
void operinfo_free(ModData *m);
const char *operinfo_serialize(ModData *m);
void operinfo_unserialize(const char *str, ModData *m);
@ -68,12 +68,12 @@ MOD_UNLOAD()
return MOD_SUCCESS;
}
int operinfo_local_oper(Client *client, int up, ConfigItem_oper *oper_block)
int operinfo_local_oper(Client *client, int up, const char *oper_block, const char *operclass)
{
if (up)
{
moddata_client_set(client, "operlogin", oper_block->name);
moddata_client_set(client, "operclass", oper_block->operclass);
moddata_client_set(client, "operlogin", oper_block);
moddata_client_set(client, "operclass", operclass);
} else {
moddata_client_set(client, "operlogin", NULL);
moddata_client_set(client, "operclass", NULL);

View File

@ -206,7 +206,7 @@ CMD_FUNC(cmd_protoctl)
if ((aclient = hash_find_id(sid, NULL)) != NULL)
{
unreal_log(ULOG_ERROR, "link", "LINK_DENIED_SID_COLLISION", client,
"Server link $client rejected. Server with SID $sid already exist via uplink $exiting_client.server.uplink.",
"Server link $client rejected. Server with SID $sid already exist via uplink $existing_client.server.uplink.",
log_data_string("sid", sid),
log_data_client("existing_client", aclient));
exit_client(client, NULL, "SID collision");
@ -266,7 +266,7 @@ CMD_FUNC(cmd_protoctl)
*/
strlcpy(client->name, servername, sizeof(client->name));
if (!verify_link(client, &aconf))
if (!(aconf = verify_link(client)))
return;
/* note: software, protocol and flags may be NULL */

View File

@ -1,7 +1,7 @@
/*
* reputation - Provides a scoring system for "known users".
* (C) Copyright 2015-2019 Bram Matthys (Syzop) and the UnrealIRCd team.
* License: GPLv2
* License: GPLv2 or later
*
* How this works is simple:
* Every 5 minutes the IP address of all the connected users receive
@ -918,6 +918,24 @@ static inline int is_reputation_expired(ReputationEntry *e)
return 0;
}
/** If the reputation changed (due to server syncing) then update the
* individual users reputation score as well.
*/
void reputation_changed_update_users(ReputationEntry *e)
{
Client *client;
list_for_each_entry(client, &client_list, client_node)
{
if (client->ip && !strcmp(e->ip, client->ip))
{
/* With some (possibly unneeded) care to only go forward */
if (Reputation(client) < e->score)
Reputation(client) = e->score;
}
}
}
EVENT(delete_old_records)
{
int i;
@ -1279,6 +1297,7 @@ CMD_FUNC(reputation_server_cmd)
log_data_integer("score", e->score));
#endif
e->score = score;
reputation_changed_update_users(e);
}
/* If we don't have any entry for this IP, add it now. */
@ -1296,6 +1315,7 @@ CMD_FUNC(reputation_server_cmd)
e->score = score;
e->last_seen = TStime();
add_reputation_entry(e);
reputation_changed_update_users(e);
}
/* Propagate to the non-client direction (score may be updated) */

View File

@ -32,11 +32,7 @@ struct RestrictedCommand {
RestrictedCommand *prev, *next;
char *cmd;
char *conftag;
long connect_delay;
int exempt_identified;
int exempt_reputation_score;
int exempt_webirc;
int exempt_tls;
SecurityGroup *except;
};
typedef struct {
@ -103,6 +99,7 @@ MOD_UNLOAD()
next = rcmd->next;
safe_free(rcmd->conftag);
safe_free(rcmd->cmd);
free_security_group(rcmd->except);
DelListItem(rcmd, RestrictedCommandList);
safe_free(rcmd);
}
@ -170,6 +167,12 @@ int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
continue;
}
if (!strcmp(cep2->name, "except"))
{
test_match_block(cf, cep2, &errors);
continue;
}
if (!cep2->value)
{
config_error("%s:%i: blank set::restrict-commands::%s:%s without value", cep2->file->filename, cep2->line_number, cep->name, cep2->name);
@ -249,7 +252,13 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
config_warn("[restrict-commands] Command '%s' does not exist. Did you mistype? Or is the module providing it not loaded?", cmd);
continue;
}
if (find_restrictions_bycmd(cmd))
{
config_warn("[restrict-commands] Multiple set::restrict-commands items for command '%s'. "
"Only one config block will be effective.",
cmd);
continue;
}
if (!CommandOverrideAdd(ModInf.handle, cmd, 0, rcmd_override))
{
config_warn("[restrict-commands] Failed to add override for '%s' (NO RESTRICTIONS APPLY)", cmd);
@ -260,38 +269,46 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
rcmd = safe_alloc(sizeof(RestrictedCommand));
safe_strdup(rcmd->cmd, cmd);
safe_strdup(rcmd->conftag, conftag);
rcmd->except = safe_alloc(sizeof(SecurityGroup));
for (cep2 = cep->items; cep2; cep2 = cep2->next)
{
if (!strcmp(cep2->name, "except"))
{
conf_match_block(cf, cep2, &rcmd->except);
continue;
}
if (!cep2->value)
continue;
if (!strcmp(cep2->name, "connect-delay"))
{
rcmd->connect_delay = config_checkval(cep2->value, CFG_TIME);
rcmd->except->connect_time = config_checkval(cep2->value, CFG_TIME);
continue;
}
if (!strcmp(cep2->name, "exempt-identified"))
{
rcmd->exempt_identified = config_checkval(cep2->value, CFG_YESNO);
rcmd->except->identified = config_checkval(cep2->value, CFG_YESNO);
continue;
}
if (!strcmp(cep2->name, "exempt-webirc"))
{
rcmd->exempt_webirc = config_checkval(cep2->value, CFG_YESNO);
rcmd->except->webirc = config_checkval(cep2->value, CFG_YESNO);
continue;
}
if (!strcmp(cep2->name, "exempt-tls"))
{
rcmd->exempt_tls = config_checkval(cep2->value, CFG_YESNO);
rcmd->except->tls = config_checkval(cep2->value, CFG_YESNO);
continue;
}
if (!strcmp(cep2->name, "exempt-reputation-score"))
{
rcmd->exempt_reputation_score = atoi(cep2->value);
rcmd->except->reputation_score = atoi(cep2->value);
continue;
}
}
@ -305,15 +322,7 @@ int rcmd_canbypass(Client *client, RestrictedCommand *rcmd)
{
if (!client || !rcmd)
return 1;
if (rcmd->exempt_identified && IsLoggedIn(client))
return 1;
if (rcmd->exempt_webirc && moddata_client_get(client, "webirc"))
return 1;
if (rcmd->exempt_tls && IsSecureConnect(client))
return 1;
if (rcmd->exempt_reputation_score > 0 && (GetReputation(client) >= rcmd->exempt_reputation_score))
return 1;
if (rcmd->connect_delay && client->local && (TStime() - client->local->creationtime >= rcmd->connect_delay))
if (user_allowed_by_security_group(client, rcmd->except))
return 1;
return 0;
}
@ -351,11 +360,11 @@ int rcmd_block_message(Client *client, const char *text, SendType sendtype, cons
if (rcmd && !rcmd_canbypass(client, rcmd))
{
int notice = (sendtype == SEND_TYPE_NOTICE ? 1 : 0); // temporary hack FIXME !!!
if (rcmd->connect_delay)
if (rcmd->except->connect_time)
{
ircsnprintf(errbuf, sizeof(errbuf),
"You cannot send %ss to %ss until you've been connected for %ld seconds or more",
(notice ? "notice" : "message"), display, rcmd->connect_delay);
(notice ? "notice" : "message"), display, rcmd->except->connect_time);
} else {
ircsnprintf(errbuf, sizeof(errbuf),
"Sending of %ss to %ss been disabled by the network administrators",
@ -382,11 +391,11 @@ CMD_OVERRIDE_FUNC(rcmd_override)
rcmd = find_restrictions_bycmd(ovr->command->cmd);
if (rcmd && !rcmd_canbypass(client, rcmd))
{
if (rcmd->connect_delay)
if (rcmd->except->connect_time)
{
sendnumericfmt(client, ERR_UNKNOWNCOMMAND,
"%s :You must be connected for at least %ld seconds before you can use this command",
ovr->command->cmd, rcmd->connect_delay);
ovr->command->cmd, rcmd->except->connect_time);
} else {
sendnumericfmt(client, ERR_UNKNOWNCOMMAND,
"%s :This command is disabled by the network administrator",

View File

@ -167,6 +167,7 @@ CMD_FUNC(cmd_sapart)
log_sapart(client, target, request, comment);
/***
if (comment)
{
snprintf(commentx, sizeof(commentx), "SAPart: %s", comment);
@ -174,6 +175,7 @@ CMD_FUNC(cmd_sapart)
} else {
sendnotice(target, "*** You were forced to part %s", request);
}
***/
parv[0] = target->name; // nick
parv[1] = request; // chan

View File

@ -45,7 +45,7 @@ EVENT(server_handshake_timeout);
void send_channel_modes_sjoin3(Client *to, Channel *channel);
CMD_FUNC(cmd_server);
CMD_FUNC(cmd_sid);
int _verify_link(Client *client, ConfigItem_link **link_out);
ConfigItem_link *_verify_link(Client *client);
void _send_protoctl_servers(Client *client, int response);
void _send_server_message(Client *client);
void _introduce_user(Client *to, Client *acptr);
@ -59,7 +59,6 @@ void _connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp);
static int connect_server_helper(ConfigItem_link *, Client *);
/* Global variables */
static char buf[BUFSIZE];
static cfgstruct cfg;
static char *last_autoconnect_server = NULL;
@ -77,7 +76,7 @@ MOD_TEST()
MARK_AS_OFFICIAL_MODULE(modinfo);
EfunctionAddVoid(modinfo->handle, EFUNC_SEND_PROTOCTL_SERVERS, _send_protoctl_servers);
EfunctionAddVoid(modinfo->handle, EFUNC_SEND_SERVER_MESSAGE, _send_server_message);
EfunctionAdd(modinfo->handle, EFUNC_VERIFY_LINK, _verify_link);
EfunctionAddPVoid(modinfo->handle, EFUNC_VERIFY_LINK, TO_PVOIDFUNC(_verify_link));
EfunctionAddVoid(modinfo->handle, EFUNC_INTRODUCE_USER, _introduce_user);
EfunctionAdd(modinfo->handle, EFUNC_CHECK_DENY_VERSION, _check_deny_version);
EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_SINFO, _broadcast_sinfo);
@ -254,8 +253,13 @@ int server_needs_linking(ConfigItem_link *aconf)
Client *client;
ConfigItem_class *class;
/* We're only interested in autoconnect blocks that are valid. Also, we ignore temporary link blocks. */
if (!(aconf->outgoing.options & CONNECT_AUTO) || !aconf->outgoing.hostname || (aconf->flag.temporary == 1))
/* We're only interested in autoconnect blocks that also have
* a valid link::outgoing configuration. We also ignore
* temporary link blocks (not that they should exist...).
*/
if (!(aconf->outgoing.options & CONNECT_AUTO) ||
(!aconf->outgoing.hostname && !aconf->outgoing.file) ||
(aconf->flag.temporary == 1))
return 0;
class = aconf->class;
@ -635,12 +639,10 @@ void _send_server_message(Client *client)
/** Verify server link.
* This does authentication and authorization checks.
* @param cptr The client directly connected to us (cptr).
* @param client The client which (originally) issued the server command (client).
* @param link_out Pointer-to-pointer-to-link block. Will be set when auth OK. Caller may pass NULL if he doesn't care.
* @returns This function returns 1 on successful authentication, 0 otherwise - in which case the client has been killed.
* @param client The client which issued the command
* @returns On successfull authentication, the link block is returned. On failure NULL is returned (client has been killed!).
*/
int _verify_link(Client *client, ConfigItem_link **link_out)
ConfigItem_link *_verify_link(Client *client)
{
ConfigItem_link *link, *orig_link;
Client *acptr = NULL, *ocptr = NULL;
@ -652,15 +654,12 @@ int _verify_link(Client *client, ConfigItem_link **link_out)
if (client->local->hostp && client->local->hostp->h_name)
set_sockhost(client, client->local->hostp->h_name);
if (link_out)
*link_out = NULL;
if (!client->local->passwd)
{
unreal_log(ULOG_ERROR, "link", "LINK_DENIED_NO_PASSWORD", client,
"Link with server $client.details denied: No password provided. Protocol error.");
exit_client(client, NULL, "Missing password");
return 0;
return NULL;
}
if (client->server && client->server->conf)
@ -679,7 +678,7 @@ int _verify_link(Client *client, ConfigItem_link **link_out)
log_data_link_block(client->server->conf));
exit_client_fmt(client, NULL, "Servername (%s) does not match name in my link block (%s)",
client->name, client->server->conf->servername);
return 0;
return NULL;
}
link = client->server->conf;
goto skip_host_check;
@ -695,16 +694,16 @@ int _verify_link(Client *client, ConfigItem_link **link_out)
unreal_log(ULOG_ERROR, "link", "LINK_DENIED_UNKNOWN_SERVER", client,
"Link with server $client.details denied: No link block named '$client'");
exit_client(client, NULL, LINK_DEFAULT_ERROR_MSG);
return 0;
return NULL;
}
if (!link->incoming.mask)
if (!link->incoming.match)
{
unreal_log(ULOG_ERROR, "link", "LINK_DENIED_NO_INCOMING", client,
"Link with server $client.details denied: Link block exists, but there is no link::incoming::mask set.",
"Link with server $client.details denied: Link block exists, but there is no link::incoming::match set.",
log_data_link_block(link));
exit_client(client, NULL, LINK_DEFAULT_ERROR_MSG);
return 0;
return NULL;
}
orig_link = link;
@ -716,7 +715,7 @@ int _verify_link(Client *client, ConfigItem_link **link_out)
"Link with server $client.details denied: Server is in link block but link::incoming::mask didn't match",
log_data_link_block(orig_link));
exit_client(client, NULL, LINK_DEFAULT_ERROR_MSG);
return 0;
return NULL;
}
skip_host_check:
@ -767,7 +766,7 @@ skip_host_check:
log_data_link_block(link));
}
exit_client(client, NULL, "Link denied (Authentication failed)");
return 0;
return NULL;
}
/* Verify the TLS certificate (if requested) */
@ -782,7 +781,7 @@ skip_host_check:
log_data_string("certificate_failure_msg", "not using TLS"),
log_data_link_block(link));
exit_client(client, NULL, "Link denied (Not using TLS)");
return 0;
return NULL;
}
if (!verify_certificate(client->local->ssl, link->servername, &errstr))
{
@ -791,7 +790,7 @@ skip_host_check:
log_data_string("certificate_failure_msg", errstr),
log_data_link_block(link));
exit_client(client, NULL, "Link denied (Certificate verification failed)");
return 0;
return NULL;
}
}
@ -803,7 +802,7 @@ skip_host_check:
log_data_string("ban_reason", bconf->reason),
log_data_link_block(link));
exit_client_fmt(client, NULL, "Banned server: %s", bconf->reason);
return 0;
return NULL;
}
if (link->class->clients + 1 > link->class->maxclients)
@ -813,7 +812,7 @@ skip_host_check:
"class '$link_block.class' is full",
log_data_link_block(link));
exit_client(client, NULL, "Full class");
return 0;
return NULL;
}
if (!IsLocalhost(client) && (iConf.plaintext_policy_server == POLICY_DENY) && !IsSecure(client))
{
@ -823,7 +822,7 @@ skip_host_check:
"See https://www.unrealircd.org/docs/FAQ#server-requires-tls",
log_data_link_block(link));
exit_client(client, NULL, "Servers need to use TLS (set::plaintext-policy::server is 'deny')");
return 0;
return NULL;
}
if (IsSecure(client) && (iConf.outdated_tls_policy_server == POLICY_DENY) && outdated_tls_client(client))
{
@ -834,7 +833,7 @@ skip_host_check:
log_data_link_block(link),
log_data_string("tls_cipher", tls_get_cipher(client)));
exit_client(client, NULL, "Server using outdates TLS protocol or cipher (set::outdated-tls-policy::server is 'deny')");
return 0;
return NULL;
}
/* This one is at the end, because it causes us to delink another server,
* so we want to be (reasonably) sure that this one will succeed before
@ -850,7 +849,7 @@ skip_host_check:
log_data_string("me_name", me.name),
log_data_link_block(link));
exit_client(client, NULL, "Server Exists (server trying to link with same name as myself)");
return 0;
return NULL;
} else {
unreal_log(ULOG_ERROR, "link", "LINK_DROPPED_REINTRODUCED", client,
"Link with server $client.details causes older link "
@ -861,9 +860,7 @@ skip_host_check:
}
}
if (link_out)
*link_out = link;
return 1;
return link;
}
/** Server command. Only for locally connected servers!!.
@ -935,7 +932,7 @@ CMD_FUNC(cmd_server)
*/
strlcpy(client->name, servername, sizeof(client->name));
if (!verify_link(client, &aconf))
if (!(aconf = verify_link(client)))
return; /* Rejected */
/* From this point the server is authenticated, so we can be more verbose
@ -1200,6 +1197,7 @@ CMD_FUNC(cmd_sid)
unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_SERVER_BAN", client,
"Denied remote server $servername which was introduced by $client: "
"Server is banned ($ban_reason)",
log_data_string("servername", servername),
log_data_string("ban_reason", bconf->reason));
/* Before UnrealIRCd 6 this would SQUIT the server who introduced
* this server. That seems a bit of an overreaction, so we now
@ -1330,6 +1328,8 @@ CMD_FUNC(cmd_sid)
void _introduce_user(Client *to, Client *acptr)
{
char buf[512];
build_umode_string(acptr, 0, SEND_UMODES, buf);
sendto_one_nickcmd(to, NULL, acptr, buf);
@ -1807,13 +1807,13 @@ void _connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp)
{
Client *client;
if (!aconf->outgoing.hostname)
if (!aconf->outgoing.hostname && !aconf->outgoing.file)
{
/* Actually the caller should make sure that this doesn't happen,
* so this error may never be triggered:
*/
unreal_log(ULOG_ERROR, "link", "LINK_ERROR_NO_OUTGOING", NULL,
"Connect to $link_block failed: link block is for incoming only (no link::outgoing::hostname set)",
"Connect to $link_block failed: link block is for incoming only (no link::outgoing::hostname or link::outgoing::file set)",
log_data_link_block(aconf));
return;
}
@ -1827,7 +1827,7 @@ void _connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp)
* If we dont know the IP# for this host and itis a hostname and
* not a ip# string, then try and find the appropriate host record.
*/
if (!aconf->connect_ip)
if (!aconf->connect_ip && !aconf->outgoing.file)
{
if (is_valid_ip(aconf->outgoing.hostname))
{
@ -1861,7 +1861,7 @@ void _connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp)
* Copy these in so we have something for error detection.
*/
strlcpy(client->name, aconf->servername, sizeof(client->name));
strlcpy(client->local->sockhost, aconf->outgoing.hostname, HOSTLEN + 1);
strlcpy(client->local->sockhost, aconf->outgoing.hostname ? aconf->outgoing.hostname : aconf->outgoing.file, HOSTLEN + 1);
if (!connect_server_helper(aconf, client))
{
@ -1884,7 +1884,7 @@ void _connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp)
SetOutgoing(client);
irccounts.unknown++;
list_add(&client->lclient_node, &unknown_list);
set_sockhost(client, aconf->outgoing.hostname);
set_sockhost(client, aconf->outgoing.hostname ? aconf->outgoing.hostname : "127.0.0.1");
add_client_to_list(client);
if (aconf->outgoing.options & CONNECT_TLS)
@ -1896,7 +1896,9 @@ void _connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp)
fd_setselect(client->local->fd, FD_SELECT_WRITE, completed_connection, client);
unreal_log(ULOG_INFO, "link", "LINK_CONNECTING", client,
"Trying to activate link with server $client ($link_block.ip:$link_block.port)...",
aconf->outgoing.file
? "Trying to activate link with server $client ($link_block.file)..."
: "Trying to activate link with server $client ($link_block.ip:$link_block.port)...",
log_data_link_block(aconf));
}
@ -1911,21 +1913,23 @@ static int connect_server_helper(ConfigItem_link *aconf, Client *client)
char *bindip;
char buf[BUFSIZE];
if (!aconf->connect_ip)
if (!aconf->connect_ip && !aconf->outgoing.file)
{
unreal_log(ULOG_ERROR, "link", "LINK_ERROR_NOIP", client,
"Connect to $client failed: no IP address to connect to",
"Connect to $client failed: no IP address or file to connect to",
log_data_link_block(aconf));
return 0; /* handled upstream or shouldn't happen */
}
if (strchr(aconf->connect_ip, ':'))
if (aconf->outgoing.file)
SetUnixSocket(client);
else if (strchr(aconf->connect_ip, ':'))
SetIPV6(client);
safe_strdup(client->ip, aconf->connect_ip);
safe_strdup(client->ip, aconf->connect_ip ? aconf->connect_ip : "127.0.0.1");
snprintf(buf, sizeof buf, "Outgoing connection: %s", get_client_name(client, TRUE));
client->local->fd = fd_socket(IsIPV6(client) ? AF_INET6 : AF_INET, SOCK_STREAM, 0, buf);
client->local->fd = fd_socket(IsUnixSocket(client) ? AF_UNIX : (IsIPV6(client) ? AF_INET6 : AF_INET), SOCK_STREAM, 0, buf);
if (client->local->fd < 0)
{
if (ERRNO == P_EMFILE)
@ -1949,7 +1953,7 @@ static int connect_server_helper(ConfigItem_link *aconf, Client *client)
return 0;
}
set_sockhost(client, aconf->outgoing.hostname);
set_sockhost(client, aconf->outgoing.hostname ? aconf->outgoing.hostname : "127.0.0.1");
if (!aconf->outgoing.bind_ip && iConf.link_bindip)
bindip = iConf.link_bindip;
@ -1971,10 +1975,14 @@ static int connect_server_helper(ConfigItem_link *aconf, Client *client)
set_sock_opts(client->local->fd, client, IsIPV6(client));
if (!unreal_connect(client->local->fd, client->ip, aconf->outgoing.port, IsIPV6(client)))
if (!unreal_connect(client->local->fd,
aconf->outgoing.file ? aconf->outgoing.file : client->ip,
aconf->outgoing.port, client->local->socket_type))
{
unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client,
"Connect to $client ($link_block.ip:$link_block.port) failed: $socket_error",
aconf->outgoing.file
? "Connect to $client ($link_block.file) failed: $socket_error"
: "Connect to $client ($link_block.ip:$link_block.port) failed: $socket_error",
log_data_socket_error(client->local->fd),
log_data_link_block(aconf));
return 0;

View File

@ -143,7 +143,6 @@ CMD_FUNC(cmd_sethost)
if (MyConnect(client))
{
sendto_one(client, NULL, ":%s MODE %s :+xt", client->name, client->name);
sendnumeric(client, RPL_HOSTHIDDEN, vhost);
sendnotice(client,
"Your nick!user@host-mask is now (%s!%s@%s) - To disable it type /mode %s -x",
client->name, client->user->username, vhost,

View File

@ -1,7 +1,7 @@
/*
* cmd_sinfo - Server information
* (C) Copyright 2019 Bram Matthys (Syzop) and the UnrealIRCd team.
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -487,14 +487,14 @@ int stats_except(Client *client, const char *para)
int stats_allow(Client *client, const char *para)
{
ConfigItem_allow *allows;
ConfigItem_mask *m;
NameValuePrioList *m;
for (allows = conf_allow; allows; allows = allows->next)
{
for (m = allows->mask; m; m = m->next)
for (m = allows->match->printable_list; m; m = m->next)
{
sendnumeric(client, RPL_STATSILINE,
m->mask, "-",
namevalue_nospaces(m), "-",
allows->maxperip,
allows->global_maxperip,
allows->class->name,
@ -521,14 +521,14 @@ int stats_command(Client *client, const char *para)
int stats_oper(Client *client, const char *para)
{
ConfigItem_oper *o;
ConfigItem_mask *m;
NameValuePrioList *m;
for (o = conf_oper; o; o = o->next)
{
for (m = o->mask; m; m = m->next)
for (m = o->match->printable_list; m; m = m->next)
{
sendnumeric(client, RPL_STATSOLINE,
'O', m->mask, o->name,
'O', namevalue_nospaces(m), o->name,
o->operclass ? o->operclass: "",
o->class->name ? o->class->name : "");
}
@ -668,15 +668,19 @@ int stats_uline(Client *client, const char *para)
}
int stats_vhost(Client *client, const char *para)
{
ConfigItem_mask *m;
ConfigItem_vhost *vhosts;
NameValuePrioList *m;
for (vhosts = conf_vhost; vhosts; vhosts = vhosts->next)
{
for (m = vhosts->mask; m; m = m->next)
for (m = vhosts->match->printable_list; m; m = m->next)
{
sendtxtnumeric(client, "vhost %s%s%s %s %s", vhosts->virtuser ? vhosts->virtuser : "", vhosts->virtuser ? "@" : "",
vhosts->virthost, vhosts->login, m->mask);
sendtxtnumeric(client, "vhost %s%s%s %s %s",
vhosts->virtuser ? vhosts->virtuser : "",
vhosts->virtuser ? "@" : "",
vhosts->virthost,
vhosts->login,
namevalue_nospaces(m));
}
}
return 0;
@ -935,12 +939,16 @@ int stats_set(Client *client, const char *para)
int stats_tld(Client *client, const char *para)
{
ConfigItem_tld *tld;
ConfigItem_mask *m;
NameValuePrioList *m;
for (tld = conf_tld; tld; tld = tld->next)
{
for (m = tld->mask; m; m = m->next)
sendnumeric(client, RPL_STATSTLINE, m->mask, tld->motd_file, tld->rules_file ? tld->rules_file : "none");
for (m = tld->match->printable_list; m; m = m->next)
{
sendnumeric(client, RPL_STATSTLINE, namevalue_nospaces(m),
tld->motd_file,
tld->rules_file ? tld->rules_file : "none");
}
}
return 0;

View File

@ -395,7 +395,7 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *pa
* so remove all oper-only modes and snomasks.
*/
if (MyUser(client))
RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL);
RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL, NULL);
remove_oper_privileges(target, 0);
}
goto setmodex;

View File

@ -82,7 +82,7 @@ CMD_FUNC(cmd_svsnoop)
if (!list_empty(&acptr->special_node))
list_del(&acptr->special_node);
RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL);
RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL, NULL);
remove_oper_privileges(acptr, 1);
}
}

138
src/modules/svso.c Normal file
View File

@ -0,0 +1,138 @@
/* src/modules/svso.c - Grant IRCOp rights (for Services)
* (C) Copyright 2022 Bram Matthys (Syzop) and the UnrealIRCd team
* License: GPLv2 or later
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"svso",
"6.0.0",
"Grant oper privileges via SVSO services command",
"UnrealIRCd Team",
"unrealircd-6",
};
/* Forward declarations */
CMD_FUNC(cmd_svso);
MOD_INIT()
{
MARK_AS_OFFICIAL_MODULE(modinfo);
CommandAdd(modinfo->handle, "SVSO", cmd_svso, MAXPARA, CMD_USER|CMD_SERVER);
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
/* Syntax: SVSO <uid|nick> <oper account> <operclass> <class> <modes> <snomask> <vhost>
* All these parameters need to be set, you cannot leave any of them out,
* HOWEVER some can be set to "-" to skip setting them, this is true for:
* <class>, <modes>, <snomask>, <vhost>
*
* In UnrealIRCd the <operclass> will be prefixed by "services:" if not already
* present. It is up to you to include or omit it.
*/
CMD_FUNC(cmd_svso)
{
Client *acptr;
char oper_account[64];
const char *operclass;
const char *clientclass;
ConfigItem_class *clientclass_c;
const char *modes;
long modes_i = 0;
const char *snomask;
const char *vhost;
if (!IsULine(client))
return;
if ((parc < 8) || BadPtr(parv[7]))
{
sendnumeric(client, ERR_NEEDMOREPARAMS, "SVSO");
return;
}
operclass = parv[3];
clientclass = parv[4];
modes = parv[5];
snomask = parv[6];
vhost = parv[7];
acptr = find_user(parv[1], NULL);
if (!acptr)
{
sendnumeric(client, ERR_NOSUCHNICK, parv[1]);
return;
}
if (!MyUser(acptr))
{
/* Forward it to the correct server, and we are done... */
sendto_one(acptr, recv_mtags, ":%s SVSO %s %s %s %s %s %s %s",
client->name, acptr->id, parv[2], parv[3], parv[4], parv[5], parv[6], parv[7]);
return;
}
/* CAVEAT ALERT !
* Don't mix up 'client' and 'acptr' below...
* 'client' is the server or services pseudouser that requests the change
* 'acptr' is the person that will be made OPER
*/
/* If we get here, we validate the request and then make the user oper. */
if (!find_operclass(operclass))
{
sendnumeric(client, ERR_CANNOTDOCOMMAND, "SVSO", "Operclass does not exist");
return;
}
/* Set any items to NULL if they are skipped (on request) */
if (!strcmp(clientclass, "-"))
clientclass = NULL;
if (!strcmp(modes, "-"))
modes = NULL;
if (!strcmp(snomask, "-"))
snomask = NULL;
if (!strcmp(vhost, "-"))
vhost = NULL;
/* First, maybe the user is oper already? Then de-oper them.. */
if (IsOper(acptr))
{
int was_hidden_oper = IsHideOper(acptr) ? 1 : 0;
list_del(&acptr->special_node);
RunHook(HOOKTYPE_LOCAL_OPER, acptr, 0, NULL, NULL);
remove_oper_privileges(acptr, 1);
if (!was_hidden_oper)
irccounts.operators--;
VERIFY_OPERCOUNT(acptr, "svso");
}
/* Prefix the oper block name with "services:" if it hasn't already */
if (!strncmp(parv[2], "services:", 9))
strlcpy(oper_account, parv[2], sizeof(oper_account));
else
snprintf(oper_account, sizeof(oper_account), "services:%s", parv[2]);
/* These needs to be looked up... */
clientclass_c = find_class(clientclass); /* NULL is fine! */
if (modes)
modes_i = set_usermode(modes);
if (!make_oper(acptr, oper_account, operclass, clientclass_c, modes_i, snomask, vhost))
sendnumeric(client, ERR_CANNOTDOCOMMAND, "SVSO", "Failed to make user oper");
}

View File

@ -1,6 +1,6 @@
/* Target flood protection
* (C)Copyright 2020 Bram Matthys and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -60,8 +60,9 @@ char *_tkl_type_config_string(TKL *tk);
char *tkl_banexception_configname_to_chars(char *name);
TKL *_tkl_add_serverban(int type, char *usermask, char *hostmask, char *reason, char *set_by,
time_t expire_at, time_t set_at, int soft, int flags);
TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, char *reason, char *set_by,
time_t expire_at, time_t set_at, int soft, char *bantypes, int flags);
TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, SecurityGroup *match,
char *reason, char *set_by,
time_t expire_at, time_t set_at, int soft, char *bantypes, int flags);
TKL *_tkl_add_nameban(int type, char *name, int hold, char *reason, char *set_by,
time_t expire_at, time_t set_at, int flags);
TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction action, Match *match, char *set_by,
@ -91,6 +92,7 @@ int check_mtag_spamfilters_present(void);
int _join_viruschan(Client *client, TKL *tk, int type);
void _spamfilter_build_user_string(char *buf, char *nick, Client *client);
int _match_user(const char *rmask, Client *client, int options);
int _unreal_match_iplist(Client *client, NameList *l);
int _match_user_extended_server_ban(const char *banstr, Client *client);
void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, const char **tkl_username, const char **tkl_hostname);
int _tkl_ip_hash(char *ip);
@ -204,6 +206,7 @@ MOD_TEST()
EfunctionAddVoid(modinfo->handle, EFUNC_SENDNOTICE_TKL_DEL, _sendnotice_tkl_del);
EfunctionAdd(modinfo->handle, EFUNC_FIND_TKL_EXCEPTION, _find_tkl_exception);
EfunctionAddString(modinfo->handle, EFUNC_TKL_UHOST, _tkl_uhost);
EfunctionAdd(modinfo->handle, EFUNC_UNREAL_MATCH_IPLIST, _unreal_match_iplist);
return MOD_SUCCESS;
}
@ -678,7 +681,7 @@ int tkl_config_test_except(ConfigFile *cf, ConfigEntry *ce, int configtype, int
{
ConfigEntry *cep, *cepp;
int errors = 0;
int has_mask = 0;
char has_mask = 0, has_match = 0;
/* We are only interested in except { } blocks */
if (configtype != CONFIG_EXCEPT)
@ -705,32 +708,18 @@ int tkl_config_test_except(ConfigFile *cf, ConfigEntry *ce, int configtype, int
{
if (!strcmp(cep->name, "mask"))
{
if (cep->items)
if (cep->value || cep->items)
{
/* mask { *@1.1.1.1; *@2.2.2.2; *@3.3.3.3; }; */
for (cepp = cep->items; cepp; cepp = cepp->next)
{
if (!cepp->name)
{
config_error_empty(cepp->file->filename,
cepp->line_number, "except ban", "mask");
errors++;
continue;
}
has_mask = 1;
}
} else
if (cep->value)
{
/* mask *@1.1.1.1; */
if (!cep->value)
{
config_error_empty(cep->file->filename,
cep->line_number, "except ban", "mask");
errors++;
continue;
}
has_mask = 1;
test_match_block(cf, cep, &errors);
}
} else
if (!strcmp(cep->name, "match"))
{
if (cep->value || cep->items)
{
has_match = 1;
test_match_block(cf, cep, &errors);
}
} else
if (!strcmp(cep->name, "type"))
@ -766,10 +755,17 @@ int tkl_config_test_except(ConfigFile *cf, ConfigEntry *ce, int configtype, int
}
}
if (!has_mask)
if (!has_mask && !has_match)
{
config_error_missing(ce->file->filename, ce->line_number,
"except ban::mask");
"except ban::match");
errors++;
}
if (has_mask && has_match)
{
config_error("%s:%d: You cannot have both ::mask and ::match. "
"You should only use except::match.",
ce->file->filename, ce->line_number);
errors++;
}
@ -777,60 +773,10 @@ int tkl_config_test_except(ConfigFile *cf, ConfigEntry *ce, int configtype, int
return errors ? -1 : 1;
}
void config_create_tkl_except(char *mask, char *bantypes)
{
char *usermask = NULL;
char *hostmask = NULL;
int soft = 0;
char buf[256];
char mask1buf[512];
char mask2buf[512];
char *p;
if (*mask == '%')
{
soft = 1;
mask++;
}
strlcpy(buf, mask, sizeof(buf));
if (is_extended_server_ban(buf))
{
char *err = NULL;
if (!parse_extended_server_ban(buf, NULL, &err, 0, mask1buf, sizeof(mask1buf), mask2buf, sizeof(mask2buf)))
{
config_warn("Could not add extended server ban '%s': %s", buf, err);
return;
}
usermask = mask1buf;
hostmask = mask2buf;
} else
{
p = strchr(buf, '@');
if (!p)
{
usermask = "*";
hostmask = buf;
} else {
*p++ = '\0';
usermask = buf;
hostmask = p;
}
}
if ((*usermask == ':') || (*hostmask == ':'))
{
config_error("Cannot add illegal ban '%s': for a given user@host - neither "
"user nor host may start with a : character (semicolon)", mask);
return;
}
tkl_add_banexception(TKL_EXCEPTION, usermask, hostmask, "Added in configuration file",
"-config-", 0, TStime(), soft, bantypes, TKL_FLAG_CONFIG);
}
int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype)
{
ConfigEntry *cep, *cepp;
SecurityGroup *match = NULL;
char bantypes[64];
/* We are only interested in except { } blocks */
@ -867,6 +813,10 @@ int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype)
char *str = tkl_banexception_configname_to_chars(cep->value);
strlcat(bantypes, str, sizeof(bantypes));
}
} else
if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask"))
{
conf_match_block(cf, cep, &match);
}
}
@ -885,24 +835,8 @@ int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype)
abort(); /* someone can't code */
}
/* Now walk through all mask entries */
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "mask"))
{
if (cep->items)
{
/* mask { *@1.1.1.1; *@2.2.2.2; *@3.3.3.3; }; */
for (cepp = cep->items; cepp; cepp = cepp->next)
config_create_tkl_except(cepp->name, bantypes);
} else
if (cep->value)
{
/* mask *@1.1.1.1; */
config_create_tkl_except(cep->value, bantypes);
}
}
}
tkl_add_banexception(TKL_EXCEPTION, "-", "-", match, "Added in configuration file",
"-config-", 0, TStime(), 0, bantypes, TKL_FLAG_CONFIG);
return 1;
}
@ -2564,6 +2498,9 @@ TKL *_tkl_add_serverban(int type, char *usermask, char *hostmask, char *reason,
* @param type TKL_EXCEPTION or TKLEXCEPT|TKL_GLOBAL.
* @param usermask The user mask
* @param hostmask The host mask
* @param match A securitygroup used for matching (can be NULL,
* if not NULL then this field is used as-is and not copied
* so caller should not free!)
* @param reason The reason for the ban
* @param set_by Who (or what) set the ban
* @param expire_at When will the ban expire (0 for permanent)
@ -2577,15 +2514,15 @@ TKL *_tkl_add_serverban(int type, char *usermask, char *hostmask, char *reason,
* Be sure not to call this function for spamfilters,
* qlines or exempts, which have their own function!
*/
TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, char *reason, char *set_by,
time_t expire_at, time_t set_at, int soft, char *bantypes, int flags)
TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, SecurityGroup *match,
char *reason, char *set_by,
time_t expire_at, time_t set_at, int soft, char *bantypes, int flags)
{
TKL *tkl;
int index, index2;
if (!TKLIsBanExceptionType(type))
abort();
tkl = safe_alloc(sizeof(TKL));
/* First the common fields */
tkl->type = type;
@ -2597,6 +2534,7 @@ TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, char *reaso
tkl->ptr.banexception = safe_alloc(sizeof(BanException));
safe_strdup(tkl->ptr.banexception->usermask, usermask);
safe_strdup(tkl->ptr.banexception->hostmask, hostmask);
tkl->ptr.banexception->match = match;
if (soft)
tkl->ptr.banexception->subtype = TKL_SUBTYPE_SOFT;
safe_strdup(tkl->ptr.banexception->bantypes, bantypes);
@ -2702,6 +2640,8 @@ void _free_tkl(TKL *tkl)
{
safe_free(tkl->ptr.banexception->usermask);
safe_free(tkl->ptr.banexception->hostmask);
if (tkl->ptr.banexception->match)
free_security_group(tkl->ptr.banexception->match);
safe_free(tkl->ptr.banexception->bantypes);
safe_free(tkl->ptr.banexception->reason);
safe_free(tkl->ptr.banexception);
@ -2770,7 +2710,7 @@ static void add_default_exempts(void)
* Currently the list is: gline, kline, gzline, zline, shun, blacklist,
* connect-flood, handshake-data-flood.
*/
tkl_add_banexception(TKL_EXCEPTION, "*", "127.0.0.0/8", "localhost is always exempt",
tkl_add_banexception(TKL_EXCEPTION, "*", "127.0.0.0/8", NULL, "localhost is always exempt",
"-default-", 0, TStime(), 0, "GkZzsbcd", TKL_FLAG_CONFIG);
}
@ -2962,6 +2902,10 @@ static int find_tkl_exception_matcher(Client *client, int ban_type, TKL *except_
if (!tkl_banexception_matches_type(except_tkl, ban_type))
return 0;
/* For config file except ban { } we use security groups instead of simple user/host */
if (except_tkl->ptr.banexception->match)
return user_allowed_by_security_group(client, except_tkl->ptr.banexception->match);
tkl_uhost(except_tkl, uhost, sizeof(uhost), NO_SOFT_PREFIX);
if (match_user(uhost, client, MATCH_CHECK_REAL))
@ -3565,12 +3509,26 @@ int tkl_stats_matcher(Client *client, int type, const char *para, TKLFlag *tklfl
} else
if (TKLIsBanException(tkl))
{
char uhostbuf[BUFSIZE];
char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0);
sendnumeric(client, RPL_STATSEXCEPTTKL, uhost,
tkl->ptr.banexception->bantypes,
(tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0,
(long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.banexception->reason);
if (tkl->ptr.banexception->match)
{
/* Config-added: uses security groups */
NameValuePrioList *m;
for (m = tkl->ptr.banexception->match->printable_list; m; m = m->next)
{
sendnumeric(client, RPL_STATSEXCEPTTKL, namevalue_nospaces(m),
tkl->ptr.banexception->bantypes,
(tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0,
(long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.banexception->reason);
}
} else {
/* IRC-added: uses simple user/host mask */
char uhostbuf[BUFSIZE];
char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0);
sendnumeric(client, RPL_STATSEXCEPTTKL, uhost,
tkl->ptr.banexception->bantypes,
(tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0,
(long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.banexception->reason);
}
} else
{
/* That's weird, unknown TKL type */
@ -3706,7 +3664,7 @@ void tkl_sync_send_entry(int add, Client *sender, Client *to, TKL *tkl)
} else
{
unreal_log(ULOG_FATAL, "tkl", "BUG_TKL_SYNC_SEND_ENTRY", NULL,
"[BUG] tkl_sync_send_entry() called for '%s' but unknown type: $tkl.type_string ($tkl_type_int)",
"[BUG] tkl_sync_send_entry() called, but unknown type: $tkl.type_string ($tkl_type_int)",
log_data_tkl("tkl", tkl),
log_data_integer("tkl_type_int", typ));
abort();
@ -4072,7 +4030,7 @@ CMD_FUNC(cmd_tkl_add)
{
tkl_entry_exists = 1;
} else {
tkl = tkl_add_banexception(type, usermask, hostmask, reason,
tkl = tkl_add_banexception(type, usermask, hostmask, NULL, reason,
set_by, expire_at, set_at, softban, bantypes, 0);
}
} else
@ -4123,7 +4081,7 @@ CMD_FUNC(cmd_tkl_add)
{
unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client,
"Invalid TKL entry from $client: "
"Spamfilter '$spamfilter_string' has unkown match-type '$spamfilter_type'",
"Spamfilter '$spamfilter_string' has unknown match-type '$spamfilter_type'",
log_data_string("spamfilter_string", match_string),
log_data_string("spamfilter_type", parv[10]));
return;
@ -4133,7 +4091,7 @@ CMD_FUNC(cmd_tkl_add)
{
unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client,
"Invalid TKL entry from $client: "
"Spamfilter '$spamfilter_string' has unkown targets '$spamfilter_targets'",
"Spamfilter '$spamfilter_string' has unknown targets '$spamfilter_targets'",
log_data_string("spamfilter_string", match_string),
log_data_string("spamfilter_targets", parv[3]));
return;
@ -4143,7 +4101,7 @@ CMD_FUNC(cmd_tkl_add)
{
unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client,
"Invalid TKL entry from $client: "
"Spamfilter '$spamfilter_string' has unkown action '$spamfilter_action'",
"Spamfilter '$spamfilter_string' has unknown action '$spamfilter_action'",
log_data_string("spamfilter_string", match_string),
log_data_string("spamfilter_action", parv[4]));
return;
@ -4312,7 +4270,7 @@ CMD_FUNC(cmd_tkl_del)
{
unreal_log(ULOG_WARNING, "tkl", "TKL_DEL_INVALID", client,
"Invalid TKL deletion request from $client: "
"Spamfilter '$spamfilter_string' has unkown targets '$spamfilter_targets'",
"Spamfilter '$spamfilter_string' has unknown targets '$spamfilter_targets'",
log_data_string("spamfilter_string", match_string),
log_data_string("spamfilter_targets", parv[3]));
return;
@ -4322,7 +4280,7 @@ CMD_FUNC(cmd_tkl_del)
{
unreal_log(ULOG_WARNING, "tkl", "TKL_DEL_INVALID", client,
"Invalid TKL deletion request from $client: "
"Spamfilter '$spamfilter_string' has unkown action '$spamfilter_action'",
"Spamfilter '$spamfilter_string' has unknown action '$spamfilter_action'",
log_data_string("spamfilter_string", match_string),
log_data_string("spamfilter_action", parv[4]));
return;
@ -5147,6 +5105,102 @@ int _match_user(const char *rmask, Client *client, int options)
return 0; /* NOMATCH: nothing of the above matched */
}
/** Returns 1 if the user is allowed by any of the security groups in the named list.
* This is only used by security-group::security-group and
* security-group::exclude-security-group.
* @param client Client to check
* @param l The NameList
* @returns 1 if any of the security groups match, 0 if none of them matched.
*/
int _unreal_match_iplist(Client *client, NameList *l)
{
char client_ipv6 = 0;
char clientip[IPSZ], maskip[IPSZ];
if (!client->ip)
return 0; /* unusual, maybe services? */
if (strchr(client->ip, ':'))
{
client_ipv6 = 1;
if (!inet_pton(AF_INET6, client->ip, clientip))
return 0; /* unusual failure */
} else {
if (!inet_pton(AF_INET, client->ip, clientip))
return 0; /* unusual failure */
}
for (; l; l = l->next)
{
char mask[512], *p;
int cidr = -1; /* CIDR length, -1 for no CIDR */
strlcpy(mask, l->name, sizeof(mask));
p = strchr(mask, '/');
if (p)
{
*p++ = '\0';
cidr = atoi(p);
if (cidr <= 0)
return 0; /* NOMATCH: invalid CIDR */
}
/* Three possible types: wildcard, ipv6, ipv4 */
if (strchr(mask, '*') || strchr(mask, '?'))
{
/* Wildcards */
if (match_simple(mask, client->ip))
return 1; /* MATCH by wildcard IP */
}
else if (strchr(mask, ':'))
{
/* IPv6 */
if (!client_ipv6)
continue; /* NOMATCH: client is IPv4 */
if (!inet_pton(AF_INET6, mask, maskip))
continue; /* NOMATCH: invalid IPv6 IP in mask */
if (cidr < 0)
{
/* Try to match by exact IP */
if (comp_with_mask(clientip, maskip, 128))
return 1; /* MATCH by exact IP */
} else
if (cidr > 128)
{
continue; /* NOMATCH: invalid CIDR */
} else
if (comp_with_mask(clientip, maskip, cidr))
{
return 1; /* MATCH by CIDR */
}
} else
{
/* IPv4 */
if (client_ipv6)
continue; /* NOMATCH: client is IPv6 */
if (!inet_pton(AF_INET, mask, maskip))
continue; /* NOMATCH: invalid IPv6 IP in mask */
if (cidr < 0)
{
/* Try to match by exact IP */
if (comp_with_mask(clientip, maskip, 32))
return 1; /* MATCH: by exact IP */
} else
if (cidr > 32)
{
continue; /* NOMATCH: invalid CIDR */
} else
if (comp_with_mask(clientip, maskip, cidr))
{
return 1; /* MATCH by CIDR */
}
}
}
return 0;
}
int _match_user_extended_server_ban(const char *banstr, Client *client)
{
const char *nextbanstr;

View File

@ -625,6 +625,7 @@ int read_tkldb(void)
{
tkl_add_banexception(tkl->type, tkl->ptr.banexception->usermask,
tkl->ptr.banexception->hostmask,
NULL,
tkl->ptr.banexception->reason,
tkl->set_by, tkl->expire_at,
tkl->set_at, softban,

View File

@ -5,7 +5,7 @@
*
* (C) Copyright 2015- Bram Matthys and the UnrealIRCd team.
*
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -1,7 +1,7 @@
/*
* Store TLS cipher in ModData
* (C) Copyright 2021-.. Syzop and The UnrealIRCd Team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"
@ -22,6 +22,7 @@ void tls_cipher_unserialize(const char *str, ModData *m);
int tls_cipher_handshake(Client *client);
int tls_cipher_connect(Client *client);
int tls_cipher_whois(Client *client, Client *target);
int log_tls_cipher(Client *client, int detail, json_t *j);
ModDataInfo *tls_cipher_md; /* Module Data structure which we acquire */
@ -45,6 +46,8 @@ ModDataInfo mreq;
HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, tls_cipher_handshake);
HookAdd(modinfo->handle, HOOKTYPE_SERVER_HANDSHAKE_OUT, 0, tls_cipher_handshake);
HookAdd(modinfo->handle, HOOKTYPE_JSON_EXPAND_CLIENT, 0, log_tls_cipher);
return MOD_SUCCESS;
}
@ -89,3 +92,24 @@ void tls_cipher_unserialize(const char *str, ModData *m)
{
safe_strdup(m->str, str);
}
int log_tls_cipher(Client *client, int detail, json_t *j)
{
json_t *tls;
const char *str;
str = moddata_client_get(client, "tls_cipher");
if (!str)
return 0;
tls = json_object_get(j, "tls");
if (!tls)
{
tls = json_object();
json_object_set_new(j, "tls", tls);
}
json_object_set_new(tls, "cipher", json_string_unreal(str));
return 0;
}

View File

@ -1,7 +1,7 @@
/*
* unreal_server_compat - Compatibility with pre-U6 servers
* (C) Copyright 2016-2021 Bram Matthys (Syzop)
* License: GPLv2
* License: GPLv2 or later
*
* Currently the only purpose of this module is to rewrite MODE
* and SJOIN lines to older servers so any bans/exempts/invex

View File

@ -89,7 +89,7 @@ CMD_FUNC(cmd_vhost)
return;
}
if (!unreal_mask_match(client, vhost->mask))
if (!user_allowed_by_security_group(client, vhost->match))
{
unreal_log(ULOG_WARNING, "vhost", "VHOST_FAILED", client,
"Failed VHOST attempt by $client.details [reason: $reason] [vhost-block: $vhost_block]",
@ -157,7 +157,6 @@ CMD_FUNC(cmd_vhost)
for (s = vhost->swhois; s; s = s->next)
swhois_add(client, "vhost", -100, s->line, &me, NULL);
}
sendnumeric(client, RPL_HOSTHIDDEN, vhost->virthost);
sendnotice(client, "*** Your vhost is now %s%s%s",
vhost->virtuser ? vhost->virtuser : "",
vhost->virtuser ? "@" : "",

View File

@ -85,7 +85,7 @@ MOD_INIT()
}
LoadPersistentPointer(modinfo, watchTable, watch_generic_free);
if (watchTable == NULL)
watchTable = safe_alloc(sizeof(Watch) * WATCH_HASH_TABLE_SIZE);
watchTable = safe_alloc(sizeof(Watch *) * WATCH_HASH_TABLE_SIZE);
memset(&mreq, 0 , sizeof(mreq));
mreq.type = MODDATATYPE_LOCAL_CLIENT;

View File

@ -119,8 +119,6 @@ static void show_watch_removed(Client *client, char *name)
}
}
static char buf[BUFSIZE];
#define WATCHES(client) (moddata_local_client(client, watchCounterMD).i)
#define WATCH(client) (moddata_local_client(client, watchListMD).ptr)
@ -130,6 +128,7 @@ static char buf[BUFSIZE];
CMD_FUNC(cmd_watch)
{
char request[BUFSIZE];
char buf[BUFSIZE];
Client *target;
char *s, *user;
char *p = NULL, *def = "l";

View File

@ -1,7 +1,7 @@
/*
* websocket - WebSocket support (RFC6455)
* (C)Copyright 2016 Bram Matthys and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
* This module was sponsored by Aberrant Software Inc.
*/

View File

@ -52,7 +52,6 @@ struct WhoisConfig {
};
/* Global variables */
static char buf[BUFSIZE];
WhoisConfig *whoisconfig = NULL;
/* Forward declarations */
@ -147,6 +146,8 @@ static void whois_config_setdefaults(void)
whois_config_add("reputation", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
whois_config_add("security-groups", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
whois_config_add("geo", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL);
whois_config_add("certfp", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL);
@ -294,6 +295,7 @@ CMD_FUNC(cmd_whois)
char *p = NULL;
int len, mlen;
char querybuf[BUFSIZE];
char buf[BUFSIZE];
int ntargets = 0;
int maxtargets = max_targets_for_command("WHOIS");
@ -561,6 +563,51 @@ CMD_FUNC(cmd_whois)
}
}
/* The following code deals with security-groups */
policy = whois_get_policy(client, target, "security-groups");
if ((policy > WHOIS_CONFIG_DETAILS_NONE) && !IsULine(target))
{
SecurityGroup *s;
int security_groups_whois_lines = 0;
mlen = strlen(me.name) + strlen(client->name) + 10 + strlen(target->name) + strlen("is in security-groups: ");
if (user_allowed_by_security_group_name(target, "known-users"))
strlcpy(buf, "known-users,", sizeof(buf));
else
strlcpy(buf, "unknown-users,", sizeof(buf));
len = strlen(buf);
for (s = securitygroups; s; s = s->next)
{
if (len + strlen(s->name) > (size_t)BUFSIZE - 4 - mlen)
{
buf[len-1] = '\0';
add_nvplist_numeric_fmt(&list, -15000-security_groups_whois_lines, "security-groups",
target, RPL_WHOISSPECIAL,
"%s :is in security-groups: %s", target->name, buf);
security_groups_whois_lines++;
*buf = '\0';
len = 0;
}
if (strcmp(s->name, "known-users") && user_allowed_by_security_group(target, s))
{
strcpy(buf + len, s->name);
len += strlen(buf+len);
strcpy(buf + len, ",");
len++;
}
}
if (*buf)
{
buf[len-1] = '\0';
add_nvplist_numeric_fmt(&list, -15000-security_groups_whois_lines, "security-groups",
client, RPL_WHOISSPECIAL,
"%s :is in security-groups: %s", target->name, buf);
security_groups_whois_lines++;
}
}
if (MyUser(target) && IsShunned(target) && (whois_get_policy(client, target, "shunned") > WHOIS_CONFIG_DETAILS_NONE))
{
add_nvplist_numeric(&list, -20000, "shunned", client, RPL_WHOISSPECIAL,

View File

@ -3,7 +3,7 @@
* was originally made for tircd and modified to work with u4.
* - 2018 i <ircd@servx.org>
* - 2019 Bram Matthys (Syzop) <syzop@vulnscan.org>
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"

View File

@ -1,6 +1,6 @@
/** Oper classes code.
* (C) Copyright 2015-present tmcarthur and the UnrealIRCd team
* License: GPLv2
* License: GPLv2 or later
*/
#include "unrealircd.h"
@ -282,6 +282,7 @@ OperPermission ValidatePermissionsForPathEx(OperClassACL *acl, OperClassACLPath
OperPermission ValidatePermissionsForPath(const char *path, Client *client, Client *victim, Channel *channel, const void *extra)
{
ConfigItem_oper *ce_oper;
const char *operclass;
ConfigItem_operclass *ce_operClass;
OperClass *oc = NULL;
OperClassACLPath *operPath;
@ -299,15 +300,18 @@ OperPermission ValidatePermissionsForPath(const char *path, Client *client, Clie
ce_oper = find_oper(client->user->operlogin);
if (!ce_oper)
{
return OPER_DENY;
}
ce_operClass = find_operclass(ce_oper->operclass);
if (!ce_operClass)
operclass = moddata_client_get(client, "operclass");
if (!operclass)
return OPER_DENY;
} else
{
return OPER_DENY;
operclass = ce_oper->operclass;
}
ce_operClass = find_operclass(operclass);
if (!ce_operClass)
return OPER_DENY;
oc = ce_operClass->classStruct;
operPath = OperClass_parsePath(path);
while (oc && operPath)

840
src/securitygroup.c Normal file
View File

@ -0,0 +1,840 @@
/*
* Mask & security-group routines.
* (C) Copyright 2015-.. Syzop and the UnrealIRCd team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "unrealircd.h"
/* Global variables */
SecurityGroup *securitygroups = NULL;
/** Free all masks in the mask list */
void unreal_delete_masks(ConfigItem_mask *m)
{
ConfigItem_mask *m_next;
for (; m; m = m_next)
{
m_next = m->next;
safe_free(m->mask);
safe_free(m);
}
}
/** Internal function to add one individual mask to the list */
static void unreal_add_mask(ConfigItem_mask **head, ConfigEntry *ce)
{
ConfigItem_mask *m = safe_alloc(sizeof(ConfigItem_mask));
/* Since we allow both mask "xyz"; and mask { abc; def; };... */
if (ce->value)
safe_strdup(m->mask, ce->value);
else
safe_strdup(m->mask, ce->name);
add_ListItem((ListStruct *)m, (ListStruct **)head);
}
/** Add mask entries from config */
void unreal_add_masks(ConfigItem_mask **head, ConfigEntry *ce)
{
if (ce->items)
{
ConfigEntry *cep;
for (cep = ce->items; cep; cep = cep->next)
unreal_add_mask(head, cep);
} else
{
unreal_add_mask(head, ce);
}
}
/** Check if a client matches any of the masks in the mask list.
* The following rules apply:
* - If you have only negating entries, like '!abc' and '!def', then
* we assume an implicit * rule first, since that is clearly what
* the user wants.
* - If you have a mix, like '*.com', '!irc1*', '!irc2*' then the
* implicit * is dropped and we assume you only want to match *.com,
* with the exception of irc1*.com and irc2*.com.
* - If you only have normal entries without ! then things are
* as they always are.
* @param client The client to run the mask match against
* @param mask The mask entry from the config file
* @returns 1 on match, 0 on non-match.
*/
int unreal_mask_match(Client *client, ConfigItem_mask *mask)
{
int retval = 1;
ConfigItem_mask *m;
if (!mask)
return 0; /* Empty mask block is no match */
/* First check normal matches (without ! prefix) */
for (m = mask; m; m = m->next)
{
if (m->mask[0] != '!')
{
retval = 0; /* no implicit * */
if (match_user(m->mask, client, MATCH_CHECK_REAL|MATCH_CHECK_EXTENDED))
{
retval = 1;
break;
}
}
}
if (retval)
{
/* We matched. Check for exceptions (with ! prefix) */
for (m = mask; m; m = m->next)
{
if ((m->mask[0] == '!') && match_user(m->mask+1, client, MATCH_CHECK_REAL|MATCH_CHECK_EXTENDED))
return 0;
}
}
return retval;
}
/** Check if a string matches any of the masks in the mask list.
* The following rules apply:
* - If you have only negating entries, like '!abc' and '!def', then
* we assume an implicit * rule first, since that is clearly what
* the user wants.
* - If you have a mix, like '*.com', '!irc1*', '!irc2*' then the
* implicit * is dropped and we assume you only want to match *.com,
* with the exception of irc1*.com and irc2*.com.
* - If you only have normal entries without ! then things are
* as they always are.
* @param name The name to run the mask matching on
* @param mask The mask entry from the config file
* @returns 1 on match, 0 on non-match.
*/
int unreal_mask_match_string(const char *name, ConfigItem_mask *mask)
{
int retval = 1;
ConfigItem_mask *m;
if (!mask)
return 0; /* Empty mask block is no match */
/* First check normal matches (without ! prefix) */
for (m = mask; m; m = m->next)
{
if (m->mask[0] != '!')
{
retval = 0; /* no implicit * */
if (match_simple(m->mask, name))
{
retval = 1;
break;
}
}
}
if (retval)
{
/* We matched. Check for exceptions (with ! prefix) */
for (m = mask; m; m = m->next)
{
if ((m->mask[0] == '!') && match_simple(m->mask+1, name))
return 0;
}
}
return retval;
}
#define CheckNullX(x) if ((!(x)->value) || (!(*((x)->value)))) { config_error("%s:%i: missing parameter", (x)->file->filename, (x)->line_number); *errors = *errors + 1; return 0; }
int test_match_item(ConfigFile *conf, ConfigEntry *cep, int *errors)
{
ConfigEntry *cepp;
if (!strcmp(cep->name, "webirc") || !strcmp(cep->name, "exclude-webirc"))
{
CheckNullX(cep);
} else
if (!strcmp(cep->name, "identified") || !strcmp(cep->name, "exclude-identified"))
{
CheckNullX(cep);
} else
if (!strcmp(cep->name, "tls") || !strcmp(cep->name, "exclude-tls"))
{
CheckNullX(cep);
} else
if (!strcmp(cep->name, "reputation-score") || !strcmp(cep->name, "exclude-reputation-score"))
{
const char *str = cep->value;
int v;
CheckNullX(cep);
if (*str == '<')
str++;
v = atoi(str);
if ((v < 1) || (v > 10000))
{
config_error("%s:%i: %s needs to be a value of 1-10000",
cep->file->filename, cep->line_number, cep->name);
*errors = *errors + 1;
}
} else
if (!strcmp(cep->name, "connect-time") || !strcmp(cep->name, "exclude-connect-time"))
{
const char *str = cep->value;
long v;
CheckNullX(cep);
if (*str == '<')
str++;
v = config_checkval(str, CFG_TIME);
if (v < 1)
{
config_error("%s:%i: %s needs to be a time value (and more than 0 seconds)",
cep->file->filename, cep->line_number, cep->name);
*errors = *errors + 1;
}
} else
if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "include-mask") || !strcmp(cep->name, "exclude-mask"))
{
for (cepp = cep->items; cepp; cepp = cepp->next)
{
if (!strcmp(cepp->name, "mask"))
continue;
if (cepp->items || cepp->value)
{
config_error("%s:%i: security-group::mask should contain hostmasks only. "
"Perhaps you meant to use it in security-group { %s ... } directly?",
cepp->file->filename, cepp->line_number,
cepp->name);
*errors = *errors + 1;
}
}
} else
if (!strcmp(cep->name, "ip"))
{
} else
if (!strcmp(cep->name, "security-group") || !strcmp(cep->name, "exclude-security-group"))
{
} else
{
/* Let's see if an extended server ban exists for this item... */
Extban *extban;
if (!strncmp(cep->name, "exclude-", 8))
extban = findmod_by_bantype_raw(cep->name+8, strlen(cep->name+8));
else
extban = findmod_by_bantype_raw(cep->name, strlen(cep->name));
if (extban && (extban->options & EXTBOPT_TKL) && (extban->is_banned_events & BANCHK_TKL))
{
test_extended_list(extban, cep, errors);
return 1; /* Yup, handled */
}
return 0; /* Unhandled: unknown item for us */
}
return 1; /* Handled, but there could be errors */
}
int test_match_block(ConfigFile *conf, ConfigEntry *ce, int *errors_out)
{
int errors = 0;
ConfigEntry *cep;
/* (If there is only a ce->value, trust that it is OK) */
/* Test ce->items... */
for (cep = ce->items; cep; cep = cep->next)
{
/* Only complain about things with values,
* as valueless things like "10.0.0.0/8" are treated as a mask.
*/
if (!test_match_item(conf, cep, &errors) && cep->value)
{
config_error_unknown(cep->file->filename, cep->line_number,
ce->name, cep->name);
errors++;
continue;
}
}
*errors_out = *errors_out + errors;
return errors ? 0 : 1;
}
#define tmbbw_is_wildcard(x) (!strcmp(x, "*") || !strcmp(x, "*@*"))
int test_match_block_too_broad(ConfigFile *conf, ConfigEntry *ce)
{
ConfigEntry *cep, *cepp;
// match *;
if (ce->value && tmbbw_is_wildcard(ce->value))
return 1;
for (cep = ce->items; cep; cep = cep->next)
{
// match { *; }
if (!cep->value && tmbbw_is_wildcard(cep->name))
return 1;
if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "include-mask") || !strcmp(cep->name, "ip"))
{
// match { mask *; }
if (cep->value && tmbbw_is_wildcard(cep->value))
return 1;
// match { mask { *; } }
for (cepp = cep->items; cepp; cepp = cepp->next)
if (tmbbw_is_wildcard(cepp->name))
return 1;
}
}
return 0;
}
int _test_security_group(ConfigFile *conf, ConfigEntry *ce)
{
int errors = 0;
ConfigEntry *cep;
/* First, check the name of the security group */
if (!ce->value)
{
config_error("%s:%i: security-group block needs a name, eg: security-group web-users {",
ce->file->filename, ce->line_number);
errors++;
} else {
if (!strcasecmp(ce->value, "unknown-users"))
{
config_error("%s:%i: The 'unknown-users' group is a special group that is the "
"inverse of 'known-users', you cannot create or adjust it in the "
"config file, as it is created automatically by UnrealIRCd.",
ce->file->filename, ce->line_number);
errors++;
return errors;
}
if (!security_group_valid_name(ce->value))
{
config_error("%s:%i: security-group block name '%s' contains invalid characters or is too long. "
"Only letters, numbers, underscore and hyphen are allowed.",
ce->file->filename, ce->line_number, ce->value);
errors++;
}
}
for (cep = ce->items; cep; cep = cep->next)
{
if (!test_match_item(conf, cep, &errors))
{
config_error_unknown(cep->file->filename, cep->line_number,
"security-group", cep->name);
errors++;
continue;
}
}
return errors;
}
int conf_match_item(ConfigFile *conf, ConfigEntry *cep, SecurityGroup **block)
{
int errors = 0; /* unused */
SecurityGroup *s = *block;
/* The following code is there so we don't create a security group
* unless there is actually a valid config item for it encountered.
* This so the security group '*s' can stay NULL if there are zero
* items, so we don't waste any CPU if it is unused.
*/
if (*block == NULL)
{
/* Yeah we call a TEST routine from a CONFIG RUN routine ;). */
if (!test_match_item(conf, cep, &errors))
return 0; /* not for us */
/* If we are still here then we must create the security group */
*block = s = safe_alloc(sizeof(SecurityGroup));
}
if (!strcmp(cep->name, "webirc"))
s->webirc = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "identified"))
s->identified = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "tls"))
s->tls = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "reputation-score"))
{
if (*cep->value == '<')
s->reputation_score = 0 - atoi(cep->value+1);
else
s->reputation_score = atoi(cep->value);
}
else if (!strcmp(cep->name, "connect-time"))
{
if (*cep->value == '<')
s->connect_time = 0 - config_checkval(cep->value+1, CFG_TIME);
else
s->connect_time = config_checkval(cep->value, CFG_TIME);
}
else if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "include-mask"))
{
unreal_add_masks(&s->mask, cep);
}
else if (!strcmp(cep->name, "ip"))
{
unreal_add_names(&s->ip, cep);
}
else if (!strcmp(cep->name, "security-group"))
{
unreal_add_names(&s->security_group, cep);
}
else if (!strcmp(cep->name, "exclude-webirc"))
s->exclude_webirc = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "exclude-identified"))
s->exclude_identified = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "exclude-tls"))
s->exclude_tls = config_checkval(cep->value, CFG_YESNO);
else if (!strcmp(cep->name, "exclude-reputation-score"))
{
if (*cep->value == '<')
s->exclude_reputation_score = 0 - atoi(cep->value+1);
else
s->exclude_reputation_score = atoi(cep->value);
}
else if (!strcmp(cep->name, "exclude-mask"))
{
unreal_add_masks(&s->exclude_mask, cep);
}
else if (!strcmp(cep->name, "exclude-security-group"))
{
unreal_add_names(&s->security_group, cep);
}
else
{
/* Let's see if an extended server ban exists for this item... this needs to be LAST! */
Extban *extban;
const char *name = cep->name;
if (!strncmp(cep->name, "exclude-", 8))
{
/* Extended (exclusive) ? */
name = cep->name + 8;
if (findmod_by_bantype_raw(name, strlen(name)))
unreal_add_name_values(&s->exclude_extended, name, cep);
else
return 0; /* Unhandled */
} else {
/* Extended (inclusive) */
if (findmod_by_bantype_raw(name, strlen(name)))
unreal_add_name_values(&s->extended, name, cep);
else
return 0; /* Unhandled */
}
}
add_nvplist(&s->printable_list, s->printable_list_counter++, cep->name, cep->value);
return 1; /* Handled by us (guaranteed earlier) */
}
int conf_match_block(ConfigFile *conf, ConfigEntry *ce, SecurityGroup **block)
{
ConfigEntry *cep;
SecurityGroup *s = *block;
if (*block == NULL)
*block = s = safe_alloc(sizeof(SecurityGroup));
/* Check for simple form: match *; / mask *; */
if (ce->value)
{
unreal_add_masks(&s->mask, ce);
add_nvplist(&s->printable_list, s->printable_list_counter++, "mask", ce->value);
}
/* Check for long form: match { .... } / mask { .... } */
for (cep = ce->items; cep; cep = cep->next)
{
if (!conf_match_item(conf, cep, &s) && !cep->value && !cep->items)
{
/* Valueless? Then it must be a mask like 10.0.0.0/8 */
unreal_add_masks(&s->mask, cep);
add_nvplist(&s->printable_list, s->printable_list_counter++, "mask", cep->name);
}
}
return 1;
}
int _conf_security_group(ConfigFile *conf, ConfigEntry *ce)
{
ConfigEntry *cep;
SecurityGroup *s = add_security_group(ce->value, 1);
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "priority"))
{
s->priority = atoi(cep->value);
DelListItem(s, securitygroups);
AddListItemPrio(s, securitygroups, s->priority);
} else
conf_match_item(conf, cep, &s);
}
return 1;
}
/** Check if the name of the security-group contains only valid characters.
* @param name The name of the group
* @returns 1 if name is valid, 0 if not (eg: illegal characters)
*/
int security_group_valid_name(const char *name)
{
const char *p;
if (strlen(name) > SECURITYGROUPLEN)
return 0; /* Too long */
for (p = name; *p; p++)
{
if (!isalnum(*p) && !strchr("_-", *p))
return 0; /* Character not allowed */
}
return 1;
}
/** Find a security-group.
* @param name The name of the security group
* @returns A SecurityGroup struct, or NULL if not found.
*/
SecurityGroup *find_security_group(const char *name)
{
SecurityGroup *s;
for (s = securitygroups; s; s = s->next)
if (!strcasecmp(name, s->name))
return s;
return NULL;
}
/** Checks if a security-group exists.
* This function takes the 'unknown-users' magic group into account as well.
* @param name The name of the security group
* @returns 1 if it exists, 0 if not
*/
int security_group_exists(const char *name)
{
if (!strcmp(name, "unknown-users") || find_security_group(name))
return 1;
return 0;
}
/** Add a new security-group and add it to the list, but search for existing one first.
* @param name The name of the security group
* @returns A SecurityGroup struct (already added to the 'securitygroups' linked list)
*/
SecurityGroup *add_security_group(const char *name, int priority)
{
SecurityGroup *s = find_security_group(name);
/* Existing? */
if (s)
return s;
/* Otherwise, create a new entry */
s = safe_alloc(sizeof(SecurityGroup));
strlcpy(s->name, name, sizeof(s->name));
s->priority = priority;
AddListItemPrio(s, securitygroups, priority);
return s;
}
/** Free a SecurityGroup struct */
void free_security_group(SecurityGroup *s)
{
if (s == NULL)
return;
unreal_delete_masks(s->mask);
unreal_delete_masks(s->exclude_mask);
free_entire_name_list(s->security_group);
free_entire_name_list(s->exclude_security_group);
free_entire_name_list(s->ip);
free_entire_name_list(s->exclude_ip);
free_nvplist(s->extended);
free_nvplist(s->exclude_extended);
free_nvplist(s->printable_list);
safe_free(s);
}
/** Initialize the default security-group blocks */
void set_security_group_defaults(void)
{
SecurityGroup *s, *s_next;
/* First free all security groups */
for (s = securitygroups; s; s = s_next)
{
s_next = s->next;
free_security_group(s);
}
securitygroups = NULL;
/* Default group: webirc */
s = add_security_group("webirc-users", 50);
s->webirc = 1;
/* Default group: known-users */
s = add_security_group("known-users", 100);
s->identified = 1;
s->reputation_score = 25;
s->webirc = 0;
/* Default group: tls-and-known-users */
s = add_security_group("tls-and-known-users", 200);
s->identified = 1;
s->reputation_score = 25;
s->webirc = 0;
s->tls = 1;
/* Default group: tls-users */
s = add_security_group("tls-users", 300);
s->tls = 1;
}
int user_matches_extended_list(Client *client, NameValuePrioList *e)
{
Extban *extban;
BanContext b;
for (; e; e = e->next)
{
extban = findmod_by_bantype_raw(e->name, strlen(e->name));
if (!extban ||
!(extban->options & EXTBOPT_TKL) ||
!(extban->is_banned_events & BANCHK_TKL))
{
continue; /* extban not found or of incorrect type */
}
memset(&b, 0, sizeof(BanContext));
b.client = client;
b.banstr = e->value;
b.ban_check_types = BANCHK_TKL;
if (extban->is_banned(&b))
return 1;
}
return 0;
}
int test_extended_list(Extban *extban, ConfigEntry *cep, int *errors)
{
BanContext b;
if (cep->value)
{
memset(&b, 0, sizeof(BanContext));
b.banstr = cep->value;
b.ban_check_types = BANCHK_TKL;
b.what = MODE_ADD;
if (!extban->conv_param(&b, extban))
{
config_error("%s:%i: %s has an invalid value",
cep->file->filename, cep->line_number, cep->name);
*errors = *errors + 1;
return 0;
}
}
for (cep = cep->items; cep; cep = cep->next)
{
memset(&b, 0, sizeof(BanContext));
b.banstr = cep->name;
b.ban_check_types = BANCHK_TKL;
b.what = MODE_ADD;
if (!extban->conv_param(&b, extban))
{
config_error("%s:%i: %s has an invalid value",
cep->file->filename, cep->line_number, cep->name);
*errors = *errors + 1;
return 0;
}
}
return 1;
}
/** Returns 1 if the user is allowed by any of the security groups in the named list.
* This is only used by security-group::security-group and
* security-group::exclude-security-group.
* @param client Client to check
* @param l The NameList
* @returns 1 if any of the security groups match, 0 if none of them matched.
*/
int user_allowed_by_security_group_list(Client *client, NameList *l)
{
for (; l; l = l->next)
if (user_allowed_by_security_group_name(client, l->name))
return 1;
return 0;
}
/** Returns 1 if the user is OK as far as the security-group is concerned.
* @param client The client to check
* @param s The security-group to check against
* @retval 1 if user is allowed by security-group, 0 if not.
*/
int user_allowed_by_security_group(Client *client, SecurityGroup *s)
{
static int recursion_security_group = 0;
/* Allow NULL securitygroup, makes it easier in the code elsewhere */
if (!s)
return 0;
if (recursion_security_group > 8)
{
unreal_log(ULOG_WARNING, "main", "SECURITY_GROUP_LOOP_DETECTED", client,
"Loop detected while processing security-group '$security_group' -- "
"are you perhaps referencing a security-group from a security-group?",
log_data_string("security_group", s->name));
return 0;
}
recursion_security_group++;
/* DO NOT USE 'return' IN CODE BELOW!!!!!!!!!
* - use 'goto user_not_allowed' to reject
* - use 'goto user_allowed' to accept
*/
/* Process EXCLUSION criteria first... */
if (s->exclude_identified && IsLoggedIn(client))
goto user_not_allowed;
if (s->exclude_webirc && moddata_client_get(client, "webirc"))
goto user_not_allowed;
if ((s->exclude_reputation_score > 0) && (GetReputation(client) >= s->exclude_reputation_score))
goto user_not_allowed;
if ((s->exclude_reputation_score < 0) && (GetReputation(client) < 0 - s->exclude_reputation_score))
goto user_not_allowed;
if (s->exclude_connect_time != 0)
{
long connect_time = get_connected_time(client);
if ((s->exclude_connect_time > 0) && (connect_time >= s->exclude_connect_time))
goto user_not_allowed;
if ((s->exclude_connect_time < 0) && (connect_time < 0 - s->exclude_connect_time))
goto user_not_allowed;
}
if (s->exclude_tls && (IsSecureConnect(client) || (MyConnect(client) && IsSecure(client))))
goto user_not_allowed;
if (s->exclude_mask && unreal_mask_match(client, s->exclude_mask))
goto user_not_allowed;
if (s->exclude_ip && unreal_match_iplist(client, s->exclude_ip))
goto user_not_allowed;
if (s->exclude_security_group && user_allowed_by_security_group_list(client, s->exclude_security_group))
goto user_not_allowed;
if (s->exclude_extended && user_matches_extended_list(client, s->exclude_extended))
goto user_not_allowed;
/* Then process INCLUSION criteria... */
if (s->identified && IsLoggedIn(client))
goto user_allowed;
if (s->webirc && moddata_client_get(client, "webirc"))
goto user_allowed;
if ((s->reputation_score > 0) && (GetReputation(client) >= s->reputation_score))
goto user_allowed;
if ((s->reputation_score < 0) && (GetReputation(client) < 0 - s->reputation_score))
goto user_allowed;
if (s->connect_time != 0)
{
long connect_time = get_connected_time(client);
if ((s->connect_time > 0) && (connect_time >= s->connect_time))
goto user_allowed;
if ((s->connect_time < 0) && (connect_time < 0 - s->connect_time))
goto user_allowed;
}
if (s->tls && (IsSecureConnect(client) || (MyConnect(client) && IsSecure(client))))
goto user_allowed;
if (s->mask && unreal_mask_match(client, s->mask))
goto user_allowed;
if (s->ip && unreal_match_iplist(client, s->ip))
goto user_allowed;
if (s->security_group && user_allowed_by_security_group_list(client, s->security_group))
goto user_allowed;
if (s->extended && user_matches_extended_list(client, s->extended))
goto user_allowed;
user_not_allowed:
recursion_security_group--;
return 0;
user_allowed:
recursion_security_group--;
return 1;
}
/** Returns 1 if the user is OK as far as the security-group is concerned - "by name" version.
* @param client The client to check
* @param secgroupname The name of the security-group to check against
* @retval 1 if user is allowed by security-group, 0 if not.
*/
int user_allowed_by_security_group_name(Client *client, const char *secgroupname)
{
SecurityGroup *s;
/* Handle the magical 'unknown-users' case. */
if (!strcmp(secgroupname, "unknown-users"))
{
/* This is simply the inverse of 'known-users' */
s = find_security_group("known-users");
if (!s)
return 0; /* that's weird!? pretty impossible. */
return !user_allowed_by_security_group(client, s);
}
/* Find the group and evaluate it */
s = find_security_group(secgroupname);
if (!s)
return 0; /* security group not found: no match */
return user_allowed_by_security_group(client, s);
}
/** Get comma separated list of matching security groups for 'client'.
* This is usually only used for displaying purposes.
* @returns string like "unknown-users,tls-users" from a static buffer.
*/
const char *get_security_groups(Client *client)
{
SecurityGroup *s;
static char buf[512];
*buf = '\0';
/* We put known-users or unknown-users at the beginning.
* The latter is special and doesn't actually exist
* in the linked list, hence the special code here,
* and again later in the for loop to skip it.
*/
if (user_allowed_by_security_group_name(client, "known-users"))
strlcat(buf, "known-users,", sizeof(buf));
else
strlcat(buf, "unknown-users,", sizeof(buf));
for (s = securitygroups; s; s = s->next)
{
if (strcmp(s->name, "known-users") &&
user_allowed_by_security_group(client, s))
{
strlcat(buf, s->name, sizeof(buf));
strlcat(buf, ",", sizeof(buf));
}
}
if (*buf)
buf[strlen(buf)-1] = '\0';
return buf;
}

View File

@ -1208,7 +1208,9 @@ void lost_server_link(Client *client, const char *tls_error_string)
if (client->server->conf)
{
unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client,
"Unable to link with server $client [$link_block.ip:$link_block.port]: $tls_error_string",
client->server->conf->outgoing.file
? "Unable to link with server $client [$link_block.file]: $tls_error_string"
: "Unable to link with server $client [$link_block.ip:$link_block.port]: $tls_error_string",
log_data_string("tls_error_string", tls_error_string),
log_data_link_block(client->server->conf));
} else {
@ -1221,7 +1223,9 @@ void lost_server_link(Client *client, const char *tls_error_string)
if (client->server->conf)
{
unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client,
"Unable to link with server $client [$link_block.ip:$link_block.port]: $socket_error",
client->server->conf->outgoing.file
? "Unable to link with server $client [$link_block.file]: $socket_error"
: "Unable to link with server $client [$link_block.ip:$link_block.port]: $socket_error",
log_data_socket_error(client->local->fd),
log_data_link_block(client->server->conf));
} else {

View File

@ -1300,11 +1300,11 @@ int deliver_it(Client *client, char *str, int len, int *want_read)
}
/** Initiate an outgoing connection, the actual connect() call. */
int unreal_connect(int fd, const char *ip, int port, int ipv6)
int unreal_connect(int fd, const char *ip, int port, SocketType socket_type)
{
int n;
if (ipv6)
if (socket_type == SOCKET_TYPE_IPV6)
{
struct sockaddr_in6 server;
memset(&server, 0, sizeof(server));
@ -1312,13 +1312,22 @@ int unreal_connect(int fd, const char *ip, int port, int ipv6)
inet_pton(AF_INET6, ip, &server.sin6_addr);
server.sin6_port = htons(port);
n = connect(fd, (struct sockaddr *)&server, sizeof(server));
} else {
}
else if (socket_type == SOCKET_TYPE_IPV4)
{
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
inet_pton(AF_INET, ip, &server.sin_addr);
server.sin_port = htons(port);
n = connect(fd, (struct sockaddr *)&server, sizeof(server));
} else
{
struct sockaddr_un server;
memset(&server, 0, sizeof(server));
server.sun_family = AF_UNIX;
strlcpy(server.sun_path, ip, sizeof(server.sun_path));
n = connect(fd, (struct sockaddr *)&server, sizeof(server));
}
#ifndef _WIN32

View File

@ -1269,7 +1269,7 @@ void buildvarstring(const char *inbuf, char *outbuf, size_t len, const char *nam
/* find variable name in list */
found = 0;
for (cnt = 0; name[cnt]; cnt++)
if (!strncasecmp(name[cnt], i, p - i))
if (!strncasecmp(name[cnt], i, strlen(name[cnt])))
{
/* Found */
found = 1;

View File

@ -354,7 +354,7 @@ SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server)
if (SSL_CTX_set_ciphersuites(ctx, tlsoptions->ciphersuites) == 0)
{
unreal_log(ULOG_ERROR, "config", "TLS_INVALID_CIPHERSUITES_LIST", NULL,
"Failed to set TLS ciphersuites list '$tls_ciphers_list'\n$tls_error.all",
"Failed to set TLS ciphersuites list '$tls_ciphersuites_list'\n$tls_error.all",
log_data_string("tls_ciphersuites_list", tlsoptions->ciphersuites),
log_data_tls_error());
goto fail;

Some files were not shown because too many files have changed in this diff Show More