1 | /* $Id: misc.c,v 1.52 2001/07/11 07:40:46 alexk Exp $ |
---|
2 | ** |
---|
3 | ** Miscellaneous support routines. |
---|
4 | */ |
---|
5 | |
---|
6 | #include "config.h" |
---|
7 | #include "clibrary.h" |
---|
8 | #include <netinet/in.h> |
---|
9 | |
---|
10 | /* Needed on AIX 4.1 to get fd_set and friends. */ |
---|
11 | #ifdef HAVE_SYS_SELECT_H |
---|
12 | # include <sys/select.h> |
---|
13 | #endif |
---|
14 | |
---|
15 | #include "nnrpd.h" |
---|
16 | |
---|
17 | #ifdef HAVE_SSL |
---|
18 | # include <openssl/ssl.h> |
---|
19 | # include <openssl/err.h> |
---|
20 | # include <openssl/bio.h> |
---|
21 | # include <openssl/pem.h> |
---|
22 | # include "tls.h" |
---|
23 | # include "sasl_config.h" |
---|
24 | #endif |
---|
25 | |
---|
26 | static bool setup = FALSE; |
---|
27 | static FILE *hfp = NULL; |
---|
28 | static ino_t ino = 0; |
---|
29 | |
---|
30 | #ifdef HAVE_SSL |
---|
31 | extern SSL *tls_conn; |
---|
32 | extern int nnrpd_starttls_done; |
---|
33 | #endif |
---|
34 | |
---|
35 | |
---|
36 | |
---|
37 | /* |
---|
38 | ** Parse a string into a NULL-terminated array of words; return number |
---|
39 | ** of words. If argvp isn't NULL, it and what it points to will be |
---|
40 | ** DISPOSE'd. |
---|
41 | */ |
---|
42 | int |
---|
43 | Argify(line, argvp) |
---|
44 | char *line; |
---|
45 | char ***argvp; |
---|
46 | { |
---|
47 | return nArgify(line,argvp,-1); |
---|
48 | } |
---|
49 | |
---|
50 | |
---|
51 | /* |
---|
52 | ** Parse a string into a NULL-terminated array of at most n words; |
---|
53 | ** return number of words. If there are more than n words, stop |
---|
54 | ** processing at the beginning of the (n+1)th word, store everything |
---|
55 | ** from the beginning of word n+1 in argv[n] and return n+1. If n is |
---|
56 | ** negative, parses all words. If argvp isn't NULL, it and what it |
---|
57 | ** points to will be DISPOSE'd. |
---|
58 | */ |
---|
59 | int |
---|
60 | nArgify(line, argvp, n) |
---|
61 | char *line; |
---|
62 | char ***argvp; |
---|
63 | int n; |
---|
64 | { |
---|
65 | register char *p; |
---|
66 | register int i; |
---|
67 | |
---|
68 | if (*argvp != NULL) { |
---|
69 | DISPOSE(*argvp[0]); |
---|
70 | DISPOSE(*argvp); |
---|
71 | } |
---|
72 | |
---|
73 | /* Copy the line, which we will split up. */ |
---|
74 | while (ISWHITE(*line)) |
---|
75 | line++; |
---|
76 | i = strlen(line); |
---|
77 | p = NEW(char, i + 1); |
---|
78 | (void)strcpy(p, line); |
---|
79 | |
---|
80 | *argvp = NEW(char*, i + 2); |
---|
81 | |
---|
82 | return reArgify(p, *argvp, n); |
---|
83 | } |
---|
84 | |
---|
85 | |
---|
86 | /* |
---|
87 | ** Destructively parse a string into a NULL-terminated array of at most |
---|
88 | ** n words; return number of words. Behavior on negative n and strings |
---|
89 | ** of more than n words matches that of nArgify (see above). Caller |
---|
90 | ** must supply an array of sufficient size (such as created by |
---|
91 | ** nArgify). |
---|
92 | ** |
---|
93 | ** Note that the sequence |
---|
94 | ** ac = nArgify(line, &argv, n1); |
---|
95 | ** ac--; |
---|
96 | ** ac += reArgify(argv[ac], &argv[ac], n2); |
---|
97 | ** is equivalent to |
---|
98 | ** ac = nArgify(line, &argv, n1 + n2); |
---|
99 | */ |
---|
100 | int |
---|
101 | reArgify(p, argv, n) |
---|
102 | register char *p; |
---|
103 | char **argv; |
---|
104 | int n; |
---|
105 | { |
---|
106 | char **save=argv; |
---|
107 | |
---|
108 | |
---|
109 | /* Should never happen unless caller modifies argv between calls */ |
---|
110 | while (ISWHITE(*p)) |
---|
111 | p++; |
---|
112 | |
---|
113 | for ( ; *p; ) { |
---|
114 | if (n == 0) { |
---|
115 | *argv++ = p; |
---|
116 | break; |
---|
117 | } |
---|
118 | /* Decrement limit, mark start of this word, find its end. */ |
---|
119 | for (n--, *argv++ = p; *p && !ISWHITE(*p); ) |
---|
120 | p++; |
---|
121 | if (*p == '\0') |
---|
122 | break; |
---|
123 | |
---|
124 | /* Nip off word, skip whitespace. */ |
---|
125 | for (*p++ = '\0'; ISWHITE(*p); ) |
---|
126 | p++; |
---|
127 | } |
---|
128 | *argv = NULL; |
---|
129 | return argv - save; |
---|
130 | } |
---|
131 | |
---|
132 | |
---|
133 | /* |
---|
134 | ** Take a vector which Argify made and glue it back together with |
---|
135 | ** spaces between each element. Returns a pointer to dynamic space. |
---|
136 | */ |
---|
137 | char * |
---|
138 | Glom(av) |
---|
139 | char **av; |
---|
140 | { |
---|
141 | register char **v; |
---|
142 | register char *p; |
---|
143 | register int i; |
---|
144 | char *save; |
---|
145 | |
---|
146 | /* Get space. */ |
---|
147 | for (i = 0, v = av; *v; v++) |
---|
148 | i += strlen(*v) + 1; |
---|
149 | |
---|
150 | for (save = p = NEW(char, i + 1), v = av; *v; v++) { |
---|
151 | if (p > save) |
---|
152 | *p++ = ' '; |
---|
153 | p += strlen(strcpy(p, *v)); |
---|
154 | } |
---|
155 | |
---|
156 | return save; |
---|
157 | } |
---|
158 | |
---|
159 | |
---|
160 | /* |
---|
161 | ** Match a list of newsgroup specifiers against a list of newsgroups. |
---|
162 | ** func is called to see if there is a match. |
---|
163 | */ |
---|
164 | bool PERMmatch(char **Pats, char **list) |
---|
165 | { |
---|
166 | int i; |
---|
167 | char *p; |
---|
168 | int match = FALSE; |
---|
169 | |
---|
170 | if (Pats == NULL || Pats[0] == NULL) |
---|
171 | return TRUE; |
---|
172 | |
---|
173 | for ( ; *list; list++) { |
---|
174 | for (i = 0; (p = Pats[i]) != NULL; i++) { |
---|
175 | if (p[0] == '!') { |
---|
176 | if (wildmat(*list, ++p)) |
---|
177 | match = FALSE; |
---|
178 | } |
---|
179 | else if (wildmat(*list, p)) |
---|
180 | match = TRUE; |
---|
181 | } |
---|
182 | if (match) |
---|
183 | /* If we can read it in one group, we can read it, period. */ |
---|
184 | return TRUE; |
---|
185 | } |
---|
186 | |
---|
187 | return FALSE; |
---|
188 | } |
---|
189 | |
---|
190 | |
---|
191 | /* |
---|
192 | ** Check to see if user is allowed to see this article by matching |
---|
193 | ** Newsgroups line. |
---|
194 | */ |
---|
195 | bool |
---|
196 | PERMartok() |
---|
197 | { |
---|
198 | static char **grplist; |
---|
199 | char *p, **grp; |
---|
200 | |
---|
201 | if (!PERMspecified) |
---|
202 | return FALSE; |
---|
203 | |
---|
204 | if ((p = GetHeader("Xref", FALSE)) == NULL) { |
---|
205 | /* in case article does not include Xref */ |
---|
206 | if ((p = GetHeader("Newsgroups", FALSE)) == NULL) |
---|
207 | if (!NGgetlist(&grplist, p)) |
---|
208 | /* No newgroups or null entry. */ |
---|
209 | return TRUE; |
---|
210 | } else { |
---|
211 | /* skip path element */ |
---|
212 | if ((p = strchr(p, ' ')) == NULL) |
---|
213 | return TRUE; |
---|
214 | for (p++ ; *p == ' ' ; p++); |
---|
215 | if (*p == '\0') |
---|
216 | return TRUE; |
---|
217 | if (!NGgetlist(&grplist, p)) |
---|
218 | /* No newgroups or null entry. */ |
---|
219 | return TRUE; |
---|
220 | /* chop ':' and article number */ |
---|
221 | for (grp = grplist ; *grp != NULL ; grp++) { |
---|
222 | if ((p = strchr(*grp, ':')) == NULL) |
---|
223 | return TRUE; |
---|
224 | *p = '\0'; |
---|
225 | } |
---|
226 | } |
---|
227 | |
---|
228 | #ifdef DO_PYTHON |
---|
229 | if (innconf->nnrppythonauth) { |
---|
230 | char *reply; |
---|
231 | |
---|
232 | /* Authorize user at a Python authorization module */ |
---|
233 | if (PY_authorize(ClientHost, ClientIp, ServerHost, PERMuser, p, FALSE, &reply) < 0) { |
---|
234 | syslog(L_NOTICE, "PY_authorize(): authorization skipped due to no Python authorization method defined."); |
---|
235 | } else { |
---|
236 | if (reply != NULL) { |
---|
237 | syslog(L_TRACE, "PY_authorize() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, ClientHost, p, reply); |
---|
238 | return TRUE; |
---|
239 | } |
---|
240 | } |
---|
241 | } |
---|
242 | #endif /* DO_PYTHON */ |
---|
243 | |
---|
244 | return PERMmatch(PERMreadlist, grplist); |
---|
245 | } |
---|
246 | |
---|
247 | |
---|
248 | /* |
---|
249 | ** Parse a newsgroups line, return TRUE if there were any. |
---|
250 | */ |
---|
251 | bool |
---|
252 | NGgetlist(argvp, list) |
---|
253 | char ***argvp; |
---|
254 | char *list; |
---|
255 | { |
---|
256 | register char *p; |
---|
257 | |
---|
258 | for (p = list; *p; p++) |
---|
259 | if (*p == ',') |
---|
260 | *p = ' '; |
---|
261 | |
---|
262 | return Argify(list, argvp) != 0; |
---|
263 | } |
---|
264 | |
---|
265 | |
---|
266 | /* |
---|
267 | ** Take an NNTP distribution list <d1,d2,...> and turn it into an array. |
---|
268 | */ |
---|
269 | bool |
---|
270 | ParseDistlist(argvp, list) |
---|
271 | char ***argvp; |
---|
272 | char *list; |
---|
273 | { |
---|
274 | static char **argv; |
---|
275 | register char *p; |
---|
276 | |
---|
277 | if (list[0] != '<' || (p = strchr(&list[1], '>')) == NULL) |
---|
278 | return FALSE; |
---|
279 | *p = '\0'; |
---|
280 | |
---|
281 | for (p = list + 1; *p; p++) |
---|
282 | if (*p == ',') |
---|
283 | *p = ' '; |
---|
284 | (void)Argify(list + 1, &argv); |
---|
285 | *argvp = argv; |
---|
286 | return TRUE; |
---|
287 | } |
---|
288 | |
---|
289 | |
---|
290 | /* |
---|
291 | ** Read a line of input, with timeout. |
---|
292 | ** |
---|
293 | ** 'size' is both the size of the input buffer and the max line |
---|
294 | ** size as defined by the RFCs. The max line size includes |
---|
295 | ** the trailing CR LF (which we strip off here), but not the '\0'. |
---|
296 | ** For NNTP commands, this limit is 512 (or 510 without the CR LF), |
---|
297 | ** and for POST data (article) lines it's 1000 (or 998 without the CR LF). |
---|
298 | */ |
---|
299 | READTYPE READline(char *start, int size, int timeout) |
---|
300 | { |
---|
301 | static int count; |
---|
302 | static char buffer[BUFSIZ]; |
---|
303 | static char *bp; |
---|
304 | register char *p; |
---|
305 | register char *end; |
---|
306 | struct timeval t, stv, etv; |
---|
307 | fd_set rmask; |
---|
308 | int i; |
---|
309 | char c; |
---|
310 | bool toolong; |
---|
311 | TIMEINFO Start, End; |
---|
312 | |
---|
313 | toolong = FALSE; |
---|
314 | |
---|
315 | for (p = start, end = &start[size - 1]; ; ) { |
---|
316 | if (count == 0) { |
---|
317 | /* Fill the buffer. */ |
---|
318 | Again: |
---|
319 | FD_ZERO(&rmask); |
---|
320 | FD_SET(STDIN_FILENO, &rmask); |
---|
321 | t.tv_sec = timeout; |
---|
322 | t.tv_usec = 0; |
---|
323 | gettimeofday(&stv, NULL); |
---|
324 | i = select(STDIN_FILENO + 1, &rmask, NULL, NULL, &t); |
---|
325 | gettimeofday(&etv, NULL); |
---|
326 | Start.time = stv.tv_sec; |
---|
327 | Start.usec = stv.tv_usec; |
---|
328 | End.time = etv.tv_sec; |
---|
329 | End.usec = etv.tv_usec; |
---|
330 | IDLEtime += TIMEINFOasDOUBLE(End) - TIMEINFOasDOUBLE(Start); |
---|
331 | if (i < 0) { |
---|
332 | if (errno == EINTR) |
---|
333 | goto Again; |
---|
334 | syslog(L_ERROR, "%s cant select %m", ClientHost); |
---|
335 | return RTtimeout; |
---|
336 | } |
---|
337 | if (i == 0 || !FD_ISSET(STDIN_FILENO, &rmask)) |
---|
338 | return RTtimeout; |
---|
339 | #ifdef HAVE_SSL |
---|
340 | if (tls_conn) |
---|
341 | count = SSL_read(tls_conn, buffer, sizeof buffer); |
---|
342 | else |
---|
343 | count = read(STDIN_FILENO, buffer, sizeof buffer); |
---|
344 | #else |
---|
345 | count = read(STDIN_FILENO, buffer, sizeof buffer); |
---|
346 | #endif |
---|
347 | if (count < 0) { |
---|
348 | syslog(L_TRACE, "%s cant read %m", ClientHost); |
---|
349 | return RTtimeout; |
---|
350 | } |
---|
351 | if (count == 0) { |
---|
352 | *p = '\0'; |
---|
353 | return RTeof; |
---|
354 | } |
---|
355 | bp = buffer; |
---|
356 | } |
---|
357 | |
---|
358 | /* Process next character. */ |
---|
359 | count--; |
---|
360 | c = *bp++; |
---|
361 | if (c == '\n') { |
---|
362 | /* If last two characters are \r\n, kill the \r as well as the \n. */ |
---|
363 | if (!toolong && p > start && p[-1] == '\r') |
---|
364 | p--; |
---|
365 | break; |
---|
366 | } |
---|
367 | if (p < end) |
---|
368 | *p++ = c; |
---|
369 | else |
---|
370 | toolong = TRUE; |
---|
371 | } |
---|
372 | |
---|
373 | *p = '\0'; |
---|
374 | return toolong ? RTlong : RTok; |
---|
375 | } |
---|
376 | |
---|
377 | /********************************************************************* |
---|
378 | * POSTING RATE LIMITS - The following code implements posting rate |
---|
379 | * limits. News clients are indexed by IP number (or PERMuser, see |
---|
380 | * config file). After a relatively configurable number of posts, the nnrpd |
---|
381 | * process will sleep for a period of time before posting anything. |
---|
382 | * |
---|
383 | * Each time that IP number posts a message, the time of |
---|
384 | * posting and the previous sleep time is stored. The new sleep time |
---|
385 | * is computed based on these values. |
---|
386 | * |
---|
387 | * To compute the new sleep time, the previous sleep time is, for most |
---|
388 | * cases multiplied by a factor (backoff_k). |
---|
389 | * |
---|
390 | * See inn.conf(5) for how this code works |
---|
391 | * |
---|
392 | *********************************************************************/ |
---|
393 | |
---|
394 | /* Defaults are pass through, i.e. not enabled |
---|
395 | * NEW for INN 1.8 - Use the inn.conf file to specify the following: |
---|
396 | * |
---|
397 | * backoff_k: <integer> |
---|
398 | * backoff_postfast: <integer> |
---|
399 | * backoff_postslow: <integer> |
---|
400 | * backoff_trigger: <integer> |
---|
401 | * backoff_db: <path> |
---|
402 | * backoff_auth: <on|off> |
---|
403 | * |
---|
404 | * You may also specify posting backoffs on a per user basis. To do this |
---|
405 | * turn on "backoff_auth" |
---|
406 | * |
---|
407 | * Now these are runtime constants. <grin> |
---|
408 | */ |
---|
409 | static char postrec_dir[SMBUF]; /* Where is the post record directory? */ |
---|
410 | |
---|
411 | void |
---|
412 | InitBackoffConstants() |
---|
413 | { |
---|
414 | struct stat st; |
---|
415 | |
---|
416 | /* Default is not to enable this code */ |
---|
417 | BACKOFFenabled = FALSE; |
---|
418 | |
---|
419 | /* Read the runtime config file to get parameters */ |
---|
420 | |
---|
421 | if ((PERMaccessconf->backoff_db == NULL) || |
---|
422 | !(PERMaccessconf->backoff_k >= 0L && PERMaccessconf->backoff_postfast >= 0L && PERMaccessconf->backoff_postslow >= 1L)) |
---|
423 | return; |
---|
424 | |
---|
425 | /* Need this database for backing off */ |
---|
426 | (void)strncpy(postrec_dir,PERMaccessconf->backoff_db,SMBUF); |
---|
427 | if (stat(postrec_dir, &st) < 0) { |
---|
428 | if (ENOENT == errno) { |
---|
429 | if (!MakeDirectory(postrec_dir, true)) { |
---|
430 | syslog(L_ERROR, "%s cannot create backoff_db '%s': %s",ClientHost,postrec_dir,strerror(errno)); |
---|
431 | return; |
---|
432 | } |
---|
433 | } else { |
---|
434 | syslog(L_ERROR, "%s cannot stat backoff_db '%s': %s",ClientHost,postrec_dir,strerror(errno)); |
---|
435 | return; |
---|
436 | } |
---|
437 | } |
---|
438 | if (!S_ISDIR(st.st_mode)) { |
---|
439 | syslog(L_ERROR, "%s backoff_db '%s' is not a directory",ClientHost,postrec_dir); |
---|
440 | return; |
---|
441 | } |
---|
442 | |
---|
443 | BACKOFFenabled = TRUE; |
---|
444 | |
---|
445 | return; |
---|
446 | } |
---|
447 | |
---|
448 | /* |
---|
449 | * PostRecs are stored in individual files. I didn't have a better |
---|
450 | * way offhand, don't want to touch DBZ, and the number of posters is |
---|
451 | * small compared to the number of readers. This is the filename corresponding |
---|
452 | * to an IP number. |
---|
453 | */ |
---|
454 | char |
---|
455 | *PostRecFilename(ip,user) |
---|
456 | unsigned long ip; |
---|
457 | char *user; |
---|
458 | { |
---|
459 | static char buff[SPOOLNAMEBUFF]; |
---|
460 | char dirbuff[SPOOLNAMEBUFF]; |
---|
461 | unsigned char addr[4]; |
---|
462 | unsigned int i; |
---|
463 | |
---|
464 | if (PERMaccessconf->backoff_auth) { |
---|
465 | sprintf(buff,"%s/%s",postrec_dir,user); |
---|
466 | return(buff); |
---|
467 | } |
---|
468 | |
---|
469 | for (i=0; i<4; i++) { |
---|
470 | addr[i] = (unsigned char) (0x000000ff & (ip>>(i*8))); |
---|
471 | } |
---|
472 | |
---|
473 | sprintf(dirbuff,"%s/%03d%03d/%03d",postrec_dir,addr[3],addr[2],addr[1]); |
---|
474 | if (!MakeDirectory(dirbuff,TRUE)) { |
---|
475 | syslog(L_ERROR,"%s Unable to create postrec directories '%s': %s", |
---|
476 | ClientHost,dirbuff,strerror(errno)); |
---|
477 | return NULL; |
---|
478 | } |
---|
479 | sprintf(buff,"%s/%03d",dirbuff,addr[0]); |
---|
480 | return(buff); |
---|
481 | } |
---|
482 | |
---|
483 | /* |
---|
484 | * Lock the post rec file. Return 1 on lock, 0 on error |
---|
485 | */ |
---|
486 | int |
---|
487 | LockPostRec(path) |
---|
488 | char *path; |
---|
489 | { |
---|
490 | char lockname[SPOOLNAMEBUFF]; |
---|
491 | char temp[SPOOLNAMEBUFF]; |
---|
492 | int statfailed = 0; |
---|
493 | |
---|
494 | sprintf(lockname, "%s.lock", path); |
---|
495 | |
---|
496 | for (;; sleep(5)) { |
---|
497 | int fd; |
---|
498 | struct stat st; |
---|
499 | time_t now; |
---|
500 | |
---|
501 | fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0600); |
---|
502 | if (fd >= 0) { |
---|
503 | /* We got the lock! */ |
---|
504 | sprintf(temp, "pid:%ld\n", (unsigned long) getpid()); |
---|
505 | write(fd, temp, strlen(temp)); |
---|
506 | close(fd); |
---|
507 | return(1); |
---|
508 | } |
---|
509 | |
---|
510 | /* No lock. See if the file is there. */ |
---|
511 | if (stat(lockname, &st) < 0) { |
---|
512 | syslog(L_ERROR, "%s cannot stat lock file %s", ClientHost, strerror(errno)); |
---|
513 | if (statfailed++ > 5) return(0); |
---|
514 | continue; |
---|
515 | } |
---|
516 | |
---|
517 | /* If lockfile is older than the value of PERMaccessconf->backoff_postslow, remove it |
---|
518 | */ |
---|
519 | statfailed = 0; |
---|
520 | time(&now); |
---|
521 | if (now < st.st_ctime + PERMaccessconf->backoff_postslow) continue; |
---|
522 | syslog(L_ERROR, "%s removing stale lock file %s", ClientHost, lockname); |
---|
523 | unlink(lockname); |
---|
524 | } |
---|
525 | } |
---|
526 | |
---|
527 | void |
---|
528 | UnlockPostRec(path) |
---|
529 | char *path; |
---|
530 | { |
---|
531 | char lockname[SPOOLNAMEBUFF]; |
---|
532 | |
---|
533 | sprintf(lockname, "%s.lock", path); |
---|
534 | if (unlink(lockname) < 0) { |
---|
535 | syslog(L_ERROR, "%s can't unlink lock file: %s", ClientHost,strerror(errno)) ; |
---|
536 | } |
---|
537 | return; |
---|
538 | } |
---|
539 | |
---|
540 | /* |
---|
541 | * Get the stored postrecord for that IP |
---|
542 | */ |
---|
543 | static int |
---|
544 | GetPostRecord(path, lastpost, lastsleep, lastn) |
---|
545 | char *path; |
---|
546 | long *lastpost; |
---|
547 | long *lastsleep; |
---|
548 | long *lastn; |
---|
549 | { |
---|
550 | static char buff[SMBUF]; |
---|
551 | FILE *fp; |
---|
552 | char *s; |
---|
553 | |
---|
554 | fp = fopen(path,"r"); |
---|
555 | if (fp == NULL) { |
---|
556 | if (errno == ENOENT) { |
---|
557 | return 1; |
---|
558 | } |
---|
559 | syslog(L_ERROR, "%s Error opening '%s': %s", |
---|
560 | ClientHost, path, strerror(errno)); |
---|
561 | return 0; |
---|
562 | } |
---|
563 | |
---|
564 | if (fgets(buff,SMBUF,fp) == NULL) { |
---|
565 | syslog(L_ERROR, "%s Error reading '%s': %s", |
---|
566 | ClientHost, path, strerror(errno)); |
---|
567 | return 0; |
---|
568 | } |
---|
569 | *lastpost = atol(buff); |
---|
570 | |
---|
571 | if ((s = strchr(buff,',')) == NULL) { |
---|
572 | syslog(L_ERROR, "%s bad data in postrec file: '%s'", |
---|
573 | ClientHost, buff); |
---|
574 | return 0; |
---|
575 | } |
---|
576 | s++; *lastsleep = atol(s); |
---|
577 | |
---|
578 | if ((s = strchr(s,',')) == NULL) { |
---|
579 | syslog(L_ERROR, "%s bad data in postrec file: '%s'", |
---|
580 | ClientHost, buff); |
---|
581 | return 0; |
---|
582 | } |
---|
583 | s++; *lastn = atol(s); |
---|
584 | |
---|
585 | (void)fclose(fp); |
---|
586 | return 1; |
---|
587 | } |
---|
588 | |
---|
589 | /* |
---|
590 | * Store the postrecord for that IP |
---|
591 | */ |
---|
592 | static int |
---|
593 | StorePostRecord(path, lastpost, lastsleep, lastn) |
---|
594 | char *path; |
---|
595 | time_t lastpost; |
---|
596 | long lastsleep; |
---|
597 | long lastn; |
---|
598 | { |
---|
599 | FILE *fp; |
---|
600 | |
---|
601 | fp = fopen(path,"w"); |
---|
602 | if (fp == NULL) { |
---|
603 | syslog(L_ERROR, "%s Error opening '%s': %s", |
---|
604 | ClientHost, path, strerror(errno)); |
---|
605 | return 0; |
---|
606 | } |
---|
607 | |
---|
608 | fprintf(fp,"%ld,%ld,%ld\n",lastpost,lastsleep,lastn); |
---|
609 | (void)fclose(fp); |
---|
610 | return 1; |
---|
611 | } |
---|
612 | |
---|
613 | /* |
---|
614 | * Return the proper sleeptime. Return false on error. |
---|
615 | */ |
---|
616 | int |
---|
617 | RateLimit(sleeptime,path) |
---|
618 | long *sleeptime; |
---|
619 | char *path; |
---|
620 | { |
---|
621 | TIMEINFO Now; |
---|
622 | long prevpost,prevsleep,prevn,n; |
---|
623 | |
---|
624 | if (GetTimeInfo(&Now) < 0) |
---|
625 | return 0; |
---|
626 | |
---|
627 | prevpost = 0L; prevsleep = 0L; prevn = 0L; |
---|
628 | if (!GetPostRecord(path,&prevpost,&prevsleep,&prevn)) { |
---|
629 | syslog(L_ERROR, "%s can't get post record: %s", |
---|
630 | ClientHost, strerror(errno)); |
---|
631 | return 0; |
---|
632 | } |
---|
633 | /* |
---|
634 | * Just because yer paranoid doesn't mean they ain't out ta get ya |
---|
635 | * This is called paranoid clipping |
---|
636 | */ |
---|
637 | if (prevn < 0L) prevn = 0L; |
---|
638 | if (prevsleep < 0L) prevsleep = 0L; |
---|
639 | if (prevsleep > PERMaccessconf->backoff_postfast) prevsleep = PERMaccessconf->backoff_postfast; |
---|
640 | |
---|
641 | /* |
---|
642 | * Compute the new sleep time |
---|
643 | */ |
---|
644 | *sleeptime = 0L; |
---|
645 | if (prevpost <= 0L) { |
---|
646 | prevpost = 0L; |
---|
647 | prevn = 1L; |
---|
648 | } else { |
---|
649 | n = Now.time - prevpost; |
---|
650 | if (n < 0L) { |
---|
651 | syslog(L_NOTICE,"%s previous post was in the future (%ld sec)", |
---|
652 | ClientHost,n); |
---|
653 | n = 0L; |
---|
654 | } |
---|
655 | if (n < PERMaccessconf->backoff_postfast) { |
---|
656 | if (prevn >= PERMaccessconf->backoff_trigger) { |
---|
657 | *sleeptime = 1 + (prevsleep * PERMaccessconf->backoff_k); |
---|
658 | } |
---|
659 | } else if (n < PERMaccessconf->backoff_postslow) { |
---|
660 | if (prevn >= PERMaccessconf->backoff_trigger) { |
---|
661 | *sleeptime = prevsleep; |
---|
662 | } |
---|
663 | } else { |
---|
664 | prevn = 0L; |
---|
665 | } |
---|
666 | prevn++; |
---|
667 | } |
---|
668 | |
---|
669 | *sleeptime = ((*sleeptime) > PERMaccessconf->backoff_postfast) ? PERMaccessconf->backoff_postfast : (*sleeptime); |
---|
670 | /* This ought to trap this bogon */ |
---|
671 | if ((*sleeptime) < 0L) { |
---|
672 | syslog(L_ERROR,"%s Negative sleeptime detected: %ld, prevsleep: %ld, N: %ld",ClientHost,*sleeptime,prevsleep,n); |
---|
673 | *sleeptime = 0L; |
---|
674 | } |
---|
675 | |
---|
676 | /* Store the postrecord */ |
---|
677 | if (!StorePostRecord(path,Now.time,*sleeptime,prevn)) { |
---|
678 | syslog(L_ERROR, "%s can't store post record: %s", ClientHost, strerror(errno)); |
---|
679 | return 0; |
---|
680 | } |
---|
681 | |
---|
682 | return 1; |
---|
683 | } |
---|
684 | |
---|
685 | #ifdef HAVE_SSL |
---|
686 | /* |
---|
687 | ** The "STARTTLS" command. RFC2595. |
---|
688 | */ |
---|
689 | /* ARGSUSED0 */ |
---|
690 | |
---|
691 | void |
---|
692 | CMDstarttls(ac, av) |
---|
693 | int ac; |
---|
694 | char *av[]; |
---|
695 | { |
---|
696 | SSL_CTX *ctx; |
---|
697 | int result; |
---|
698 | |
---|
699 | sasl_config_read(); |
---|
700 | |
---|
701 | if (nnrpd_starttls_done == 1) |
---|
702 | { |
---|
703 | Reply("%d Already successfully executed STARTTLS\r\n", NNTP_STARTTLS_DONE_VAL); |
---|
704 | return; |
---|
705 | } |
---|
706 | |
---|
707 | result=tls_init_serverengine(5, /* depth to verify */ |
---|
708 | 1, /* can client auth? */ |
---|
709 | 0, /* required client to auth? */ |
---|
710 | (char *)sasl_config_getstring("tls_ca_file", ""), |
---|
711 | (char *)sasl_config_getstring("tls_ca_path", ""), |
---|
712 | (char *)sasl_config_getstring("tls_cert_file", ""), |
---|
713 | (char *)sasl_config_getstring("tls_key_file", "")); |
---|
714 | |
---|
715 | if (result == -1) { |
---|
716 | Reply("%d Error initializing TLS\r\n", NNTP_STARTTLS_BAD_VAL); |
---|
717 | |
---|
718 | syslog(L_ERROR, "error initializing TLS: " |
---|
719 | "[CA_file: %s] [CA_path: %s] [cert_file: %s] [key_file: %s]", |
---|
720 | (char *) sasl_config_getstring("tls_ca_file", ""), |
---|
721 | (char *) sasl_config_getstring("tls_ca_path", ""), |
---|
722 | (char *) sasl_config_getstring("tls_cert_file", ""), |
---|
723 | (char *) sasl_config_getstring("tls_key_file", "")); |
---|
724 | return; |
---|
725 | } |
---|
726 | Reply("%d Begin TLS negotiation now\r\n", NNTP_STARTTLS_NEXT_VAL); |
---|
727 | (void)fflush(stdout); |
---|
728 | |
---|
729 | /* must flush our buffers before starting tls */ |
---|
730 | |
---|
731 | result=tls_start_servertls(0, /* read */ |
---|
732 | 1); /* write */ |
---|
733 | if (result==-1) { |
---|
734 | Reply("%d Starttls failed\r\n", NNTP_STARTTLS_BAD_VAL); |
---|
735 | return; |
---|
736 | } |
---|
737 | nnrpd_starttls_done = 1; |
---|
738 | } |
---|
739 | #endif /* HAVE_SSL */ |
---|