From 4440a86cfa359b8e40a484a2cd46d33db5455d8a Mon Sep 17 00:00:00 2001 From: Jonas Gunz Date: Mon, 25 May 2020 20:09:04 +0200 Subject: Initial --- contrib/tkserv/CHANGES | 37 ++ contrib/tkserv/CREDITS | 60 ++ contrib/tkserv/INSTALL | 20 + contrib/tkserv/README | 290 ++++++++++ contrib/tkserv/proto.h | 24 + contrib/tkserv/tkserv.access.example | 10 + contrib/tkserv/tkserv.c | 1022 ++++++++++++++++++++++++++++++++++ 7 files changed, 1463 insertions(+) create mode 100644 contrib/tkserv/CHANGES create mode 100644 contrib/tkserv/CREDITS create mode 100644 contrib/tkserv/INSTALL create mode 100644 contrib/tkserv/README create mode 100644 contrib/tkserv/proto.h create mode 100644 contrib/tkserv/tkserv.access.example create mode 100644 contrib/tkserv/tkserv.c (limited to 'contrib/tkserv') diff --git a/contrib/tkserv/CHANGES b/contrib/tkserv/CHANGES new file mode 100644 index 0000000..e983ded --- /dev/null +++ b/contrib/tkserv/CHANGES @@ -0,0 +1,37 @@ +1.0.0b8 : Ported TkServ to C. +1.0.0b8 : It is now possible to QUIT the service via SQUERY. +1.0.0b9 : Added default expiration if no time value was specified (koopal) +1.0.0b9 : It is now possible to remove existing tklines "manually" (koopal) +1.0.0b10: the pwd of the service went to the cmd line (koopal) +1.0.0 : Official release of TkServ +1.0.1 : Applied some fixes to make TkServ work under BSDI (primetime) +1.0.1 : no address of a local var is returned anymore :) (primetime) +1.0.2 : Added forgotten tkserv.access.example to the package (primetime) +1.0.2 : IP masks (x.x.x.*) are now also be recognized as IPs (primetime) +1.0.2 : hostnames are now also possible in the restriction field (primetime) +1.0.3 : DEBUGGING is now functional and displays all service<->server + traffic to the standard output +1.0.3 : TkServ should now also compile under Irix (Szern) +1.0.3 : IP restrictions are now also possible in the access file + - wildcards are allowed (Szern) +1.0.3 : Wildcards do now also work in the oper u@h field of the access + file (Szern) +1.0.3 : Replaced FNM_CASEFOLD by FNM_NOESCAPE +1.0.4 : ':' is not allowed anymore in the TKLINE cmd line (Eumel) +1.0.4 : Fixed lame bug which kept tklines active altho already expired (Eumel) +1.0.5 : TkServ should now also compile under Redhat Linux. (Tero) +1.0.5 : a few changes for ircd/contrib compliancy +1.0.5 : the semantics for the access file have changed (check out README) +1.0.5 : it is now possible to disallow tklining of specific hosts (tumble) +1.0.6 : Added leading colons for the /squery commands (tumble) +1.0.6 : Added a hint concerning the distribution to the README +1.0.6 : it's now possible to decide whether a user needs to be opered or + not when accessing TkServ +1.0.7 : TkServ won't crash anymore if a bad lifetime is given (Tero) +1.0.8 : A few changes to make TkServ fit better into ircd/contrib. +1.0.8 : TkServ now sends out a hint when it receives an unknown cmd + (Fusion & viha) +1.0.9 : renamed the function logf due to a conflict in *BSD? (Virginie) +1.0.9 : General rename of some tkserv functions +1.2 : Password migration from cmd line to conf file (Earthpig) +1.2 : TkServ now daemonizes itself. (Earthpig) diff --git a/contrib/tkserv/CREDITS b/contrib/tkserv/CREDITS new file mode 100644 index 0000000..617fb16 --- /dev/null +++ b/contrib/tkserv/CREDITS @@ -0,0 +1,60 @@ +Thanks to the following people, who have contributed in one way or another +to the development of TkServ: + +- Andre 'koopal' Koopal + + beta testing + + default expiration + + removal of tklines before expiration (manual removal) + + password migration from config.h to cmd line + +- Asu 'primetime' Pala + + fix for BSDI + + IP recognition bug + + forgotten tkserv.access.example file + + hostnames in the restriction field of the access file + +- 'Szern' + + fix for Irix + + IPs in the restriction field + + wildcards in the u@h oper field of the access file + +- Michael 'Eumel' Neumayer + + colons are not allowed anymore outside the password parameter + + expired tklines do now get removed really automatically + +- Tero 'Tero' Saari + + RedHat Linux cludge + + no crashes anymore when a bad lifetime is given + +- Andrew ´Earthpig´ Snare + + password back to conf file (more secure) + + automatic daemonization + +- 'tumble' + + possibility to explicitly disallow specific hosts from getting tklined + + leading colons for the /squery commands in the README + +- Kristian 'Fusion' Bergmann & 'viha' + + Sending out of a hint when an unknown command is received + +- 'Virginie' + + rename of the logf function because of conflict under *BSD? + +- 'jbn' + + expiration of tklines / default expiration + +- The EPIC Software Labs + + for their nice ircII-EPIC client from which i took some networking + stuff, see http://www.snafu.de/~kl/epic/index.html for more about + epic + +- Kai 'Oswald' Seidler + + for his NoteServ, on which TkServ is based and which has helped me a + lot in making TkServ, see http://oswald.pages.de for more + +- Christophe 'syrk' Kalt and Vesa 'Vesa' Ruokonen + + for giving me the impulse to this kind of service :) + + +And finally thanks to all the Linux people out there for contributing +to the world's best OS! :-) diff --git a/contrib/tkserv/INSTALL b/contrib/tkserv/INSTALL new file mode 100644 index 0000000..9bc69bd --- /dev/null +++ b/contrib/tkserv/INSTALL @@ -0,0 +1,20 @@ +How to install TkServ +--------------------- + +Yes, there isn't any configure yet. That's why the compilation on some +machines could be difficult because you may have to include one or two +libraries by hand. I'll try to create a configure file in the future. + +Up to now, i've only compiled TkServ on Linux, Solaris and Irix. I hope you +won't get into trouble when trying to compile on another OS. But everyone +should be running Linux anyway. :-) + +So here we go: + +1. Unpack and untar the archive. (which you might already have done :) +2. RTFM. :-> +3. run "./configure" (if you haven't already done so) +3. Edit the tkconf.h file. +4. "make tkserv" + +Good luck, Kasi diff --git a/contrib/tkserv/README b/contrib/tkserv/README new file mode 100644 index 0000000..a7b7c47 --- /dev/null +++ b/contrib/tkserv/README @@ -0,0 +1,290 @@ +Welcome to TkServ ! +=+=+=+=+=+=+=+=+=+= + +This program is released under the GNU General Public License. +A copy of this license is included in the distribution. + +I) Introduction +--------------- + +TkServ is a so-called temporary k-line service. + +[If you don't know what a service is, consult the documentation which comes +along with the irc2.10.x package (if you don't know services, you shouldn't +be reading this anyway ;).] + +This is what TkServ does - roughly: On request, it adds a given k-line +pattern to the server's k-line list and (sooner or later, see below) then +removes it. The adding/removing is done via the ircd.conf file. + +The purpose/advantages of a temporary k-line service: + + - it facilitates the process of k-lining users + - added k-lines disappear automatically (see below) + - it allows _also_ remote users (only if they are listed in the access + file of the service - see below) to temp k-line users on the respective + server + - it allows people to specify a duration for each tkline + - it is more effective than /kill but practically as easy to use + - it could therefore act as a replacement for the /kill command - which in + fact is more or less its longtime goal... + + +II) Security concerns +--------------------- + +Of course, when allowing remote "access" to the ircd.conf file, the main +concern of most admins is security. Therefore, here's a list of the +procedures used by TkServ to ensure that only authorized users may add +temporary k-lines to the server's conf file [origin == the person who is +sending a request]: + +- the origin's user@host has to match one of the u@h's in the tkserv.access + file (case sensitive!) +- the origin has always to specify a password which has to match the + password that belongs to the corresponding user@host in the access file + (case sensitive!) +- the origin has to be opered, if you want to +- it is possible to allow an authorized user to only tkline given hostnames + or TLDs + +Especially the third point equals to strong security, because: Could you +imagine any cracker who has gained O: on some server, wasting his time on +trying to get access to a remote server's temp k-line list instead of +playing around with his O: ? See, neither could i. ;-) + + +III) Installation +----------------- + +A) Precondition + + The only thing you need in order to run TkServ is an "ircd.conf" file, + an "ircd.pid" file and an IRC server which has been compiled with + USE_SERVICES #define'd in the config.h file (!). + +B) Editing the configuration file (config.h) + + You have to change the following entries in the config file: + + TKSERV_ADMIN_NAME (your real name) + TKSERV_ADMIN_CONTACT (mail address) + TKSERV_ADMIN_OTHER (your nick, for example) + + TKSERV_NAME (the name of the service appearing on /SERVLIST) + TKSERV_DESC (a neato description :) + TKSERV_DIST (the distribution of the service) + +| A few words to the distribution of the service. Here are the pros & +| contras of a global TkServ from my point of view: +| +| [contra] It makes the /servlist become unnecessarily big and less handy +| [contra] You can detect also local services by /trace'ing a server +| [contra] You can access a local TkServ by setting up a special client +| on the concerned server (maybe with a special I: line) +| +| [pro] A global TkServ is more comfortable to use for remote users. +| [pro] If you want to see which server is running one (for e.g. to +| request access to it), you do only have to do a /servlist. + + TKSERV_PATH (the abs. path to your ircd.conf, no trailing slash please!) + TKSERV_LOGFILE (the abs. path to your tkserv logfile, no trailing slash) + TKSERV_ACCESSFILE (the abs. path to your tkserv access file, ditto) + + TKSERV_DEBUG (for debugging only, displayes traffic to standard output) + +C) Compiling the source + +TkServ will be compiled along with the irc server. + +D) Setting up the access file: tkserv.access + + If anyone should be authorized to use TkServ on your server, he/she has + to figure in the access file. The format of it is: + +| [!][] +| +| Which means: +| [!] [] +| +| A '!' before the u@h means that the specified user doesn't need to be +| opered when accessing TkServ. Before the FQDN/IP, it means that the user +| is not allowed to tkline that given FQDN/IP. See below. +| +| Examples: +| (1) foo@bar.com foo-pass +| (2) some@user.on.the.net some.passw1 *.net,207.128.* +| another@user.foo.com Akfdsv.df *netcom.com,*.ca +| you@get.the.picture.org ditto. *.org,dialup*.lamer.net,194.44.44.* +| oper@dialup*.dialup.net pwd1.2 *dialup.net,145.44.*,*.fr +| (3) !luser@doesnt.need.to.be.opered.org bleh !elite.org,*.org +| (4) !~luser@no.ident.no.oper.com yo *.com +| +| (1) Any oper who is running identd and whose userhost equals to +| foo@bar.com can tkline everyone if supplied password equals +| foo-pass. +| (2) Any oper with identd whose u@h equals to some@user.on.the.net +| can tkline everyone whose TLD is ".net" or who is IRCing within +| the IP subnet 207.128.*, if correct password is given. +| (3) Any luser (no need to be opered) whose u@h/passwd equals can tkline +| everyone whose TLD is ".org" except the host "elite.org" +| ("*@elite.org"). +| +| Generally, you should be aware of the fact that if you put something +| in the FQDN/IP field, then you automatically restrict the access. +| Therefore, you must then also indicate all the allowed FQDNs/IPs. +| +| The access routines are pretty flexible. So pay attention in what order +| you allow/disallow what. :) +| +| Other examples: +| !foo@bar.com foo-pass !*.net,*.com,!*.com +| some@user.on.the.net some.passw1 *.net,207.128.*,!127.0.0.* +| another@user.foo.com Akfdsv.df *netcom.com,!*trends.ca,*.ca +| +| Notice that if you allow "*.de" and after it you forbid "blah.de", +| this won't work. The oper will still be able to tkline blah.de since +| "*.de" appears before "!blah.de" in the access field. +| +| Are we confused, yet? ;-) + + For some other examples, refer to the tkserv.access.example file, which + is included in the package. + + The u@h field for the user can also contain wildcards, as you can see. + +| If you specify an FQDN (or several of them in a comma-separated +| list), the concerned user will only be allowed to tkline users from +| that FQDN(s). Everything is done via 'wildcard-matching' (if any). +| I.e. that "home.blah.de" matches only "*@home.blah.de". If you want +| to allow a whole TLD, you have to do this by putting "*.tld" in the +| access field. + +! An empty FQDN/IP field means that the concerned user can tkline everyone. ! + [Except *@* that is.] + +E) Setting up the S: line in your ircd.conf file + + If you're not yet familiar with S: lines, consult the documentation of + the ircd package. + + S::::33554432: + + is the FQDN from which the service will connect to the server. + + The service type 33554432 is mandatory! Currently, TkServ will refuse + any other service type. [Actually, it won't, but it's good to think that + it will... ;-] + + The service class should refer to an existing class (according to the + documentation :). + +F) Starting TkServ + + The command line syntax of TkServ looks like this: + + tkserv + or + tkserv + + Example: + + tkserv localhost 6667 my-serv.pass + + Where is the address of your IRC server, the port to + which TkServ will connect and the password for the service. + +G) Adding temp k-klines (TKLINE) + + Provided that TkServ recognizes you, you can add temporary k-lines via + the TKLINE command, which has three different variants: + + (1) :TKLINE + (2) :TKLINE + (3) :TKLINE -1 + + (1) adds a tkline for with an expire time of hours and + with the reason . + (2) adds a tkline for with the default expire time (2 hours) and + with the reason . + (3) removes any existing tklines found for . + + Examples: + + (1) :TKLINE my.pass 5 lamer@lamers.org dont flood + (2) :TKLINE blah. *@foo.com stop spamming + (3) :TKLINE my.pass -1 lamer@lamers.org + + must be > 0 and < 168. + +| [If your client doesn't support SQUERY, the entire cmd line has to be: +| "/quote squery :tkline ...". If it does support it, +| then "/squery tkline ..." should do it.] +| +| [And yes, ircII-EPIC4 supports SQUERY and SERVLIST! ;-)] +| +| If a non-opered user tries to use TKLINE without having a matching entry +| in the access file, he gets "Only IRC-Operators may use this command" as +| an error message. This is not correct anymore, but i didn't bother to +| change it (since it may prevent kids from playing around with TkServ :). + +H) Online help/info + +TkServ does also have a little online help which is accessible via +"/squery help". + +I) Quitting the service + +To make TkServ quit IRC you have to send him the following SQUERY: + +QUIT + +Where corresponds to password that you indicated at startup. +Be aware that anyone who knows that password can make your TkServ quit. + +J) Debugging + +If you #define TKSERV_DEBUG in the config.h file, everything which is sent +to the server from the service and from the server to the service will be +displayed to the standard output. + + +IV) Misc, or what goes where + +TkServ will create the following permanent files in your ircd directory: + +ircd.conf.tkserv (backup of the ircd.conf file) +tkserv.log (TkServ's log file) + +The latter will contain most of the error messages (in case something goes +wrong - what we all don't hope ;) as well as logs of successful TKLINE +requests. + +The tkserv.access file has to be in the directory specified in the config.h +file. If no tkserv.access file is found, no one will be able to add temp +k-lines. + +Now and then you should zero your TkServ logfile because this won't happen +by itself. =) + + +V) Credits + +See the CREDITS file. + + +VI) Bugs, comments, suggestions... + +Send everything to kl@snafu.de or to Kasi@IRC. + + +VII) The TkServ-mailinglist + +There's now a mailinglist for TkServ out there. It is used for general +announcements (bugs, new releases, etc.) concerning TkServ. Since it's a +read-only mailinglist, it doesn't have much traffic. You can subscribe to +it by sending a mail to tkserv@kl.Snafu.DE with "subscribe" in the subject +or in the body of the mail. + + +Enjoy, -Kasi diff --git a/contrib/tkserv/proto.h b/contrib/tkserv/proto.h new file mode 100644 index 0000000..edf23b7 --- /dev/null +++ b/contrib/tkserv/proto.h @@ -0,0 +1,24 @@ +void sendto_server(char *buf, ...); +void sendto_user(char *text, ...); +void process_server_output(char *line); +void parse_server_output(char *buffer); +int server_output(int fd, char *buffer); + +void service_pong(void); +void service_notice(char **args); +void service_squery(char **args); +int service_userhost(char *args); +void squery_help(char **args); +void squery_tkline(char **args); +void squery_quit(char **args); + +void sendlog(char *text, ...); +char *ts(void); + +int is_opered(void); +int is_authorized(char *pwd, char *host); + +void exec_cmd(char *cmd, ...); +int add_tkline(char *host, char *user, char *reason, int lifetime); +int check_tklines(char *host, char *user, int lifetime); +void rehash(int what); diff --git a/contrib/tkserv/tkserv.access.example b/contrib/tkserv/tkserv.access.example new file mode 100644 index 0000000..4269c93 --- /dev/null +++ b/contrib/tkserv/tkserv.access.example @@ -0,0 +1,10 @@ +foo@bar.com foo.pass *netcom.com,*.net +someone@SomeOrg.org aldkf.23 eleet.org,165.55.45.* +~blah@ppp*.blah.de igh.pass +user@users.Online.net ukuk.ua1 +some@ip*.user.com blah 204.85.*,194.55.43.* +you.get@the.picture.com pass ppp*.lame.com,*.org,dialin*.whitehouse.gov,*.net,*.uk +disallow@something.com bleh !*.de,!*.fr,*.net,*.org,*.com,!195.144.45.* +disallow@another.host.fr yo !hugo.fr,!admin.irc.fr,*.fr,*.ca,195.55.66.* +!luser@who.doesnt.need.to.be.opered.de yeh *.de + diff --git a/contrib/tkserv/tkserv.c b/contrib/tkserv/tkserv.c new file mode 100644 index 0000000..92f281d --- /dev/null +++ b/contrib/tkserv/tkserv.c @@ -0,0 +1,1022 @@ +/* +** Powered by Linux. :-) +** +** Copyright (c) 1998 Kaspar 'Kasi' Landsberg, +** +** File : tkserv.c v1.2 +** Author : Kaspar 'Kasi' Landsberg, +** Desc. : Temporary K-line Service. +** For further info see the README file. +** Location : http://www.snafu.de/~kl/tkserv +** Usage : tkserv +** E.g. : tkserv localhost 6667 +** +** This program is distributed under the GNU General Public License 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 details. +** +** Note: The C version of this service is based on parts of the +** ircII-EPIC IRC client by the EPIC Software Labs and on +** the NoteServ service by Kai 'Oswald' Seidler - see +** http://oswald.pages.de for more about NoteServ. +** Thanks to both. =) +** +** PS: Casting rules the world! (doh) +*/ + +#include "os.h" +#undef strcasecmp +#include "config.h" +#include "tkconf.h" +#include "proto.h" + +/* Max. kill reason length */ +#define TKS_MAXKILLREASON 128 + +/* Used max. length for an absolute Unix path */ +#define TKS_MAXPATH 128 + +/* Max. buffer length (don't change this?) */ +#define TKS_MAXBUFFER 8192 + +/* don't change this either(?) */ +#define TKS_MAXARGS 250 + +/* The version information */ +#define TKS_VERSION "Hello, i'm TkServ v1.2." + +static char *nuh; +FILE *tks_logf; +int fd = -1, tklined = 0; + +/* +** Returns the current time in a formated way. +** Yes, "ts" stands for "time stamp". I know, +** this hurts, but i couldn't find any better +** description. ;-> +*/ +char *tks_ts(void) +{ + static char tempus[256]; + time_t now; + struct tm *loctime; + + /* Get the current time */ + now = time(NULL); + + /* Convert it to local time representation */ + loctime = localtime(&now); + + strftime(tempus, 256, "@%H:%M %m/%d", loctime); + + return(tempus); +} + +/* logging routine, with timestamps */ +void tks_log(char *text, ...) +{ + char txt[TKS_MAXBUFFER]; + va_list va; + + tks_logf = fopen(TKSERV_LOGFILE, "a"); + va_start(va, text); + vsprintf(txt, text, va); + + if (tks_logf != NULL) + fprintf(tks_logf, "%s %s\n", txt, tks_ts()); + else + { + perror(TKSERV_LOGFILE); + va_end(va); + return; + } + + va_end(va); + fclose(tks_logf); +} + +/* an optimized system() function */ +void exec_cmd(char *cmd, ...) +{ + char command[TKS_MAXBUFFER]; + va_list va; + + va_start(va, cmd); + vsprintf(command, cmd, va); + system(command); + va_end(va); +} + +/* sends a string (<= TKS_MAXBUFFER) to the server */ +void sendto_server(char *buf, ...) +{ + char buffer[TKS_MAXBUFFER]; + va_list va; + + va_start(va, buf); + vsprintf(buffer, buf, va); + write(fd, buffer, strlen(buffer)); + va_end(va); + +#ifdef TKSERV_DEBUG + printf("%s", buffer); +#endif +} + +/* sends a NOTICE to the SQUERY origin */ +void sendto_user(char *text, ...) +{ + char *nick, *ch; + char txt[TKS_MAXBUFFER]; + va_list va; + + nick = (char *) strdup(nuh); + ch = (char *) strchr(nick, '!'); + *ch = '\0'; + + va_start(va, text); + vsprintf(txt, text, va); + sendto_server("NOTICE %s :%s\n", nick, txt); + va_end(va); +} + +void process_server_output(char *line) +{ + char *ptr; + static char *args[TKS_MAXARGS]; + int i, argc = 0; + + while ((ptr = (char *) strchr(line, ' ')) && argc < TKS_MAXARGS) + { + args[argc] = line; + argc++; + *ptr = '\0'; + line = ptr + 1; + } + + args[argc] = line; + + for (i = argc + 1; i < TKS_MAXARGS; i++) + args[i] = ""; + + /* + ** After successfull registering, backup the ircd.conf file + ** and set the perms of the log file -- the easy way :) + */ + if ((*args[0] == ':') && (!strcmp(args[1], "SERVSET"))) + { + chmod(TKSERV_ACCESSFILE, S_IRUSR | S_IWRITE); + chmod(TKSERV_LOGFILE, S_IRUSR | S_IWRITE); + exec_cmd("cp "CPATH" "TKSERV_IRCD_CONFIG_BAK); + tks_log("Registration successful."); + } + + /* We do only react upon PINGs, SQUERYs and &NOTICES */ + if (!strcmp(args[0], "PING")) + service_pong(); + + if ((*args[0] == ':') && (!strcmp(args[1], "SQUERY"))) + service_squery(args); + + if (!strcmp(args[0], "&NOTICES")) + service_notice(args); +} + +/* reformats the server output */ +void parse_server_output(char *buffer) +{ + char *ch, buf[TKS_MAXBUFFER]; + static char tmp[TKS_MAXBUFFER]; + + /* server sent an empty line, so just return */ + if (!buffer && !*buffer) + return; + + while ((ch = (char *) strchr(buffer, '\n'))) + { + *ch = '\0'; + + if (*(ch - 1) == '\r') + *(ch - 1) == '\0'; + + sprintf(buf, "%s%s", tmp, buffer); + *tmp = '\0'; + process_server_output(buf); + buffer = ch + 1; + } + + if (*buffer) + strcpy(tmp, buffer); +} + +/* reads and returns output from the server */ +int server_output(int fd, char *buffer) +{ + int n = read(fd, buffer, TKS_MAXBUFFER); + buffer[n] = '\0'; + +#ifdef TKSERV_DEBUG + printf("%s", buffer); +#endif + + return(n); +} + +/* is the origin of the /squery opered? */ +int is_opered(void) +{ + char *nick, *ch, *token, *u_num, *userh; + char buffer[TKS_MAXBUFFER]; + + nick = (char *) strdup(nuh); + ch = (char *) strchr(nick, '!'); + *ch = '\0'; + sendto_server("USERHOST %s\n", nick); + + /* get the USERHOST reply (hopefully) */ + server_output(fd, buffer); + + token = (char *) strtok(buffer, " "); + token = (char *) strtok(NULL, " "); + u_num = (char *) strdup(token); + token = (char *) strtok(NULL, " "); + token = (char *) strtok(NULL, " "); + userh = (char *) strdup(token); + + /* if we got the USERHOST reply, perform the check */ + if (!strcmp(u_num, "302")) + { + char *ch; + ch = (char *) strchr(userh, '=') - 1; + + /* is the origin opered? */ + if (*ch == '*') + { + char *old_uh, *new_uh, *ch; + + old_uh = (char *) (strchr(nuh, '!') + 1); + new_uh = (char *) (strchr(userh, '=') + 2); + + if (ch = (char *) strchr(new_uh, '\r')) + *ch = '\0'; + + /* Does the u@h of the USERHOST reply correspond to the u@h of our origin? */ + if (!strcmp(old_uh, new_uh)) + return(1); + else + /* + ** race condition == we sent a USERHOST request and got the USERHHOST reply, + ** but this reply doesn't correspond to our origin of the SQUERY -- + ** this should never happen (but never say never ;) + */ + sendto_user("A race condition has occured -- please try again."); + } + } + else + /* + ** race condition == we sent a USERHOST request but the next message from + ** the server was not a USERHOST reply (usually due to lag) + */ + sendto_user("A race condition has occured -- please try again (and ignore the following error message)."); + + return(0); +} + +/* +** Look for an entry in the access file and +** see if the origin needs to be opered +*/ +int must_be_opered() +{ + FILE *fp; + + /* if the access file exists, check for auth */ + if ((fp = fopen(TKSERV_ACCESSFILE, "r")) != NULL) + { + char buffer[TKS_MAXBUFFER]; + char *access_uh, *token, *uh; + + while (fgets(buffer, TKS_MAXBUFFER, fp)) + { + uh = (char *) (strchr(nuh, '!') + 1); + token = (char *) strtok(buffer, " "); + + if (token) + access_uh = (char *) strdup(token); + + /* check for access file corruption */ + if (!access_uh) + { + tks_log("Corrupt access file. RTFM. :-)"); + + return(0); + } + + /* do we need an oper? */ + if (*access_uh == '!') + { + if (!fnmatch((char *) (strchr(access_uh, '!') + 1), uh, 0)) + return(0); + } + } + } + else + tks_log("%s not found.", TKSERV_ACCESSFILE); + + return(1); +} + +/* check whether origin is authorized to use the service */ +int is_authorized(char *pwd, char *host) +{ + FILE *fp; + + /* if the access file exists, check for authorization */ + if ((fp = fopen(TKSERV_ACCESSFILE, "r")) != NULL) + { + char buffer[TKS_MAXBUFFER]; + char *access_uh, *access_pwd; + char *token, *uh, *ch, *tlds = NULL; + + while (fgets(buffer, TKS_MAXBUFFER, fp)) + { + uh = (char *) (strchr(nuh, '!') + 1); + token = (char *) strtok(buffer, " "); + + if (token) + access_uh = (char *) strdup(token); + + if (*access_uh == '!') + access_uh = (char *) (strchr(access_uh, '!') + 1); + + token = (char *) strtok(NULL, " "); + + if (token) + access_pwd = (char *) strdup(token); + + token = (char *) strtok(NULL, " "); + + if (token) + tlds = (char *) strdup(token); + else + if (ch = (char *) strchr(access_pwd, '\n')) + *ch = '\0'; + + /* check for access file corruption */ + if (!access_uh || !access_pwd) + { + tks_log("Corrupt access file. RTFM. :-)"); + + return(0); + } + + /* check uh, pass and TLD */ + if (!fnmatch(access_uh, uh, 0)) + if (!strcmp(pwd, access_pwd)) + if (!tlds) + return(1); + else + { + char *token, *ch; + + /* blah */ + if (ch = (char *) strchr(tlds, '\n')) + *ch = '\0'; + + token = (char *) strtok(tlds, ","); + + /* '!' negates the given host/domain -> not allowed to tkline */ + if (*token == '!') + { + if (!fnmatch(((char *) strchr(token, '!') + 1), host, 0)) + { + sendto_user("You are not allowed to tkline \"%s\",", host); + return(0); + } + } + else if (!fnmatch(token, host, 0)) + return(1); + + /* walk thru the list */ + while (token = (char *) strtok(NULL, ",")) + { + if (*token == '!') + { + if (!fnmatch((char *) (strchr(token, '!') + 1), host, 0)) + { + sendto_user("You are not allowed to tkline \"%s\",", host); + return(0); + } + } + else if (!fnmatch(token, host, 0)) + return(1); + } + + sendto_user("You are not allowed to tkline \"%s\".", host); + } + } + + } + else + tks_log("%s not found.", TKSERV_ACCESSFILE); + + return(0); +} + +/*************** ircd.conf section ****************/ + +/* Appends new tklines to the ircd.conf file */ +int add_tkline(char *host, char *user, char *reason, int lifetime) +{ + FILE *iconf; + + if (iconf = fopen(CPATH, "a")) + { + time_t now; + + now = time(NULL); + fprintf(iconf, "K:%s:%s:%s:0 # %d %u tkserv\n", + host, reason, user, lifetime, now); + fclose(iconf); + rehash(1); + tks_log("K:%s:%s:%s:0 added for %d hour(s) by %s.", + host, reason, user, lifetime, nuh); + + return(1); + } + else + tks_log("Couldn't write to "CPATH); + + return(0); +} + +/* Check for expired tklines in the ircd.conf file */ +int check_tklines(char *host, char *user, int lifetime) +{ + FILE *iconf, *iconf_tmp; + + if ((iconf = fopen(CPATH, "r")) && (iconf_tmp = fopen(TKSERV_IRCD_CONFIG_TMP, "w"))) + { + int count = 0, found = 0; + time_t now; + char buffer[TKS_MAXBUFFER]; + char buf_tmp[TKS_MAXBUFFER]; + + /* just in case... */ + chmod(TKSERV_IRCD_CONFIG_TMP, S_IRUSR | S_IWRITE); + + now = time(NULL); + + while (fgets(buffer, TKS_MAXBUFFER, iconf)) + { + if ((*buffer != 'K') || (!strstr(buffer, "tkserv"))) + fprintf(iconf_tmp, buffer); + else + { + /* + ** If lifetime has a value of -1, look for matching + ** tklines and remove them. Otherwise, perform + ** the expiration check. + */ + if (lifetime == -1) + { + char *token; + char buf[TKS_MAXBUFFER]; + + strcpy(buf, buffer); + token = (char *) strtok(buf, ":"); + token = (char *) strtok(NULL, ":"); + + if (!strcasecmp(token, host)) + { + token = (char *) strtok(NULL, ":"); + token = (char *) strtok(NULL, ":"); + + if (!strcasecmp(token, user)) + { + count++; + found = 1; + } + else + fprintf(iconf_tmp, buffer); + } + else + fprintf(iconf_tmp, buffer); + } + else + { + char *ch, *token; + char buf[TKS_MAXBUFFER]; + unsigned long int lifetime, then; + + strcpy(buf, buffer); + ch = (char *) strrchr(buf, '#'); + token = (char *) strtok(ch, " "); + token = (char *) strtok(NULL, " "); + lifetime = strtoul(token, NULL, 0); + token = (char *) strtok(NULL, " "); + then = strtoul(token, NULL, 0); + + if (!(((now - then) / (60 * 60)) >= lifetime)) + fprintf(iconf_tmp, buffer); + else + found = 1; + } + } + } + + fclose(iconf); + fclose(iconf_tmp); + exec_cmd("cp %s %s", TKSERV_IRCD_CONFIG_TMP,CPATH); + unlink(TKSERV_IRCD_CONFIG_TMP); + + if (found) + rehash(-1); + + return(count); + } + else + tks_log("Error while checking for expired tklines..."); +} + +/* reloads the ircd.conf file -- the easy way */ +void rehash(int what) +{ + exec_cmd("kill -HUP `cat "PPATH"`"); + + if (what != -1) + tklined = what; +} + +/*************** end of ircd.conf section **************/ + +/*************** The service command section *************/ + +/* On PING, send PONG and check for expired tklines */ +void service_pong(void) +{ + sendto_server("PONG %s\n", TKSERV_NAME); + check_tklines(NULL, NULL, 0); +} + +/* +** If we get a rehash, tell the origin that the tklines are active/removed +** and check for expired tklines... +*/ +void service_notice(char **args) +{ + if ((!strcmp(args[4], "reloading") && (!strcmp(args[5], TKSERV_IRCD_CONF))) || + (!strcmp(args[3], "rehashing") && (!strcmp(args[4], "Server")))) + { + if (tklined) + { + sendto_user("TK-line%s.", (tklined > 1) ? "(s) removed" : " active"); + tklined = 0; + } + } +} + +/* parse the received SQUERY */ +void service_squery(char **args) +{ + char *cmd, *ch; + + nuh = (char *) strdup(args[0] + 1); + cmd = (char *) strdup(args[3] + 1); + + if (ch = (char *) strchr(cmd, '\r')) + *ch = '\0'; + + if (!strcasecmp(cmd, "admin")) + { + sendto_user(TKSERV_ADMIN_NAME); + sendto_user(TKSERV_ADMIN_CONTACT); + sendto_user(TKSERV_ADMIN_OTHER); + } + + else if (!strcasecmp(cmd, "help")) + squery_help(args); + + else if (!strcasecmp(cmd, "info")) + { + sendto_user("This service is featuring temporary k-lines."); + sendto_user("It's available at http://www.snafu.de/~kl/tkserv."); + } + + else if (!strcasecmp(cmd, "quit")) + squery_quit(args); + + else if (!strcasecmp(cmd, "tkline")) + squery_tkline(args); + + else if (!strcasecmp(cmd, "version")) + sendto_user(TKS_VERSION); + + else + sendto_user("Unknown command. Try HELP."); +} + +/* SQUERY HELP */ +void squery_help(char **args) +{ + char *ch, *help_about; + + help_about = args[4]; + + if (help_about && *help_about) + { + if (ch = (char *) strchr(help_about, '\r')) + *ch = '\0'; + + if (!strcasecmp(help_about, "admin")) + sendto_user("ADMIN shows you the administrative info for this service."); + + if (!strcasecmp(help_about, "help")) + sendto_user("HELP shows you the help text for ."); + + if (!strcasecmp(help_about, "info")) + sendto_user("INFO shows you a short description about this service."); + + if (!strcasecmp(help_about, "tkline")) + { + sendto_user("TKLINE adds a temporary entry to the server's k-line list."); + sendto_user("TKLINE is a privileged command."); + } + + if (!strcasecmp(help_about, "version")) + sendto_user("VERSION shows you the version information of this service."); + } + else + { + sendto_user("Available commands:"); + sendto_user("HELP, INFO, VERSION, ADMIN, TKLINE."); + sendto_user("Send HELP for further help."); + } +} + +/* SQUERY TKLINE */ +void squery_tkline(char **args) +{ + int lifetime, i; + char *passwd, *pattern, *host, *ch, *user = "*"; + char reason[TKS_MAXKILLREASON]; + + /* Before we go thru all this, make sure we don't waste our time... */ + if (must_be_opered()) + { + if (!is_opered()) + { + sendto_user("Only IRC-Operators may use this command."); + return; + } + } + + i = 5; + + while (args[i] && *args[i]) + { + if (strchr(args[i], ':')) + { + sendto_user("Colons are only allowed in the password."); + return; + } + + i++; + } + + if (args[5] && *args[5]) + { + if (isdigit(*args[5]) || (*args[5] == '-')) + lifetime = atoi(args[5]); + else + { + sendto_user("The lifetime may only contain digits."); + return; + } + } + else + { + sendto_user("Usage: TKLINE [] "); + return; + } + + /* TKLINE */ + if ((lifetime > 0) && !(args[7] && *args[7])) + { + sendto_user("Usage: TKLINE "); + return; + } + + /* TKLINE (default expiration) */ + if ((lifetime == 0) && !(args[6] && *args[6])) + { + sendto_user("Usage: TKLINE "); + return; + } + + /* TKLINE -1 (removal of tklines) */ + if ((lifetime == -1) && !(args[6] && *args[6])) + { + sendto_user("Usage: TKLINE -1 "); + return; + } + + if ((lifetime >= 768) || (lifetime < -1)) + { + sendto_user(" must be greater than 0 and less than 768."); + return; + } + + /* I don't want to get confused, so all this may be a bit redundant */ + + if (lifetime > 0) + { + passwd = args[4]; + pattern = args[6]; + strcpy(reason, args[7]); + i = 8; + + /* I know... */ + while(args[i] && *args[i]) + { + strncat(reason, " ", TKS_MAXKILLREASON - strlen(reason) - 1); + strncat(reason, args[i], TKS_MAXKILLREASON - strlen(reason) - 1); + i++; + } + + if (ch = (char *) strchr(reason, '\r')) + *ch = '\0'; + } + + if (lifetime == 0) + { + if (!(strchr(args[5], '@') || strchr(args[5], '*') || + strchr(args[5], '.'))) + { + sendto_user(" must be greater than 0."); + return; + } + + passwd = args[4]; + lifetime = 2; /* Default lifetime */ + pattern = args[5]; + strcpy(reason, args[6]); + i = 7; + + while(args[i] && *args[i]) + { + strncat(reason, " ", TKS_MAXKILLREASON - strlen(reason) - 1); + strncat(reason, args[i], TKS_MAXKILLREASON - strlen(reason) - 1); + i++; + } + + if (ch = (char *) strchr(reason, '\r')) + *ch = '\0'; + } + + if (lifetime == -1) + { + passwd = args[4]; + pattern = args[6]; + + if (ch = (char *) strchr(pattern, '\r')) + *ch = '\0'; + } + + + /* Don't allow "*@*" and "*" in the pattern */ + if (!strcmp(pattern, "*@*") || !strcmp(pattern, "*")) + { + sendto_user("The pattern \"%s\" is not allowed.", pattern); + tks_log("%s tried to tkline/untkline \"%s\".", nuh, pattern); + return; + } + + /* Split the pattern up into username and hostname */ + if (ch = (char *) strchr(pattern, '@')) + { + host = (char *) (strchr(pattern, '@') + 1); + user = pattern; + *ch = '\0'; + } + else /* user defaults to "*" */ + host = pattern; + + /* + ** Make sure there's a dot in the hostname. + ** The reason for this being that i "need" a dot + ** later on and that i don't want to perform + ** extra checks whether it's there or not... + ** Call this lazyness, but it also makes the service faster. ;-) + */ + if (!strchr(host, '.')) + { + sendto_user("The hostname must contain at least one dot."); + return; + } + + if (!is_authorized(passwd, host)) + { + sendto_user("Authorization failed."); + return; + } + + if (lifetime == -1) + { + int i; + + i = check_tklines(host, user, lifetime); + sendto_user("%d tkline%sfor \"%s@%s\" found.", i, + (i > 1) ? "s " : " ", user, host); + + if (i > 0) + rehash(2); + } + else + if (!add_tkline(host, user, reason, lifetime)) + sendto_user("Error while trying to edit the "CPATH" file."); +} + +/* SQUERY QUIT +** Each time we receive a QUIT via SQUERY we check whether +** the supplied password matches the one in the conf file or not. +** If not, an error is sent out. If yes, we close the connection to +** the server. +*/ +void squery_quit(char **args) +{ + char *ch; + + if (ch = (char *) strchr(args[4], '\r')) + *ch = '\0'; + + if (!strcmp(args[4], TKSERV_PASSWORD)) + { + tks_log("Got QUIT from %s. Terminating.", nuh); + sendto_server("QUIT :Linux makes the world go round. :)\n"); + } + else + { + sendto_user("I refuse to QUIT. Have a look at the log to see why."); + tks_log("Got QUIT from %s with wrong password. Continuing.", nuh); + } +} + +/**************** End of service command section ***************/ + +int main(int argc, char *argv[]) +{ + + char *host, *port, buffer[TKS_MAXBUFFER], last_buf[TKS_MAXBUFFER]; + char tmp[TKS_MAXPATH]; + + int is_unix = (argv[1] && *argv[1] == '/'); + int sock_type = (is_unix) ? AF_UNIX : AF_INET; + int proto_type = SOCK_STREAM; + int eof = 0; + + struct in_addr LocalHostAddr; + struct sockaddr_in server; + struct sockaddr_in localaddr; + struct hostent *hp; + struct timeval timeout; + + fd_set read_set; + fd_set write_set; + + if ((is_unix) && (argc != 2)) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, " %s \n", argv[0]); + exit(1); + } + else if (argc != 3) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, " %s \n", argv[0]); + exit(1); + } + + if (!strcmp(TKSERV_DIST, "*")) + { + printf("Your service has a global distribution. Please make sure that\n"); + printf("you read the part about the service distribution in the README.\n"); + } + + tks_log("Welcome to TkServ. Lean back and enjoy the show..."); + + if ((fd = socket(sock_type, proto_type, 0)) < 0) + { + perror("socket"); + exit(1); + } + + /* copy the args into something more documentable */ + host = argv[1]; + + if (!is_unix) + port = argv[2]; + + /* Unix domain socket */ + if (is_unix) + { + struct sockaddr_un name; + memset(&name, 0, sizeof(struct sockaddr_un)); + name.sun_family = AF_UNIX; + strcpy(name.sun_path, host); + + if (connect(fd, (struct sockaddr *) &name, strlen(name.sun_path) + 2) == -1) + { + perror("connect"); + close(fd); + exit(1); + } + } + + memset(&localaddr, 0, sizeof(struct sockaddr_in)); + localaddr.sin_family = AF_INET; + localaddr.sin_addr = LocalHostAddr; + localaddr.sin_port = 0; + + if (bind(fd, (struct sockaddr *) &localaddr, sizeof(localaddr))) + { + perror("bind"); + close(fd); + exit(1); + } + + memset(&server, 0, sizeof(struct sockaddr_in)); + memset(&LocalHostAddr, 0, sizeof(LocalHostAddr)); + + if (!(hp = gethostbyname(host))) + { + perror("resolv"); + close(fd); + exit(1); + } + + memmove(&(server.sin_addr), hp->h_addr, hp->h_length); + memmove((void *) &LocalHostAddr, hp->h_addr, sizeof(LocalHostAddr)); + server.sin_family = AF_INET; + server.sin_port = htons(atoi(port)); + + if (connect(fd, (struct sockaddr *) &server, sizeof(server)) == -1) + { + perror("connect"); + exit(1); + } + + /* register the service with SERVICE_WANT_NOTICE */ + sendto_server("PASS %s\n", TKSERV_PASSWORD); + sendto_server("SERVICE %s localhost %s 33554432 0 :%s\n", TKSERV_NAME, TKSERV_DIST, TKSERV_DESC); + sendto_server("SERVSET 33619968\n"); + + timeout.tv_usec = 1000; + timeout.tv_sec = 10; + + /* daemonization... i'm sure it's not complete */ + switch (fork()) + { + case -1: + perror("fork()"); + exit(3); + case 0: + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + if (setsid() == -1) + exit(4); + break; + default: + return 0; + } + + /* listen for server output and parse it */ + while (!eof) + { + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_SET(fd, &read_set); + + if (select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout) == -1) + { + perror("select"); + } + + if (!server_output(fd, buffer)) + { + printf("Connection closed.\n"); + printf("Last server output was: %s\n", last_buf); + eof = 1; + } + + strcpy(last_buf, buffer); + parse_server_output(buffer); + } + + close(fd); + + exit(0); +} +/* eof */ -- cgit v1.2.3