1 /*
2  * ident.c -- part of ident.mod
3  */
4 /*
5  * Copyright (c) 2018 - 2019 Michael Ortmann MIT License
6  * Copyright (C) 2019 - 2021 Eggheads Development Team
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22 
23 #define MODULE_NAME "ident"
24 
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include "src/mod/module.h"
29 #include "server.mod/server.h"
30 
31 #define IDENT_METHOD_OIDENT     0
32 #define IDENT_METHOD_BUILTIN    1
33 #define IDENT_SIZE           1000 /* rfc1413 */
34 
35 static Function *global = NULL, *server_funcs = NULL;
36 
37 static int ident_method = IDENT_METHOD_OIDENT;
38 static int ident_port = 113;
39 
40 static tcl_ints identints[] = {
41   {"ident-method", &ident_method, 0},
42   {"ident-port",   &ident_port,   0},
43   {NULL,           NULL,          0}
44 };
45 
46 static void ident_builtin_off();
47 
48 static cmd_t ident_raw[] = {
49   {"001", "",   (IntFunc) ident_builtin_off, "ident:001"},
50   {NULL,  NULL, NULL,                        NULL}
51 };
52 
ident_activity(int idx,char * buf,int len)53 static void ident_activity(int idx, char *buf, int len)
54 {
55   int s;
56   char buf2[IDENT_SIZE + sizeof " : USERID : UNIX : \r\n" + NICKLEN], *pos;
57   size_t count;
58   ssize_t i;
59 
60   s = answer(dcc[idx].sock, &dcc[idx].sockname, 0, 0);
61   killsock(dcc[idx].sock);
62   dcc[idx].sock = s;
63   if ((i = read(s, buf2, IDENT_SIZE)) < 0) {
64     putlog(LOG_MISC, "*", "Ident error: %s", strerror(errno));
65     return;
66   }
67   buf2[i - 1] = 0;
68   if (!(pos = strpbrk(buf2, "\r\n"))) {
69     putlog(LOG_MISC, "*", "Ident error: could not read request.");
70     return;
71   }
72   snprintf(pos, (sizeof buf2) - (pos - buf2), " : USERID : UNIX : %s\r\n", botname);
73   count = strlen(buf2) + 1;
74   if ((i = write(s, buf2, count)) != count) {
75     if (i < 0)
76       putlog(LOG_MISC, "*", "Ident error: %s", strerror(errno));
77     else
78       putlog(LOG_MISC, "*", "Ident error: Wrote %i bytes instead of %i bytes.", i, count);
79     return;
80   }
81   putlog(LOG_MISC, "*", "Ident: Responded.");
82   ident_builtin_off();
83 }
84 
ident_display(int idx,char * buf)85 static void ident_display(int idx, char *buf)
86 {
87   strcpy(buf, "ident (ready)");
88 }
89 
90 static struct dcc_table DCC_IDENTD = {
91   "IDENTD",
92   DCT_LISTEN,
93   NULL,
94   ident_activity,
95   NULL,
96   NULL,
97   ident_display,
98   NULL,
99   NULL,
100   NULL
101 };
102 
ident_oidentd()103 static void ident_oidentd()
104 {
105   char *home = getenv("HOME");
106   FILE *fd;
107   long filesize;
108   char *data = NULL;
109   char path[121], line[256], buf[256], identstr[256];
110 #ifdef IPV6
111   char s[INET6_ADDRSTRLEN];
112 #else
113   char s[INET_ADDRSTRLEN];
114 #endif
115   int ret, prevtime, servidx, i;
116   socklen_t namelen;
117   struct sockaddr_storage ss;
118 
119   snprintf(identstr, sizeof identstr, "### eggdrop_%s", pid_file);
120 
121   if (!home) {
122     putlog(LOG_MISC, "*",
123            "Ident error: variable HOME is not in the current environment.");
124     return;
125   }
126   if (snprintf(path, sizeof path, "%s/.oidentd.conf", home) >= sizeof path) {
127     putlog(LOG_MISC, "*", "Ident error: path too long.");
128     return;
129   }
130   fd = fopen(path, "r");
131   if (fd != NULL) {
132     /* Calculate file size for buffer */
133     if (fseek(fd, 0, SEEK_END) == 0) {
134       filesize = ftell(fd);
135       if (filesize == -1) {
136         putlog(LOG_MISC, "*", "IDENT: Error reading oident.conf");
137       }
138       data = nmalloc(filesize + 256); /* Room for Eggdrop adds */
139       data[0] = '\0';
140 
141       /* Read the file into buffer */
142       if (fseek(fd, 0, SEEK_SET) != 0) {
143         putlog(LOG_MISC, "*", "IDENT: Error setting oident.conf file pointer");
144       } else {
145         while (fgets(line, 255, fd)) {
146           /* If it is not an Eggdrop entry, don't mess with it */
147           if (!strstr(line, "### eggdrop_")) {
148             strncat(data, line, ((filesize + 256) - strlen(data)));
149           } else {
150             /* If it is Eggdrop but not me, check for expiration and remove */
151             if (!strstr(line, identstr)) {
152               strlcpy(buf, line, sizeof buf);
153               strtok(buf, "!");
154               prevtime = atoi(strtok(NULL, "!"));
155               if ((now - prevtime) > 300) {
156                 putlog(LOG_DEBUG, "*", "IDENT: Removing expired oident.conf "
157                     "entry: \"%s\"", buf);
158               } else {
159                 strncat(data, line, ((filesize + 256) - strlen(data)));
160               }
161             }
162           }
163         }
164       }
165     }
166     fclose(fd);
167   } else {
168     putlog(LOG_MISC, "*", "IDENT: oident.conf missing, or error opening "
169             "for reading");
170   }
171   /* To minimize a known race condition, this code is called now */
172   servidx = -1;
173   for (i = 0; i < dcc_total; i++)
174     if (dcc[i].status & STAT_SERV) {
175       servidx = i;
176       break;
177     }
178   if (servidx < 0 ) {
179     putlog(LOG_MISC, "*", "IDENT: Error could not find server socket");
180     if (data)
181       nfree(data);
182     return;
183   }
184   namelen = sizeof ss;
185   ret = getsockname(dcc[servidx].sock, (struct sockaddr *) &ss, &namelen);
186   if (ret) {
187     putlog(LOG_DEBUG, "*", "IDENT: Error getting socket info for writing");
188   }
189   fd = fopen(path, "w");
190   if (fd != NULL) {
191     if (data) {
192       fprintf(fd, "%s", data);
193     }
194     if (ss.ss_family == AF_INET) {
195       struct sockaddr_in *saddr = (struct sockaddr_in *)&ss;
196       fprintf(fd, "lport %i from %s { reply \"%s\" } "
197                 "### eggdrop_%s !%" PRId64 "\n", ntohs(saddr->sin_port),
198                 inet_ntop(AF_INET, &(saddr->sin_addr), s, INET_ADDRSTRLEN),
199                 botuser, pid_file, (int64_t) now);
200 #ifdef IPV6
201     } else if (ss.ss_family == AF_INET6) {
202       struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&ss;
203       fprintf(fd, "lport %i from %s { reply \"%s\" } "
204                 "### eggdrop_%s !%" PRId64 "\n", ntohs(saddr->sin6_port),
205                 inet_ntop(AF_INET6, &(saddr->sin6_addr), s, INET6_ADDRSTRLEN),
206                 botuser, pid_file, (int64_t) now);
207 #endif
208     } else {
209       putlog(LOG_MISC, "*", "IDENT: Error writing oident.conf line");
210     }
211     fclose(fd);
212   } else {
213     putlog(LOG_MISC, "*", "IDENT: Error opening oident.conf for writing");
214   }
215   if (data) {
216     nfree(data);
217   }
218 }
219 
ident_builtin_on()220 static void ident_builtin_on()
221 {
222   int idx, s;
223 
224   debug0("Ident: Starting ident server.");
225   for (idx = 0; idx < dcc_total; idx++)
226     if (dcc[idx].type == &DCC_IDENTD)
227       return;
228   idx = new_dcc(&DCC_IDENTD, 0);
229   if (idx < 0) {
230     putlog(LOG_MISC, "*", "Ident error: could not get new dcc.");
231     return;
232   }
233   s = open_listen(&ident_port);
234   if (s == -2) {
235     lostdcc(idx);
236     putlog(LOG_MISC, "*", "Ident error: could not bind socket port %i.", ident_port);
237     return;
238   } else if (s == -1) {
239     lostdcc(idx);
240     putlog(LOG_MISC, "*", "Ident error: could not get socket.");
241     return;
242   }
243   dcc[idx].sock = s;
244   dcc[idx].port = ident_port;
245   strcpy(dcc[idx].nick, "(ident)");
246   add_builtins(H_raw, ident_raw);
247 }
248 
ident_builtin_off()249 static void ident_builtin_off()
250 {
251   int idx;
252 
253   for (idx = 0; idx < dcc_total; idx++)
254     if (dcc[idx].type == &DCC_IDENTD) {
255       debug0("Ident: Stopping ident server.");
256       killsock(dcc[idx].sock);
257       lostdcc(idx);
258       break;
259     }
260   rem_builtins(H_raw, ident_raw);
261 }
262 
ident_ident()263 static void ident_ident()
264 {
265   if (ident_method == IDENT_METHOD_OIDENT)
266     ident_oidentd();
267   else if (ident_method == IDENT_METHOD_BUILTIN)
268     ident_builtin_on();
269 }
270 
271 static cmd_t ident_event[] = {
272   {"ident", "",   (IntFunc) ident_ident, "ident:ident"},
273   {NULL,    NULL, NULL,                  NULL}
274 };
275 
ident_close()276 static char *ident_close()
277 {
278   ident_builtin_off();
279   rem_builtins(H_event, ident_event);
280   rem_tcl_ints(identints);
281   module_undepend(MODULE_NAME);
282   return NULL;
283 }
284 
285 EXPORT_SCOPE char *ident_start();
286 
287 static Function ident_table[] = {
288   (Function) ident_start,
289   (Function) ident_close,
290   NULL,
291   NULL,
292 };
293 
ident_start(Function * global_funcs)294 char *ident_start(Function *global_funcs)
295 {
296   global = global_funcs;
297 
298   module_register(MODULE_NAME, ident_table, 0, 9);
299 
300   if (!module_depend(MODULE_NAME, "eggdrop", 109, 0)) {
301     module_undepend(MODULE_NAME);
302     return "This module requires Eggdrop 1.9.0 or later.";
303   }
304   if (!(server_funcs = module_depend(MODULE_NAME, "server", 1, 0))) {
305     module_undepend(MODULE_NAME);
306     return "This module requires server module 1.0 or later.";
307   }
308 
309   add_builtins(H_event, ident_event);
310   add_tcl_ints(identints);
311 
312   return NULL;
313 }
314