1 /*
2 ** Miscellaneous commands.
3 */
4
5 #include "portable/system.h"
6
7 #include <sys/wait.h>
8
9 #include "inn/fdflag.h"
10 #include "inn/innconf.h"
11 #include "inn/messages.h"
12 #include "inn/ov.h"
13 #include "inn/version.h"
14 #include "nnrpd.h"
15 #include "tls.h"
16
17
18 typedef struct {
19 char *name;
20 ARTNUM high;
21 ARTNUM low;
22 unsigned long count;
23 } GROUPDATA;
24
25
26 /*
27 ** Check after a successful authentication if the currently selected
28 ** newsgroup is still readable. AUTHINFO SASL and STARTTLS do not need
29 ** it because the NNTP protocol is reset after it.
30 **
31 ** Return true if the group must be made invalid.
32 */
33 static bool
makeGroupInvalid(void)34 makeGroupInvalid(void)
35 {
36 bool hookpresent = false;
37 char *grplist[2];
38
39 /* If no group has been selected yet, it is considered as valid. */
40 if (GRPcur == NULL) {
41 return false;
42 }
43
44 #ifdef DO_PYTHON
45 hookpresent = PY_use_dynamic;
46 if (hookpresent) {
47 char *reply;
48
49 /* Authorize user using Python module method dynamic. */
50 if (PY_dynamic(PERMuser, GRPcur, false, &reply) < 0) {
51 syslog(L_NOTICE, "PY_dynamic(): authorization skipped due to no "
52 "Python dynamic method defined");
53 } else {
54 if (reply != NULL) {
55 syslog(L_TRACE,
56 "PY_dynamic() returned a refuse string for user %s at "
57 "%s who wants to read %s: %s",
58 PERMuser, Client.host, GRPcur, reply);
59 free(reply);
60 return true;
61 }
62 }
63 }
64 #endif /* DO_PYTHON */
65
66 if (!hookpresent) {
67 if (PERMspecified) {
68 grplist[0] = GRPcur;
69 grplist[1] = NULL;
70 if (!PERMmatch(PERMreadlist, grplist)) {
71 return true;
72 }
73 } else {
74 return true;
75 }
76 }
77
78 if (!hookpresent && !PERMcanread) {
79 return true;
80 }
81
82 return false;
83 }
84
85
86 /* Returns:
87 ** -1 for problem (such as no such authenticator, etc.).
88 ** 1 for authentication succeeded.
89 ** 0 for authentication failed.
90 */
91 static char *PERMauthstring;
92
93 static int
PERMgeneric(char * av[],char * accesslist,size_t size)94 PERMgeneric(char *av[], char *accesslist, size_t size)
95 {
96 char path[BIG_BUFFER], *fields[6], *p;
97 size_t j;
98 int i, pan[2], status;
99 pid_t pid;
100 struct stat stb;
101
102 av += 2;
103
104 PERMcanread = false;
105 PERMcanpost = false;
106 PERMaccessconf->locpost = false;
107 PERMaccessconf->allowapproved = false;
108 PERMaccessconf->allowihave = false;
109 PERMaccessconf->allownewnews = false;
110
111 if (!*av) {
112 Reply("%d No authenticator provided\r\n", NNTP_ERR_SYNTAX);
113 return -1;
114 }
115
116 /* Check for ".." (not "../"). Path must not be changed! */
117 if (strstr(av[0], "..") != NULL) {
118 Reply("%d .. in authenticator %s\r\n", NNTP_ERR_SYNTAX, av[0]);
119 return -1;
120 }
121
122 /* 502 if authentication will fail. */
123 if (!PERMcanauthenticate) {
124 if (PERMauthorized && !PERMneedauth)
125 Reply("%d Already authenticated\r\n", NNTP_ERR_ACCESS);
126 else
127 Reply("%d Authentication will fail\r\n", NNTP_ERR_ACCESS);
128 return -1;
129 }
130
131 if (strchr(INN_PATH_AUTHDIR, '/') == NULL)
132 snprintf(path, sizeof(path), "%s/%s/%s/%s", innconf->pathbin,
133 INN_PATH_AUTHDIR, INN_PATH_AUTHDIR_GENERIC, av[0]);
134 else
135 snprintf(path, sizeof(path), "%s/%s/%s", INN_PATH_AUTHDIR,
136 INN_PATH_AUTHDIR_GENERIC, av[0]);
137
138 #if !defined(S_IXUSR) && defined(_S_IXUSR)
139 # define S_IXUSR _S_IXUSR
140 #endif /* !defined(S_IXUSR) && defined(_S_IXUSR) */
141
142 #if !defined(S_IXUSR) && defined(S_IEXEC)
143 # define S_IXUSR S_IEXEC
144 #endif /* !defined(S_IXUSR) && defined(S_IEXEC) */
145
146 if (stat(path, &stb) || !(stb.st_mode & S_IXUSR)) {
147 Reply("%d No such authenticator %s\r\n", NNTP_ERR_UNAVAILABLE, av[0]);
148 return -1;
149 }
150
151 /* Create a pipe. */
152 if (pipe(pan) < 0) {
153 Reply("%d Can't pipe %s\r\n", NNTP_FAIL_ACTION, strerror(errno));
154 syslog(L_FATAL, "can't pipe for %s %m", av[0]);
155 return -1;
156 }
157
158 for (i = 0; (pid = fork()) < 0; i++) {
159 if (i == (long) innconf->maxforks) {
160 Reply("%d Can't fork %s\r\n", NNTP_FAIL_ACTION, strerror(errno));
161 syslog(L_FATAL, "can't fork %s %m", av[0]);
162 return -1;
163 }
164 syslog(L_NOTICE, "can't fork %s -- waiting", av[0]);
165 sleep(5);
166 }
167
168 /* Run the child, with redirection. */
169 if (pid == 0) {
170 close(STDERR_FILENO); /* Close existing stderr. */
171 close(pan[PIPE_READ]);
172
173 /* stderr goes down the pipe. */
174 if (pan[PIPE_WRITE] != STDERR_FILENO) {
175 if ((i = dup2(pan[PIPE_WRITE], STDERR_FILENO)) != STDERR_FILENO) {
176 syslog(L_FATAL, "can't dup2 %d to %d got %d %m",
177 pan[PIPE_WRITE], STDERR_FILENO, i);
178 _exit(1);
179 }
180 close(pan[PIPE_WRITE]);
181 }
182
183 fdflag_close_exec(STDIN_FILENO, false);
184 fdflag_close_exec(STDOUT_FILENO, false);
185 fdflag_close_exec(STDERR_FILENO, false);
186
187 execv(path, av);
188 /* RFC 2980 requires 500 if there are unspecified errors during
189 * the execution of the provided program. */
190 Reply("%d Program error occurred\r\n", NNTP_ERR_COMMAND);
191
192 syslog(L_FATAL, "can't execv %s %m", path);
193 _exit(1);
194 }
195
196 close(pan[PIPE_WRITE]);
197 if (read(pan[PIPE_READ], path, sizeof(path)) < 0) {
198 syslog(L_FATAL, "can't read %s %m", path);
199 return 0;
200 }
201
202 waitpid(pid, &status, 0);
203 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
204 return 0;
205
206 if ((p = strchr(path, '\n')) != NULL)
207 *p = '\0';
208
209 if (PERMauthstring)
210 free(PERMauthstring);
211
212 PERMauthstring = xstrdup(path);
213
214 // syslog(L_NOTICE, "%s (%ld) returned: %d %s %d\n", av[0], (long) pid, i,
215 // path, status);
216 /* Split "host:permissions:user:pass:groups" into fields. */
217 for (fields[0] = path, j = 0, p = path; *p; p++)
218 if (*p == ':') {
219 *p = '\0';
220 ++j;
221 if (j < ARRAY_SIZE(fields)) {
222 fields[j] = p + 1;
223 } else {
224 Reply("%d Program error occurred\r\n", NNTP_FAIL_ACTION);
225 syslog(L_FATAL, "over-long response from %s", av[0]);
226 return -1;
227 }
228 }
229
230 if (j < 4) {
231 Reply("%d Program error occurred\r\n", NNTP_FAIL_ACTION);
232 syslog(L_FATAL, "short response from %s", av[0]);
233 return -1;
234 }
235
236 PERMcanread = strchr(fields[1], 'R') != NULL;
237 PERMcanpost = strchr(fields[1], 'P') != NULL;
238 PERMaccessconf->allowapproved = strchr(fields[1], 'A') != NULL;
239 PERMaccessconf->locpost = strchr(fields[1], 'L') != NULL;
240 PERMaccessconf->allowihave = strchr(fields[1], 'I') != NULL;
241 PERMaccessconf->allownewnews = strchr(fields[1], 'N') != NULL;
242 strlcpy(PERMuser, fields[2], sizeof(PERMuser));
243 strlcat(PERMuser, "@", sizeof(PERMuser));
244 strlcat(PERMuser, fields[0], sizeof(PERMuser));
245 // strlcpy(PERMpass, fields[3], sizeof(PERMpass));
246 strlcpy(accesslist, fields[4], size);
247
248 // for (i = 0; fields[i] && i < 5; i++)
249 // syslog(L_NOTICE, "fields[%d] = %s\n", i, fields[i]);
250
251 return 1;
252 }
253
254
255 /*
256 ** The AUTHINFO command.
257 */
258 void
CMDauthinfo(int ac,char * av[])259 CMDauthinfo(int ac, char *av[])
260 {
261 static char User[SMBUF];
262 static char Password[SMBUF];
263 /* XXX BIG_BUFFER, if changed, should also be changed in perl.c and
264 * python.c. */
265 char accesslist[BIG_BUFFER];
266 char errorstr[BIG_BUFFER];
267 int code;
268
269 #if defined(HAVE_ZLIB)
270 /* If a compression layer is active, AUTHINFO is not possible. */
271 if (compression_layer_on && !tls_compression_on) {
272 Reply("%d Already using a compression layer\r\n", NNTP_ERR_ACCESS);
273 return;
274 }
275 #endif
276
277 if (strcasecmp(av[1], "GENERIC") == 0) {
278 char *logrec = Glom(av);
279
280 /* Go on parsing the command line. */
281 ac--;
282 (void) reArgify(av[ac], &av[ac], -1, true);
283
284 strlcpy(PERMuser, "<none>", sizeof(PERMuser));
285
286 /* Arguments are checked by PERMgeneric(). */
287 switch (PERMgeneric(av, accesslist, sizeof(accesslist))) {
288 case 1:
289 PERMspecified = NGgetlist(&PERMreadlist, accesslist);
290 PERMpostlist = PERMreadlist;
291 syslog(L_NOTICE, "%s auth %s (%s -> %s)", Client.host, PERMuser,
292 logrec, PERMauthstring ? PERMauthstring : "");
293 Reply("%d Authentication succeeded\r\n", NNTP_OK_AUTHINFO);
294 PERMneedauth = false;
295 PERMauthorized = true;
296 PERMcanauthenticate = false;
297 PERMgroupmadeinvalid = makeGroupInvalid();
298 free(logrec);
299 return;
300 case 0:
301 syslog(L_NOTICE, "%s bad_auth %s (%s)", Client.host, PERMuser,
302 logrec);
303 /* We keep the right 481 code here instead of the wrong 502
304 * answer suggested in RFC 2080. */
305 Reply("%d Authentication failed\r\n", NNTP_FAIL_AUTHINFO_BAD);
306 free(logrec);
307 return;
308 default:
309 /* Lower level (-1) has already issued a reply. */
310 return;
311 }
312 } else if (strcasecmp(av[1], "SASL") == 0) {
313 #ifdef HAVE_SASL
314 /* Go on parsing the command line. */
315 ac--;
316 ac += reArgify(av[ac], &av[ac], -1, true);
317
318 /* Arguments are checked by SASLauth(). */
319 SASLauth(ac, av);
320 #else
321 Reply("%d SASL authentication unsupported\r\n", NNTP_ERR_SYNTAX);
322 return;
323 #endif /* HAVE_SASL */
324 } else {
325 /* Each time AUTHINFO USER is used, the new username is cached. */
326 if (strcasecmp(av[1], "USER") == 0) {
327 /* 502 if authentication will fail. */
328 if (!PERMcanauthenticate) {
329 if (PERMauthorized && !PERMneedauth)
330 Reply("%d Already authenticated\r\n", NNTP_ERR_ACCESS);
331 else
332 Reply("%d Authentication will fail\r\n", NNTP_ERR_ACCESS);
333 return;
334 }
335
336 #ifdef HAVE_OPENSSL
337 /* Check whether STARTTLS must be used before trying to
338 * authenticate. */
339 if (PERMcanauthenticate && !PERMcanauthenticatewithoutSSL
340 && !encryption_layer_on) {
341 Reply("%d Encryption required\r\n", NNTP_FAIL_PRIVACY_NEEDED);
342 return;
343 }
344 #endif
345
346 strlcpy(User, av[2], sizeof(User));
347 Reply("%d Enter password\r\n", NNTP_CONT_AUTHINFO);
348 return;
349 }
350
351 /* If it is not AUTHINFO PASS, we do not support the provided
352 * subcommand. */
353 if (strcasecmp(av[1], "PASS") != 0) {
354 Reply("%d Bad AUTHINFO param\r\n", NNTP_ERR_SYNTAX);
355 return;
356 }
357
358 /* 502 if authentication will fail. */
359 if (!PERMcanauthenticate) {
360 if (PERMauthorized && !PERMneedauth)
361 Reply("%d Already authenticated\r\n", NNTP_ERR_ACCESS);
362 else
363 Reply("%d Authentication will fail\r\n", NNTP_ERR_ACCESS);
364 return;
365 }
366
367 #ifdef HAVE_OPENSSL
368 /* Check whether STARTTLS must be used before trying to authenticate.
369 */
370 if (PERMcanauthenticate && !PERMcanauthenticatewithoutSSL
371 && !encryption_layer_on) {
372 Reply("%d Encryption required\r\n", NNTP_FAIL_PRIVACY_NEEDED);
373 return;
374 }
375 #endif
376
377 /* AUTHINFO PASS cannot be sent before AUTHINFO USER. */
378 if (User[0] == '\0') {
379 Reply("%d Authentication commands issued out of sequence\r\n",
380 NNTP_FAIL_AUTHINFO_REJECT);
381 return;
382 }
383
384 /* There is a cached username and a password is provided. */
385 strlcpy(Password, av[2], sizeof(Password));
386
387 errorstr[0] = '\0';
388 code = NNTP_FAIL_AUTHINFO_BAD;
389
390 PERMlogin(User, Password, &code, errorstr);
391 PERMgetpermissions();
392
393 /* If authentication is successful. */
394 if (!PERMneedauth) {
395 syslog(L_NOTICE, "%s user %s", Client.host, PERMuser);
396 if (LLOGenable) {
397 fprintf(locallog, "%s user (%s):%s\n", Client.host, Username,
398 PERMuser);
399 fflush(locallog);
400 }
401 Reply("%d Authentication succeeded\r\n", NNTP_OK_AUTHINFO);
402 PERMneedauth = false;
403 PERMauthorized = true;
404 PERMcanauthenticate = false;
405 PERMgroupmadeinvalid = makeGroupInvalid();
406 return;
407 }
408
409 /* For backwards compatibility, we return 481 instead of 502 (which had
410 * the same meaning as 481 in RFC 2980). */
411 if (code == NNTP_ERR_ACCESS) {
412 code = NNTP_FAIL_AUTHINFO_BAD;
413 }
414
415 syslog(L_NOTICE, "%s bad_auth", Client.host);
416 /* Return 403 in case the return code is not 481. */
417 if (errorstr[0] != '\0') {
418 syslog(L_NOTICE, "%s script error str: %s", Client.host, errorstr);
419 Reply("%d %s\r\n",
420 code != NNTP_FAIL_AUTHINFO_BAD ? NNTP_FAIL_ACTION : code,
421 errorstr);
422 } else {
423 Reply("%d Authentication failed\r\n",
424 code != NNTP_FAIL_AUTHINFO_BAD ? NNTP_FAIL_ACTION : code);
425 }
426 }
427 }
428
429
430 /*
431 ** The DATE command. Useful mainly in conjunction with NEWNEWS.
432 */
433 void
CMDdate(int ac UNUSED,char * av[]UNUSED)434 CMDdate(int ac UNUSED, char *av[] UNUSED)
435 {
436 time_t now;
437 struct tm *gmt;
438
439 now = time(NULL);
440 gmt = gmtime(&now);
441 if (now == (time_t) -1 || gmt == NULL) {
442 Reply("%d Can't get time, %s\r\n", NNTP_FAIL_ACTION, strerror(errno));
443 return;
444 }
445 Reply("%d %04d%02d%02d%02d%02d%02d\r\n", NNTP_INFO_DATE,
446 gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour,
447 gmt->tm_min, gmt->tm_sec);
448 }
449
450
451 /*
452 ** Handle the MODE command.
453 ** Note that MODE STREAM must return 501 as an unknown MODE variant
454 ** because nnrpd does not implement STREAMING.
455 */
456 void
CMDmode(int ac UNUSED,char * av[])457 CMDmode(int ac UNUSED, char *av[])
458 {
459 if (strcasecmp(av[1], "READER") == 0)
460 /* In the case AUTHINFO has already been successfully used,
461 * nnrpd must answer as a no-op (it still advertises the READER
462 * capability but not MODE-READER). */
463 Reply("%d %s InterNetNews NNRP server %s ready (%s)\r\n",
464 (PERMcanpost || (PERMcanauthenticate && PERMcanpostgreeting))
465 ? NNTP_OK_BANNER_POST
466 : NNTP_OK_BANNER_NOPOST,
467 PERMaccessconf->pathhost, INN_VERSION_STRING,
468 (!PERMneedauth && PERMcanpost) ? "posting ok" : "no posting");
469 else
470 Reply("%d Unknown MODE variant\r\n", NNTP_ERR_SYNTAX);
471 }
472
473
474 static int
GroupCompare(const void * a1,const void * b1)475 GroupCompare(const void *a1, const void *b1)
476 {
477 const GROUPDATA *a = a1;
478 const GROUPDATA *b = b1;
479
480 return strcmp(a->name, b->name);
481 }
482
483
484 /*
485 ** Display new newsgroups since a given date and time.
486 */
487 void
CMDnewgroups(int ac,char * av[])488 CMDnewgroups(int ac, char *av[])
489 {
490 char *p;
491 char *q;
492 QIOSTATE *qp;
493 time_t date;
494 char *grplist[2];
495 int hi, lo, count, flag;
496 GROUPDATA *grouplist = NULL;
497 GROUPDATA key;
498 GROUPDATA *gd;
499 int listsize = 0;
500 int numgroups = 0;
501 int numfound = 0;
502 int i;
503 bool local = true;
504
505 /* Check the arguments and parse the date. */
506 if (ac > 3) {
507 if (strcasecmp(av[3], "GMT") == 0)
508 local = false;
509 else {
510 Reply("%d Syntax error for \"GMT\"\r\n", NNTP_ERR_SYNTAX);
511 return;
512 }
513 }
514 date = parsedate_nntp(av[1], av[2], local);
515 if (date == (time_t) -1) {
516 Reply("%d Bad date\r\n", NNTP_ERR_SYNTAX);
517 return;
518 }
519
520 /* Log an error if active.times doesn't exist, but don't return an error
521 to the client. The most likely cause of this is a new server
522 installation that's yet to have any new groups created, and returning
523 an error was causing needless confusion. Just return the empty list
524 of groups. */
525 if ((qp = QIOopen(ACTIVETIMES)) == NULL) {
526 syslog(L_ERROR, "%s can't fopen %s %m", Client.host, ACTIVETIMES);
527 Reply("%d No new newsgroups\r\n", NNTP_OK_NEWGROUPS);
528 Printf(".\r\n");
529 return;
530 }
531
532 /* Read the file, ignoring long lines. */
533 while ((p = QIOread(qp)) != NULL) {
534 if ((q = strchr(p, ' ')) == NULL)
535 continue;
536 *q++ = '\0';
537 if ((time_t) atol(q) < date)
538 continue;
539 if (!OVgroupstats(p, &lo, &hi, &count, &flag))
540 continue;
541
542 if (PERMspecified) {
543 grplist[0] = p;
544 grplist[1] = NULL;
545 if (!PERMmatch(PERMreadlist, grplist))
546 continue;
547 } else
548 continue;
549
550 if (grouplist == NULL) {
551 grouplist = xmalloc(1000 * sizeof(GROUPDATA));
552 listsize = 1000;
553 }
554 if (listsize <= numgroups) {
555 listsize += 1000;
556 grouplist = xrealloc(grouplist, listsize * sizeof(GROUPDATA));
557 }
558
559 grouplist[numgroups].high = hi;
560 grouplist[numgroups].low = lo;
561 grouplist[numgroups].count = count;
562 grouplist[numgroups].name = xstrdup(p);
563 numgroups++;
564 }
565 QIOclose(qp);
566
567 if ((qp = QIOopen(ACTIVE)) == NULL) {
568 syslog(L_ERROR, "%s can't fopen %s %m", Client.host, ACTIVE);
569 Reply("%d Can't open active file\r\n", NNTP_FAIL_ACTION);
570
571 for (i = 0; i < numgroups; i++) {
572 free(grouplist[i].name);
573 }
574 free(grouplist);
575
576 return;
577 }
578 qsort(grouplist, numgroups, sizeof(GROUPDATA), GroupCompare);
579 Reply("%d New newsgroups follow\r\n", NNTP_OK_NEWGROUPS);
580 for (numfound = numgroups; (p = QIOread(qp)) && numfound;) {
581 /* p will contain the name of the newsgroup.
582 * When the syntax of the line is not correct, we continue
583 * with the following line. */
584 if ((q = strchr(p, ' ')) == NULL)
585 continue;
586 *q++ = '\0';
587 /* Find out the end of the high water mark. */
588 if ((q = strchr(q, ' ')) == NULL)
589 continue;
590 q++;
591 /* Find out the end of the low water mark.
592 * q will contain the flag of the newsgroup. */
593 if ((q = strchr(q, ' ')) == NULL)
594 continue;
595 q++;
596 key.name = p;
597 if ((gd = bsearch(&key, grouplist, numgroups, sizeof(GROUPDATA),
598 GroupCompare))
599 == NULL)
600 continue;
601 Printf("%s %lu %lu %s\r\n", p, gd->high, gd->low, q);
602 numfound--;
603 }
604 for (i = 0; i < numgroups; i++) {
605 free(grouplist[i].name);
606 }
607 free(grouplist);
608 QIOclose(qp);
609 Printf(".\r\n");
610 }
611
612
613 /*
614 ** Handle the POST and IHAVE commands.
615 */
616 void
CMDpost(int ac,char * av[])617 CMDpost(int ac, char *av[])
618 {
619 static char *article;
620 static int size;
621 char *p, *q;
622 char *end;
623 int longline;
624 READTYPE r;
625 int i;
626 long l;
627 long sleeptime;
628 char *path;
629 const char *response;
630 char idbuff[SMBUF];
631 static int backoff_inited = false;
632 bool ihave, permanent;
633
634 ihave = (strcasecmp(av[0], "IHAVE") == 0);
635
636 /* Check whether the Message-ID is valid for IHAVE. */
637 if (ihave && ac == 2 && !IsValidMessageID(av[1], true, laxmid)) {
638 Reply("%d Syntax error in Message-ID\r\n", NNTP_ERR_SYNTAX);
639 return;
640 }
641
642 /* Check authorizations. */
643 if (ihave && (!PERMaccessconf->allowihave || !PERMcanpost)) {
644 syslog(L_NOTICE, "%s noperm ihave without permission", Client.host);
645 Reply("%d IHAVE command disabled by administrator\r\n",
646 PERMcanauthenticate ? NNTP_FAIL_AUTH_NEEDED : NNTP_ERR_ACCESS);
647 return;
648 }
649 if (!ihave && !PERMcanpost) {
650 syslog(L_NOTICE, "%s noperm post without permission", Client.host);
651 Reply("%d Posting not allowed\r\n",
652 PERMcanauthenticate && PERMcanpostgreeting
653 ? NNTP_FAIL_AUTH_NEEDED
654 : NNTP_FAIL_POST_AUTH);
655 return;
656 }
657
658 if (!backoff_inited) {
659 /* Exponential posting backoff. */
660 InitBackoffConstants();
661 backoff_inited = true;
662 }
663
664 /* Dave's posting limiter. Limit postings to a certain rate.
665 * And now we support multiprocess rate limits. Questions?
666 * E-mail <dave@jetcafe.org>. */
667 if (BACKOFFenabled) {
668 /* Acquire lock (this could be in RateLimit but that would
669 * invoke the spaghetti factor). */
670 if ((path = (char *) PostRecFilename(Client.ip, PERMuser)) == NULL) {
671 Reply("%d Retry later\r\n",
672 ihave ? NNTP_FAIL_IHAVE_DEFER : NNTP_FAIL_POST_AUTH);
673 return;
674 }
675
676 if (LockPostRec(path) == 0) {
677 syslog(L_ERROR, "%s error write locking '%s'", Client.host, path);
678 Reply("%d Retry later\r\n",
679 ihave ? NNTP_FAIL_IHAVE_DEFER : NNTP_FAIL_POST_AUTH);
680 return;
681 }
682
683 if (!RateLimit(&sleeptime, path)) {
684 syslog(L_ERROR, "%s can't check rate limit info", Client.host);
685 Reply("%d Retry later\r\n",
686 ihave ? NNTP_FAIL_IHAVE_DEFER : NNTP_FAIL_POST_AUTH);
687 UnlockPostRec(path);
688 return;
689 } else if (sleeptime != 0L) {
690 syslog(L_NOTICE, "%s post sleep time is now %ld", Client.host,
691 sleeptime);
692 sleep(sleeptime);
693 }
694
695 /* Remove the lock here so that only one nnrpd process does the
696 * backoff sleep at once. Other procs are sleeping for the lock. */
697 UnlockPostRec(path);
698 } /* End backoff code. */
699
700 /* Start at beginning of buffer. */
701 if (article == NULL) {
702 size = 4096;
703 article = xmalloc(size);
704 }
705 idbuff[0] = 0;
706 if (ihave) {
707 /* Check whether it is a duplicate. */
708 if (History == NULL) {
709 time_t statinterval = 30;
710 History = HISopen(HISTORY, innconf->hismethod, HIS_RDONLY);
711 if (!History) {
712 syslog(L_NOTICE, "can't initialize history");
713 Reply("%d NNTP server unavailable; try later\r\n",
714 NNTP_FAIL_TERMINATING);
715 ExitWithStats(1, true);
716 }
717 HISctl(History, HISCTLS_STATINTERVAL, &statinterval);
718 }
719 if (HIScheck(History, av[1])) {
720 Reply("%d Duplicate\r\n", NNTP_FAIL_IHAVE_REFUSE);
721 return;
722 } else {
723 Reply("%d Send it; end with <CR-LF>.<CR-LF>\r\n", NNTP_CONT_IHAVE);
724 }
725 } else {
726 if ((p = GenerateMessageID(PERMaccessconf->domain)) != NULL) {
727 if (VirtualPathlen > 0) {
728 q = p;
729 if ((p = strchr(p, '@')) != NULL) {
730 *p = '\0';
731 snprintf(idbuff, sizeof(idbuff), "%s%s@%s>", q,
732 NNRPinstance, PERMaccessconf->domain);
733 }
734 } else {
735 strlcpy(idbuff, p, sizeof(idbuff));
736 }
737 }
738 Reply("%d Ok, recommended Message-ID %s\r\n", NNTP_CONT_POST, idbuff);
739 }
740 fflush(stdout);
741
742 p = article;
743 end = &article[size];
744
745 longline = 0;
746 for (l = 1;; l++) {
747 size_t len;
748 const char *line;
749
750 r = line_read(&NNTPline, PERMaccessconf->clienttimeout, &line, &len,
751 NULL);
752 switch (r) {
753 default:
754 warn("%s internal %d in post", Client.host, r);
755 goto fallthrough;
756 case RTtimeout:
757 fallthrough:
758 warn("%s timeout in post", Client.host);
759 ExitWithStats(1, false);
760 /* NOTREACHED */
761 case RTeof:
762 warn("%s EOF in post", Client.host);
763 ExitWithStats(1, false);
764 /* NOTREACHED */
765 case RTlong:
766 if (longline == 0)
767 longline = l;
768 continue;
769 case RTok:
770 break;
771 }
772
773 /* If it is the terminator, break out. */
774 if (strcmp(line, ".") == 0) {
775 break;
776 }
777
778 /* If they broke our line length limit, there's little point
779 * in processing any more of their input. */
780 if (longline != 0) {
781 continue;
782 }
783
784 /* +2 because of the \n\0 we append; note we don't add the 2
785 * when increasing the size of the buffer as ART_LINE_MALLOC
786 * will always be larger than 2 bytes. */
787 if ((len + 2) > (size_t)(end - p)) {
788 i = p - article;
789 size += len + 4096;
790 article = xrealloc(article, size);
791 end = &article[size];
792 p = i + article;
793 }
794
795 /* Reverse any byte-stuffing. */
796 if (*line == '.') {
797 ++line;
798 --len;
799 }
800 memcpy(p, line, len);
801 p += len;
802 *p++ = '\n';
803 *p = '\0';
804 }
805
806 if (longline) {
807 warn("%s too long in post", Client.host);
808 Reply("%d Line %d too long\r\n",
809 ihave ? NNTP_FAIL_IHAVE_REJECT : NNTP_FAIL_POST_REJECT,
810 longline);
811 POSTrejected++;
812 return;
813 }
814
815 /* Send the article to the server. */
816 response = ARTpost(article, idbuff, &permanent);
817 if (response == NULL) {
818 notice("%s %s ok %s", Client.host, ihave ? "ihave" : "post", idbuff);
819 Reply("%d Article received %s\r\n",
820 ihave ? NNTP_OK_IHAVE : NNTP_OK_POST, idbuff);
821 POSTreceived++;
822 } else {
823 if ((p = strchr(response, '\r')) != NULL)
824 *p = '\0';
825 if ((p = strchr(response, '\n')) != NULL)
826 *p = '\0';
827 notice("%s %s failed %s", Client.host, ihave ? "ihave" : "post",
828 response);
829 if (!ihave || permanent) {
830 /* For permanent errors, reject the message. */
831 Reply("%d %s\r\n",
832 ihave ? NNTP_FAIL_IHAVE_REJECT : NNTP_FAIL_POST_REJECT,
833 response);
834 } else {
835 /* Non-permanent errors only have relevance to IHAVE, for
836 * these we have the error status from the upstream
837 * server to report. It includes the answer code. */
838 Reply("%s\r\n", response);
839 }
840 POSTrejected++;
841 }
842 }
843