# HG changeset patch # Parent 6fd814ecf94bb1713ebf96bffcab63d6c3bdd72b diff -r 6fd814ecf94b include/client.h --- a/include/client.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/client.h Fri Jul 19 22:26:20 2013 +0100 @@ -168,6 +168,7 @@ 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 */ @@ -601,6 +602,9 @@ #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) @@ -640,6 +644,8 @@ #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). */ @@ -681,6 +687,8 @@ #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 -r 6fd814ecf94b include/handlers.h --- a/include/handlers.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/handlers.h Fri Jul 19 22:26:20 2013 +0100 @@ -124,6 +124,7 @@ 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*[]); @@ -213,6 +214,7 @@ extern int ms_rping(struct Client*, struct Client*, int, char*[]); extern int ms_rpong(struct Client*, struct Client*, int, char*[]); extern int ms_server(struct Client*, struct Client*, int, char*[]); +extern int ms_sethost(struct Client*, struct Client*, int, char*[]); extern int ms_settime(struct Client*, struct Client*, int, char*[]); extern int ms_silence(struct Client*, struct Client*, int, char*[]); extern int ms_squit(struct Client*, struct Client*, int, char*[]); diff -r 6fd814ecf94b include/ircd_features.h --- a/include/ircd_features.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/ircd_features.h Fri Jul 19 22:26:20 2013 +0100 @@ -103,6 +103,9 @@ /* features that affect all operators */ FEAT_CONFIG_OPERCMDS, + FEAT_SETHOST, + FEAT_SETHOST_USER, + FEAT_SETHOST_AUTO, /* HEAD_IN_SAND Features */ FEAT_HIS_SNOTICES, @@ -131,6 +134,7 @@ 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 -r 6fd814ecf94b include/ircd_log.h --- a/include/ircd_log.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/ircd_log.h Fri Jul 19 22:26:20 2013 +0100 @@ -65,6 +65,7 @@ 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 -r 6fd814ecf94b include/msg.h --- a/include/msg.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/msg.h Fri Jul 19 22:26:20 2013 +0100 @@ -356,6 +356,10 @@ #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 -r 6fd814ecf94b include/numeric.h --- a/include/numeric.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/numeric.h Fri Jul 19 22:26:20 2013 +0100 @@ -207,7 +207,7 @@ #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 @@ /* 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 @@ -461,6 +463,9 @@ #define ERR_QUARANTINED 524 /* Undernet extension -Vampire */ #define ERR_INVALIDKEY 525 /* Undernet extension */ +#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 */ #define ERR_CHANSECURED 562 /* Undernet extension */ diff -r 6fd814ecf94b include/s_conf.h --- a/include/s_conf.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/s_conf.h Fri Jul 19 22:26:20 2013 +0100 @@ -80,6 +80,20 @@ 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 int GlobalConfCount; extern struct s_map* GlobalServiceMapList; extern struct qline* GlobalQuarantineList; +extern struct sline* GlobalSList; /* * Proto types @@ -188,6 +203,11 @@ 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 -r 6fd814ecf94b include/s_user.h --- a/include/s_user.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/s_user.h Fri Jul 19 22:26:20 2013 +0100 @@ -80,6 +80,8 @@ 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[], int allow_modes); extern int is_silenced(struct Client *sptr, struct Client *acptr); diff -r 6fd814ecf94b include/struct.h --- a/include/struct.h Fri Jul 19 21:53:51 2013 +0100 +++ b/include/struct.h Fri Jul 19 22:26:20 2013 +0100 @@ -80,10 +80,11 @@ * 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 -r 6fd814ecf94b ircd/Makefile.in --- a/ircd/Makefile.in Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/Makefile.in Fri Jul 19 22:26:20 2013 +0100 @@ -167,6 +167,7 @@ m_rpong.c \ m_server.c \ m_set.c \ + m_sethost.c \ m_settime.c \ m_silence.c \ m_squit.c \ diff -r 6fd814ecf94b ircd/channel.c --- a/ircd/channel.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/channel.c Fri Jul 19 22:26:20 2013 +0100 @@ -374,22 +374,28 @@ char tmphost[HOSTLEN + 1]; char iphost[SOCKIPLEN + 1]; char *hostmask; - char *sr; + char *sr = NULL; + char *sa = NULL; struct Ban *found; /* Build nick!user and alternate host names. */ ircd_snprintf(0, nu, sizeof(nu), "%s!%s", cli_name(cptr), cli_user(cptr)->username); ircd_ntoa_r(iphost, &cli_ip(cptr)); - if (!IsAccount(cptr)) - sr = NULL; - else if (HasHiddenHost(cptr)) + + /* sr is real host if +h */ + if (HasSetHost(cptr)) sr = cli_user(cptr)->realhost; - else - { - ircd_snprintf(0, tmphost, HOSTLEN, "%s.%s", - cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST)); - sr = tmphost; + + /* if +x and not +h sa is real host, if -x or +h sa is the account host */ + if (IsAccount(cptr)) { + if (HasHiddenHost(cptr) && !HasSetHost(cptr)) { + sa = cli_user(cptr)->realhost; + } else { + ircd_snprintf(0, tmphost, HOSTLEN, "%s.%s", + cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST)); + sa = tmphost; + } } /* Walk through ban list. */ @@ -409,7 +415,8 @@ if (!((banlist->flags & BAN_IPMASK) && ipmask_check(&cli_ip(cptr), &banlist->address, banlist->addrbits)) && match(hostmask, cli_user(cptr)->host) - && !(sr && !match(hostmask, sr))) + && !(sr && !match(hostmask, sr)) + && !(sa && !match(hostmask, sa))) continue; /* If an exception matches, no ban can match. */ if (banlist->flags & BAN_EXCEPTION) diff -r 6fd814ecf94b ircd/gline.c --- a/ircd/gline.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/gline.c Fri Jul 19 22:26:20 2013 +0100 @@ -221,7 +221,7 @@ 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 -r 6fd814ecf94b ircd/ircd_features.c --- a/ircd/ircd_features.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/ircd_features.c Fri Jul 19 22:26:20 2013 +0100 @@ -368,6 +368,9 @@ /* features that affect all operators */ F_B(CONFIG_OPERCMDS, 0, 0, 0), + F_B(SETHOST, 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), @@ -396,6 +399,7 @@ 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 -r 6fd814ecf94b ircd/ircd_lexer.l --- a/ircd/ircd_lexer.l Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/ircd_lexer.l Fri Jul 19 22:26:20 2013 +0100 @@ -103,6 +103,7 @@ TOKEN(USERMODE), TOKEN(FAST), TOKEN(AUTOCONNECT), + TOKEN(SPOOFHOST), TOKEN(PROGRAM), TOKEN(DNS), #undef TOKEN diff -r 6fd814ecf94b ircd/ircd_log.c --- a/ircd/ircd_log.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/ircd_log.c Fri Jul 19 22:26:20 2013 +0100 @@ -164,6 +164,7 @@ 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 -r 6fd814ecf94b ircd/ircd_parser.y --- a/ircd/ircd_parser.y Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/ircd_parser.y Fri Jul 19 22:26:20 2013 +0100 @@ -69,6 +69,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 :/... */ @@ -81,6 +82,7 @@ struct DenyConf *dconf; struct ServerConf *sconf; struct s_map *smap; + struct sline *spoof; struct Privs privs; struct Privs privs_dirty; @@ -173,6 +175,7 @@ %token FAST %token AUTOCONNECT %token PROGRAM +%token SPOOFHOST %token TOK_IPV4 TOK_IPV6 %token DNS /* and now a lot of privileges... */ @@ -202,7 +205,7 @@ 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. */ @@ -1160,3 +1163,71 @@ MyFree(stringlist[stringno]); } } stringlist ';'; + +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; + int valid = 0; + + 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."); + } else + valid = 1; + + if (valid) { + 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; + } else { + MyFree(spoof->spoofhost); + MyFree(spoof->passwd); + MyFree(spoof->realhost); + MyFree(spoof->username); + MyFree(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 -r 6fd814ecf94b ircd/ircd_snprintf.c --- a/ircd/ircd_snprintf.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/ircd_snprintf.c Fri Jul 19 22:26:20 2013 +0100 @@ -180,8 +180,8 @@ #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 @@ 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 @@ 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 @@ 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 -r 6fd814ecf94b ircd/m_oper.c --- a/ircd/m_oper.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/m_oper.c Fri Jul 19 22:26:20 2013 +0100 @@ -151,7 +151,7 @@ { 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 @@ 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 @@ 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 -r 6fd814ecf94b ircd/m_sethost.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ircd/m_sethost.c Fri Jul 19 22:26:20 2013 +0100 @@ -0,0 +1,256 @@ +/* + * 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_snprintf.h" +#include "ircd_features.h" +#include "msgq.h" +#include "numeric.h" +#include "s_conf.h" +#include "s_user.h" +#include "s_debug.h" +#include "send.h" +#include "struct.h" +#include "numnicks.h" + +#include "channel.h" +#include "msg.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[USERLEN + HOSTLEN + 2]; + char curhostmask[USERLEN + HOSTLEN + 2]; + + 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 + 2, "%s@%s", parv[1], parv[2]); + if (!is_hostmask(hostmask)) { + send_reply(sptr, ERR_BADHOSTMASK, hostmask); + return 0; + } + if (IsSetHost(sptr) || IsAccount(sptr)) { + ircd_snprintf(0, curhostmask, USERLEN + HOSTLEN + 2, "%s@%s", sptr->cli_user->username, sptr->cli_user->host); + if (0 == strcmp(hostmask, curhostmask)) { + send_reply(sptr, RPL_HOSTHIDDEN, curhostmask); + 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 (IsSetHost(sptr) || IsAccount(sptr)) { + if (0 == strcmp(parv[1], sptr->cli_user->host)) { + send_reply(sptr, RPL_HOSTHIDDEN, parv[1]); + return 0; + } + } + if (set_hostmask(sptr, parv[1], parv[2])) + FlagClr(&setflags, FLAG_SETHOST); + } + } + + send_umode_out(cptr, sptr, &setflags, 0); + return 0; +} + + +/* + * ms_sethost - sethost server message handler + * + * parv[0] = sender prefix + * parv[1] = target user numeric + * parv[2] = target user's new ident + * parv[3] = target user's new host + */ +int ms_sethost(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +{ + struct Client *target; + char hostmask[USERLEN + HOSTLEN + 2]; + struct Membership *chan; + struct Flags setflags; + + if (parc < 4) + return need_more_params(sptr, "SETHOST"); + + if (!IsServer(sptr)) + return protocol_violation(cptr, "SETHOST from non-server %s", + cli_name(sptr)); + + /* Locate our target user; ignore the message if we can't */ + if(!(target = findNUser(parv[1]))) + return 0; + + /* Fake host assignments must be from services */ + if (!find_conf_byhost(cli_confs(sptr), cli_name(sptr), CONF_UWORLD)) + return protocol_violation(cptr, "Non-U:lined server %s set fake host on user %s", cli_name(sptr), cli_name(target)); + + if (!MyConnect(target)) { + sendcmdto_one(sptr, CMD_SETHOST, cli_user(target)->server, "%C %s %s", target, + parv[2], parv[3]); + return 0; + } + + /* Back up the flags first */ + setflags = cli_flags(target); + FlagClr(&setflags, FLAG_SETHOST); + + if (IsSetHost(target) || IsAccount(target)) { + if ((0 == strcmp(parv[2], target->cli_user->username)) && (0 == strcmp(parv[3], target->cli_user->host))) + return 0; + } + + ircd_snprintf(0, hostmask, USERLEN + HOSTLEN + 2, "%s@%s", parv[2], parv[3]); + if (!is_hostmask(hostmask)) + return protocol_violation(cptr, "Bad Host mask %s for user %s", hostmask, cli_name(target)); + + sendcmdto_common_channels_butone(target, CMD_QUIT, target, ":Host change"); + + /* Assign and propagate the fakehost */ + SetSetHost(target); + ircd_strncpy(cli_user(target)->username, parv[2], USERLEN); + ircd_strncpy(cli_user(target)->host, parv[3], HOSTLEN); + + send_reply(target, RPL_HOSTHIDDEN, hostmask); + + /* + * Go through all channels the client was on, rejoin him + * and set the modes, if any + */ + for (chan = cli_user(target)->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(target, CMD_JOIN, chan->channel, target, 0, + "%H", chan->channel); + } + if (IsChanOp(chan) && HasVoice(chan)) { + sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, target, 0, + "%H +ov %C %C", chan->channel, target, target); + } else if (IsChanOp(chan) || HasVoice(chan)) { + sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan->channel, target, 0, + "%H +%c %C", chan->channel, IsChanOp(chan) ? 'o' : 'v', target); + } + } + + send_umode_out(target, target, &setflags, 0); + return 0; +} + diff -r 6fd814ecf94b ircd/m_userhost.c --- a/ircd/m_userhost.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/m_userhost.c Fri Jul 19 22:26:20 2013 +0100 @@ -104,7 +104,7 @@ * 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 -r 6fd814ecf94b ircd/m_userip.c --- a/ircd/m_userip.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/m_userip.c Fri Jul 19 22:26:20 2013 +0100 @@ -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 @@ * 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 -r 6fd814ecf94b ircd/m_who.c --- a/ircd/m_who.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/m_who.c Fri Jul 19 22:26:20 2013 +0100 @@ -408,13 +408,14 @@ && ((!(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))) @@ -446,13 +447,14 @@ && ((!(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 -r 6fd814ecf94b ircd/m_whois.c --- a/ircd/m_whois.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/m_whois.c Fri Jul 19 22:26:20 2013 +0100 @@ -211,8 +211,8 @@ 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 -r 6fd814ecf94b ircd/parse.c --- a/ircd/parse.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/parse.c Fri Jul 19 22:26:20 2013 +0100 @@ -618,6 +618,13 @@ { 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, ms_sethost, m_sethost, m_ignore } + }, + { MSG_XQUERY, TOK_XQUERY, 0, MAXPARA, MFLG_SLOW, 0, NULL, diff -r 6fd814ecf94b ircd/s_conf.c --- a/ircd/s_conf.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/s_conf.c Fri Jul 19 22:26:20 2013 +0100 @@ -74,6 +74,8 @@ 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; @@ -943,6 +945,7 @@ clearNickJupes(); clear_quarantines(); + clear_slines(); class_mark_delete(); mark_listeners_closing(); @@ -1193,3 +1196,81 @@ 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 -r 6fd814ecf94b ircd/s_err.c --- a/ircd/s_err.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/s_err.c Fri Jul 19 22:26:20 2013 +0100 @@ -640,7 +640,7 @@ /* 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 @@ /* 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 @@ /* 529 */ { 0 }, /* 530 */ - { 0 }, + { ERR_BADHOSTMASK, "%s :Invalid username/hostmask", "530" }, /* 531 */ - { 0 }, + { ERR_HOSTUNAVAIL, "%s :sethost not found", "531" }, /* 532 */ { 0 }, /* 533 */ diff -r 6fd814ecf94b ircd/s_serv.c --- a/ircd/s_serv.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/s_serv.c Fri Jul 19 22:26:20 2013 +0100 @@ -247,7 +247,7 @@ 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 -r 6fd814ecf94b ircd/s_stats.c --- a/ircd/s_stats.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/s_stats.c Fri Jul 19 22:26:20 2013 +0100 @@ -400,6 +400,44 @@ } } +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). @@ -593,6 +631,9 @@ 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 -r 6fd814ecf94b ircd/s_user.c --- a/ircd/s_user.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/s_user.c Fri Jul 19 22:26:20 2013 +0100 @@ -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; @@ -368,6 +371,13 @@ if (feature_bool(FEAT_AUTOINVISIBLE)) SetInvisible(sptr); + if(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); @@ -433,6 +443,7 @@ sptr, cli_name(&me)); return exit_client(cptr, sptr, &me,"Too many connections from your host -- throttled"); } + SetUser(sptr); } @@ -454,7 +465,7 @@ "%s %d %Tu %s %s %s%s%s%s %s%s :%s", cli_name(sptr), 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)); @@ -464,7 +475,7 @@ "%s %d %Tu %s %s %s%s%s%s %s%s :%s", cli_name(sptr), 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)); @@ -506,7 +517,8 @@ { FLAG_ACCOUNTONLY, 'R' }, { FLAG_XTRAOP, 'X' }, { FLAG_NOCHAN, 'n' }, - { FLAG_NOIDLE, 'I' } + { FLAG_NOIDLE, 'I' }, + { FLAG_SETHOST, 'h' } }; /** Length of #userModeList. */ @@ -559,6 +571,7 @@ 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)->username, 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); @@ -837,7 +850,7 @@ { 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); @@ -905,7 +918,7 @@ } SetFlag(cptr, flag); - if (!HasFlag(cptr, FLAG_HIDDENHOST) || !HasFlag(cptr, FLAG_ACCOUNT)) + if (!HasFlag(cptr, FLAG_HIDDENHOST) || !HasFlag(cptr, FLAG_ACCOUNT) || HasSetHost(cptr)) return 0; sendcmdto_common_channels_butone(cptr, CMD_QUIT, cptr, ":Registered"); @@ -939,6 +952,190 @@ 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? and only opers can remove a sethost */ + if (IsSetHost(cptr) && IsAnOper(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 @@ -965,6 +1162,7 @@ char buf[BUFSIZE]; int prop = 0; int do_host_hiding = 0; + int do_set_host = 0; char* account = NULL; what = MODE_ADD; @@ -976,7 +1174,8 @@ 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'; @@ -1103,6 +1302,30 @@ if (what == MODE_ADD) 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 (*(p + 1) && (what == MODE_ADD)) { account = *(++p); @@ -1269,10 +1492,15 @@ 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() */ } @@ -1289,6 +1517,7 @@ { int i; int flag; + int needhost = 0; char *m; int what = MODE_NULL; @@ -1318,6 +1547,16 @@ 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) @@ -1341,9 +1580,14 @@ } } } - *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); } /** @@ -1366,6 +1610,110 @@ 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 -r 6fd814ecf94b ircd/send.c --- a/ircd/send.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/send.c Fri Jul 19 22:26:20 2013 +0100 @@ -281,7 +281,7 @@ { 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 -r 6fd814ecf94b ircd/whocmds.c --- a/ircd/whocmds.c Fri Jul 19 21:53:51 2013 +0100 +++ b/ircd/whocmds.c Fri Jul 19 22:26:20 2013 +0100 @@ -129,7 +129,7 @@ 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++) = ' '; @@ -205,6 +205,8 @@ *(p1++) = 'w'; if (SendDebug(acptr)) *(p1++) = 'g'; + if (HasSetHost(acptr)) + *(p1++) = 'h'; } if (HasHiddenHost(acptr)) *(p1++) = 'x'; diff -r 6fd814ecf94b tools/convert_slines.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/convert_slines.sh Fri Jul 19 22:26:20 2013 +0100 @@ -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