From 55dbd232d16ec5afa4cfd2e98388c58dcc93c928 Mon Sep 17 00:00:00 2001 From: ThiefMaster Date: Sat, 28 Mar 2009 14:12:07 +0100 Subject: [PATCH] Add support for snircd-style fakehosts (usermode +h, fake idents) --- src/hash.c | 9 ++- src/hash.h | 4 +- src/nickserv.c | 151 ++++++++++++++++++++++++++++++++++++++------ src/nickserv.h | 1 + src/opserv.c | 25 ++++---- src/proto-bahamut.c | 2 +- src/proto-p10.c | 35 +++++++--- src/proto.h | 2 +- 8 files changed, 185 insertions(+), 44 deletions(-) diff --git a/src/hash.c b/src/hash.c index 75769fa..5a8df93 100644 --- a/src/hash.c +++ b/src/hash.c @@ -253,11 +253,14 @@ StampUser(struct userNode *user, const char *stamp, unsigned long timestamp, uns } void -assign_fakehost(struct userNode *user, const char *host, int announce) +assign_fakehost(struct userNode *user, const char *host, const char *ident, int force, int announce) { - safestrncpy(user->fakehost, host, sizeof(user->fakehost)); + if (host) + safestrncpy(user->fakehost, host, sizeof(user->fakehost)); + if (ident) + safestrncpy(user->fakeident, ident, sizeof(user->ident)); if (announce) - irc_fakehost(user, host); + irc_fakehost(user, host, ident, force); } static new_channel_func_t *ncf_list; diff --git a/src/hash.h b/src/hash.h index 71b9251..bd7b335 100644 --- a/src/hash.h +++ b/src/hash.h @@ -79,6 +79,7 @@ #define IsDummy(x) ((x)->modes & FLAGS_DUMMY) #define IsNoIdle(x) ((x)->modes & FLAGS_NOIDLE) #define IsFakeHost(x) ((x)->fakehost[0] != '\0') +#define IsFakeIdent(x) ((x)->fakeident[0] != '\0') #define IsLocal(x) ((x)->uplink == self) #define NICKLEN 30 @@ -107,6 +108,7 @@ struct userNode { char info[REALLEN + 1]; /* Free form additional client information */ char hostname[HOSTLEN + 1]; /* DNS name or IP address */ char fakehost[HOSTLEN + 1]; /* Assigned fake host */ + char fakeident[USERLEN + 1]; /* Assigned fake ident */ #ifdef WITH_PROTOCOL_P10 char numeric[COMBO_NUMERIC_LEN+1]; unsigned int num_local : 18; @@ -219,7 +221,7 @@ typedef void (*account_func_t) (struct userNode *user, const char *stamp, unsign void reg_account_func(account_func_t handler); void call_account_func(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial); void StampUser(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial); -void assign_fakehost(struct userNode *user, const char *host, int announce); +void assign_fakehost(struct userNode *user, const char *host, const char *ident, int force, int announce); typedef void (*new_channel_func_t) (struct chanNode *chan); void reg_new_channel_func(new_channel_func_t handler); diff --git a/src/nickserv.c b/src/nickserv.c index 0ba125e..b1f0d29 100644 --- a/src/nickserv.c +++ b/src/nickserv.c @@ -51,6 +51,7 @@ #define KEY_SET_EPITHET_LEVEL "set_epithet_level" #define KEY_SET_TITLE_LEVEL "set_title_level" #define KEY_SET_FAKEHOST_LEVEL "set_fakehost_level" +#define KEY_SET_FAKEIDENT_LEVEL "set_fakeident_level" #define KEY_TITLEHOST_SUFFIX "titlehost_suffix" #define KEY_FLAG_LEVELS "flag_levels" #define KEY_HANDLE_EXPIRE_FREQ "handle_expire_freq" @@ -101,6 +102,7 @@ #define KEY_TABLE_WIDTH "table_width" #define KEY_MAXLOGINS "maxlogins" #define KEY_FAKEHOST "fakehost" +#define KEY_FAKEIDENT "fakeident" #define KEY_NOTES "notes" #define KEY_NOTE_EXPIRES "expires" #define KEY_NOTE_SET "set" @@ -193,6 +195,7 @@ static const struct message_entry msgtab[] = { { "NSMSG_TITLE_INVALID", "Titles cannot contain any dots; please choose another." }, { "NSMSG_TITLE_TRUNCATED", "That title combined with the user's account name would result in a truncated host; please choose a shorter title." }, { "NSMSG_FAKEHOST_INVALID", "Fake hosts must be shorter than %d characters and cannot start with a dot." }, + { "NSMSG_FAKEIDENT_INVALID", "Fake idents must be shorter than %d characters." }, { "NSMSG_HANDLEINFO_ON", "Account information for $b%s$b:" }, { "NSMSG_HANDLEINFO_ID", " Account ID: %lu" }, { "NSMSG_HANDLEINFO_REGGED", " Registered on: %s" }, @@ -209,6 +212,7 @@ static const struct message_entry msgtab[] = { { "NSMSG_HANDLEINFO_INFOLINE", " Infoline: %s" }, { "NSMSG_HANDLEINFO_FLAGS", " Flags: %s" }, { "NSMSG_HANDLEINFO_EPITHET", " Epithet: %s" }, + { "NSMSG_HANDLEINFO_FAKEIDENT", " Fake ident: %s" }, { "NSMSG_HANDLEINFO_FAKEHOST", " Fake host: %s" }, { "NSMSG_HANDLEINFO_LAST_HOST", " Last quit hostmask: %s" }, { "NSMSG_HANDLEINFO_NO_NOTES", " Notes: None" }, @@ -312,6 +316,7 @@ static const struct message_entry msgtab[] = { { "NSMSG_SET_EPITHET", "$bEPITHET: $b%s" }, { "NSMSG_SET_TITLE", "$bTITLE: $b%s" }, { "NSMSG_SET_FAKEHOST", "$bFAKEHOST: $b%s" }, + { "NSMSG_SET_FAKEIDENT", "$bFAKEIDENT: $b%s" }, { "NSMSG_INVALID_KARMA", "$b%s$b is not a valid karma modifier." }, { "NSMSG_SET_KARMA", "$bKARMA: $b%d$b" }, { "NSEMAIL_ACTIVATION_SUBJECT", "Account verification for %s" }, @@ -374,6 +379,7 @@ static struct { unsigned long set_epithet_level; unsigned long set_title_level; unsigned long set_fakehost_level; + unsigned long set_fakeident_level; unsigned long handles_per_email; unsigned long email_search_level; const char *network_name; @@ -538,6 +544,7 @@ free_handle_info(void *vhi) free(hi->infoline); free(hi->epithet); free(hi->fakehost); + free(hi->fakeident); if (hi->cookie) { timeq_del(hi->cookie->expires, nickserv_free_cookie, hi->cookie, 0); nickserv_free_cookie(hi->cookie); @@ -883,17 +890,41 @@ generate_fakehost(struct handle_info *handle) return handle->fakehost; } +static char * +generate_fakeident(struct handle_info *handle, struct userNode *user) +{ + static char buffer[USERLEN+1]; + + if (!handle->fakeident) { + if (!user) + return NULL; + safestrncpy(buffer, user->ident, sizeof(buffer)); + return buffer; + } + return handle->fakeident; +} + static void -apply_fakehost(struct handle_info *handle) +apply_fakehost(struct handle_info *handle, struct userNode *user) { struct userNode *target; - char *fake; + char *fakehost, *fakeident; if (!handle->users) return; - fake = generate_fakehost(handle); - for (target = handle->users; target; target = target->next_authed) - assign_fakehost(target, fake, 1); + + fakehost = generate_fakehost(handle); + + if (user) { + fakeident = generate_fakeident(handle, user); + assign_fakehost(user, fakehost, fakeident, 0, 1); + return; + } + + for (target = handle->users; target; target = target->next_authed) { + fakeident = generate_fakeident(handle, target); + assign_fakehost(target, fakehost, fakeident, 0, 1); + } } static void @@ -958,8 +989,8 @@ set_user_handle_info(struct userNode *user, struct handle_info *hi, int stamp) if (IsHelper(user) && !userList_contains(&curr_helpers, user)) userList_append(&curr_helpers, user); - if (hi->fakehost || old_info) - apply_fakehost(hi); + if (hi->fakehost || hi->fakeident || old_info) + apply_fakehost(hi, user); if (stamp) { if (!nickserv_conf.disable_nicks) { @@ -1410,6 +1441,9 @@ static NICKSERV_FUNC(cmd_handleinfo) reply("NSMSG_HANDLEINFO_EPITHET", (hi->epithet ? hi->epithet : nsmsg_none)); } + if (hi->fakeident) + reply("NSMSG_HANDLEINFO_FAKEIDENT", (hi->fakeident ? hi->fakeident : handle_find_message(hi, "MSG_NONE"))); + if (hi->fakehost) reply("NSMSG_HANDLEINFO_FAKEHOST", (hi->fakehost ? hi->fakehost : handle_find_message(hi, "MSG_NONE"))); @@ -2575,7 +2609,7 @@ static OPTION_FUNC(opt_title) hi->fakehost[0] = '.'; strcpy(hi->fakehost+1, title); } - apply_fakehost(hi); + apply_fakehost(hi, NULL); } else if (hi->fakehost && (hi->fakehost[0] == '.')) title = hi->fakehost + 1; else @@ -2588,7 +2622,8 @@ static OPTION_FUNC(opt_title) static OPTION_FUNC(opt_fakehost) { - const char *fake; + char mask[USERLEN + HOSTLEN + 2]; + char *host, *ident; if (!override) { send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); @@ -2596,23 +2631,83 @@ static OPTION_FUNC(opt_fakehost) } if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakehost_level, 0)) { - fake = argv[1]; - if ((strlen(fake) > HOSTLEN) || (fake[0] == '.')) { + safestrncpy(mask, argv[1], sizeof(mask)); + + if ((host = strrchr(mask, '@')) && host != mask && + oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) { + ident = mask; + *host++ = '\0'; + } else { + ident = NULL; + host = mask; + } + + if ((strlen(host) > HOSTLEN) || (host[0] == '.')) { send_message(user, nickserv, "NSMSG_FAKEHOST_INVALID", HOSTLEN); return 0; } + + if (ident && strlen(ident) > USERLEN) { + send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN); + return 0; + } + free(hi->fakehost); - if (!strcmp(fake, "*")) + if (!strcmp(host, "*")) hi->fakehost = NULL; else - hi->fakehost = strdup(fake); - fake = hi->fakehost; - apply_fakehost(hi); + hi->fakehost = strdup(host); + host = hi->fakehost; + + if (ident) { + free(hi->fakeident); + if (!strcmp(ident, "*")) + hi->fakeident = NULL; + else + hi->fakeident = strdup(ident); + ident = hi->fakeident; + } + + apply_fakehost(hi, NULL); + } else { + host = generate_fakehost(hi); + ident = generate_fakeident(hi, NULL); + } + if (!host) + host = (char *) user_find_message(user, "MSG_NONE"); + send_message(user, nickserv, "NSMSG_SET_FAKEHOST", host); + if (ident) + send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident); + return 1; +} + +static OPTION_FUNC(opt_fakeident) +{ + const char *ident; + + if (!override) { + send_message(user, nickserv, "MSG_SETTING_PRIVILEGED", argv[0]); + return 0; + } + + if ((argc > 1) && oper_has_access(user, nickserv, nickserv_conf.set_fakeident_level, 0)) { + ident = argv[1]; + if (strlen(ident) > USERLEN) { + send_message(user, nickserv, "NSMSG_FAKEIDENT_INVALID", USERLEN); + return 0; + } + free(hi->fakeident); + if (!strcmp(ident, "*")) + hi->fakeident = NULL; + else + hi->fakeident = strdup(ident); + ident = hi->fakeident; + apply_fakehost(hi, NULL); } else - fake = generate_fakehost(hi); - if (!fake) - fake = user_find_message(user, "MSG_NONE"); - send_message(user, nickserv, "NSMSG_SET_FAKEHOST", fake); + ident = generate_fakeident(hi, NULL); /* NULL if no fake ident set */ + if (!ident) + ident = user_find_message(user, "MSG_NONE"); + send_message(user, nickserv, "NSMSG_SET_FAKEIDENT", ident); return 1; } @@ -2915,6 +3010,8 @@ nickserv_saxdb_write(struct saxdb_context *ctx) { saxdb_write_string(ctx, KEY_EPITHET, hi->epithet); if (hi->fakehost) saxdb_write_string(ctx, KEY_FAKEHOST, hi->fakehost); + if (hi->fakeident) + saxdb_write_string(ctx, KEY_FAKEIDENT, hi->fakeident); if (hi->flags) { int ii, flen; @@ -3090,6 +3187,8 @@ static NICKSERV_FUNC(cmd_merge) */ if (hi_from->fakehost && !hi_to->fakehost) hi_to->fakehost = strdup(hi_from->fakehost); + if (hi_from->fakeident && !hi_to->fakeident) + hi_to->fakeident = strdup(hi_from->fakeident); /* Notify of success. */ sprintf(buffer, "%s (%s) merged account %s into %s.", user->nick, user->handle_info->handle, hi_from->handle, hi_to->handle); @@ -3113,6 +3212,7 @@ struct nickserv_discrim { const char *nickmask; const char *hostmask; const char *fakehostmask; + const char *fakeidentmask; const char *handlemask; const char *emailmask; }; @@ -3212,6 +3312,12 @@ nickserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[]) } else { discrim->fakehostmask = argv[i]; } + } else if (!irccasecmp(argv[i], "fakeident")) { + if (!irccasecmp(argv[++i], "*")) { + discrim->fakeidentmask = 0; + } else { + discrim->fakeidentmask = argv[i]; + } } else if (!irccasecmp(argv[i], "handlemask") || !irccasecmp(argv[i], "accountmask")) { if (!irccasecmp(argv[++i], "*")) { discrim->handlemask = 0; @@ -3287,6 +3393,7 @@ nickserv_discrim_match(struct nickserv_discrim *discrim, struct handle_info *hi) || (discrim->lastseen < (hi->users?now:hi->lastseen)) || (discrim->handlemask && !match_ircglob(hi->handle, discrim->handlemask)) || (discrim->fakehostmask && (!hi->fakehost || !match_ircglob(hi->fakehost, discrim->fakehostmask))) + || (discrim->fakeidentmask && (!hi->fakeident || !match_ircglob(hi->fakeident, discrim->fakeidentmask))) || (discrim->emailmask && (!hi->email_addr || !match_ircglob(hi->email_addr, discrim->emailmask))) || (discrim->min_level > hi->opserv_level) || (discrim->max_level < hi->opserv_level) @@ -3571,6 +3678,9 @@ nickserv_db_read_handle(const char *handle, dict_t obj) str = database_get_data(obj, KEY_FAKEHOST, RECDB_QSTRING); if (str) hi->fakehost = strdup(str); + str = database_get_data(obj, KEY_FAKEIDENT, RECDB_QSTRING); + if (str) + hi->fakeident = strdup(str); /* Read the "cookie" sub-database (if it exists). */ subdb = database_get_data(obj, KEY_COOKIE, RECDB_OBJECT); if (subdb) { @@ -3813,6 +3923,8 @@ nickserv_conf_read(void) nickserv_conf.set_title_level = str ? strtoul(str, NULL, 0) : 900; str = database_get_data(conf_node, KEY_SET_FAKEHOST_LEVEL, RECDB_QSTRING); nickserv_conf.set_fakehost_level = str ? strtoul(str, NULL, 0) : 1000; + str = database_get_data(conf_node, KEY_SET_FAKEIDENT_LEVEL, RECDB_QSTRING); + nickserv_conf.set_fakeident_level = str ? strtoul(str, NULL, 0) : 1000; str = database_get_data(conf_node, KEY_HANDLE_EXPIRE_FREQ, RECDB_QSTRING); if (!str) str = database_get_data(conf_node, KEY_ACCOUNT_EXPIRE_FREQ, RECDB_QSTRING); @@ -4161,6 +4273,7 @@ init_nickserv(const char *nick) if (nickserv_conf.titlehost_suffix) { dict_insert(nickserv_opt_dict, "TITLE", opt_title); dict_insert(nickserv_opt_dict, "FAKEHOST", opt_fakehost); + dict_insert(nickserv_opt_dict, "FAKEIDENT", opt_fakeident); } dict_insert(nickserv_opt_dict, "MAXLOGINS", opt_maxlogins); dict_insert(nickserv_opt_dict, "LANGUAGE", opt_language); diff --git a/src/nickserv.h b/src/nickserv.h index 3311c90..6d6bcd5 100644 --- a/src/nickserv.h +++ b/src/nickserv.h @@ -97,6 +97,7 @@ struct handle_info { char *infoline; char *handle; char *fakehost; + char *fakeident; unsigned long id; unsigned long registered; unsigned long lastseen; diff --git a/src/opserv.c b/src/opserv.c index 488f2a7..198f5e7 100644 --- a/src/opserv.c +++ b/src/opserv.c @@ -116,17 +116,18 @@ static const struct message_entry msgtab[] = { { "OSMSG_OP_DONE", "Opped the requested lusers." }, { "OSMSG_OPALL_DONE", "Opped everyone on $b%s$b." }, { "OSMSG_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" }, - { "OSMSG_WHOIS_NICK", "Nick : %s" }, - { "OSMSG_WHOIS_HOST", "Host : %s@%s" }, - { "OSMSG_WHOIS_FAKEHOST", "Fakehost: %s" }, - { "OSMSG_WHOIS_IP", "Real IP : %s" }, - { "OSMSG_WHOIS_MODES", "Modes : +%s " }, - { "OSMSG_WHOIS_INFO", "Info : %s" }, - { "OSMSG_WHOIS_NUMERIC", "Numnick : %s" }, - { "OSMSG_WHOIS_SERVER", "Server : %s" }, - { "OSMSG_WHOIS_NICK_AGE", "Nick Age: %s" }, - { "OSMSG_WHOIS_ACCOUNT", "Account : %s" }, - { "OSMSG_WHOIS_CHANNELS", "Channels: %s" }, + { "OSMSG_WHOIS_NICK", "Nick : %s" }, + { "OSMSG_WHOIS_HOST", "Host : %s@%s" }, + { "OSMSG_WHOIS_FAKEHOST", "Fakehost : %s" }, + { "OSMSG_WHOIS_FAKEIDENT", "Fakeident: %s" }, + { "OSMSG_WHOIS_IP", "Real IP : %s" }, + { "OSMSG_WHOIS_MODES", "Modes : +%s " }, + { "OSMSG_WHOIS_INFO", "Info : %s" }, + { "OSMSG_WHOIS_NUMERIC", "Numnick : %s" }, + { "OSMSG_WHOIS_SERVER", "Server : %s" }, + { "OSMSG_WHOIS_NICK_AGE", "Nick Age : %s" }, + { "OSMSG_WHOIS_ACCOUNT", "Account : %s" }, + { "OSMSG_WHOIS_CHANNELS", "Channels : %s" }, { "OSMSG_WHOIS_HIDECHANS", "Channel list omitted for your sanity." }, { "OSMSG_UNBAN_DONE", "Ban(s) removed from channel %s." }, { "OSMSG_CHANNEL_VOICED", "All users on %s voiced." }, @@ -1252,6 +1253,8 @@ static MODCMD_FUNC(cmd_whois) } reply("OSMSG_WHOIS_NICK", target->nick); reply("OSMSG_WHOIS_HOST", target->ident, target->hostname); + if (IsFakeIdent(target)) + reply("OSMSG_WHOIS_FAKEIDENT", target->fakeident); if (IsFakeHost(target)) reply("OSMSG_WHOIS_FAKEHOST", target->fakehost); reply("OSMSG_WHOIS_IP", irc_ntoa(&target->ip)); diff --git a/src/proto-bahamut.c b/src/proto-bahamut.c index 01c71d8..aa1436c 100644 --- a/src/proto-bahamut.c +++ b/src/proto-bahamut.c @@ -263,7 +263,7 @@ irc_account(struct userNode *user, UNUSED_ARG(const char *stamp), UNUSED_ARG(uns } void -irc_fakehost(UNUSED_ARG(struct userNode *user), UNUSED_ARG(const char *host)) +irc_fakehost(UNUSED_ARG(struct userNode *user), UNUSED_ARG(const char *host), UNUSED_ARG(const char *ident), UNUSED_ARG(int force)) { /* not supported in bahamut */ } diff --git a/src/proto-p10.c b/src/proto-p10.c index f6eb772..4f7f290 100644 --- a/src/proto-p10.c +++ b/src/proto-p10.c @@ -490,9 +490,9 @@ irc_account(struct userNode *user, const char *stamp, unsigned long timestamp, u } void -irc_fakehost(struct userNode *user, const char *host) +irc_fakehost(struct userNode *user, const char *host, const char *ident, int force) { - putsock("%s " P10_FAKEHOST " %s %s", self->numeric, user->numeric, host); + putsock("%s " P10_FAKEHOST " %s %s %s%s", self->numeric, user->numeric, ident, host, force ? " FORCE" : ""); } void @@ -1242,12 +1242,22 @@ static CMD_FUNC(cmd_account) static CMD_FUNC(cmd_fakehost) { struct userNode *user; + const char *host, *ident; if ((argc < 3) || !origin || !GetServerH(origin)) return 0; if (!(user = GetUserN(argv[1]))) return 1; - assign_fakehost(user, argv[2], 0); + + if (argc > 3) { + ident = argv[2]; + host = argv[3]; + } else { + ident = NULL; + host = argv[2]; + } + + assign_fakehost(user, host, ident, 0, 0); return 1; } @@ -2297,16 +2307,25 @@ void mod_usermode(struct userNode *user, const char *mode_change) { call_account_func(user, tag, ts, id); } break; - case 'f': + case 'h': if (*word) { - char host[MAXLEN]; + char mask[MAXLEN]; + char *host, *ident; unsigned int ii; for (ii=0; (*word != ' ') && (*word != '\0'); ) - host[ii++] = *word++; - host[ii] = 0; + mask[ii++] = *word++; + mask[ii] = 0; while (*word == ' ') word++; - assign_fakehost(user, host, 0); + + if ((host = strrchr(mask, '@'))) { + ident = mask; + *host++ = '\0'; + } else { + ident = NULL; + host = mask; + } + assign_fakehost(user, host, ident, 0, 0); } break; } diff --git a/src/proto.h b/src/proto.h index 8a223bf..3d8e2e7 100644 --- a/src/proto.h +++ b/src/proto.h @@ -146,7 +146,7 @@ void irc_svsnick(struct userNode *from, struct userNode *target, const char *new /* account maintenance */ void irc_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial); void irc_regnick(struct userNode *user); -void irc_fakehost(struct userNode *user, const char *host); +void irc_fakehost(struct userNode *user, const char *host, const char *ident, int force); /* numeric messages */ void irc_numeric(struct userNode *user, unsigned int num, const char *format, ...); -- 2.20.1