From ec9db8fd5f5fe61c5ef8c116f8e31197bb0e77ff Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sat, 18 Mar 2017 15:00:34 +0100 Subject: [PATCH] Move match_user() to module (efunc in m_tkl) --- include/h.h | 5 +- include/modules.h | 1 + src/match.c | 171 ------------------------------------------- src/modules.c | 4 +- src/modules/m_tkl.c | 173 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+), 174 deletions(-) diff --git a/include/h.h b/include/h.h index 6bca573bd..bf29fe7d5 100644 --- a/include/h.h +++ b/include/h.h @@ -122,7 +122,6 @@ extern OperPermission ValidatePermissionsForPath(char *path, aClient *sptr, aCli extern void OperClassValidatorDel(OperClassValidator *validator); extern int AllowClient(aClient *cptr, struct hostent *hp, char *sockhost, char *username); -extern int match_user(char *rmask, aClient *acptr, int options); extern ConfigItem_ban *Find_ban_ip(aClient *sptr); extern void add_ListItem(ListStruct *, ListStruct **); extern void add_ListItemPrio(ListStructPrio *, ListStructPrio **, int); @@ -614,6 +613,7 @@ extern int __attribute__((warn_unused_result)) do_cmd(aClient *cptr, aClient *sp extern void create_snomask(aClient *sptr, anUser *user, char *snomask); extern MODVAR char *me_hash; extern MODVAR int dontspread; + /* Efuncs */ extern MODVAR int (*do_join)(aClient *, aClient *, int, char **); extern MODVAR void (*join_channel)(aChannel *chptr, aClient *cptr, aClient *sptr, int flags); @@ -666,8 +666,9 @@ extern MODVAR void (*broadcast_moddata_client)(aClient *acptr); extern MODVAR int (*check_banned)(aClient *cptr); extern MODVAR void (*introduce_user)(aClient *to, aClient *acptr); extern MODVAR int (*check_deny_version)(aClient *cptr, char *version_string, int protocol, char *flags); - +extern MODVAR int (*match_user)(char *rmask, aClient *acptr, int options); /* /Efuncs */ + extern MODVAR aMotdFile opermotd, svsmotd, motd, botmotd, smotd, rules; extern MODVAR int max_connection_count; extern int add_listmode(Ban **list, aClient *cptr, aChannel *chptr, char *banid); diff --git a/include/modules.h b/include/modules.h index 43ea28215..e7900626d 100644 --- a/include/modules.h +++ b/include/modules.h @@ -1042,6 +1042,7 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum #define EFUNC_SEND_MODDATA_CHANNEL 50 #define EFUNC_SEND_MODDATA_MEMBERS 51 #define EFUNC_BROADCAST_MODDATA_CLIENT 52 +#define EFUNC_MATCH_USER 53 /* Module flags */ #define MODFLAG_NONE 0x0000 diff --git a/src/match.c b/src/match.c index 40b8eea2e..337ceeda9 100644 --- a/src/match.c +++ b/src/match.c @@ -579,174 +579,3 @@ char *unreal_match_method_valtostr(int val) return "unknown"; } - -/* compares the first 'mask' bits. returns 1 if equal, 0 if not. - * taken from atheme - */ -static int comp_with_mask(void *addr, void *dest, u_int mask) -{ - if (memcmp(addr, dest, mask / 8) == 0) - { - int n = mask / 8; - int m = ((-1) << (8 - (mask % 8))); - if (mask % 8 == 0 || (((u_char *) addr)[n] & m) == (((u_char *) dest)[n] & m)) - { - return (1); - } - } - return (0); -} - -#define IPSZ 16 - -/** Match a user against a mask. - * This will deal with 'nick!user@host', 'user@host' and just 'host'. - * We try to match the 'host' portion against the client IP, real host, etc... - * CIDR support is available so 'host' may be like '1.2.0.0/16'. - * @returns 1 on match, 0 on no match. - */ -int match_user(char *rmask, aClient *acptr, int options) -{ - char mask[NICKLEN+USERLEN+HOSTLEN+8]; - char clientip[IPSZ], maskip[IPSZ]; - char *p = NULL; - char *nmask = NULL, *umask = NULL, *hmask = NULL; - int cidr = -1; /* CIDR length, -1 for no CIDR */ - - strlcpy(mask, rmask, sizeof(mask)); - - if (!(options & MATCH_MASK_IS_UHOST)) - { - p = strchr(mask, '!'); - if (p) - { - *p++ = '\0'; - if (!*mask) - return 0; /* NOMATCH: '!...' */ - nmask = mask; - umask = p; - - /* Could just as well check nick right now */ - if (match(nmask, acptr->name) != 0) - return 0; /* NOMATCH: nick mask did not match */ - } - } - - if (!(options & (MATCH_MASK_IS_HOST))) - { - p = strchr(p ? p : mask, '@'); - if (p) - { - char *client_username = (acptr->user && *acptr->user->username) ? acptr->user->username : acptr->username; - - *p++ = '\0'; - if (!*p || !*mask) - return 0; /* NOMATCH: '...@' or '@...' */ - hmask = p; - if (!umask) - umask = mask; - - /* Check user portion right away */ - if (match(umask, client_username) != 0) - return 0; /* NOMATCH: user mask did not match */ - } else { - if (nmask) - return 0; /* NOMATCH: 'abc!def' (or even just 'abc!') */ - hmask = mask; - } - } else { - hmask = mask; - } - - /* If we get here then we have done checking nick / ident (if it was needed) - * and now need to match the 'host' portion. - */ - - /**** Check visible host ****/ - if (options & MATCH_CHECK_VISIBLE_HOST) - { - char *hostname = acptr->user ? GetHost(acptr) : (MyClient(acptr) ? acptr->local->sockhost : NULL); - if (hostname && (match(hmask, hostname) == 0)) - return 1; /* MATCH: visible host */ - } - - /**** Check cloaked host ****/ - if (options & MATCH_CHECK_CLOAKED_HOST) - { - if (acptr->user && (match(hmask, acptr->user->cloakedhost) == 0)) - return 1; /* MATCH: cloaked host */ - } - - /**** check on IP ****/ - if (options & MATCH_CHECK_IP) - { - p = strchr(hmask, '/'); - if (p) - { - *p++ = '\0'; - cidr = atoi(p); - if (cidr <= 0) - return 0; /* NOMATCH: invalid CIDR */ - } - - if (strchr(hmask, '?') || strchr(hmask, '*')) - { - /* Wildcards */ - if (acptr->ip && (match(hmask, acptr->ip) == 0)) - return 1; /* MATCH (IP with wildcards) */ - } else - if (strchr(hmask, ':')) - { - /* IPv6 hostmask */ - - /* We can actually return here on match/nomatch as we don't need to check the - * virtual host and things like that since ':' can never be in a hostname. - */ - if (!acptr->ip || !strchr(acptr->ip, ':')) - return 0; /* NOMATCH: hmask is IPv6 address and client is not IPv6 */ - if (!inet_pton6(acptr->ip, clientip)) - return 0; /* NOMATCH: unusual failure */ - if (!inet_pton6(hmask, maskip)) - return 0; /* NOMATCH: invalid IPv6 IP in hostmask */ - - if (cidr < 0) - return comp_with_mask(clientip, maskip, 128); /* MATCH/NOMATCH by exact IP */ - - if (cidr > 128) - return 0; /* NOMATCH: invalid CIDR */ - - return comp_with_mask(clientip, maskip, cidr); - } else - { - /* Host is not IPv6 and does not contain wildcards. - * So could be a literal IPv4 address or IPv4 CIDR. - * NOTE: could also be neither (like a real hostname), so don't return 0 on nomatch, - * in that case we should just continue... - * The exception is CIDR. If we have CIDR mask then don't bother checking for - * virtual hosts and things like that since '/' can never be in a hostname. - */ - if (acptr->ip && inet_pton4(acptr->ip, clientip) && inet_pton4(hmask, maskip)) - { - if (cidr < 0) - { - if (comp_with_mask(clientip, maskip, 32)) - return 1; /* MATCH: exact IP */ - } - else if (cidr > 32) - return 0; /* NOMATCH: invalid CIDR */ - else - return comp_with_mask(clientip, maskip, cidr); /* MATCH/NOMATCH by CIDR */ - } - } - } - - /**** Check real host ****/ - if (options & MATCH_CHECK_REAL_HOST) - { - char *hostname = acptr->user ? acptr->user->realhost : (MyClient(acptr) ? acptr->local->sockhost : NULL); - if (hostname && (match(hmask, hostname) == 0)) - return 1; /* MATCH: hostname match */ - } - - return 0; /* NOMATCH: nothing of the above matched */ -} diff --git a/src/modules.c b/src/modules.c index 6607ea089..039e9dcf6 100644 --- a/src/modules.c +++ b/src/modules.c @@ -131,6 +131,7 @@ void (*send_moddata_client)(aClient *srv, aClient *acptr); void (*send_moddata_channel)(aClient *srv, aChannel *chptr); void (*send_moddata_members)(aClient *srv); void (*broadcast_moddata_client)(aClient *acptr); +int (*match_user)(char *rmask, aClient *acptr, int options); static const EfunctionsList efunction_table[MAXEFUNCTIONS] = { /* 00 */ {NULL, NULL}, @@ -186,7 +187,8 @@ static const EfunctionsList efunction_table[MAXEFUNCTIONS] = { /* 50 */ {"send_moddata_channel", (void *)&send_moddata_channel}, /* 51 */ {"send_moddata_members", (void *)&send_moddata_members}, /* 52 */ {"broadcast_moddata_client", (void *)&broadcast_moddata_client}, -/* 53 */ {NULL, NULL} +/* 53 */ {"match_user", (void *)&match_user}, +/* 54 */ {NULL, NULL} }; #ifdef UNDERSCORE diff --git a/src/modules/m_tkl.c b/src/modules/m_tkl.c index c59acd0a1..44487a90f 100644 --- a/src/modules/m_tkl.c +++ b/src/modules/m_tkl.c @@ -71,6 +71,7 @@ int _place_host_ban(aClient *sptr, int action, char *reason, long duration); int _dospamfilter(aClient *sptr, char *str_in, int type, char *target, int flags, aTKline **rettk); int _dospamfilter_viruschan(aClient *sptr, aTKline *tk, int type); void _spamfilter_build_user_string(char *buf, char *nick, aClient *acptr); +int _match_user(char *rmask, aClient *acptr, int options); extern MODVAR char zlinebuf[BUFSIZE]; extern MODVAR aTKline *tklines[TKLISTLEN]; @@ -121,6 +122,7 @@ MOD_TEST(m_tkl) EfunctionAdd(modinfo->handle, EFUNC_DOSPAMFILTER, _dospamfilter); EfunctionAdd(modinfo->handle, EFUNC_DOSPAMFILTER_VIRUSCHAN, _dospamfilter_viruschan); EfunctionAddVoid(modinfo->handle, EFUNC_SPAMFILTER_BUILD_USER_STRING, _spamfilter_build_user_string); + EfunctionAdd(modinfo->handle, EFUNC_MATCH_USER, _match_user); return MOD_SUCCESS; } @@ -2625,3 +2627,174 @@ long ms_past; return 0; /* NOTREACHED */ } + +/* compares the first 'mask' bits. returns 1 if equal, 0 if not. + * taken from atheme + */ +static int comp_with_mask(void *addr, void *dest, u_int mask) +{ + if (memcmp(addr, dest, mask / 8) == 0) + { + int n = mask / 8; + int m = ((-1) << (8 - (mask % 8))); + if (mask % 8 == 0 || (((u_char *) addr)[n] & m) == (((u_char *) dest)[n] & m)) + { + return (1); + } + } + return (0); +} + +#define IPSZ 16 + +/** Match a user against a mask. + * This will deal with 'nick!user@host', 'user@host' and just 'host'. + * We try to match the 'host' portion against the client IP, real host, etc... + * CIDR support is available so 'host' may be like '1.2.0.0/16'. + * @returns 1 on match, 0 on no match. + */ +int _match_user(char *rmask, aClient *acptr, int options) +{ + char mask[NICKLEN+USERLEN+HOSTLEN+8]; + char clientip[IPSZ], maskip[IPSZ]; + char *p = NULL; + char *nmask = NULL, *umask = NULL, *hmask = NULL; + int cidr = -1; /* CIDR length, -1 for no CIDR */ + + strlcpy(mask, rmask, sizeof(mask)); + + if (!(options & MATCH_MASK_IS_UHOST)) + { + p = strchr(mask, '!'); + if (p) + { + *p++ = '\0'; + if (!*mask) + return 0; /* NOMATCH: '!...' */ + nmask = mask; + umask = p; + + /* Could just as well check nick right now */ + if (match(nmask, acptr->name) != 0) + return 0; /* NOMATCH: nick mask did not match */ + } + } + + if (!(options & (MATCH_MASK_IS_HOST))) + { + p = strchr(p ? p : mask, '@'); + if (p) + { + char *client_username = (acptr->user && *acptr->user->username) ? acptr->user->username : acptr->username; + + *p++ = '\0'; + if (!*p || !*mask) + return 0; /* NOMATCH: '...@' or '@...' */ + hmask = p; + if (!umask) + umask = mask; + + /* Check user portion right away */ + if (match(umask, client_username) != 0) + return 0; /* NOMATCH: user mask did not match */ + } else { + if (nmask) + return 0; /* NOMATCH: 'abc!def' (or even just 'abc!') */ + hmask = mask; + } + } else { + hmask = mask; + } + + /* If we get here then we have done checking nick / ident (if it was needed) + * and now need to match the 'host' portion. + */ + + /**** Check visible host ****/ + if (options & MATCH_CHECK_VISIBLE_HOST) + { + char *hostname = acptr->user ? GetHost(acptr) : (MyClient(acptr) ? acptr->local->sockhost : NULL); + if (hostname && (match(hmask, hostname) == 0)) + return 1; /* MATCH: visible host */ + } + + /**** Check cloaked host ****/ + if (options & MATCH_CHECK_CLOAKED_HOST) + { + if (acptr->user && (match(hmask, acptr->user->cloakedhost) == 0)) + return 1; /* MATCH: cloaked host */ + } + + /**** check on IP ****/ + if (options & MATCH_CHECK_IP) + { + p = strchr(hmask, '/'); + if (p) + { + *p++ = '\0'; + cidr = atoi(p); + if (cidr <= 0) + return 0; /* NOMATCH: invalid CIDR */ + } + + if (strchr(hmask, '?') || strchr(hmask, '*')) + { + /* Wildcards */ + if (acptr->ip && (match(hmask, acptr->ip) == 0)) + return 1; /* MATCH (IP with wildcards) */ + } else + if (strchr(hmask, ':')) + { + /* IPv6 hostmask */ + + /* We can actually return here on match/nomatch as we don't need to check the + * virtual host and things like that since ':' can never be in a hostname. + */ + if (!acptr->ip || !strchr(acptr->ip, ':')) + return 0; /* NOMATCH: hmask is IPv6 address and client is not IPv6 */ + if (!inet_pton6(acptr->ip, clientip)) + return 0; /* NOMATCH: unusual failure */ + if (!inet_pton6(hmask, maskip)) + return 0; /* NOMATCH: invalid IPv6 IP in hostmask */ + + if (cidr < 0) + return comp_with_mask(clientip, maskip, 128); /* MATCH/NOMATCH by exact IP */ + + if (cidr > 128) + return 0; /* NOMATCH: invalid CIDR */ + + return comp_with_mask(clientip, maskip, cidr); + } else + { + /* Host is not IPv6 and does not contain wildcards. + * So could be a literal IPv4 address or IPv4 CIDR. + * NOTE: could also be neither (like a real hostname), so don't return 0 on nomatch, + * in that case we should just continue... + * The exception is CIDR. If we have CIDR mask then don't bother checking for + * virtual hosts and things like that since '/' can never be in a hostname. + */ + if (acptr->ip && inet_pton4(acptr->ip, clientip) && inet_pton4(hmask, maskip)) + { + if (cidr < 0) + { + if (comp_with_mask(clientip, maskip, 32)) + return 1; /* MATCH: exact IP */ + } + else if (cidr > 32) + return 0; /* NOMATCH: invalid CIDR */ + else + return comp_with_mask(clientip, maskip, cidr); /* MATCH/NOMATCH by CIDR */ + } + } + } + + /**** Check real host ****/ + if (options & MATCH_CHECK_REAL_HOST) + { + char *hostname = acptr->user ? acptr->user->realhost : (MyClient(acptr) ? acptr->local->sockhost : NULL); + if (hostname && (match(hmask, hostname) == 0)) + return 1; /* MATCH: hostname match */ + } + + return 0; /* NOMATCH: nothing of the above matched */ +}