1 /*
2 ** NNTP server for readers (NNRP) for InterNetNews.
3 **
4 ** This server doesn't do any real load-limiting, except for what has
5 ** proven empirically necessary (i.e. look at GRPscandir).
6 */
7
8 #include "portable/system.h"
9
10 #include "portable/setproctitle.h"
11 #include "portable/socket.h"
12 #include <netdb.h>
13 #include <signal.h>
14 #if defined(INN_BSDI_HOST)
15 # include <netinet/tcp.h>
16 #endif
17
18 #if HAVE_GETSPNAM
19 # include <shadow.h>
20 #endif
21 #include <sys/wait.h>
22
23 #include "inn/innconf.h"
24 #include "inn/libinn.h"
25 #include "inn/messages.h"
26 #include "inn/network-innbind.h"
27 #include "inn/network.h"
28 #include "inn/newsuser.h"
29 #include "inn/ov.h"
30 #include "inn/overview.h"
31 #include "inn/version.h"
32
33 /* Silent this warning because of the way we deal with EXTERN. */
34 #if defined(__llvm__) || defined(__clang__)
35 # pragma GCC diagnostic ignored "-Wmissing-variable-declarations"
36 #endif
37
38 #define MAINLINE
39 #include "nnrpd.h"
40
41 #include "tls.h"
42
43 #if defined(HAVE_OPENSSL) || defined(HAVE_SASL)
44 bool encryption_layer_on = false;
45 #endif
46
47 static void Usage(void) __attribute__((__noreturn__));
48
49 /*
50 ** If we have getloadavg, include the appropriate header file. Otherwise,
51 ** just assume that we always have a load of 0.
52 */
53 #if HAVE_GETLOADAVG
54 # if HAVE_SYS_LOADAVG_H
55 # include <sys/loadavg.h>
56 # endif
57 #else
58 static int
getloadavg(double loadavg[],int nelem)59 getloadavg(double loadavg[], int nelem)
60 {
61 int i;
62
63 for (i = 0; i < nelem && i < 3; i++)
64 loadavg[i] = 0;
65 return i;
66 }
67 #endif
68
69
70 #define CMDany -1
71
72
73 typedef struct _CMDENT {
74 const char *Name;
75 void (*Function)(int, char **);
76 bool Needauth;
77 int Minac;
78 int Maxac;
79 bool Stripspaces;
80 const char *Help;
81 } CMDENT;
82
83
84 char *ACTIVE = NULL;
85 char *ACTIVETIMES = NULL;
86 char *HISTORY = NULL;
87 char *NEWSGROUPS = NULL;
88 char *NNRPACCESS = NULL;
89
90 static char *LocalLogFileName = NULL;
91 static char *LocalLogDirName;
92
93 static double STATstart;
94 static double STATfinish;
95 static char *PushedBack;
96 static sig_atomic_t ChangeTrace;
97 static bool DaemonMode = false;
98 static bool ForeGroundMode = false;
99 static const char *HostErrorStr;
100 static bool GetHostByAddr = true; /* Formerly DO_NNRP_GETHOSTBYADDR. */
101 const char *NNRPinstance = "";
102
103 /* Default values for the syntaxchecks parameter in inn.conf. */
104 bool laxmid = false;
105
106 /* Other default values. */
107 bool Tracing = false;
108
109 #ifdef DO_PERL
110 bool PerlLoaded = false;
111 #endif
112
113 #ifdef DO_PYTHON
114 bool PY_use_dynamic = false;
115 #endif
116
117 static char CMDfetchhelp[] = "[message-ID|number]";
118
119
120 /*
121 ** { command base name, function to call, need authentication,
122 ** min args, max args, strip spaces, help string }
123 */
124 /* clang-format off */
125 static CMDENT CMDtable[] = {
126 { "ARTICLE", CMDfetch, true, 1, 2, true,
127 CMDfetchhelp },
128 /* Parse AUTHINFO in a special way so as to keep white spaces
129 * in usernames and passwords. */
130 { "AUTHINFO", CMDauthinfo, false, 3, CMDany, false,
131 "USER name|PASS password"
132 #ifdef HAVE_SASL
133 "|SASL mechanism [initial-response]"
134 #endif
135 "|GENERIC program [argument ...]" },
136 { "BODY", CMDfetch, true, 1, 2, true,
137 CMDfetchhelp },
138 { "CAPABILITIES", CMDcapabilities, false, 1, 2, true,
139 "[keyword]" },
140 #if defined(HAVE_ZLIB)
141 { "COMPRESS", CMDcompress, false, 2, 2, true,
142 "DEFLATE" },
143 #endif
144 { "DATE", CMDdate, false, 1, 1, true,
145 NULL },
146 { "GROUP", CMDgroup, true, 2, 2, true,
147 "newsgroup" },
148 { "HDR", CMDpat , true, 2, 3, true,
149 "header [message-ID|range]" },
150 { "HEAD", CMDfetch, true, 1, 2, true,
151 CMDfetchhelp },
152 { "HELP", CMDhelp, false, 1, 1, true,
153 NULL },
154 { "IHAVE", CMDpost, true, 2, 2, true,
155 "message-ID" },
156 { "LAST", CMDnextlast, true, 1, 1, true,
157 NULL },
158 { "LIST", CMDlist, true, 1, 3, true,
159 "[ACTIVE [wildmat]|ACTIVE.TIMES [wildmat]|COUNTS [wildmat]|DISTRIB.PATS"
160 "|DISTRIBUTIONS|HEADERS [MSGID|RANGE]|MODERATORS|MOTD|NEWSGROUPS [wildmat]"
161 "|OVERVIEW.FMT|SUBSCRIPTIONS [wildmat]]" },
162 { "LISTGROUP", CMDgroup, true, 1, 3, true,
163 "[newsgroup [range]]" },
164 { "MODE", CMDmode, false, 2, 2, true,
165 "READER" },
166 { "NEWGROUPS", CMDnewgroups, true, 3, 4, true,
167 "[yy]yymmdd hhmmss [GMT]" },
168 { "NEWNEWS", CMDnewnews, true, 4, 5, true,
169 "wildmat [yy]yymmdd hhmmss [GMT]" },
170 { "NEXT", CMDnextlast, true, 1, 1, true,
171 NULL },
172 { "OVER", CMDover, true, 1, 2, true,
173 "[range]" },
174 { "POST", CMDpost, true, 1, 1, true,
175 NULL },
176 { "QUIT", CMDquit, false, 1, 1, true,
177 NULL },
178 /* SLAVE (which was ill-defined in RFC 977) was removed from the NNTP
179 * protocol in RFC 3977. */
180 { "SLAVE", CMD_unimp, false, 1, 1, true,
181 NULL },
182 #ifdef HAVE_OPENSSL
183 { "STARTTLS", CMDstarttls, false, 1, 1, true,
184 NULL },
185 #endif
186 { "STAT", CMDfetch, true, 1, 2, true,
187 CMDfetchhelp },
188 { "XGTITLE", CMDxgtitle, true, 1, 2, true,
189 "[wildmat]" },
190 { "XHDR", CMDpat, true, 2, 3, true,
191 "header [message-ID|range]" },
192 { "XOVER", CMDover, true, 1, 2, true,
193 "[range]" },
194 { "XPAT", CMDpat, true, 4, CMDany, true,
195 "header message-ID|range pattern [pattern ...]" },
196 { NULL, CMD_unimp, false, 0, 0, true,
197 NULL }
198 };
199 /* clang-format on */
200
201 static const char *const timer_name[] = {
202 "idle", "newnews", "readart", "checkart", "nntpread", "nntpwrite",
203 };
204
205
206 /*
207 ** Log a summary status message and exit.
208 */
209 void
ExitWithStats(int x,bool readconf)210 ExitWithStats(int x, bool readconf)
211 {
212 double usertime;
213 double systime;
214
215 line_free(&NNTPline);
216 fflush(stdout);
217 STATfinish = TMRnow_double();
218 if (GetResourceUsage(&usertime, &systime) < 0) {
219 usertime = 0;
220 systime = 0;
221 }
222
223 GRPreport();
224 if (ARTcount)
225 syslog(L_NOTICE, "%s exit articles %ld groups %ld", Client.host,
226 ARTcount, GRPcount);
227 if (POSTreceived || POSTrejected)
228 syslog(L_NOTICE, "%s posts received %ld rejected %ld", Client.host,
229 POSTreceived, POSTrejected);
230 syslog(L_NOTICE, "%s times user %.3f system %.3f idle %.3f elapsed %.3f",
231 Client.host, usertime, systime, IDLEtime, STATfinish - STATstart);
232 /* Tracking code - Make entries in the logfile(s) to show that we have
233 * finished with this session. */
234 if (!readconf && PERMaccessconf && PERMaccessconf->readertrack) {
235 syslog(L_NOTICE, "%s Tracking Disabled (%s)", Client.host, Username);
236 if (LLOGenable) {
237 fprintf(locallog, "%s Tracking Disabled (%s)\n", Client.host,
238 Username);
239 fclose(locallog);
240 syslog(L_NOTICE, "%s Local Logging ends (%s) %s", Client.host,
241 Username, LocalLogFileName);
242 }
243 }
244 if (ARTget)
245 syslog(L_NOTICE, "%s artstats get %ld time %ld size %ld", Client.host,
246 ARTget, ARTgettime, ARTgetsize);
247 if (!readconf && PERMaccessconf && PERMaccessconf->nnrpdoverstats
248 && OVERcount)
249 syslog(L_NOTICE,
250 "%s overstats count %ld hit %ld miss %ld time %ld size %ld dbz "
251 "%ld seek %ld get %ld artcheck %ld",
252 Client.host, OVERcount, OVERhit, OVERmiss, OVERtime, OVERsize,
253 OVERdbz, OVERseek, OVERget, OVERartcheck);
254
255 #ifdef HAVE_OPENSSL
256 if (tls_conn) {
257 SSL_shutdown(tls_conn);
258 SSL_free(tls_conn);
259 tls_conn = NULL;
260 }
261 #endif
262
263 #ifdef HAVE_SASL
264 if (sasl_conn) {
265 sasl_dispose(&sasl_conn);
266 sasl_conn = NULL;
267 sasl_ssf = 0;
268 sasl_maxout = NNTP_MAXLEN_COMMAND;
269 }
270 sasl_done();
271 #endif /* HAVE_SASL */
272
273 #if defined(HAVE_ZLIB)
274 if (compression_layer_on) {
275 inflateEnd(zstream_in);
276 free(zstream_in);
277 free(zbuf_in);
278 deflateEnd(zstream_out);
279 free(zstream_out);
280 free(zbuf_out);
281 }
282 #endif /* HAVE_ZLIB */
283
284 if (DaemonMode) {
285 shutdown(STDIN_FILENO, 2);
286 shutdown(STDOUT_FILENO, 2);
287 shutdown(STDERR_FILENO, 2);
288 close(STDIN_FILENO);
289 close(STDOUT_FILENO);
290 close(STDERR_FILENO);
291 }
292
293 OVclose();
294 SMshutdown();
295
296 #ifdef DO_PYTHON
297 PY_close_python();
298 #endif /* DO_PYTHON */
299
300 if (History)
301 HISclose(History);
302
303 if (innconf->timer != 0) {
304 TMRsummary(Client.host, timer_name);
305 TMRfree();
306 }
307
308 if (LocalLogFileName != NULL)
309 free(LocalLogFileName);
310 closelog();
311 exit(x);
312 }
313
314
315 /*
316 ** The HELP command.
317 */
318 void
CMDhelp(int ac UNUSED,char * av[]UNUSED)319 CMDhelp(int ac UNUSED, char *av[] UNUSED)
320 {
321 CMDENT *cp;
322 char *p, *q;
323 static const char *newsmaster = NEWSMASTER;
324
325 Reply("%d Legal commands\r\n", NNTP_INFO_HELP);
326 for (cp = CMDtable; cp->Name; cp++) {
327 if (cp->Function == CMD_unimp)
328 continue;
329 if (cp->Help == NULL)
330 Printf(" %s\r\n", cp->Name);
331 else
332 Printf(" %s %s\r\n", cp->Name, cp->Help);
333 }
334 if (PERMaccessconf && (VirtualPathlen > 0)) {
335 if (PERMaccessconf->newsmaster) {
336 if (strchr(PERMaccessconf->newsmaster, '@') == NULL) {
337 Printf("Report problems to <%s@%s>.\r\n",
338 PERMaccessconf->newsmaster, PERMaccessconf->domain);
339 } else {
340 Printf("Report problems to <%s>.\r\n",
341 PERMaccessconf->newsmaster);
342 }
343 } else {
344 /* Sigh, pickup from newsmaster anyway. */
345 if ((p = strchr(newsmaster, '@')) == NULL)
346 Printf("Report problems to <%s@%s>.\r\n", newsmaster,
347 PERMaccessconf->domain);
348 else {
349 q = xstrndup(newsmaster, p - newsmaster);
350 Printf("Report problems to <%s@%s>.\r\n", q,
351 PERMaccessconf->domain);
352 free(q);
353 }
354 }
355 } else {
356 if (strchr(newsmaster, '@') == NULL)
357 Printf("Report problems to <%s@%s>.\r\n", newsmaster,
358 innconf->fromhost);
359 else
360 Printf("Report problems to <%s>.\r\n", newsmaster);
361 }
362 Printf(".\r\n");
363 }
364
365
366 /*
367 ** The CAPABILITIES command.
368 **
369 ** nnrpd does not advertise the MODE-READER capability; only innd may
370 ** advertise it.
371 */
372 void
CMDcapabilities(int ac,char * av[])373 CMDcapabilities(int ac, char *av[])
374 {
375 #ifdef HAVE_SASL
376 const char *mechlist = NULL;
377 #endif
378
379 if (ac == 2 && !IsValidKeyword(av[1])) {
380 Reply("%d Syntax error in keyword\r\n", NNTP_ERR_SYNTAX);
381 return;
382 }
383
384 Reply("%d Capability list:\r\n", NNTP_INFO_CAPABILITIES);
385
386 Printf("VERSION 2\r\n");
387
388 Printf("IMPLEMENTATION %s\r\n", INN_VERSION_STRING);
389
390 #ifdef HAVE_SASL
391 /* Check for available SASL mechanisms.
392 * Start the string with a space for the strstr() calls afterwards. */
393 sasl_listmech(sasl_conn, NULL, " ", " ", "", &mechlist, NULL, NULL);
394 #endif
395
396 /* The client is not already authenticated. */
397 if ((!PERMauthorized || PERMneedauth || PERMcanauthenticate)) {
398 Printf("AUTHINFO");
399
400 /* No arguments if the server does not permit any authentication
401 * commands in its current state (either a compression layer other
402 * than the one negotiated along with TLS is active, or the user
403 * has no way to authenticate successfully). */
404 if (
405 #if defined(HAVE_ZLIB)
406 (!compression_layer_on || tls_compression_on) &&
407 #endif /* HAVE_ZLIB */
408 PERMcanauthenticate) {
409 #ifdef HAVE_OPENSSL
410 if (PERMcanauthenticatewithoutSSL || encryption_layer_on) {
411 #endif
412 /* AUTHINFO USER is advertised only if a TLS layer is active,
413 * if compiled with TLS support. */
414 Printf(" USER");
415 #ifdef HAVE_OPENSSL
416 } else {
417 # ifdef HAVE_SASL
418 /* Remove unsecure PLAIN, LOGIN and EXTERNAL SASL mechanisms,
419 * if compiled with TLS support and a TLS layer is not active.
420 */
421 if (mechlist != NULL) {
422 char *p;
423
424 if ((p = strstr(mechlist, " PLAIN")) != NULL
425 && (p[6] == '\0' || p[6] == ' ')) {
426 memmove(p, p + 6, strlen(p) - 5);
427 }
428 if ((p = strstr(mechlist, " LOGIN")) != NULL
429 && (p[6] == '\0' || p[6] == ' ')) {
430 memmove(p, p + 6, strlen(p) - 5);
431 }
432 if ((p = strstr(mechlist, " EXTERNAL")) != NULL
433 && (p[9] == '\0' || p[9] == ' ')) {
434 memmove(p, p + 9, strlen(p) - 8);
435 }
436 }
437 # endif /* HAVE_SASL */
438 }
439 #endif /* HAVE_OPENSSL */
440 #ifdef HAVE_SASL
441 /* Check whether at least one SASL mechanism is available. */
442 if (mechlist != NULL && strlen(mechlist) > 2) {
443 Printf(" SASL");
444 }
445 #endif
446 }
447 Printf("\r\n");
448 }
449
450 #if defined(HAVE_ZLIB)
451 /* A compression layer is not active. */
452 if (!compression_layer_on) {
453 Printf("COMPRESS DEFLATE\r\n");
454 }
455 #endif /* HAVE_ZLIB */
456
457 if (PERMcanread) {
458 Printf("HDR\r\n");
459 }
460
461 if (PERMaccessconf->allowihave && PERMcanpost) {
462 Printf("IHAVE\r\n");
463 }
464
465 Printf(
466 "LIST ACTIVE ACTIVE.TIMES COUNTS DISTRIB.PATS DISTRIBUTIONS"
467 " HEADERS MODERATORS MOTD NEWSGROUPS OVERVIEW.FMT SUBSCRIPTIONS\r\n");
468
469 if (PERMaccessconf->allownewnews && PERMcanread) {
470 Printf("NEWNEWS\r\n");
471 }
472
473 if (PERMcanread) {
474 Printf("OVER\r\n");
475 }
476
477 if (PERMcanpost) {
478 Printf("POST\r\n");
479 }
480
481 Printf("READER\r\n");
482
483 #if defined(HAVE_SASL)
484 /* Check whether at least one SASL mechanism is available.
485 * The SASL capability has to be advertised, even after authentication,
486 * so that the client can detect a possible active down-negotiation
487 * attack. */
488 if (mechlist != NULL && strlen(mechlist) > 2) {
489 Printf("SASL%s\r\n", mechlist);
490 }
491 #endif /* HAVE_SASL */
492
493 #if defined(HAVE_OPENSSL)
494 /* A TLS layer is not active, a compression layer is not active,
495 * and the client is not already authenticated. */
496 if (!encryption_layer_on
497 # if defined(HAVE_ZLIB)
498 && !compression_layer_on
499 # endif /* HAVE_ZLIB */
500 && (!PERMauthorized || PERMneedauth || PERMcanauthenticate)) {
501 Printf("STARTTLS\r\n");
502 }
503 #endif /* HAVE_OPENSSL */
504
505 if (PERMcanread) {
506 Printf("XPAT\r\n");
507 }
508
509 Printf(".\r\n");
510 }
511
512
513 /*
514 ** Catch-all for unimplemented functions.
515 */
516 void
CMD_unimp(int ac UNUSED,char * av[])517 CMD_unimp(int ac UNUSED, char *av[])
518 {
519 Reply("%d \"%s\" not implemented; try \"HELP\"\r\n", NNTP_ERR_COMMAND,
520 av[0]);
521 }
522
523
524 /*
525 ** The QUIT command.
526 */
527 void
CMDquit(int ac UNUSED,char * av[]UNUSED)528 CMDquit(int ac UNUSED, char *av[] UNUSED)
529 {
530 Reply("%d Bye!\r\n", NNTP_OK_QUIT);
531 ExitWithStats(0, false);
532 }
533
534
535 /*
536 ** Convert an address to a hostname. Don't trust the reverse lookup
537 ** since anyone can fake reverse DNS entries.
538 */
539 static bool
Address2Name(struct sockaddr * sa,socklen_t len,char * hostname,size_t size)540 Address2Name(struct sockaddr *sa, socklen_t len, char *hostname, size_t size)
541 {
542 static const char MISMATCH[] = "reverse lookup validation failed";
543 struct addrinfo hints, *ai, *host;
544 char *p;
545 int ret;
546 bool valid = false;
547
548 /* Get the official hostname, store it away. */
549 ret = getnameinfo(sa, len, hostname, size, NULL, 0, NI_NAMEREQD);
550 if (ret != 0) {
551 HostErrorStr = gai_strerror(ret);
552 return false;
553 }
554
555 /* Get addresses for this host. */
556 memset(&hints, 0, sizeof(hints));
557 hints.ai_family = AF_UNSPEC;
558 hints.ai_socktype = SOCK_STREAM;
559 ret = getaddrinfo(hostname, NULL, &hints, &ai);
560 if (ret != 0) {
561 HostErrorStr = gai_strerror(ret);
562 return false;
563 }
564
565 /* Make sure one of those addresses is the address we got. */
566 for (host = ai; host != NULL; host = host->ai_next)
567 if (network_sockaddr_equal(sa, host->ai_addr)) {
568 valid = true;
569 break;
570 }
571 freeaddrinfo(ai);
572
573 /* Lowercase the returned name since wildmats are case-sensitive. */
574 if (valid) {
575 for (p = hostname; *p != '\0'; p++)
576 if (isupper((unsigned char) *p))
577 *p = tolower((unsigned char) *p);
578 return true;
579 } else {
580 HostErrorStr = MISMATCH;
581 return false;
582 }
583 }
584
585
586 /*
587 ** Determine hostname and IP of the client, amongst other information.
588 */
589 static void
GetClientInfo(unsigned short port)590 GetClientInfo(unsigned short port)
591 {
592 static const char *default_host_error = "unknown error";
593 struct sockaddr_storage ssc, sss;
594 struct sockaddr *sac = (struct sockaddr *) &ssc;
595 struct sockaddr *sas = (struct sockaddr *) &sss;
596 socklen_t length;
597 size_t size;
598
599 memset(&Client, 0, sizeof(Client));
600 strlcpy(Client.host, "?", sizeof(Client.host));
601
602 /* Get the peer's name. */
603 length = sizeof(ssc);
604 if (getpeername(STDIN_FILENO, sac, &length) < 0) {
605 if (!isatty(STDIN_FILENO)) {
606 sysnotice("? can't getpeername");
607 Reply("%d Can't get your name. Goodbye!\r\n", NNTP_ERR_ACCESS);
608 ExitWithStats(1, true);
609 }
610 strlcpy(Client.host, "localhost", sizeof(Client.host));
611 } else {
612 /* Figure out client's IP address/hostname. */
613 HostErrorStr = default_host_error;
614 if (!network_sockaddr_sprint(Client.ip, sizeof(Client.ip), sac)) {
615 notice("? can't get client numeric address: %s", HostErrorStr);
616 Reply("%d Can't get your numeric address. Goodbye!\r\n",
617 NNTP_ERR_ACCESS);
618 ExitWithStats(1, true);
619 }
620 if (GetHostByAddr) {
621 HostErrorStr = default_host_error;
622 if (!Address2Name(sac, length, Client.host, sizeof(Client.host))) {
623 notice("? reverse lookup for %s failed: %s -- using IP"
624 " address for access",
625 Client.ip, HostErrorStr);
626 strlcpy(Client.host, Client.ip, sizeof(Client.host));
627 }
628 } else {
629 strlcpy(Client.host, Client.ip, sizeof(Client.host));
630 }
631
632 /* Figure out server's IP address/hostname. */
633 length = sizeof(sss);
634 if (getsockname(STDIN_FILENO, sas, &length) < 0) {
635 sysnotice("%s can't getsockname", Client.host);
636 Reply("%d Can't figure out where you connected to. Goodbye!\r\n",
637 NNTP_ERR_ACCESS);
638 ExitWithStats(1, true);
639 }
640 HostErrorStr = default_host_error;
641 size = sizeof(Client.serverip);
642 if (!network_sockaddr_sprint(Client.serverip, size, sas)) {
643 notice("? can't get server numeric address: %s", HostErrorStr);
644 Reply("%d Can't get my own numeric address. Goodbye!\r\n",
645 NNTP_ERR_ACCESS);
646 ExitWithStats(1, true);
647 }
648 if (GetHostByAddr) {
649 HostErrorStr = default_host_error;
650 size = sizeof(Client.serverhost);
651 if (!Address2Name(sas, length, Client.serverhost, size)) {
652 notice("? reverse lookup for %s failed: %s -- using IP"
653 " address for access",
654 Client.serverip, HostErrorStr);
655 strlcpy(Client.serverhost, Client.serverip,
656 sizeof(Client.serverhost));
657 }
658 } else {
659 strlcpy(Client.serverhost, Client.serverip,
660 sizeof(Client.serverhost));
661 }
662
663 /* Get port numbers. */
664 Client.port = network_sockaddr_port(sac);
665 Client.serverport = network_sockaddr_port(sas);
666 }
667
668 #if defined(INN_BSDI_HOST)
669 /* Setting TCP_NODELAY to nnrpd fixes a problem of slow downloading
670 * of overviews and slow answers on some architectures (like BSD/OS
671 * where TCP delayed acknowledgements are enabled). */
672 int nodelay = 1;
673 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &nodelay,
674 sizeof(nodelay));
675 #endif
676
677 notice("%s (%s) connect - port %u", Client.host, Client.ip, port);
678 }
679
680
681 /*
682 ** Write a buffer, via compression layer and/or SASL security layer
683 ** and/or TLS if necessary.
684 */
685 void
write_buffer(const char * buff,ssize_t len)686 write_buffer(const char *buff, ssize_t len)
687 {
688 const char *p;
689 ssize_t n;
690
691 TMRstart(TMR_NNTPWRITE);
692 p = buff;
693
694 #if defined(HAVE_ZLIB)
695 if (compression_layer_on) {
696 int r;
697
698 zstream_out->next_in = (unsigned char *) p;
699 zstream_out->avail_in = len;
700 zstream_out->next_out = zbuf_out;
701 zstream_out->avail_out = zbuf_out_size;
702
703 do {
704 /* Grow the output buffer if needed. */
705 if (zstream_out->avail_out == 0) {
706 size_t newsize = zbuf_out_size * 2;
707 zbuf_out = xrealloc(zbuf_out, newsize);
708 zstream_out->next_out = zbuf_out + zbuf_out_size;
709 zstream_out->avail_out = zbuf_out_size;
710 zbuf_out_size = newsize;
711 }
712
713 r = deflate(zstream_out,
714 zstream_flush_needed ? Z_PARTIAL_FLUSH : Z_NO_FLUSH);
715
716 if (!(r == Z_OK || r == Z_BUF_ERROR || r == Z_STREAM_END)) {
717 sysnotice("deflate() failed: %d; %s", r,
718 zstream_out->msg != NULL ? zstream_out->msg
719 : "no detail");
720 TMRstop(TMR_NNTPWRITE);
721 return;
722 }
723 } while (r == Z_OK && zstream_out->avail_out == 0);
724
725 p = (char *) zbuf_out;
726 len = zbuf_out_size - zstream_out->avail_out;
727 zstream_flush_needed = false;
728 }
729 #endif /* HAVE_ZLIB */
730
731 while (len > 0) {
732 const char *out;
733 unsigned outlen;
734
735 #if defined(HAVE_SASL)
736 if (sasl_conn != NULL && sasl_ssf > 0) {
737 int r;
738
739 /* Can only encode as much as the client can handle at one time. */
740 n = (len > sasl_maxout) ? sasl_maxout : len;
741 if ((r = sasl_encode(sasl_conn, p, n, &out, &outlen)) != SASL_OK) {
742 const char *ed = sasl_errdetail(sasl_conn);
743
744 sysnotice("sasl_encode() failed: %s; %s",
745 sasl_errstring(r, NULL, NULL),
746 ed != NULL ? ed : "no detail");
747 TMRstop(TMR_NNTPWRITE);
748 return;
749 }
750 } else
751 #endif /* HAVE_SASL */
752 {
753 /* Output the entire unencoded string. */
754 n = len;
755 out = p;
756 outlen = len;
757 }
758
759 len -= n;
760 p += n;
761
762 #ifdef HAVE_OPENSSL
763 if (tls_conn) {
764 int r;
765
766 Again:
767 r = SSL_write(tls_conn, out, outlen);
768 switch (SSL_get_error(tls_conn, r)) {
769 case SSL_ERROR_NONE:
770 break;
771 case SSL_ERROR_WANT_WRITE:
772 goto Again;
773 /* NOTREACHED */
774 case SSL_ERROR_ZERO_RETURN:
775 SSL_shutdown(tls_conn);
776 goto fallthrough;
777 case SSL_ERROR_SSL:
778 case SSL_ERROR_SYSCALL:
779 fallthrough:
780 /* SSL_shutdown() must not be called. */
781 tls_conn = NULL;
782 errno = ECONNRESET;
783 break;
784 }
785 } else
786 #endif /* HAVE_OPENSSL */
787 do {
788 n = write(STDIN_FILENO, out, outlen);
789 } while (n == -1 && errno == EINTR);
790 }
791
792 TMRstop(TMR_NNTPWRITE);
793 }
794
795
796 /*
797 ** Send formatted output, possibly with debugging output.
798 */
799 static void __attribute__((__format__(printf, 1, 0)))
VPrintf(const char * fmt,va_list args,int dotrace)800 VPrintf(const char *fmt, va_list args, int dotrace)
801 {
802 char buff[2048], *p;
803 ssize_t len;
804
805 len = vsnprintf(buff, sizeof(buff), fmt, args);
806 if (len < 0)
807 len = 0;
808 else if ((size_t) len >= sizeof(buff))
809 len = sizeof(buff) - 1;
810 write_buffer(buff, len);
811
812 if (dotrace && Tracing) {
813 int oerrno = errno;
814
815 /* Copy output, but strip trailing CR-LF. Note we're assuming here
816 * that no output line can ever be longer than 2045 characters. */
817 p = buff + strlen(buff) - 1;
818 while (p >= buff && (*p == '\n' || *p == '\r'))
819 *p-- = '\0';
820 syslog(L_TRACE, "%s > %s", Client.host, buff);
821
822 errno = oerrno;
823 }
824 }
825
826
827 /*
828 ** Send a reply, possibly with debugging output.
829 ** Reply() is used for answers which can possibly be traced (response codes).
830 ** Printf() is used in other cases.
831 */
832 void
Reply(const char * fmt,...)833 Reply(const char *fmt, ...)
834 {
835 va_list args;
836
837 #if defined(HAVE_ZLIB)
838 /* For single-line responses, immediately flush the output stream. */
839 zstream_flush_needed = true;
840 #endif /* HAVE_ZLIB */
841
842 va_start(args, fmt);
843 VPrintf(fmt, args, 1);
844 va_end(args);
845 }
846
847 void
Printf(const char * fmt,...)848 Printf(const char *fmt, ...)
849 {
850 va_list args;
851
852 #if defined(HAVE_ZLIB)
853 /* Last line of a multi-line data block response.
854 * Time to flush the compressed output stream.
855 * Check that only when the compression layer is active. */
856 if (compression_layer_on && strlen(fmt) == 3
857 && strcasecmp(fmt, ".\r\n") == 0) {
858 zstream_flush_needed = true;
859 }
860 #endif /* HAVE_ZLIB */
861
862 va_start(args, fmt);
863 VPrintf(fmt, args, 0);
864 va_end(args);
865 }
866
867
868 #ifdef HAVE_SIGACTION
869 # define NO_SIGACTION_UNUSED UNUSED
870 #else
871 # define NO_SIGACTION_UNUSED
872 #endif
873
874
875 /*
876 ** Got a signal; toggle tracing.
877 */
878 static void
ToggleTrace(int s NO_SIGACTION_UNUSED)879 ToggleTrace(int s NO_SIGACTION_UNUSED)
880 {
881 ChangeTrace = true;
882 #ifndef HAVE_SIGACTION
883 xsignal(s, ToggleTrace);
884 #endif
885 }
886
887 /*
888 ** Got a SIGPIPE; exit cleanly.
889 */
890 __attribute__((__noreturn__)) static void
CatchPipe(int s UNUSED)891 CatchPipe(int s UNUSED)
892 {
893 ExitWithStats(0, false);
894 }
895
896
897 /*
898 ** Got a signal; wait for children.
899 */
900 static void
WaitChild(int s NO_SIGACTION_UNUSED)901 WaitChild(int s NO_SIGACTION_UNUSED)
902 {
903 int pid;
904
905 for (;;) {
906 pid = waitpid(-1, NULL, WNOHANG);
907 if (pid <= 0)
908 break;
909 }
910 #ifndef HAVE_SIGACTION
911 xsignal(s, WaitChild);
912 #endif
913 }
914
915
916 static void
SetupDaemon(void)917 SetupDaemon(void)
918 {
919 bool val;
920
921 val = true;
922 if (SMsetup(SM_PREOPEN, (void *) &val) && !SMinit()) {
923 syslog(L_NOTICE, "can't initialize storage method, %s", SMerrorstr);
924 Reply("%d NNTP server unavailable. Try later!\r\n",
925 NNTP_FAIL_TERMINATING);
926 ExitWithStats(1, true);
927 }
928 OVextra = overview_extra_fields(false);
929 if (OVextra == NULL) {
930 /* overview_extra_fields() should already have logged something
931 * useful. */
932 Reply("%d NNTP server unavailable. Try later!\r\n",
933 NNTP_FAIL_TERMINATING);
934 ExitWithStats(1, true);
935 }
936 overhdr_xref = overview_index("Xref", OVextra);
937 if (!OVopen(OV_READ)) {
938 /* This shouldn't really happen. */
939 syslog(L_NOTICE, "can't open overview %m");
940 Reply("%d NNTP server unavailable. Try later!\r\n",
941 NNTP_FAIL_TERMINATING);
942 ExitWithStats(1, true);
943 }
944 if (!OVctl(OVCACHEKEEP, &val)) {
945 syslog(L_NOTICE, "can't enable overview cache %m");
946 Reply("%d NNTP server unavailable. Try later!\r\n",
947 NNTP_FAIL_TERMINATING);
948 ExitWithStats(1, true);
949 }
950 }
951
952
953 /*
954 ** Print a usage message and exit.
955 */
956 static void
Usage(void)957 Usage(void)
958 {
959 fprintf(stderr, "Usage error.\n");
960 exit(1);
961 }
962
963
964 int
main(int argc,char * argv[])965 main(int argc, char *argv[])
966 {
967 const char *name;
968 CMDENT *cp;
969 char buff[NNTP_MAXLEN_COMMAND];
970 char **av;
971 int ac;
972 READTYPE r;
973 int i;
974 unsigned int j;
975 char **v;
976 char *Reject;
977 int timeout;
978 unsigned int vid = 0;
979 int count = 123456789;
980 struct timeval tv;
981 unsigned short ListenPort = NNTP_PORT;
982 char *ListenAddr = NULL;
983 char *ListenAddr6 = NULL;
984 int *lfds;
985 fd_set lfdset, lfdsetread;
986 unsigned int lfdcount;
987 int lfdreadcount;
988 bool lfdokay;
989 int lfdmax = 0;
990 int fd = -1;
991 pid_t pid = -1;
992 FILE *pidfile;
993 int clienttimeout;
994 char *ConfFile = NULL;
995 char *path;
996 bool validcommandtoolong;
997
998 int respawn = 0;
999
1000 setproctitle_init(argc, argv);
1001
1002 /* Parse arguments. Must xstrdup() optarg if used because setproctitle
1003 * may clobber it! */
1004 Reject = NULL;
1005 LLOGenable = false;
1006 GRPcur = NULL;
1007 MaxBytesPerSecond = 0;
1008 strlcpy(Username, "unknown", sizeof(Username));
1009
1010 /* Set up the pathname, first thing, and teach our error handlers about
1011 * the name of the program. */
1012 name = argv[0];
1013 if (name == NULL || *name == '\0')
1014 name = "nnrpd";
1015 else {
1016 const char *p;
1017
1018 /* We take the last "/" in the path. */
1019 p = strrchr(name, '/');
1020 if (p != NULL)
1021 name = p + 1;
1022 }
1023 message_program_name = xstrdup(name);
1024 openlog(message_program_name, L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG);
1025 message_handlers_die(1, message_log_syslog_crit);
1026 message_handlers_warn(1, message_log_syslog_warning);
1027 message_handlers_notice(1, message_log_syslog_notice);
1028
1029 /* Initialize the character class tables for message-IDs. */
1030 InitializeMessageIDcclass();
1031
1032 if (!innconf_read(NULL))
1033 exit(1);
1034
1035 #ifdef HAVE_SASL
1036 if (sasl_server_init(sasl_callbacks, "INN") != SASL_OK) {
1037 syslog(L_FATAL, "sasl_server_init() failed");
1038 exit(1);
1039 }
1040 #endif /* HAVE_SASL */
1041
1042 #ifdef HAVE_OPENSSL
1043 while ((i = getopt(argc, argv, "4:6:b:c:Dfi:I:nop:P:r:s:St")) != EOF)
1044 #else
1045 while ((i = getopt(argc, argv, "4:6:b:c:Dfi:I:nop:P:r:s:t")) != EOF)
1046 #endif /* HAVE_OPENSSL */
1047 switch (i) {
1048 default:
1049 Usage();
1050 /* NOTREACHED */
1051 case '4': /* Bind to a certain IPv4 address. */
1052 case 'b':
1053 if (ListenAddr != NULL)
1054 die("-b and -4 are the same and both may not be given");
1055 ListenAddr = xstrdup(optarg);
1056 break;
1057 case '6': /* Bind to a certain IPv6 address. */
1058 ListenAddr6 = xstrdup(optarg);
1059 break;
1060 case 'c': /* Use alternate readers.conf. */
1061 ConfFile = concatpath(innconf->pathetc, optarg);
1062 break;
1063 case 'D': /* Standalone daemon mode. */
1064 DaemonMode = true;
1065 break;
1066 case 'P': /* Prespawn count in daemon mode. */
1067 respawn = atoi(optarg);
1068 break;
1069 case 'f': /* Don't fork on daemon mode. */
1070 ForeGroundMode = true;
1071 break;
1072 case 'i': /* Initial command. */
1073 PushedBack = xstrdup(optarg);
1074 break;
1075 case 'I': /* Instance. */
1076 NNRPinstance = xstrdup(optarg);
1077 break;
1078 case 'n': /* No DNS lookups. */
1079 GetHostByAddr = false;
1080 break;
1081 case 'o': /* Offline posting only. */
1082 Offlinepost = true;
1083 break;
1084 case 'p': /* TCP port for daemon mode. */
1085 ListenPort = atoi(optarg);
1086 break;
1087 case 'r': /* Reject connection message. */
1088 Reject = xstrdup(optarg);
1089 break;
1090 case 's': /* Unused title string. */
1091 break;
1092 case 't': /* Tracing. */
1093 Tracing = true;
1094 break;
1095 #ifdef HAVE_OPENSSL
1096 case 'S': /* Force the negotiation of an encryption layer. */
1097 initialSSL = true;
1098 break;
1099 #endif /* HAVE_OPENSSL */
1100 }
1101 argc -= optind;
1102 if (argc)
1103 Usage();
1104
1105 /* Make other processes happier if someone is reading. This allows other
1106 * processes like overchan to keep up when there are lots of readers.
1107 * Note that this is cumulative with nicekids. */
1108 if (innconf->nicennrpd != 0) {
1109 errno = 0;
1110 if (nice(innconf->nicennrpd) < 0 && errno != 0)
1111 syswarn("could not nice to %ld", innconf->nicennrpd);
1112 }
1113
1114 HISTORY = concatpath(innconf->pathdb, INN_PATH_HISTORY);
1115 ACTIVE = concatpath(innconf->pathdb, INN_PATH_ACTIVE);
1116 ACTIVETIMES = concatpath(innconf->pathdb, INN_PATH_ACTIVETIMES);
1117 NEWSGROUPS = concatpath(innconf->pathdb, INN_PATH_NEWSGROUPS);
1118 if (ConfFile)
1119 NNRPACCESS = ConfFile;
1120 else
1121 NNRPACCESS = concatpath(innconf->pathetc, INN_PATH_NNRPACCESS);
1122
1123 /* Initialize the checks to perform or not on article syntax. */
1124 if ((innconf->syntaxchecks != NULL)
1125 && (innconf->syntaxchecks->count > 0)) {
1126 for (j = 0; j < innconf->syntaxchecks->count; j++) {
1127 if (innconf->syntaxchecks->strings[j] != NULL) {
1128 if (strcmp(innconf->syntaxchecks->strings[j], "laxmid") == 0) {
1129 laxmid = true;
1130 } else if (strcmp(innconf->syntaxchecks->strings[j],
1131 "no-laxmid")
1132 == 0) {
1133 laxmid = false;
1134 } else {
1135 syslog(L_NOTICE,
1136 "Unknown \"%s\" value in syntaxchecks "
1137 "parameter in inn.conf",
1138 innconf->syntaxchecks->strings[j]);
1139 }
1140 }
1141 }
1142 }
1143
1144 if (DaemonMode) {
1145 /* Allocate an lfds array to hold the file descriptors
1146 * for IPv4 and/or IPv6 connections. */
1147 if (ListenAddr == NULL && ListenAddr6 == NULL) {
1148 network_innbind_all(SOCK_STREAM, ListenPort, &lfds, &lfdcount);
1149 } else {
1150 if (ListenAddr != NULL && ListenAddr6 != NULL)
1151 lfdcount = 2;
1152 else
1153 lfdcount = 1;
1154 lfds = xmalloc(lfdcount * sizeof(int));
1155 i = 0;
1156 if (ListenAddr6 != NULL) {
1157 lfds[i++] =
1158 network_innbind_ipv6(SOCK_STREAM, ListenAddr6, ListenPort);
1159 }
1160 if (ListenAddr != NULL) {
1161 lfds[i] =
1162 network_innbind_ipv4(SOCK_STREAM, ListenAddr, ListenPort);
1163 }
1164 }
1165
1166 /* Bail if we couldn't listen on any sockets. */
1167 lfdokay = false;
1168 for (j = 0; j < lfdcount; j++) {
1169 if (lfds[j] < 0)
1170 continue;
1171 lfdokay = true;
1172 }
1173 if (!lfdokay)
1174 die("can't bind to any addresses");
1175
1176 /* If started as root, switch to news uid. Unlike other parts of INN,
1177 * we don't die if we can't become the news user. As long as we're not
1178 * running as root, everything's fine; it's okay to write the things we
1179 * write as a member of the news group. */
1180 if (getuid() == 0) {
1181 ensure_news_user_grp(true, true);
1182 }
1183
1184 /* Detach. */
1185 if (!ForeGroundMode) {
1186 daemonize("/");
1187 }
1188
1189 if (ListenPort == NNTP_PORT)
1190 strlcpy(buff, "nnrpd.pid", sizeof(buff));
1191 else
1192 snprintf(buff, sizeof(buff), "nnrpd-%d.pid", ListenPort);
1193 path = concatpath(innconf->pathrun, buff);
1194 pidfile = fopen(path, "w");
1195 free(path);
1196 if (pidfile == NULL) {
1197 syslog(L_ERROR, "cannot write %s %m", buff);
1198 exit(1);
1199 }
1200 fprintf(pidfile, "%lu\n", (unsigned long) getpid());
1201 fclose(pidfile);
1202
1203 /* Set signal handle to care for dead children. */
1204 if (!respawn)
1205 xsignal(SIGCHLD, WaitChild);
1206
1207 /* Arrange to toggle tracing. */
1208 xsignal(SIGHUP, ToggleTrace);
1209
1210 setproctitle("accepting connections");
1211
1212 /* Initialize the listener file descriptors set. */
1213 FD_ZERO(&lfdset);
1214
1215 for (j = 0; j < lfdcount; j++) {
1216 if (listen(lfds[j], innconf->maxlisten) < 0) {
1217 if (j != 0 && errno == EADDRINUSE)
1218 continue;
1219 syslog(L_ERROR, "can't listen to socket");
1220 } else {
1221 /* Remember the largest descriptor number
1222 * that is to be tested by select(). */
1223 FD_SET(lfds[j], &lfdset);
1224 if (lfdmax < lfds[j])
1225 lfdmax = lfds[j];
1226 }
1227 }
1228
1229 if (respawn) {
1230 /* Pre-forked mode. */
1231 for (;;) {
1232 if (respawn > 0) {
1233 --respawn;
1234 pid = fork();
1235 if (pid == 0) {
1236 do {
1237 fd = -1;
1238
1239 /* Copy the master set because lfdsetread
1240 * will be modified. */
1241 lfdsetread = lfdset;
1242 lfdreadcount = select(lfdmax + 1, &lfdsetread,
1243 NULL, NULL, NULL);
1244
1245 if (lfdreadcount > 0) {
1246 for (j = 0; j < lfdcount; j++) {
1247 if (FD_ISSET(lfds[j], &lfdsetread)) {
1248 fd = accept(lfds[j], NULL, NULL);
1249 /* Only handle the first match. Future
1250 * calls to select() will handle
1251 * possible other matches. */
1252 if (fd >= 0)
1253 break;
1254 }
1255 }
1256 }
1257 } while (fd < 0);
1258 break;
1259 }
1260 }
1261 for (;;) {
1262 if (respawn == 0)
1263 pid = wait(NULL);
1264 else
1265 pid = waitpid(-1, NULL, WNOHANG);
1266 if (pid <= 0)
1267 break;
1268 ++respawn;
1269 }
1270 }
1271 } else {
1272 /* Fork on demand. */
1273 do {
1274 fd = -1;
1275
1276 /* Copy the master set because lfdsetread will be modified. */
1277 lfdsetread = lfdset;
1278 lfdreadcount =
1279 select(lfdmax + 1, &lfdsetread, NULL, NULL, NULL);
1280
1281 if (lfdreadcount > 0) {
1282 for (j = 0; j < lfdcount; j++) {
1283 if (FD_ISSET(lfds[j], &lfdsetread)) {
1284 fd = accept(lfds[j], NULL, NULL);
1285 /* Only handle the first match. Future calls
1286 * to select() will handle possible other matches.
1287 */
1288 if (fd >= 0)
1289 break;
1290 }
1291 }
1292 }
1293 if (fd < 0)
1294 continue;
1295
1296 for (j = 0; j <= innconf->maxforks && (pid = fork()) < 0;
1297 j++) {
1298 if (j == innconf->maxforks) {
1299 syslog(L_FATAL,
1300 "can't fork (dropping connection): %m");
1301 continue;
1302 }
1303 syslog(L_NOTICE, "can't fork (waiting): %m");
1304 sleep(1);
1305 }
1306 if (ChangeTrace) {
1307 Tracing = Tracing ? false : true;
1308 syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis");
1309 ChangeTrace = false;
1310 }
1311 if (pid != 0)
1312 close(fd);
1313 } while (pid != 0);
1314 }
1315
1316 /* Child process starts here. */
1317 setproctitle("connected");
1318 for (j = 0; j < lfdcount; j++) {
1319 close(lfds[j]);
1320 }
1321 dup2(fd, 0);
1322 close(fd);
1323 dup2(0, 1);
1324 dup2(0, 2);
1325 if (innconf->timer != 0)
1326 TMRinit(TMR_MAX);
1327 STATstart = TMRnow_double();
1328 SetupDaemon();
1329
1330 /* If we are a daemon, innd didn't make us nice, so be nice kids. */
1331 if (innconf->nicekids) {
1332 if (nice(innconf->nicekids) < 0)
1333 syslog(L_ERROR, "Could not nice child to %ld: %m",
1334 innconf->nicekids);
1335 }
1336
1337 /* Only automatically reap children in the listening process. */
1338 xsignal(SIGCHLD, SIG_DFL);
1339
1340 } else {
1341 if (innconf->timer != 0)
1342 TMRinit(TMR_MAX);
1343 STATstart = TMRnow_double();
1344 SetupDaemon();
1345 /* Arrange to toggle tracing. */
1346 xsignal(SIGHUP, ToggleTrace);
1347 } /* DaemonMode */
1348
1349 #ifdef HAVE_OPENSSL
1350 if (initialSSL) {
1351 tls_init();
1352 if (tls_start_servertls(0, 1) == -1) {
1353 GetClientInfo(ListenPort);
1354 notice("%s failure to negotiate TLS session", Client.host);
1355 Reply("%d Encrypted TLS connection failed\r\n",
1356 NNTP_FAIL_TERMINATING);
1357 ExitWithStats(1, false);
1358 }
1359 encryption_layer_on = true;
1360
1361 # if defined(HAVE_ZLIB) && OPENSSL_VERSION_NUMBER >= 0x00090800fL
1362 /* Check whether a compression layer has just been added.
1363 * SSL_get_current_compression() is defined in OpenSSL versions >=
1364 * 0.9.8 final release, as well as LibreSSL. */
1365 tls_compression_on = (SSL_get_current_compression(tls_conn) != NULL);
1366 compression_layer_on = tls_compression_on;
1367 # endif /* HAVE_ZLIB && OPENSSL >= v0.9.8 */
1368 }
1369 #endif /* HAVE_OPENSSL */
1370
1371 /* If requested, check the load average. */
1372 if (innconf->nnrpdloadlimit != 0) {
1373 double load[1];
1374
1375 if (getloadavg(load, 1) < 0)
1376 warn("cannot obtain system load");
1377 else {
1378 if ((unsigned long) (load[0] + 0.5) > innconf->nnrpdloadlimit) {
1379 GetClientInfo(ListenPort);
1380 notice("%s load %.2f > %lu", Client.host, load[0],
1381 innconf->nnrpdloadlimit);
1382 Reply("%d load at %.2f, try later\r\n", NNTP_FAIL_TERMINATING,
1383 load[0]);
1384 ExitWithStats(1, true);
1385 }
1386 }
1387 }
1388
1389 /* Catch SIGPIPE so that we can exit out of long write loops. */
1390 xsignal(SIGPIPE, CatchPipe);
1391
1392 /* Get permissions and see if we can talk to this client. */
1393 GetClientInfo(ListenPort);
1394 PERMgetinitialaccess(NNRPACCESS);
1395 PERMgetaccess(true);
1396 PERMgetpermissions();
1397
1398 if (!PERMcanread && !PERMcanpost && !PERMneedauth) {
1399 syslog(L_NOTICE, "%s no_permission", Client.host);
1400 Reply("%d You have no permission to talk. Goodbye!\r\n",
1401 NNTP_ERR_ACCESS);
1402 ExitWithStats(1, false);
1403 }
1404
1405 /* Proceed with initialization. */
1406 setproctitle("%s connect", Client.host);
1407
1408 /* Were we told to reject connections? */
1409 if (Reject) {
1410 syslog(L_NOTICE, "%s rejected %s", Client.host, Reject);
1411 Reply("%d %s\r\n", NNTP_FAIL_TERMINATING,
1412 is_valid_utf8(Reject) ? Reject : "Connection rejected");
1413 ExitWithStats(0, false);
1414 }
1415
1416 if (PERMaccessconf) {
1417 if (PERMaccessconf->readertrack)
1418 PERMaccessconf->readertrack =
1419 TrackClient(Client.host, Username, sizeof(Username));
1420 } else {
1421 if (innconf->readertrack)
1422 innconf->readertrack =
1423 TrackClient(Client.host, Username, sizeof(Username));
1424 }
1425
1426 if ((PERMaccessconf && PERMaccessconf->readertrack)
1427 || (!PERMaccessconf && innconf->readertrack)) {
1428 int len;
1429 syslog(L_NOTICE, "%s Tracking Enabled (%s)", Client.host, Username);
1430 pid = getpid();
1431 gettimeofday(&tv, NULL);
1432 count += pid;
1433 vid = tv.tv_sec ^ tv.tv_usec ^ pid ^ count;
1434 len = strlen("innconf->pathlog") + strlen("/tracklogs/log-") + BUFSIZ;
1435 LocalLogFileName = xmalloc(len);
1436 sprintf(LocalLogFileName, "%s/tracklogs/log-%u", innconf->pathlog,
1437 vid);
1438 if ((locallog = fopen(LocalLogFileName, "w")) == NULL) {
1439 LocalLogDirName = concatpath(innconf->pathlog, "tracklogs");
1440 MakeDirectory(LocalLogDirName, false);
1441 free(LocalLogDirName);
1442 }
1443 if (locallog == NULL
1444 && (locallog = fopen(LocalLogFileName, "w")) == NULL) {
1445 syslog(L_ERROR, "%s Local Logging failed (%s) %s: %m", Client.host,
1446 Username, LocalLogFileName);
1447 } else {
1448 syslog(L_NOTICE, "%s Local Logging begins (%s) %s", Client.host,
1449 Username, LocalLogFileName);
1450 fprintf(locallog, "%s Tracking Enabled (%s)\n", Client.host,
1451 Username);
1452 fflush(locallog);
1453 LLOGenable = true;
1454 }
1455 }
1456
1457 #ifdef HAVE_SASL
1458 SASLnewserver();
1459 #endif /* HAVE_SASL */
1460
1461 if (PERMaccessconf) {
1462 Reply("%d %s InterNetNews NNRP server %s ready (%s)\r\n",
1463 (PERMcanpost || (PERMcanauthenticate && PERMcanpostgreeting))
1464 ? NNTP_OK_BANNER_POST
1465 : NNTP_OK_BANNER_NOPOST,
1466 PERMaccessconf->pathhost, INN_VERSION_STRING,
1467 (!PERMneedauth && PERMcanpost) ? "posting ok" : "no posting");
1468 clienttimeout = PERMaccessconf->clienttimeout;
1469 } else {
1470 Reply("%d %s InterNetNews NNRP server %s ready (%s)\r\n",
1471 (PERMcanpost || (PERMcanauthenticate && PERMcanpostgreeting))
1472 ? NNTP_OK_BANNER_POST
1473 : NNTP_OK_BANNER_NOPOST,
1474 innconf->pathhost, INN_VERSION_STRING,
1475 (!PERMneedauth && PERMcanpost) ? "posting ok" : "no posting");
1476 clienttimeout = innconf->clienttimeout;
1477 }
1478
1479 line_init(&NNTPline);
1480
1481 /* Main dispatch loop. */
1482 for (timeout = innconf->initialtimeout, av = NULL, ac = 0;;
1483 timeout = clienttimeout) {
1484 TMRstart(TMR_NNTPWRITE);
1485 fflush(stdout);
1486 TMRstop(TMR_NNTPWRITE);
1487 if (ChangeTrace) {
1488 Tracing = Tracing ? false : true;
1489 syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis");
1490 ChangeTrace = false;
1491 }
1492 if (PushedBack) {
1493 if (PushedBack[0] == '\0')
1494 continue;
1495 if (Tracing)
1496 syslog(L_TRACE, "%s < %s", Client.host, PushedBack);
1497 ac = nArgify(PushedBack, &av, 1);
1498 r = RTok;
1499 } else {
1500 size_t len;
1501 size_t lenstripped = 0;
1502 const char *p;
1503 char *q;
1504
1505 r = line_read(&NNTPline, timeout, &p, &len, &lenstripped);
1506 switch (r) {
1507 default:
1508 syslog(L_ERROR, "%s internal %d in main", Client.host, r);
1509 goto fallthroughRTtimeout;
1510 case RTtimeout:
1511 fallthroughRTtimeout:
1512 if (timeout < clienttimeout)
1513 syslog(L_NOTICE, "%s timeout short", Client.host);
1514 else
1515 syslog(L_NOTICE, "%s timeout", Client.host);
1516 ExitWithStats(1, false);
1517 /* NOTREACHED */
1518 case RTok:
1519 /* len does not count CRLF. */
1520 if (len + lenstripped <= sizeof(buff)) {
1521 /* line_read guarantees null termination. */
1522 memcpy(buff, p, len + 1);
1523 /* Do some input processing, check for blank line. */
1524 if (buff[0] != '\0')
1525 ac = nArgify(buff, &av, 1);
1526 if (Tracing) {
1527 /* Do not log passwords if AUTHINFO PASS,
1528 * AUTHINFO SASL PLAIN or AUTHINFO SASL EXTERNAL
1529 * are used. (Only one space between SASL and
1530 * PLAIN/EXTERNAL should be put; otherwise, the
1531 * whole command will be logged).
1532 * AUTHINFO SASL LOGIN does not use an initial
1533 * response;
1534 * therefore, there is nothing to hide here. */
1535 if (ac > 1 && strcasecmp(av[0], "AUTHINFO") == 0) {
1536 if (strncasecmp(av[1], "PASS", 4) == 0)
1537 syslog(L_TRACE, "%s < AUTHINFO PASS ********",
1538 Client.host);
1539 else if (strncasecmp(av[1], "SASL PLAIN", 10) == 0)
1540 syslog(L_TRACE,
1541 "%s < AUTHINFO SASL PLAIN ********",
1542 Client.host);
1543 else if (strncasecmp(av[1], "SASL EXTERNAL", 13)
1544 == 0)
1545 syslog(L_TRACE,
1546 "%s < AUTHINFO SASL EXTERNAL ********",
1547 Client.host);
1548 else
1549 syslog(L_TRACE, "%s < %s", Client.host, buff);
1550 } else {
1551 syslog(L_TRACE, "%s < %s", Client.host, buff);
1552 }
1553 }
1554 if (buff[0] == '\0')
1555 continue;
1556 break;
1557 }
1558 goto fallthroughRTlong;
1559 case RTlong:
1560 fallthroughRTlong:
1561 /* The line is too long but we have to make sure that
1562 * no recognized command has been sent. */
1563 q = (char *) p;
1564 ac = nArgify(q, &av, 1);
1565 validcommandtoolong = false;
1566 for (cp = CMDtable; cp->Name; cp++) {
1567 if ((cp->Function != CMD_unimp)
1568 && (ac > 0 && strcasecmp(cp->Name, av[0]) == 0)) {
1569 validcommandtoolong = true;
1570 break;
1571 }
1572 }
1573 Reply("%d Line too long\r\n", validcommandtoolong
1574 ? NNTP_ERR_SYNTAX
1575 : NNTP_ERR_COMMAND);
1576 continue;
1577 case RTeof:
1578 /* Handled below. */
1579 break;
1580 }
1581 }
1582 /* Client gone? */
1583 if (r == RTeof)
1584 break;
1585 if (ac == 0)
1586 continue;
1587
1588 /* Find command. */
1589 for (cp = CMDtable; cp->Name; cp++)
1590 if (strcasecmp(cp->Name, av[0]) == 0)
1591 break;
1592
1593 /* If no command has been recognized. */
1594 if (cp->Name == NULL) {
1595 if (strcasecmp(av[0], "XYZZY") == 0) {
1596 /* Acknowledge the magic word from the Colossal Cave Adventure
1597 * computer game. */
1598 Reply("%d Nothing happens\r\n", NNTP_ERR_COMMAND);
1599 } else {
1600 Reply("%d What?\r\n", NNTP_ERR_COMMAND);
1601 if ((int) strlen(buff) > 40)
1602 syslog(L_NOTICE, "%s unrecognized %.40s...", Client.host,
1603 buff);
1604 else
1605 syslog(L_NOTICE, "%s unrecognized %s", Client.host, buff);
1606 }
1607 continue;
1608 }
1609
1610 /* Go on parsing the command. */
1611 ac--;
1612 ac += reArgify(av[ac], &av[ac],
1613 cp->Stripspaces ? -1 : cp->Minac - ac - 1,
1614 cp->Stripspaces);
1615
1616 /* Check whether all arguments do not exceed their allowed size. */
1617 if (ac > 1) {
1618 validcommandtoolong = false;
1619 for (v = av; *v; v++)
1620 if (strlen(*v) > NNTP_MAXLEN_ARG) {
1621 validcommandtoolong = true;
1622 Reply("%d Argument too long\r\n", NNTP_ERR_SYNTAX);
1623 break;
1624 }
1625 if (validcommandtoolong)
1626 continue;
1627 }
1628
1629 /* Check usage. */
1630 if ((cp->Minac != CMDany && ac < cp->Minac)
1631 || (cp->Maxac != CMDany && ac > cp->Maxac)) {
1632 Reply("%d Syntax is: %s %s\r\n", NNTP_ERR_SYNTAX, cp->Name,
1633 cp->Help ? cp->Help : "(no argument allowed)");
1634 continue;
1635 }
1636
1637 /* Check permissions and dispatch. */
1638 if (cp->Needauth && PERMneedauth) {
1639 Reply("%d Authentication required for command\r\n",
1640 NNTP_FAIL_AUTH_NEEDED);
1641 continue;
1642 }
1643 setproctitle("%s %s", Client.host, av[0]);
1644
1645 (*cp->Function)(ac, av);
1646
1647 if (PushedBack)
1648 break;
1649 if (PERMaccessconf)
1650 clienttimeout = PERMaccessconf->clienttimeout;
1651 else
1652 clienttimeout = innconf->clienttimeout;
1653 }
1654
1655 ExitWithStats(0, false);
1656
1657 /* NOTREACHED */
1658 return 1;
1659 }
1660