From: paul Date: Sun, 23 Oct 2005 21:59:00 +0000 (+0100) Subject: forward port of asuka-sethost.patch to .12. X-Git-Tag: u2.10.12.10+snircd(1.3.0)~68 X-Git-Url: https://jfr.im/git/irc/quakenet/snircd.git/commitdiff_plain/1d4df40c15332c7c7d9a0cf95bf212c2cc81d505 forward port of asuka-sethost.patch to .12. Note: check - corrupted patch file manually applied. --- diff --git a/include/client.h b/include/client.h index ce6ebad..1983bfb 100644 --- a/include/client.h +++ b/include/client.h @@ -169,6 +169,7 @@ enum Flag FLAG_ACCOUNTONLY, /**< ASUKA_R: hide privmsgs/notices if user is not authed or opered */ FLAG_HIDDENHOST, /**< user's host is hidden */ + FLAG_SETHOST, /**< ASUKA_h: oper's host is changed */ FLAG_NOCHAN, /**< user's channels are hidden */ FLAG_NOIDLE, /**< user's idletime is hidden */ FLAG_XTRAOP, /**< oper has special powers */ @@ -626,6 +627,9 @@ struct Client { #define IsPrivileged(x) (IsAnOper(x) || IsServer(x)) /** Return non-zero if the client's host is hidden. */ #define HasHiddenHost(x) (IsHiddenHost(x) && IsAccount(x)) +/** Return non-zero if the client is using a spoofhost */ +#define IsSetHost(x) HasFlag(x, FLAG_SETHOST) +#define HasSetHost(x) (IsSetHost(x)) /** Mark a client as having an in-progress net.burst. */ #define SetBurst(x) SetFlag(x, FLAG_BURST) @@ -667,6 +671,8 @@ struct Client { #define SetAccount(x) SetFlag(x, FLAG_ACCOUNT) /** Mark a client as having mode +x (hidden host). */ #define SetHiddenHost(x) SetFlag(x, FLAG_HIDDENHOST) +/** Mark a client as having mode +h (spoofhost). */ +#define SetSetHost(x) SetFlag(x, FLAG_SETHOST) /** Mark a client as having mode +X (xtraop). */ #define SetXtraOp(x) SetFlag(x, FLAG_XTRAOP) /** Mark a client as having mode +n (hide channels). */ @@ -708,6 +714,8 @@ struct Client { #define ClearServNotice(x) ClrFlag(x, FLAG_SERVNOTICE) /** Remove mode +x (hidden host) from the client. */ #define ClearHiddenHost(x) ClrFlag(x, FLAG_HIDDENHOST) +/** Remove mode +h (spoofhost) from a client. */ +#define ClearSetHost(x) ClrFlag(x, FLAG_SETHOST) /** Remove mode +X (xtraop) from a client. */ #define ClearXtraOp(x) ClrFlag(x, FLAG_XTRAOP) /** Remove mode +n (hide channels) from a client. */ diff --git a/include/handlers.h b/include/handlers.h index 36c80fb..0a56025 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -124,6 +124,7 @@ extern int m_proto(struct Client*, struct Client*, int, char*[]); extern int m_pseudo(struct Client*, struct Client*, int, char*[]); extern int m_quit(struct Client*, struct Client*, int, char*[]); extern int m_registered(struct Client*, struct Client*, int, char*[]); +extern int m_sethost(struct Client*, struct Client*, int, char*[]); extern int m_silence(struct Client*, struct Client*, int, char*[]); extern int m_stats(struct Client*, struct Client*, int, char*[]); extern int m_time(struct Client*, struct Client*, int, char*[]); diff --git a/include/ircd_features.h b/include/ircd_features.h index ccba85f..589b746 100644 --- a/include/ircd_features.h +++ b/include/ircd_features.h @@ -101,6 +101,10 @@ enum Feature { /* features that affect all operators */ FEAT_CONFIG_OPERCMDS, + FEAT_SETHOST, + FEAT_SETHOST_FREEFORM, + FEAT_SETHOST_USER, + FEAT_SETHOST_AUTO, /* HEAD_IN_SAND Features */ FEAT_HIS_SNOTICES, @@ -129,6 +133,7 @@ enum Feature { FEAT_HIS_STATS_q, FEAT_HIS_STATS_R, FEAT_HIS_STATS_r, + FEAT_HIS_STATS_s, FEAT_HIS_STATS_t, FEAT_HIS_STATS_T, FEAT_HIS_STATS_u, diff --git a/include/ircd_log.h b/include/ircd_log.h index d50590e..472ca8b 100644 --- a/include/ircd_log.h +++ b/include/ircd_log.h @@ -65,6 +65,7 @@ enum LogSys { LS_SOCKET, /**< Unexpected socket operation errors. */ LS_IAUTH, /**< IAuth status. */ LS_DEBUG, /**< Debug messages. */ + LS_SETHOST, /**< Usage of the sethost command. */ LS_LAST_SYSTEM /**< Count of valid LogSys values. */ }; diff --git a/include/msg.h b/include/msg.h index 212852c..ee2e4d6 100644 --- a/include/msg.h +++ b/include/msg.h @@ -356,6 +356,10 @@ struct Client; #define TOK_PRIVS "PR" #define CMD_PRIVS MSG_PRIVS, TOK_PRIVS +#define MSG_SETHOST "SETHOST" /* SETHOST */ +#define TOK_SETHOST "SH" +#define CMD_SETHOST MSG_SETHOST, TOK_SETHOST + #define MSG_CAP "CAP" #define TOK_CAP "CAP" #define CMD_CAP MSG_CAP, TOK_CAP diff --git a/include/numeric.h b/include/numeric.h index 939561a..3e5fd20 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -207,7 +207,7 @@ extern const struct Numeric* get_error_numeric(int err); #define RPL_AWAY 301 #define RPL_USERHOST 302 #define RPL_ISON 303 -/* RPL_TEXT 304 unused */ +#define RPL_TEXT 304 /* unused */ #define RPL_UNAWAY 305 #define RPL_NOWAWAY 306 /* NotAway, aircd */ @@ -312,6 +312,8 @@ extern const struct Numeric* get_error_numeric(int err); /* RPL_END_USERS 394 Dalnet/EFnet/IRCnet */ /* RPL_NOUSERS 395 Dalnet/EFnet/IRCnet */ #define RPL_HOSTHIDDEN 396 /* UMODE +x completed succesfuly */ +#define RPL_STATSSLINE 398 /* QuakeNet extension -froo */ +#define RPL_USINGSLINE 399 /* QuakeNet extension -froo */ /* * Errors are in the range from 400-599 currently and are grouped by what @@ -457,7 +459,9 @@ extern const struct Numeric* get_error_numeric(int err); ERR_LISTSYNTAX 521 dalnet ERR_WHOSYNTAX 522 dalnet ERR_WHOLIMEXCEED 523 dalnet */ -#define ERR_QUARANTINED 524 /* Undernet extension -Vampire */ +#define ERR_QUARANTINED 524 /* Undernet extension -Vampire */ +#define ERR_BADHOSTMASK 530 /* QuakeNet extension -froo */ +#define ERR_HOSTUNAVAIL 531 /* QuakeNet extension -froo */ #define ERR_NOTLOWEROPLEVEL 560 /* Undernet extension */ #define ERR_NOTMANAGER 561 /* Undernet extension */ diff --git a/include/s_conf.h b/include/s_conf.h index e309f2f..5f119ce 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -80,6 +80,20 @@ struct qline char *reason; /**< Reason for quarantine. */ }; +struct sline { + struct sline *next; + char *spoofhost; + char *passwd; + char *realhost; + char *username; + struct irc_in_addr address; + unsigned int flags; + char bits; /* Number of bits for CIDR match on realhost */ +}; + +#define SLINE_FLAGS_HOSTNAME 0x0001 /* S-line by hostname */ +#define SLINE_FLAGS_IP 0x0002 /* S-line by IP address/CIDR */ + /** Local K-line structure. */ struct DenyConf { struct DenyConf* next; /**< Next DenyConf in #denyConfList. */ @@ -157,6 +171,7 @@ extern struct ConfItem* GlobalConfList; extern int GlobalConfCount; extern struct s_map* GlobalServiceMapList; extern struct qline* GlobalQuarantineList; +extern struct sline* GlobalSList; /* * Proto types @@ -188,6 +203,11 @@ extern void conf_parse_userhost(struct ConfItem *aconf, char *host); extern struct ConfItem *conf_debug_iline(const char *client); extern void free_mapping(struct s_map *smap); +extern void conf_add_sline(const char* const* fields, int count); +extern void clear_slines(void); +extern int conf_check_slines(struct Client *cptr); +extern void free_spoofhost(struct sline *spoof); + extern void yyerror(const char *msg); #endif /* INCLUDED_s_conf_h */ diff --git a/include/s_user.h b/include/s_user.h index 9565a6f..7c911a9 100644 --- a/include/s_user.h +++ b/include/s_user.h @@ -79,6 +79,8 @@ extern void send_user_info(struct Client* to, char* names, int rpl, InfoFormatter fmt); extern int hide_hostmask(struct Client *cptr, unsigned int flags); +extern int set_hostmask(struct Client *cptr, char *hostmask, char *password); +extern int is_hostmask(char *word); extern int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[]); extern int is_silenced(struct Client *sptr, struct Client *acptr); diff --git a/include/struct.h b/include/struct.h index 361f1ae..3583369 100644 --- a/include/struct.h +++ b/include/struct.h @@ -79,10 +79,11 @@ struct User { * overwritten with the ident response. */ char username[USERLEN + 1]; - char host[HOSTLEN + 1]; /**< displayed hostname */ - char realhost[HOSTLEN + 1]; /**< actual hostname */ - char account[ACCOUNTLEN + 1]; /**< IRC account name */ - time_t acc_create; /**< IRC account timestamp */ + char host[HOSTLEN + 1]; /**< displayed hostname */ + char realusername[USERLEN + 1]; /**< actual username */ + char realhost[HOSTLEN + 1]; /**< actual hostname */ + char account[ACCOUNTLEN + 1]; /**< IRC account name */ + time_t acc_create; /**< IRC account timestamp */ }; #endif /* INCLUDED_struct_h */ diff --git a/ircd/Makefile.in b/ircd/Makefile.in index aa28b7e..e926e8a 100644 --- a/ircd/Makefile.in +++ b/ircd/Makefile.in @@ -168,6 +168,7 @@ IRCD_SRC = \ m_rpong.c \ m_server.c \ m_set.c \ + m_sethost.c \ m_settime.c \ m_silence.c \ m_squit.c \ diff --git a/ircd/channel.c b/ircd/channel.c index 2a2d211..aaa53db 100644 --- a/ircd/channel.c +++ b/ircd/channel.c @@ -357,7 +357,7 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist) ircd_ntoa_r(iphost, &cli_ip(cptr)); if (!IsAccount(cptr)) sr = NULL; - else if (HasHiddenHost(cptr)) + else if (HasHiddenHost(cptr) || HasSetHost(cptr)) sr = cli_user(cptr)->realhost; else { diff --git a/ircd/gline.c b/ircd/gline.c index 3b4fea7..89fa06c 100644 --- a/ircd/gline.c +++ b/ircd/gline.c @@ -217,7 +217,7 @@ do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline) Debug((DEBUG_DEBUG,"Matched!")); } else { /* Host/IP gline */ if (cli_user(acptr)->username && - match(gline->gl_user, (cli_user(acptr))->username) != 0) + match(gline->gl_user, (cli_user(acptr))->realusername) != 0) continue; if (GlineIsIpMask(gline)) { diff --git a/ircd/ircd_features.c b/ircd/ircd_features.c index 92e0d02..bb44fae 100644 --- a/ircd/ircd_features.c +++ b/ircd/ircd_features.c @@ -354,6 +354,10 @@ static struct FeatureDesc { /* features that affect all operators */ F_B(CONFIG_OPERCMDS, 0, 0, 0), + F_B(SETHOST, 0, 0, 0), + F_B(SETHOST_FREEFORM, 0, 0, 0), + F_B(SETHOST_USER, 0, 0, 0), + F_B(SETHOST_AUTO, 0, 0, 0), /* HEAD_IN_SAND Features */ F_B(HIS_SNOTICES, 0, 1, 0), @@ -382,6 +386,7 @@ static struct FeatureDesc { F_B(HIS_STATS_q, 0, 1, 0), F_B(HIS_STATS_R, 0, 1, 0), F_B(HIS_STATS_r, 0, 1, 0), + F_B(HIS_STATS_s, 0, 1, 0), F_B(HIS_STATS_t, 0, 1, 0), F_B(HIS_STATS_T, 0, 1, 0), F_B(HIS_STATS_u, 0, 0, 0), diff --git a/ircd/ircd_lexer.l b/ircd/ircd_lexer.l index 6b49b54..1543dd6 100644 --- a/ircd/ircd_lexer.l +++ b/ircd/ircd_lexer.l @@ -102,6 +102,7 @@ static struct lexer_token { TOKEN(USERMODE), TOKEN(FAST), TOKEN(AUTOCONNECT), + TOKEN(SPOOFHOST), #undef TOKEN { "administrator", ADMIN }, { "apass_opmode", TPRIV_APASS_OPMODE }, diff --git a/ircd/ircd_log.c b/ircd/ircd_log.c index f5872bc..eca3302 100644 --- a/ircd/ircd_log.c +++ b/ircd/ircd_log.c @@ -164,6 +164,7 @@ static struct LogDesc { S(SOCKET, -1, 0), S(IAUTH, -1, SNO_NETWORK), S(DEBUG, -1, SNO_DEBUG), + S(SETHOST, -1, SNO_OLDSNO), #undef S { LS_LAST_SYSTEM, 0, 0, -1, 0, -1, 0 } }; diff --git a/ircd/ircd_parser.y b/ircd/ircd_parser.y index 4ec19c5..20fbc5e 100644 --- a/ircd/ircd_parser.y +++ b/ircd/ircd_parser.y @@ -65,6 +65,7 @@ extern struct ServerConf* serverConfList; extern struct s_map* GlobalServiceMapList; extern struct qline* GlobalQuarantineList; + extern struct sline* GlobalSList; int yylex(void); /* Now all the globals we need :/... */ @@ -75,6 +76,7 @@ struct DenyConf *dconf; struct ServerConf *sconf; struct s_map *smap; + struct sline *spoof; struct Privs privs; struct Privs privs_dirty; @@ -156,6 +158,7 @@ static void parse_error(char *pattern,...) { %token TIMEOUT %token FAST %token AUTOCONNECT +%token SPOOFHOST /* and now a lot of privileges... */ %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE @@ -182,7 +185,7 @@ blocks: blocks block | block; block: adminblock | generalblock | classblock | connectblock | uworldblock | operblock | portblock | jupeblock | clientblock | killblock | cruleblock | motdblock | featuresblock | quarantineblock | - pseudoblock | iauthblock | error ';'; + pseudoblock | iauthblock | spoofblock | error ';'; /* The timespec, sizespec and expr was ripped straight from * ircd-hybrid-7. */ @@ -1044,3 +1047,62 @@ iauthtimeout: TIMEOUT '=' timespec ';' { tping = $3; }; + +spoofblock: SPOOFHOST QSTRING '{' +{ + spoof = MyCalloc(1, sizeof(struct sline)); + spoof->spoofhost = $2; + spoof->passwd = NULL; + spoof->realhost = NULL; + spoof->username = NULL; +} +spoofitems '}' ';' +{ + struct irc_in_addr ip; + char bits; + + if (spoof->username == NULL && spoof->realhost) { + parse_error("Username missing in spoofhost."); + } else if (spoof->realhost == NULL && spoof->username) { + parse_error("Realhost missing in spoofhost."); + } + + if (spoof->realhost) { + if (!string_has_wildcards(spoof->realhost)) { + if (ipmask_parse(spoof->realhost, &ip, &bits) != 0) { + spoof->address = ip; + spoof->bits = bits; + spoof->flags = SLINE_FLAGS_IP; + } else { + Debug((DEBUG_DEBUG, "S-Line: \"%s\" appears not to be a valid IP address, might be wildcarded.", spoof->realhost)); + spoof->flags = SLINE_FLAGS_HOSTNAME; + } + } else + spoof->flags = SLINE_FLAGS_HOSTNAME; + } else + spoof->flags = 0; + + + spoof->next = GlobalSList; + GlobalSList = spoof; + + spoof = NULL; +}; + +spoofitems: spoofitem spoofitems | spoofitem; +spoofitem: spoofpassword | spoofrealhost | spoofrealident; +spoofpassword: PASS '=' QSTRING ';' +{ + MyFree(spoof->passwd); + spoof->passwd = $3; +}; +spoofrealhost: HOST '=' QSTRING ';' +{ + MyFree(spoof->realhost); + spoof->realhost = $3; +}; +spoofrealident: USERNAME '=' QSTRING ';' +{ + MyFree(spoof->username); + spoof->username = $3; +}; diff --git a/ircd/ircd_snprintf.c b/ircd/ircd_snprintf.c index 673daa8..af47af5 100644 --- a/ircd/ircd_snprintf.c +++ b/ircd/ircd_snprintf.c @@ -180,8 +180,8 @@ struct FieldData { #define CONV_VARARGS 0x05000000 /**< convert a %v */ #define CONV_CLIENT 0x06000000 /**< convert a struct Client */ #define CONV_CHANNEL 0x07000000 /**< convert a struct Channel */ +#define CONV_REAL 0x08000000 /**< convert a struct Client and show realhost */ -#define CONV_RESERVED7 0x08000000 /**< reserved for future expansion */ #define CONV_RESERVED6 0x09000000 /**< reserved for future expansion */ #define CONV_RESERVED5 0x0a000000 /**< reserved for future expansion */ #define CONV_RESERVED4 0x0b000000 /**< reserved for future expansion */ @@ -1778,6 +1778,11 @@ doprintf(struct Client *dest, struct BufData *buf_p, const char *fmt, fld_s.flags |= ARG_PTR | CONV_CLIENT; break; + case 'R': /* convert a client name... */ + fld_s.flags &= ~(FLAG_PLUS | FLAG_SPACE | FLAG_ZERO | TYPE_MASK); + fld_s.flags |= ARG_PTR | CONV_REAL; + break; + case 'H': /* convert a channel name... */ fld_s.flags &= ~(FLAG_PLUS | FLAG_SPACE | FLAG_ALT | FLAG_ZERO | FLAG_COLON | TYPE_MASK); @@ -2038,7 +2043,8 @@ doprintf(struct Client *dest, struct BufData *buf_p, const char *fmt, vdata->vd_chars = buf_s.buf_loc; /* return relevant data */ vdata->vd_overflow = SNP_MAX(buf_s.buf_overflow, buf_s.overflow); - } else if ((fld_s.flags & CONV_MASK) == CONV_CLIENT) { + } else if (((fld_s.flags & CONV_MASK) == CONV_CLIENT) || + ((fld_s.flags & CONV_MASK) == CONV_REAL)) { struct Client *cptr = (struct Client*) fld_s.value.v_ptr; const char *str1 = 0, *str2 = 0, *str3 = 0; int slen1 = 0, slen2 = 0, slen3 = 0, elen = 0, plen = 0; @@ -2057,8 +2063,13 @@ doprintf(struct Client *dest, struct BufData *buf_p, const char *fmt, if (!IsServer(cptr) && !IsMe(cptr) && fld_s.flags & FLAG_ALT) { assert(0 != cli_user(cptr)); assert(0 != *(cli_name(cptr))); - str2 = cli_user(cptr)->username; - str3 = cli_user(cptr)->host; + if ((fld_s.flags & CONV_MASK) == CONV_REAL) { + str2 = cli_user(cptr)->realusername; + str3 = cli_user(cptr)->realhost; + } else { + str2 = cli_user(cptr)->username; + str3 = cli_user(cptr)->host; + } } else fld_s.flags &= ~FLAG_ALT; } diff --git a/ircd/m_oper.c b/ircd/m_oper.c index 15cc509..580a5ca 100644 --- a/ircd/m_oper.c +++ b/ircd/m_oper.c @@ -151,7 +151,7 @@ int m_oper(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { send_reply(sptr, ERR_NOOPERHOST); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed OPER attempt by %s (%s@%s)", - parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); + parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr)); return 0; } assert(0 != (aconf->status & CONF_OPERATOR)); @@ -163,7 +163,7 @@ int m_oper(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) if (ACR_OK != attach_conf(sptr, aconf)) { send_reply(sptr, ERR_NOOPERHOST); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed OPER attempt by %s " - "(%s@%s)", parv[0], cli_user(sptr)->username, + "(%s@%s)", parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr)); return 0; } @@ -187,16 +187,16 @@ int m_oper(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) send_reply(sptr, RPL_YOUREOPER); sendto_opmask_butone(0, SNO_OLDSNO, "%s (%s@%s) is now operator (%c)", - parv[0], cli_user(sptr)->username, cli_sockhost(sptr), + parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr), IsOper(sptr) ? 'O' : 'o'); - log_write(LS_OPER, L_INFO, 0, "OPER (%s) by (%#C)", name, sptr); + log_write(LS_OPER, L_INFO, 0, "OPER (%s) by (%#R)", name, sptr); } else { send_reply(sptr, ERR_PASSWDMISMATCH); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed OPER attempt by %s (%s@%s)", - parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); + parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr)); } return 0; } diff --git a/ircd/m_sethost.c b/ircd/m_sethost.c new file mode 100644 index 0000000..9d05309 --- /dev/null +++ b/ircd/m_sethost.c @@ -0,0 +1,143 @@ +/* + * IRC - Internet Relay Chat, ircd/m_sethost.c + * Copyright (C) 1990 Jarkko Oikarinen and + * University of Oulu, Computing Center + * + * 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. + * + * $Id: asuka-sethost.patch,v 1.27 2005/02/13 17:28:11 froo Exp $ + */ + +/* + * m_functions execute protocol messages on this server: + * + * cptr is always NON-NULL, pointing to a *LOCAL* client + * structure (with an open socket connected!). This + * identifies the physical socket where the message + * originated (or which caused the m_function to be + * executed--some m_functions may call others...). + * + * sptr is the source of the message, defined by the + * prefix part of the message if present. If not + * or prefix not found, then sptr==cptr. + * + * (!IsServer(cptr)) => (cptr == sptr), because + * prefixes are taken *only* from servers... + * + * (IsServer(cptr)) + * (sptr == cptr) => the message didn't + * have the prefix. + * + * (sptr != cptr && IsServer(sptr) means + * the prefix specified servername. (?) + * + * (sptr != cptr && !IsServer(sptr) means + * that message originated from a remote + * user (not local). + * + * combining + * + * (!IsServer(sptr)) means that, sptr can safely + * taken as defining the target structure of the + * message in this server. + * + * *Always* true (if 'parse' and others are working correct): + * + * 1) sptr->from == cptr (note: cptr->from == cptr) + * + * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr + * *cannot* be a local connection, unless it's + * actually cptr!). [MyConnect(x) should probably + * be defined as (x == x->from) --msa ] + * + * parc number of variable parameter strings (if zero, + * parv is allowed to be NULL) + * + * parv a NULL terminated list of parameter pointers, + * + * parv[0], sender (prefix string), if not present + * this points to an empty string. + * parv[1]...parv[parc-1] + * pointers to additional parameters + * parv[parc] == NULL, *always* + * + * note: it is guaranteed that parv[0]..parv[parc-1] are all + * non-NULL pointers. + */ +#include "config.h" + +#include "client.h" +#include "ircd_reply.h" +#include "ircd_string.h" +#include "ircd_features.h" +#include "msgq.h" +#include "numeric.h" +#include "s_user.h" +#include "s_debug.h" +#include "struct.h" + +#include +#include + +/* + * m_sethost - generic message handler + * + * mimic old lain syntax: + * + * (Oper) /SETHOST ident host.cc [quit-message] + * (User) /SETHOST host.cc password + * (Both) /SETHOST undo + * + * check for undo, prepend parv w. -h or +h + */ +int m_sethost(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +{ + char hostmask[512]; + struct Flags setflags; + + /* Back up the flags first */ + setflags = cli_flags(sptr); + + if (parc < 2) + return need_more_params(sptr, "SETHOST"); + + if (0 == ircd_strcmp("undo", parv[1])) { + set_hostmask(sptr, NULL, NULL); + } else { + if (parc<3) + return need_more_params(sptr, "SETHOST"); + if (IsAnOper(sptr)) { + ircd_snprintf(0, hostmask, USERLEN + HOSTLEN + 1, "%s@%s", parv[1], parv[2]); + if (!is_hostmask(hostmask)) { + send_reply(sptr, ERR_BADHOSTMASK, hostmask); + return 0; + } + if (set_hostmask(sptr, hostmask, NULL)) + FlagClr(&setflags, FLAG_SETHOST); + } else { + if (!is_hostmask(parv[1])) { + send_reply(sptr, ERR_BADHOSTMASK, parv[1]); + return 0; + } + if (set_hostmask(sptr, parv[1], parv[2])) + FlagClr(&setflags, FLAG_SETHOST); + } + } + + send_umode_out(cptr, sptr, &setflags, 0); +} diff --git a/ircd/m_userhost.c b/ircd/m_userhost.c index 9fe392c..b679080 100644 --- a/ircd/m_userhost.c +++ b/ircd/m_userhost.c @@ -104,7 +104,7 @@ static void userhost_formatter(struct Client* cptr, struct Client *sptr, struct * of +x. If an oper wants the real host, he should go to * /whois to get it. */ - HasHiddenHost(cptr) && (sptr != cptr) ? + !IsAnOper(sptr) ? cli_user(cptr)->host : cli_user(cptr)->realhost); } diff --git a/ircd/m_userip.c b/ircd/m_userip.c index 062fe68..dd04810 100644 --- a/ircd/m_userip.c +++ b/ircd/m_userip.c @@ -95,6 +95,7 @@ static void userip_formatter(struct Client* cptr, struct Client *sptr, struct MsgBuf* mb) { + /* !!FIXME!! */ assert(IsUser(cptr)); msgq_append(0, mb, "%s%s=%c%s@%s", cli_name(cptr), SeeOper(sptr,cptr) ? "*" : "", @@ -105,7 +106,7 @@ static void userip_formatter(struct Client* cptr, struct Client *sptr, struct Ms * of +x. If an oper wants the real IP, he should go to * /whois to get it. */ - HasHiddenHost(cptr) && (sptr != cptr) ? + (HasHiddenHost(cptr) || HasSetHost(cptr)) && !IsAnOper(sptr) ? feature_str(FEAT_HIDDEN_IP) : ircd_ntoa(&cli_ip(cptr))); } diff --git a/ircd/m_who.c b/ircd/m_who.c index b0de694..ad6f11e 100644 --- a/ircd/m_who.c +++ b/ircd/m_who.c @@ -404,13 +404,14 @@ int m_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) && ((!(matchsel & WHO_FIELD_HOS)) || matchexec(cli_user(acptr)->host, mymask, minlen)) && ((!(matchsel & WHO_FIELD_HOS)) + || !HasSetHost(acptr) || !HasHiddenHost(acptr) || !IsAnOper(sptr) || matchexec(cli_user(acptr)->realhost, mymask, minlen)) && ((!(matchsel & WHO_FIELD_REN)) || matchexec(cli_info(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_NIP)) - || (HasHiddenHost(acptr) && !IsAnOper(sptr)) + || ((HasHiddenHost(acptr) || HasSetHost(acptr)) && !IsAnOper(sptr)) || !ipmask_check(&cli_ip(acptr), &imask, ibits)) && ((!(matchsel & WHO_FIELD_ACC)) || matchexec(cli_user(acptr)->account, mymask, minlen))) @@ -442,13 +443,14 @@ int m_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) && ((!(matchsel & WHO_FIELD_HOS)) || matchexec(cli_user(acptr)->host, mymask, minlen)) && ((!(matchsel & WHO_FIELD_HOS)) + || !HasSetHost(acptr) || !HasHiddenHost(acptr) || !IsAnOper(sptr) || matchexec(cli_user(acptr)->realhost, mymask, minlen)) && ((!(matchsel & WHO_FIELD_REN)) || matchexec(cli_info(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_NIP)) - || (HasHiddenHost(acptr) && !IsAnOper(sptr)) + || ((HasHiddenHost(acptr) || HasSetHost(acptr)) && !IsAnOper(sptr)) || !ipmask_check(&cli_ip(acptr), &imask, ibits)) && ((!(matchsel & WHO_FIELD_ACC)) || matchexec(cli_user(acptr)->account, mymask, minlen))) diff --git a/ircd/m_whois.c b/ircd/m_whois.c index fec4b53..63d2516 100644 --- a/ircd/m_whois.c +++ b/ircd/m_whois.c @@ -211,8 +211,8 @@ static void do_whois(struct Client* sptr, struct Client *acptr, int parc) if (IsAccount(acptr)) send_reply(sptr, RPL_WHOISACCOUNT, name, user->account); - if (HasHiddenHost(acptr) && (IsAnOper(sptr) || acptr == sptr)) - send_reply(sptr, RPL_WHOISACTUALLY, name, user->username, + if ((HasHiddenHost(acptr) || HasSetHost(acptr)) && (IsAnOper(sptr) || acptr == sptr)) + send_reply(sptr, RPL_WHOISACTUALLY, name, user->realusername, user->realhost, ircd_ntoa(&cli_ip(acptr))); /* Hint: if your looking to add more flags to a user, eg +h, here's diff --git a/ircd/parse.c b/ircd/parse.c index 9cdb6f2..fe0488e 100644 --- a/ircd/parse.c +++ b/ircd/parse.c @@ -617,6 +617,13 @@ struct Message msgtab[] = { /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_not_oper, ms_asll, mo_asll, m_ignore } }, + { + MSG_SETHOST, + TOK_SETHOST, + 0, MAXPARA, MFLG_SLOW, 0, NULL, + /* UNREG, CLIENT, SERVER, OPER, SERVICE */ + { m_unregistered, m_sethost, m_ignore, m_sethost, m_ignore } + }, #if WE_HAVE_A_REAL_CAPABILITY_NOW { MSG_CAP, diff --git a/ircd/s_auth.c b/ircd/s_auth.c index 170b2c2..75f56ce 100644 --- a/ircd/s_auth.c +++ b/ircd/s_auth.c @@ -48,6 +48,7 @@ #include "querycmds.h" #include "res.h" #include "s_bsd.h" +#include "s_conf.h" #include "s_debug.h" #include "s_misc.h" #include "send.h" diff --git a/ircd/s_conf.c b/ircd/s_conf.c index 5fb596b..fb96296 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -74,6 +74,8 @@ int GlobalConfCount; struct s_map *GlobalServiceMapList; /** Global list of channel quarantines. */ struct qline *GlobalQuarantineList; +/** Global list of spoofhosts. */ +struct sline *GlobalSList = 0; /** Current line number in scanner input. */ int lineno; @@ -942,6 +944,7 @@ int rehash(struct Client *cptr, int sig) clearNickJupes(); clear_quarantines(); + clear_slines(); if (sig != 2) restart_resolver(); @@ -1192,3 +1195,81 @@ int conf_check_server(struct Client *cptr) return 0; } +void clear_slines(void) +{ + struct sline *sline; + while ((sline = GlobalSList)) { + GlobalSList = sline->next; + MyFree(sline->spoofhost); + if (!EmptyString(sline->passwd)) + MyFree(sline->passwd); + if (!EmptyString(sline->realhost)) + MyFree(sline->realhost); + if (!EmptyString(sline->username)) + MyFree(sline->username); + MyFree(sline); + } +} + +/* + * conf_check_slines() + * + * Check S lines for the specified client, passed in cptr struct. + * If the client's IP is S-lined, process the substitution here. + * + * Precondition + * cptr != NULL + * + * Returns + * 0 = No S-line found + * 1 = S-line found and substitution done. + * + * -mbuna 9/2001 + * -froo 1/2003 + * + */ + +int +conf_check_slines(struct Client *cptr) +{ + struct sline *sconf; + char *hostonly; + + for (sconf = GlobalSList; sconf; sconf = sconf->next) { + if (sconf->flags == SLINE_FLAGS_IP) { + if (!ipmask_check(&(cli_ip(cptr)), &(sconf->address), sconf->bits)) + continue; + } else if (sconf->flags == SLINE_FLAGS_HOSTNAME) { + if ((match(sconf->realhost, cli_sockhost(cptr)) != 0) && + (match(sconf->realhost, cli_sock_ip(cptr)) != 0)) /* wildcarded IP address */ + continue; + } else { + continue; + } + + if (match(sconf->username, cli_user(cptr)->username) == 0) { + /* Ignore user part if u@h. */ + if ((hostonly = strchr(sconf->spoofhost, '@'))) + hostonly++; + else + hostonly = sconf->spoofhost; + + if(!*hostonly) + continue; + + ircd_strncpy(cli_user(cptr)->host, hostonly, HOSTLEN); + log_write(LS_USER, L_INFO, LOG_NOSNOTICE, "S-Line (%s@%s) by (%#R)", + cli_user(cptr)->username, hostonly, cptr); + return 1; + } + } + return 0; +} + +void free_spoofhost(struct sline *spoof) { + MyFree(spoof->spoofhost); + MyFree(spoof->passwd); + MyFree(spoof->realhost); + MyFree(spoof->username); + MyFree(spoof); +} diff --git a/ircd/s_err.c b/ircd/s_err.c index d5b8458..16503e4 100644 --- a/ircd/s_err.c +++ b/ircd/s_err.c @@ -640,7 +640,7 @@ static Numeric replyTable[] = { /* 303 */ { RPL_ISON, ":", "303" }, /* 304 */ - { 0 }, + { RPL_TEXT, "%s", "304" }, /* 305 */ { RPL_UNAWAY, ":You are no longer marked as being away", "305" }, /* 306 */ @@ -828,9 +828,9 @@ static Numeric replyTable[] = { /* 397 */ { 0 }, /* 398 */ - { 0 }, + { RPL_STATSSLINE, "%d %s %s %s %s", "398" }, /* 399 */ - { 0 }, + { RPL_USINGSLINE, ":Using S-line privilege", "399" }, /* 400 */ { 0 }, /* 401 */ @@ -1092,9 +1092,9 @@ static Numeric replyTable[] = { /* 529 */ { 0 }, /* 530 */ - { 0 }, + { ERR_BADHOSTMASK, "%s :Invalid username/hostmask", "530" }, /* 531 */ - { 0 }, + { ERR_HOSTUNAVAIL, "%s :sethost not found", "531" }, /* 532 */ { 0 }, /* 533 */ diff --git a/ircd/s_serv.c b/ircd/s_serv.c index 71274a1..2b3f46a 100644 --- a/ircd/s_serv.c +++ b/ircd/s_serv.c @@ -247,7 +247,7 @@ int server_estab(struct Client *cptr, struct ConfItem *aconf) sendcmdto_one(cli_user(acptr)->server, CMD_NICK, cptr, "%s %d %Tu %s %s %s%s%s%s %s%s :%s", cli_name(acptr), cli_hopcount(acptr) + 1, cli_lastnick(acptr), - cli_user(acptr)->username, cli_user(acptr)->realhost, + cli_user(acptr)->realusername, cli_user(acptr)->realhost, *s ? "+" : "", s, *s ? " " : "", iptobase64(xxx_buf, &cli_ip(acptr), sizeof(xxx_buf), IsIPv6(cptr)), NumNick(acptr), cli_info(acptr)); diff --git a/ircd/s_stats.c b/ircd/s_stats.c index 85da5e2..0c04d59 100644 --- a/ircd/s_stats.c +++ b/ircd/s_stats.c @@ -397,6 +397,44 @@ stats_quarantine(struct Client* to, const struct StatDesc* sd, char* param) } } +static void +stats_sline(struct Client* to, const struct StatDesc* sd, char* param) +{ + int y = 1, i = 1; + struct sline *sline; + + if (IsAnOper(to)) + send_reply(to, SND_EXPLICIT | RPL_TEXT, "# Type Spoofhost Realhost Ident"); + else + send_reply(to, SND_EXPLICIT | RPL_TEXT, "# Type Spoofhost"); + + for (sline = GlobalSList; sline; sline = sline->next) { + if (param && match(param, sline->spoofhost)) { /* narrow search */ + if (IsAnOper(to)) + y++; + else + if (!EmptyString(sline->passwd)) + y++; + continue; + } + + if (IsAnOper(to)) { + send_reply(to, RPL_STATSSLINE, (param) ? y : i, + (EmptyString(sline->passwd)) ? "oper" : "user", + sline->spoofhost, + (EmptyString(sline->realhost)) ? "" : sline->realhost, + (EmptyString(sline->username)) ? "" : sline->username); + i++; + } else { + if (!EmptyString(sline->passwd)) { + send_reply(to, RPL_STATSSLINE, (param) ? y : i, "user", sline->spoofhost, + "", "", ""); + i++; + } + } + } +} + /** List service pseudo-command mappings. * @param[in] to Client requesting statistics. * @param[in] sd Stats descriptor for request (ignored). @@ -586,6 +624,9 @@ struct StatDesc statsinfo[] = { send_usage, 0, "System resource usage (Debug only)." }, #endif + { 's', "spoofhosts", (STAT_FLAG_OPERFEAT | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_s, + stats_sline, 0, + "Spoofed hosts information." }, { 'T', "motds", (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_T, motd_report, 0, "Configured Message Of The Day files." }, diff --git a/ircd/s_user.c b/ircd/s_user.c index eae4b49..f35634a 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -73,6 +73,9 @@ #include #include +static char *IsVhost(char *hostmask, int oper); +static char *IsVhostPass(char *hostmask); + /** Count of allocated User structures. */ static int userCount = 0; @@ -458,7 +461,11 @@ int register_user(struct Client *cptr, struct Client *sptr, clean_user_id(user->username, HasFlag(sptr, FLAG_GOTID) ? cli_username(sptr) : username, - HasFlag(sptr, FLAG_DOID) && !HasFlag(sptr, FLAG_GOTID)); + HasFlag(sptr, FLAG_DOID) && !HasFlag(sptr, FLAG_GOTID) + && !(HasSetHost(sptr))); /* No tilde for S-lined users. */ + + /* Have to set up "realusername" before doing the gline check below */ + ircd_strncpy(user->realusername, user->username, USERLEN); if ((user->username[0] == '\0') || ((user->username[0] == '~') && (user->username[1] == '\000'))) @@ -564,6 +571,13 @@ int register_user(struct Client *cptr, struct Client *sptr, if (MyConnect(sptr) && feature_bool(FEAT_AUTOINVISIBLE)) SetInvisible(sptr); + if(MyConnect(sptr) && feature_bool(FEAT_SETHOST_AUTO)) { + if (conf_check_slines(sptr)) { + send_reply(sptr, RPL_USINGSLINE); + SetSetHost(sptr); + } + } + SetUser(sptr); cli_handler(sptr) = CLIENT_HANDLER; SetLocalNumNick(sptr); @@ -661,6 +675,14 @@ int register_user(struct Client *cptr, struct Client *sptr, sptr, cli_name(&me)); return exit_client(cptr, sptr, &me,"Too many connections from your host -- throttled"); } + + if(MyConnect(sptr) && feature_bool(FEAT_SETHOST_AUTO)) { + if (conf_check_slines(sptr)) { + send_reply(sptr, RPL_USINGSLINE); + SetSetHost(sptr); + } + } + SetUser(sptr); } @@ -675,7 +697,7 @@ int register_user(struct Client *cptr, struct Client *sptr, FLAG_IPV6, FLAG_LAST_FLAG, "%s %d %Tu %s %s %s%s%s%s %s%s :%s", nick, cli_hopcount(sptr) + 1, cli_lastnick(sptr), - user->username, user->realhost, + user->realusername, user->realhost, *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "", iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 1), NumNick(sptr), cli_info(sptr)); @@ -684,7 +706,7 @@ int register_user(struct Client *cptr, struct Client *sptr, FLAG_LAST_FLAG, FLAG_IPV6, "%s %d %Tu %s %s %s%s%s%s %s%s :%s", nick, cli_hopcount(sptr) + 1, cli_lastnick(sptr), - user->username, user->realhost, + user->realusername, user->realhost, *tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "", iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 0), NumNick(sptr), cli_info(sptr)); @@ -718,7 +740,8 @@ static const struct UserMode { { FLAG_ACCOUNTONLY, 'R' }, { FLAG_XTRAOP, 'X' }, { FLAG_NOCHAN, 'n' }, - { FLAG_NOIDLE, 'I' } + { FLAG_NOIDLE, 'I' }, + { FLAG_SETHOST, 'h' } }; /** Length of #userModeList. */ @@ -745,6 +768,8 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, if (IsServer(sptr)) { int i; const char* account = 0; + char* hostmask = 0; + char* host = 0; const char* p; /* @@ -766,6 +791,8 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, SetFlag(new_client, userModeList[i].flag); if (userModeList[i].flag == FLAG_ACCOUNT) account = parv[7]; + if (userModeList[i].flag == FLAG_SETHOST) + hostmask = parv[parc - 4]; break; } } @@ -789,6 +816,7 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, cli_serv(sptr)->ghost = 0; /* :server NICK means end of net.burst */ ircd_strncpy(cli_username(new_client), parv[4], USERLEN); + ircd_strncpy(cli_user(new_client)->realusername, parv[4], USERLEN); ircd_strncpy(cli_user(new_client)->host, parv[5], HOSTLEN); ircd_strncpy(cli_user(new_client)->realhost, parv[5], HOSTLEN); ircd_strncpy(cli_info(new_client), parv[parc - 1], REALLEN); @@ -806,8 +834,15 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, if (HasHiddenHost(new_client)) ircd_snprintf(0, cli_user(new_client)->host, HOSTLEN, "%s.%s", account, feature_str(FEAT_HIDDEN_HOST)); + if (HasSetHost(new_client)) { + if ((host = strrchr(hostmask, '@')) != NULL) { + *host++ = '\0'; + ircd_strncpy(cli_username(new_client), hostmask, USERLEN); + ircd_strncpy(cli_user(new_client)->host, host, HOSTLEN); + } + } - return register_user(cptr, new_client, cli_name(new_client), parv[4]); + return register_user(cptr, new_client, cli_name(new_client), cli_username(new_client)); } else if ((cli_name(sptr))[0]) { /* @@ -1099,7 +1134,7 @@ void send_umode_out(struct Client *cptr, struct Client *sptr, { if ((acptr = LocalClientArray[i]) && IsServer(acptr) && (acptr != cptr) && (acptr != sptr) && *umodeBuf) - sendcmdto_one(sptr, CMD_MODE, acptr, "%s :%s", cli_name(sptr), umodeBuf); + sendcmdto_one(sptr, CMD_MODE, acptr, "%s %s", cli_name(sptr), umodeBuf); } if (cptr && MyUser(cptr)) send_umode(cptr, sptr, old, ALL_UMODES); @@ -1155,6 +1190,11 @@ hide_hostmask(struct Client *cptr, unsigned int flag) /* Local users cannot set +x unless FEAT_HOST_HIDING is true. */ if (MyConnect(cptr) && !feature_bool(FEAT_HOST_HIDING)) return 0; + /* If the user is +h, we don't hide the hostmask. Set the flag to keep sync though */ + if (HasSetHost(cptr)) { + SetFlag(cptr, flag); + return 0; + } break; case FLAG_ACCOUNT: /* Invalidate all bans against the user so we check them again */ @@ -1201,6 +1241,190 @@ hide_hostmask(struct Client *cptr, unsigned int flag) return 0; } +/* + * set_hostmask() - derived from hide_hostmask() + * + */ +int set_hostmask(struct Client *cptr, char *hostmask, char *password) +{ + int restore = 0; + int freeform = 0; + char *host, *new_vhost, *vhost_pass; + char hiddenhost[USERLEN + HOSTLEN + 2]; + struct Membership *chan; + + Debug((DEBUG_INFO, "set_hostmask() %C, %s, %s", cptr, hostmask, password)); + + /* sethost enabled? */ + if (MyConnect(cptr) && !feature_bool(FEAT_SETHOST)) { + send_reply(cptr, ERR_DISABLED, "SETHOST"); + return 0; + } + + /* sethost enabled for users? */ + if (MyConnect(cptr) && !IsAnOper(cptr) && !feature_bool(FEAT_SETHOST_USER)) { + send_reply(cptr, ERR_NOPRIVILEGES); + return 0; + } + + /* MODE_DEL: restore original hostmask */ + if (EmptyString(hostmask)) { + /* is already sethost'ed? */ + if (IsSetHost(cptr)) { + restore = 1; + sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change"); + /* If they are +rx, we need to return to their +x host, not their "real" host */ + if (HasHiddenHost(cptr)) + ircd_snprintf(0, cli_user(cptr)->host, HOSTLEN, "%s.%s", + cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST)); + else + strncpy(cli_user(cptr)->host, cli_user(cptr)->realhost, HOSTLEN); + strncpy(cli_user(cptr)->username, cli_user(cptr)->realusername, USERLEN); + /* log it */ + if (MyConnect(cptr)) + log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, + "SETHOST (%s@%s) by (%#R): restoring real hostmask", + cli_user(cptr)->username, cli_user(cptr)->host, cptr); + } else + return 0; + /* MODE_ADD: set a new hostmask */ + } else { + /* chop up ident and host.cc */ + if ((host = strrchr(hostmask, '@'))) /* oper can specifiy ident@host.cc */ + *host++ = '\0'; + else /* user can only specifiy host.cc [password] */ + host = hostmask; + /* + * Oper sethost + */ + if (MyConnect(cptr)) { + if (IsAnOper(cptr)) { + if ((new_vhost = IsVhost(host, 1)) == NULL) { + if (!feature_bool(FEAT_SETHOST_FREEFORM)) { + send_reply(cptr, ERR_HOSTUNAVAIL, hostmask); + log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, + "SETHOST (%s@%s) by (%#R): no such s-line", + (host != hostmask) ? hostmask : cli_user(cptr)->username, host, cptr); + return 0; + } else /* freeform active, log and go */ + freeform = 1; + } + sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change"); + /* set the new ident and host */ + if (host != hostmask) /* oper only specified host.cc */ + strncpy(cli_user(cptr)->username, hostmask, USERLEN); + strncpy(cli_user(cptr)->host, host, HOSTLEN); + /* log it */ + log_write(LS_SETHOST, (freeform) ? L_NOTICE : L_INFO, + (freeform) ? 0 : LOG_NOSNOTICE, "SETHOST (%s@%s) by (%#R)%s", + cli_user(cptr)->username, cli_user(cptr)->host, cptr, + (freeform) ? ": using freeform" : ""); + /* + * plain user sethost, handled here + */ + } else { + /* empty password? */ + if (EmptyString(password)) { + send_reply(cptr, ERR_NEEDMOREPARAMS, "MODE"); + return 0; + } + /* no such s-line */ + if ((new_vhost = IsVhost(host, 0)) == NULL) { + send_reply(cptr, ERR_HOSTUNAVAIL, hostmask); + log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, "SETHOST (%s@%s %s) by (%#R): no such s-line", + cli_user(cptr)->username, host, password, cptr); + return 0; + } + /* no password */ + if ((vhost_pass = IsVhostPass(new_vhost)) == NULL) { + send_reply(cptr, ERR_PASSWDMISMATCH); + log_write(LS_SETHOST, L_INFO, 0, "SETHOST (%s@%s %s) by (%#R): trying to use an oper s-line", + cli_user(cptr)->username, host, password, cptr); + return 0; + } + /* incorrect password */ + if (strCasediff(vhost_pass, password)) { + send_reply(cptr, ERR_PASSWDMISMATCH); + log_write(LS_SETHOST, L_NOTICE, 0, "SETHOST (%s@%s %s) by (%#R): incorrect password", + cli_user(cptr)->username, host, password, cptr); + return 0; + } + sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change"); + /* set the new host */ + strncpy(cli_user(cptr)->host, new_vhost, HOSTLEN); + /* log it */ + log_write(LS_SETHOST, L_INFO, LOG_NOSNOTICE, "SETHOST (%s@%s) by (%#R)", + cli_user(cptr)->username, cli_user(cptr)->host, cptr); + } + } else { /* remote user */ + sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Host change"); + if (host != hostmask) /* oper only specified host.cc */ + strncpy(cli_user(cptr)->username, hostmask, USERLEN); + strncpy(cli_user(cptr)->host, host, HOSTLEN); + } + } + + if (restore) + ClearSetHost(cptr); + else + SetSetHost(cptr); + + if (MyConnect(cptr)) { + ircd_snprintf(0, hiddenhost, HOSTLEN + USERLEN + 2, "%s@%s", + cli_user(cptr)->username, cli_user(cptr)->host); + send_reply(cptr, RPL_HOSTHIDDEN, hiddenhost); + } + +#if 0 + /* Code copied from hide_hostmask(). This is the old (pre-delayedjoin) + * version. Switch this in if you're not using the delayed join patch. */ + /* + * Go through all channels the client was on, rejoin him + * and set the modes, if any + */ + for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) { + if (IsZombie(chan)) + continue; + sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, + "%H", chan->channel); + if (IsChanOp(chan) && HasVoice(chan)) { + sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, + "%H +ov %C %C", chan->channel, cptr, cptr); + } else if (IsChanOp(chan) || HasVoice(chan)) { + sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, + "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr); + } + } +#endif + + /* + * Go through all channels the client was on, rejoin him + * and set the modes, if any + */ + for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) { + if (IsZombie(chan)) + continue; + /* If this channel has delayed joins and the user has no modes, just set + * the delayed join flag rather than showing the join, even if the user + * was visible before */ + if (!IsChanOp(chan) && !HasVoice(chan) + && (chan->channel->mode.mode & MODE_DELJOINS)) { + SetDelayedJoin(chan); + } else { + sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, 0, + "%H", chan->channel); + } + if (IsChanOp(chan) && HasVoice(chan)) { + sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, 0, + "%H +ov %C %C", chan->channel, cptr, cptr); + } else if (IsChanOp(chan) || HasVoice(chan)) { + sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, 0, + "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', cptr); + } + } + return 1; +} + /** Set a user's mode. This function checks that \a cptr is trying to * set his own mode, prevents local users from setting inappropriate * modes through this function, and applies any other side effects of @@ -1223,9 +1447,12 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv unsigned int tmpmask = 0; int snomask_given = 0; char buf[BUFSIZE]; + char *hostmask, *password; int prop = 0; int do_host_hiding = 0; + int do_set_host = 0; + hostmask = password = NULL; what = MODE_ADD; if (parc < 2) @@ -1256,7 +1483,8 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv for (i = 0; i < USERMODELIST_SIZE; i++) { if (HasFlag(sptr, userModeList[i].flag) && - userModeList[i].flag != FLAG_ACCOUNT) + ((userModeList[i].flag != FLAG_ACCOUNT) && + (userModeList[i].flag != FLAG_SETHOST))) *m++ = userModeList[i].c; } *m = '\0'; @@ -1381,8 +1609,32 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv break; case 'x': if (what == MODE_ADD) - do_host_hiding = 1; - break; + do_host_hiding = 1; + break; + case 'h': + if (what == MODE_ADD) { + if (*(p + 1) && is_hostmask(*(p + 1))) { + do_set_host = 1; + hostmask = *++p; + /* DON'T step p onto the trailing NULL in the parameter array! - splidge */ + if (*(p+1)) + password = *++p; + else + password = NULL; + } else { + if (!*(p+1)) + send_reply(sptr, ERR_NEEDMOREPARAMS, "SETHOST"); + else { + send_reply(sptr, ERR_BADHOSTMASK, *(p+1)); + p++; /* Swallow the arg anyway */ + } + } + } else { /* MODE_DEL */ + do_set_host = 1; + hostmask = NULL; + password = NULL; + } + break; case 'R': if (what == MODE_ADD) SetAccountOnly(sptr); @@ -1475,6 +1727,12 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv ++UserStats.inv_clients; if (!FlagHas(&setflags, FLAG_HIDDENHOST) && do_host_hiding) hide_hostmask(sptr, FLAG_HIDDENHOST); + if (do_set_host) { + /* We clear the flag in the old mask, so that the +h will be sent */ + /* Only do this if we're SETTING +h and it succeeded */ + if (set_hostmask(sptr, hostmask, password) && hostmask) + FlagClr(&setflags, FLAG_SETHOST); + } send_umode_out(cptr, sptr, &setflags, prop); return 0; @@ -1520,10 +1778,15 @@ char *umode_str(struct Client *cptr) while ((*m++ = *t++)) ; /* Empty loop */ } + m--; /* Step back over the '\0' */ } - *m = '\0'; - + if (IsSetHost(cptr)) { + *m++ = ' '; + ircd_snprintf(0, m, USERLEN + HOSTLEN + 2, "%s@%s", cli_user(cptr)->username, + cli_user(cptr)->host); + } else + *m = '\0'; return umodeBuf; /* Note: static buffer, gets overwritten by send_umode() */ } @@ -1540,6 +1803,7 @@ void send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old, { int i; int flag; + int needhost = 0; char *m; int what = MODE_NULL; @@ -1569,6 +1833,16 @@ void send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old, continue; break; } + /* Special case for SETHOST.. */ + if (flag == FLAG_SETHOST) { + /* Don't send to users */ + if (cptr && MyUser(cptr)) + continue; + + /* If we're setting +h, add the parameter later */ + if (!FlagHas(old, flag)) + needhost++; + } if (FlagHas(old, flag)) { if (what == MODE_DEL) @@ -1592,9 +1866,14 @@ void send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old, } } } - *m = '\0'; + if (needhost) { + *m++ = ' '; + ircd_snprintf(0, m, USERLEN + HOSTLEN + 1, "%s@%s", cli_user(sptr)->username, + cli_user(sptr)->host); + } else + *m = '\0'; if (*umodeBuf && cptr) - sendcmdto_one(sptr, CMD_MODE, cptr, "%s :%s", cli_name(sptr), umodeBuf); + sendcmdto_one(sptr, CMD_MODE, cptr, "%s %s", cli_name(sptr), umodeBuf); } /** @@ -1617,6 +1896,110 @@ int is_snomask(char *word) return 0; } + /* + * Check to see if it resembles a valid hostmask. + */ +int is_hostmask(char *word) +{ + int i = 0; + char *host; + + Debug((DEBUG_INFO, "is_hostmask() %s", word)); + + if (strlen(word) > (HOSTLEN + USERLEN + 1) || strlen(word) <= 0) + return 0; + + /* if a host is specified, make sure it's valid */ + host = strrchr(word, '@'); + if (host) { + if (strlen(++host) < 1) + return 0; + if (strlen(host) > HOSTLEN) + return 0; + } + + if (word) { + if ('@' == *word) /* no leading @'s */ + return 0; + + if ('#' == *word) { /* numeric index given? */ + for (word++; *word; word++) { + if (!IsDigit(*word)) + return 0; + } + return 1; + } + + /* normal hostmask, account for at most one '@' */ + for (; *word; word++) { + if ('@' == *word) { + i++; + continue; + } + if (!IsHostChar(*word)) + return 0; + } + return (1 < i) ? 0 : 1; /* no more than on '@' */ + } + return 0; +} + + /* + * IsVhost() - Check if given host is a valid spoofhost + * (ie: configured thru a S:line) + */ +static char *IsVhost(char *hostmask, int oper) +{ + unsigned int i = 0, y = 0; + struct sline *sconf; + + Debug((DEBUG_INFO, "IsVhost() %s", hostmask)); + + if (EmptyString(hostmask)) + return NULL; + + /* spoofhost specified as index, ie: #27 */ + if ('#' == hostmask[0]) { + y = atoi(hostmask + 1); + for (i = 0, sconf = GlobalSList; sconf; sconf = sconf->next) { + if (!oper && EmptyString(sconf->passwd)) + continue; + if (y == ++i) + return sconf->spoofhost; + } + return NULL; + } + + /* spoofhost specified as host, ie: host.cc */ + for (sconf = GlobalSList; sconf; sconf = sconf->next) + if (strCasediff(hostmask, sconf->spoofhost) == 0) + return sconf->spoofhost; + + return NULL; +} + + /* + * IsVhostPass() - Check if given spoofhost has a password + * associated with it, and if, return the password (cleartext) + */ +static char *IsVhostPass(char *hostmask) +{ + struct sline *sconf; + + Debug((DEBUG_INFO, "IsVhostPass() %s", hostmask)); + + if (EmptyString(hostmask)) + return NULL; + + for (sconf = GlobalSList; sconf; sconf = sconf->next) + if (strCasediff(hostmask, sconf->spoofhost) == 0) { + Debug((DEBUG_INFO, "sconf->passwd %s", sconf->passwd)); + return EmptyString(sconf->passwd) ? NULL : sconf->passwd; + } + + return NULL; +} + /** Update snomask \a oldmask according to \a arg and \a what. * @param[in] oldmask Original user mask. * @param[in] arg Update string (either a number or '+'/'-' followed by a number). diff --git a/ircd/send.c b/ircd/send.c index f2649d3..f4c5c01 100644 --- a/ircd/send.c +++ b/ircd/send.c @@ -281,7 +281,7 @@ static int match_it(struct Client *from, struct Client *one, const char *mask, i { case MATCH_HOST: return (match(mask, cli_user(one)->host) == 0 || - (HasHiddenHost(one) && match(mask, cli_user(one)->realhost) == 0)); + ((HasHiddenHost(one) || HasSetHost(one)) && match(mask, cli_user(one)->realhost) == 0)); case MATCH_SERVER: default: return (match(mask, cli_name(cli_user(one)->server)) == 0); diff --git a/ircd/whocmds.c b/ircd/whocmds.c index 4e33e2f..195b4b1 100644 --- a/ircd/whocmds.c +++ b/ircd/whocmds.c @@ -127,7 +127,7 @@ void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan, if (fields & WHO_FIELD_NIP) { - const char* p2 = HasHiddenHost(acptr) && !IsAnOper(sptr) ? + const char* p2 = (HasHiddenHost(acptr) || HasSetHost(acptr)) && !IsAnOper(sptr) ? feature_str(FEAT_HIDDEN_IP) : ircd_ntoa(&cli_ip(acptr)); *(p1++) = ' '; @@ -203,6 +203,8 @@ void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan, *(p1++) = 'w'; if (SendDebug(acptr)) *(p1++) = 'g'; + if (HasSetHost(acptr)) + *(p1++) = 'h'; } if (HasHiddenHost(acptr)) *(p1++) = 'x'; diff --git a/tools/convert_slines.sh b/tools/convert_slines.sh new file mode 100644 index 0000000..f6a401b --- /dev/null +++ b/tools/convert_slines.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# +# $Id: asuka-sethost.patch,v 1.29 2005/02/24 01:06:52 froo Exp $ +# +# aid in converting S: and F:lines from old lain configs +# to the new "super S:line" format of asuka. +# +# When Who What +# 2003-01-05 froo@quakenet.org Created. + +PATH=/bin:/usr/bin +PROG=`basename $0` +USAGE="Usage: $PROG " + +if [ $# -lt 1 ]; then + echo $USAGE + exit +fi + +CONFIG=$1 + +if [ ! -f $CONFIG ]; then + echo "Can't open \"$CONFIG\", bailing out." + exit +fi + +{ +for LINE in `grep -E "^F:" $CONFIG` +do + IDENT=`echo $LINE | cut -f2 -d:` + REALHOST=`echo $LINE | cut -f3 -d:` + SPOOFHOST=`echo $LINE | cut -f4 -d:` + + IDENT=`echo $IDENT | sed -e 's,^~,\*,'` + + echo "S:$SPOOFHOST::$REALHOST:$IDENT" +done + +for LINE in `grep -E "^S:" $CONFIG` +do + SPOOFHOST=`echo $LINE | cut -f2 -d:` + PASSWD=`echo $LINE | cut -f3 -d:` + + IDENT=`echo $IDENT | sed -e 's,^~,\*,'` + + echo "S:$SPOOFHOST:$PASSWD::" +done +} | sort + +exit 0