chiark / gitweb /
Commit 2.4.5-5 as unpacked
[innduct.git] / nnrpd / perm.c
1 /*  $Id: perm.c 7426 2005-12-11 20:37:27Z eagle $
2 **
3 **  How to figure out where a user comes from, and what that user can do once
4 **  we know who sie is.
5 */
6
7 #include "config.h"
8 #include "clibrary.h"
9 #include "portable/wait.h"
10 #include <netdb.h>
11 #include <signal.h>
12
13 #include "conffile.h"
14 #include "inn/innconf.h"
15 #include "innperl.h"
16 #include "nnrpd.h"
17
18 /* Needed on AIX 4.1 to get fd_set and friends. */
19 #ifdef HAVE_SYS_SELECT_H
20 # include <sys/select.h>
21 #endif
22
23 /* data types */
24 typedef struct _CONFCHAIN {
25     CONFFILE *f;
26     struct _CONFCHAIN *parent;
27 } CONFCHAIN;
28
29 typedef struct _METHOD {
30     char *name;
31     char *program;
32     int  type;          /* type of auth (perl, python or external) */
33     char *users;        /* only used for auth_methods, not for res_methods. */
34     char **extra_headers;
35     char **extra_logs;
36 } METHOD;
37
38 typedef struct _AUTHGROUP {
39     char *name;
40     char *key;
41 #ifdef HAVE_SSL
42     int require_ssl;
43 #endif
44     char *hosts;
45     METHOD **res_methods;
46     METHOD **auth_methods;
47     char *default_user;
48     char *default_domain;
49     char *localaddress;
50     char *access_script;
51     int  access_type; /* type of access (perl or python) */
52     char *dynamic_script;
53     int  dynamic_type; /* type of dynamic authorization (python only) */
54 } AUTHGROUP;
55
56 typedef struct _GROUP {
57     char *name;
58     struct _GROUP *above;
59     AUTHGROUP *auth;
60     ACCESSGROUP *access;
61 } GROUP;
62
63 /* function declarations */
64 static void PERMreadfile(char *filename);
65 static void authdecl_parse(AUTHGROUP*, CONFFILE*, CONFTOKEN*);
66 static void accessdecl_parse(ACCESSGROUP *curaccess, CONFFILE *f, CONFTOKEN *tok);
67 static void method_parse(METHOD*, CONFFILE*, CONFTOKEN*, int);
68
69 static void add_authgroup(AUTHGROUP*);
70 static void add_accessgroup(ACCESSGROUP*);
71 static void strip_accessgroups(void);
72
73 static METHOD *copy_method(METHOD*);
74 static void free_method(METHOD*);
75 static AUTHGROUP *copy_authgroup(AUTHGROUP*);
76 static void free_authgroup(AUTHGROUP*);
77 static ACCESSGROUP *copy_accessgroup(ACCESSGROUP*);
78 static void free_accessgroup(ACCESSGROUP*);
79
80 static void CompressList(char*);
81 static bool MatchHost(char*, char*, char*);
82 static int MatchUser(char*, char*);
83 static char *ResolveUser(AUTHGROUP*);
84 static char *AuthenticateUser(AUTHGROUP*, char*, char*, char*);
85
86 static void GrowArray(void***, void*);
87 static void PERMvectortoaccess(ACCESSGROUP *acc, const char *name, struct vector *acccess_vec);
88
89 /* global variables */
90 static AUTHGROUP **auth_realms;
91 static AUTHGROUP *success_auth;
92 static ACCESSGROUP **access_realms;
93
94 static char     *ConfigBit;
95 static int      ConfigBitsize;
96
97 extern bool PerlLoaded;
98
99 #define PERMlbrace              1
100 #define PERMrbrace              2
101 #define PERMgroup               3
102 #define PERMauth                4
103 #define PERMaccess              5
104 #define PERMhost                6
105 #define PERMauthprog            7
106 #define PERMresolv              8
107 #define PERMresprog             9
108 #define PERMdefuser             10
109 #define PERMdefdomain           11
110 #define PERMusers               12
111 #define PERMnewsgroups          13
112 #define PERMread                14
113 #define PERMpost                15
114 #define PERMaccessrp            16
115 #define PERMheader              17
116 #define PERMalsolog             18
117 #define PERMprogram             19
118 #define PERMinclude             20
119 #define PERMkey                 21
120 #define PERMlocaltime           22
121 #define PERMstrippath           23
122 #define PERMnnrpdperlfilter     24
123 #define PERMnnrpdpythonfilter   25
124 #define PERMfromhost            26
125 #define PERMpathhost            27
126 #define PERMorganization        28
127 #define PERMmoderatormailer     29
128 #define PERMdomain              30
129 #define PERMcomplaints          31
130 #define PERMspoolfirst          32
131 #define PERMcheckincludedtext   33
132 #define PERMclienttimeout       34
133 #define PERMlocalmaxartsize     35
134 #define PERMreadertrack         36
135 #define PERMstrippostcc         37
136 #define PERMaddnntppostinghost  38
137 #define PERMaddnntppostingdate  39
138 #define PERMnnrpdposthost       40
139 #define PERMnnrpdpostport       41
140 #define PERMnnrpdoverstats      42
141 #define PERMbackoff_auth        43
142 #define PERMbackoff_db          44
143 #define PERMbackoff_k           45
144 #define PERMbackoff_postfast    46
145 #define PERMbackoff_postslow    47
146 #define PERMbackoff_trigger     48
147 #define PERMnnrpdcheckart       49
148 #define PERMnnrpdauthsender     50
149 #define PERMvirtualhost         51
150 #define PERMnewsmaster          52
151 #define PERMlocaladdress        53
152 #define PERMrejectwith          54
153 #define PERMmaxbytespersecond   55
154 #define PERMperl_auth           56
155 #define PERMpython_auth         57
156 #define PERMperl_access         58
157 #define PERMpython_access       59
158 #define PERMpython_dynamic      60
159 #ifdef HAVE_SSL
160 #define PERMrequire_ssl                61
161 #define PERMMAX                        62
162 #else
163 #define PERMMAX                 61
164 #endif
165
166 #define TEST_CONFIG(a, b) \
167     { \
168         int byte, offset; \
169         offset = a % 8; \
170         byte = (a - offset) / 8; \
171         b = ((ConfigBit[byte] & (1 << offset)) != 0) ? true : false; \
172     }
173 #define SET_CONFIG(a) \
174     { \
175         int byte, offset; \
176         offset = a % 8; \
177         byte = (a - offset) / 8; \
178         ConfigBit[byte] |= (1 << offset); \
179     }
180 #define CLEAR_CONFIG(a) \
181     { \
182         int byte, offset; \
183         offset = a % 8; \
184         byte = (a - offset) / 8; \
185         ConfigBit[byte] &= ~(1 << offset); \
186     }
187
188 static CONFTOKEN PERMtoks[] = {
189   { PERMlbrace, "{" },
190   { PERMrbrace, "}" },
191   { PERMgroup, "group" },
192   { PERMauth, "auth" },
193   { PERMaccess, "access" },
194   { PERMhost, "hosts:" },
195   { PERMauthprog, "auth:" },
196   { PERMresolv, "res" },
197   { PERMresprog, "res:" },
198   { PERMdefuser, "default:" },
199   { PERMdefdomain, "default-domain:" },
200   { PERMusers, "users:" },
201   { PERMnewsgroups, "newsgroups:" },
202   { PERMread, "read:" },
203   { PERMpost, "post:" },
204   { PERMaccessrp, "access:" },
205   { PERMheader, "header:" },
206   { PERMalsolog, "log:" },
207   { PERMprogram, "program:" },
208   { PERMinclude, "include" },
209   { PERMkey, "key:" },
210   { PERMlocaltime, "localtime:" },
211   { PERMstrippath, "strippath:" },
212   { PERMnnrpdperlfilter, "perlfilter:" },
213   { PERMnnrpdpythonfilter, "pythonfilter:" },
214   { PERMfromhost, "fromhost:" },
215   { PERMpathhost, "pathhost:" },
216   { PERMorganization, "organization:" },
217   { PERMmoderatormailer, "moderatormailer:" },
218   { PERMdomain, "domain:" },
219   { PERMcomplaints, "complaints:" },
220   { PERMspoolfirst, "spoolfirst:" },
221   { PERMcheckincludedtext, "checkincludedtext:" },
222   { PERMclienttimeout, "clienttimeout:" },
223   { PERMlocalmaxartsize, "localmaxartsize:" },
224   { PERMreadertrack, "readertrack:" },
225   { PERMstrippostcc, "strippostcc:" },
226   { PERMaddnntppostinghost, "addnntppostinghost:" },
227   { PERMaddnntppostingdate, "addnntppostingdate:" },
228   { PERMnnrpdposthost, "nnrpdposthost:" },
229   { PERMnnrpdpostport, "nnrpdpostport:" },
230   { PERMnnrpdoverstats, "nnrpdoverstats:" },
231   { PERMbackoff_auth, "backoff_auth:" },
232   { PERMbackoff_db, "backoff_db:" },
233   { PERMbackoff_k, "backoff_k:" },
234   { PERMbackoff_postfast, "backoff_postfast:" },
235   { PERMbackoff_postslow, "backoff_postslow:" },
236   { PERMbackoff_trigger, "backoff_trigger:" },
237   { PERMnnrpdcheckart, "nnrpdcheckart:" },
238   { PERMnnrpdauthsender, "nnrpdauthsender:" },
239   { PERMvirtualhost, "virtualhost:" },
240   { PERMnewsmaster, "newsmaster:" },
241   { PERMlocaladdress, "localaddress:" },
242   { PERMrejectwith, "reject_with:" },
243   { PERMmaxbytespersecond, "max_rate:" },
244   { PERMperl_auth, "perl_auth:" },
245   { PERMpython_auth, "python_auth:" },
246   { PERMperl_access, "perl_access:" },
247   { PERMpython_access, "python_access:" },
248   { PERMpython_dynamic, "python_dynamic:" },
249 #ifdef HAVE_SSL
250   { PERMrequire_ssl, "require_ssl:" },
251 #endif
252   { 0, 0 }
253 };
254
255 /* function definitions */
256 static void GrowArray(void ***array, void *el)
257 {
258     int i;
259
260     if (!*array) {
261         *array = xmalloc(2 * sizeof(void *));
262         i = 0;
263     } else {
264         for (i = 0; (*array)[i]; i++)
265             ;
266         *array = xrealloc(*array, (i + 2) * sizeof(void *));
267     }
268     (*array)[i++] = el;
269     (*array)[i] = 0;
270 }
271
272 static METHOD *copy_method(METHOD *orig)
273 {
274     METHOD *ret;
275     int i;
276
277     ret = xmalloc(sizeof(METHOD));
278     memset(ConfigBit, '\0', ConfigBitsize);
279
280     ret->name = xstrdup(orig->name);
281     ret->program = xstrdup(orig->program);
282     if (orig->users)
283         ret->users = xstrdup(orig->users);
284     else
285         ret->users = 0;
286
287     ret->extra_headers = 0;
288     if (orig->extra_headers) {
289         for (i = 0; orig->extra_headers[i]; i++)
290             GrowArray((void***) &ret->extra_headers,
291               (void*) xstrdup(orig->extra_headers[i]));
292     }
293
294     ret->extra_logs = 0;
295     if (orig->extra_logs) {
296         for (i = 0; orig->extra_logs[i]; i++)
297             GrowArray((void***) &ret->extra_logs,
298               (void*) xstrdup(orig->extra_logs[i]));
299     }
300
301     ret->type = orig->type;
302
303     return(ret);
304 }
305
306 static void free_method(METHOD *del)
307 {
308     int j;
309
310     if (del->extra_headers) {
311         for (j = 0; del->extra_headers[j]; j++)
312             free(del->extra_headers[j]);
313         free(del->extra_headers);
314     }
315     if (del->extra_logs) {
316         for (j = 0; del->extra_logs[j]; j++)
317             free(del->extra_logs[j]);
318         free(del->extra_logs);
319     }
320     if (del->program)
321         free(del->program);
322     if (del->users)
323         free(del->users);
324     free(del->name);
325     free(del);
326 }
327
328 static AUTHGROUP *copy_authgroup(AUTHGROUP *orig)
329 {
330     AUTHGROUP *ret;
331     int i;
332
333     if (!orig)
334         return(0);
335     ret = xmalloc(sizeof(AUTHGROUP));
336     memset(ConfigBit, '\0', ConfigBitsize);
337
338     if (orig->name)
339         ret->name = xstrdup(orig->name);
340     else
341         ret->name = 0;
342
343     if (orig->key)
344         ret->key = xstrdup(orig->key);
345     else
346         ret->key = 0;
347
348     if (orig->hosts)
349         ret->hosts = xstrdup(orig->hosts);
350     else
351         ret->hosts = 0;
352
353 #ifdef HAVE_SSL
354     ret->require_ssl = orig->require_ssl;
355 #endif
356
357     ret->res_methods = 0;
358     if (orig->res_methods) {
359         for (i = 0; orig->res_methods[i]; i++)
360             GrowArray((void***) &ret->res_methods,
361               (void*) copy_method(orig->res_methods[i]));;
362     }
363
364     ret->auth_methods = 0;
365     if (orig->auth_methods) {
366         for (i = 0; orig->auth_methods[i]; i++)
367             GrowArray((void***) &ret->auth_methods,
368               (void*) copy_method(orig->auth_methods[i]));
369     }
370
371     if (orig->default_user)
372         ret->default_user = xstrdup(orig->default_user);
373     else
374         ret->default_user = 0;
375
376     if (orig->default_domain)
377         ret->default_domain = xstrdup(orig->default_domain);
378     else
379         ret->default_domain = 0;
380
381     if (orig->localaddress)
382         ret->localaddress = xstrdup(orig->localaddress);
383     else
384         ret->localaddress = 0;
385
386     if (orig->access_script)
387         ret->access_script = xstrdup(orig->access_script);
388     else
389         ret->access_script = 0;
390
391     if (orig->access_type)
392         ret->access_type = orig->access_type;
393     else
394         ret->access_type = 0;
395
396     if (orig->dynamic_script)
397         ret->dynamic_script = xstrdup(orig->dynamic_script);
398     else
399         ret->dynamic_script = 0;
400
401     if (orig->dynamic_type)
402         ret->dynamic_type = orig->dynamic_type;
403     else
404         ret->dynamic_type = 0;
405
406     return(ret);
407 }
408
409 static ACCESSGROUP *copy_accessgroup(ACCESSGROUP *orig)
410 {
411     ACCESSGROUP *ret;
412
413     if (!orig)
414         return(0);
415     ret = xmalloc(sizeof(ACCESSGROUP));
416     memset(ConfigBit, '\0', ConfigBitsize);
417     /* copy all anyway, and update for local strings */
418     *ret = *orig;
419
420     if (orig->name)
421         ret->name = xstrdup(orig->name);
422     if (orig->key)
423         ret->key = xstrdup(orig->key);
424     if (orig->read)
425         ret->read = xstrdup(orig->read);
426     if (orig->post)
427         ret->post = xstrdup(orig->post);
428     if (orig->users)
429         ret->users = xstrdup(orig->users);
430     if (orig->rejectwith)
431         ret->rejectwith = xstrdup(orig->rejectwith);
432     if (orig->fromhost)
433         ret->fromhost = xstrdup(orig->fromhost);
434     if (orig->pathhost)
435         ret->pathhost = xstrdup(orig->pathhost);
436     if (orig->organization)
437         ret->organization = xstrdup(orig->organization);
438     if (orig->moderatormailer)
439         ret->moderatormailer = xstrdup(orig->moderatormailer);
440     if (orig->domain)
441         ret->domain = xstrdup(orig->domain);
442     if (orig->complaints)
443         ret->complaints = xstrdup(orig->complaints);
444     if (orig->nnrpdposthost)
445         ret->nnrpdposthost = xstrdup(orig->nnrpdposthost);
446     if (orig->backoff_db)
447         ret->backoff_db = xstrdup(orig->backoff_db);
448     if (orig->newsmaster)
449         ret->newsmaster = xstrdup(orig->newsmaster);
450     return(ret);
451 }
452
453 static void SetDefaultAuth(AUTHGROUP *curauth UNUSED)
454 {
455 #ifdef HAVE_SSL
456         curauth->require_ssl = false;
457 #endif
458 }
459
460 void SetDefaultAccess(ACCESSGROUP *curaccess)
461 {
462     curaccess->allownewnews = innconf->allownewnews;;
463     curaccess->allowihave = false;
464     curaccess->locpost = false;
465     curaccess->allowapproved = false;
466     curaccess->localtime = false;
467     curaccess->strippath = false;
468     curaccess->nnrpdperlfilter = true;
469     curaccess->nnrpdpythonfilter = true;
470     curaccess->fromhost = NULL;
471     if (innconf->fromhost)
472         curaccess->fromhost = xstrdup(innconf->fromhost);
473     curaccess->pathhost = NULL;
474     if (innconf->pathhost)
475         curaccess->pathhost = xstrdup(innconf->pathhost);
476     curaccess->organization = NULL;
477     if (innconf->organization)
478         curaccess->organization = xstrdup(innconf->organization);
479     curaccess->moderatormailer = NULL;
480     if (innconf->moderatormailer)
481         curaccess->moderatormailer = xstrdup(innconf->moderatormailer);
482     curaccess->domain = NULL;
483     if (innconf->domain)
484         curaccess->domain = xstrdup(innconf->domain);
485     curaccess->complaints = NULL;
486     if (innconf->complaints)
487         curaccess->complaints = xstrdup(innconf->complaints);
488     curaccess->spoolfirst = innconf->spoolfirst;
489     curaccess->checkincludedtext = innconf->checkincludedtext;
490     curaccess->clienttimeout = innconf->clienttimeout;
491     curaccess->localmaxartsize = innconf->localmaxartsize;
492     curaccess->readertrack = innconf->readertrack;
493     curaccess->strippostcc = innconf->strippostcc;
494     curaccess->addnntppostinghost = innconf->addnntppostinghost;
495     curaccess->addnntppostingdate = innconf->addnntppostingdate;
496     curaccess->nnrpdposthost = innconf->nnrpdposthost;
497     curaccess->nnrpdpostport = innconf->nnrpdpostport;
498     curaccess->nnrpdoverstats = innconf->nnrpdoverstats;
499     curaccess->backoff_auth = innconf->backoffauth;
500     curaccess->backoff_db = NULL;
501     if (innconf->backoffdb && *innconf->backoffdb != '\0')
502         curaccess->backoff_db = xstrdup(innconf->backoffdb);
503     curaccess->backoff_k = innconf->backoffk;
504     curaccess->backoff_postfast = innconf->backoffpostfast;
505     curaccess->backoff_postslow = innconf->backoffpostslow;
506     curaccess->backoff_trigger = innconf->backofftrigger;
507     curaccess->nnrpdcheckart = innconf->nnrpdcheckart;
508     curaccess->nnrpdauthsender = innconf->nnrpdauthsender;
509     curaccess->virtualhost = false;
510     curaccess->newsmaster = NULL;
511     curaccess->maxbytespersecond = 0;
512 }
513
514 static void free_authgroup(AUTHGROUP *del)
515 {
516     int i;
517
518     if (del->name)
519         free(del->name);
520     if (del->key)
521         free(del->key);
522     if (del->hosts)
523         free(del->hosts);
524     if (del->res_methods) {
525         for (i = 0; del->res_methods[i]; i++)
526             free_method(del->res_methods[i]);
527         free(del->res_methods);
528     }
529     if (del->auth_methods) {
530         for (i = 0; del->auth_methods[i]; i++)
531             free_method(del->auth_methods[i]);
532         free(del->auth_methods);
533     }
534     if (del->default_user)
535         free(del->default_user);
536     if (del->default_domain)
537         free(del->default_domain);
538     if (del->localaddress)
539         free(del->localaddress);
540     if (del->access_script)
541         free(del->access_script);
542     if (del->dynamic_script)
543         free(del->dynamic_script);
544     free(del);
545 }
546
547 static void free_accessgroup(ACCESSGROUP *del)
548 {
549     if (del->name)
550         free(del->name);
551     if (del->key)
552         free(del->key);
553     if (del->read)
554         free(del->read);
555     if (del->post)
556         free(del->post);
557     if (del->users)
558         free(del->users);
559     if (del->rejectwith)
560         free(del->rejectwith);
561     if (del->fromhost)
562         free(del->fromhost);
563     if (del->pathhost)
564         free(del->pathhost);
565     if (del->organization)
566         free(del->organization);
567     if (del->moderatormailer)
568         free(del->moderatormailer);
569     if (del->domain)
570         free(del->domain);
571     if (del->complaints)
572         free(del->complaints);
573     if (del->nnrpdposthost)
574         free(del->nnrpdposthost);
575     if (del->backoff_db)
576         free(del->backoff_db);
577     if (del->newsmaster)
578         free(del->newsmaster);
579     free(del);
580 }
581
582 static void ReportError(CONFFILE *f, const char *err)
583 {
584     syslog(L_ERROR, "%s syntax error in %s(%d), %s", ClientHost,
585       f->filename, f->lineno, err);
586     Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
587     ExitWithStats(1, true);
588 }
589
590 static void method_parse(METHOD *method, CONFFILE *f, CONFTOKEN *tok, int auth)
591 {
592     int oldtype;
593
594     oldtype = tok->type;
595     tok = CONFgettoken(0, f);
596
597     if (tok == NULL) {
598         ReportError(f, "Expected value.");
599     }
600
601     switch (oldtype) {
602       case PERMheader:
603         GrowArray((void***) &method->extra_headers, (void*) xstrdup(tok->name));
604         break;
605       case PERMalsolog:
606         GrowArray((void***) &method->extra_logs, (void*) xstrdup(tok->name));
607         break;
608       case PERMusers:
609
610         if (!auth) {
611             ReportError(f, "Unexpected users: directive in file.");
612         } else if (method->users) {
613             ReportError(f, "Multiple users: directive in file.");
614         }
615
616         method->users = xstrdup(tok->name);
617         break;
618       case PERMprogram:
619         if (method->program) {
620             ReportError(f, "Multiple program: directives in auth/res decl."); 
621         }
622
623         method->program = xstrdup(tok->name);
624         break;
625     }
626 }
627
628 static void authdecl_parse(AUTHGROUP *curauth, CONFFILE *f, CONFTOKEN *tok)
629 {
630     int oldtype,boolval;
631     METHOD *m;
632     bool bit;
633     char buff[SMBUF], *oldname, *p;
634
635     oldtype = tok->type;
636     oldname = tok->name;
637
638     tok = CONFgettoken(PERMtoks, f);
639
640     if (tok == NULL) {
641         ReportError(f, "Expected value.");
642     }
643     TEST_CONFIG(oldtype, bit);
644     if (bit) {
645         snprintf(buff, sizeof(buff), "Duplicated '%s' field in authgroup.",
646                  oldname);
647         ReportError(f, buff);
648     }
649
650     if (strcasecmp(tok->name, "on") == 0
651         || strcasecmp(tok->name, "true") == 0
652         || strcasecmp(tok->name, "yes") == 0)
653         boolval = true;
654     else if (strcasecmp(tok->name, "off") == 0
655              || strcasecmp(tok->name, "false") == 0
656              || strcasecmp(tok->name, "no") == 0)
657         boolval = false;
658     else
659         boolval = -1;
660
661     switch (oldtype) {
662       case PERMkey:
663         curauth->key = xstrdup(tok->name);
664         SET_CONFIG(PERMkey);
665         break;
666 #ifdef HAVE_SSL
667       case PERMrequire_ssl:
668         if (boolval != -1) curauth->require_ssl = boolval;
669         SET_CONFIG(PERMrequire_ssl);
670         break;
671 #endif
672       case PERMhost:
673         curauth->hosts = xstrdup(tok->name);
674         CompressList(curauth->hosts);
675         SET_CONFIG(PERMhost);
676
677         /* nnrpd.c downcases the names of connecting hosts.  We should
678            therefore also downcase the wildmat patterns to make sure there
679            aren't any surprises.  DNS is case-insensitive. */
680         for (p = curauth->hosts; *p; p++)
681             if (CTYPE(isupper, (unsigned char) *p))
682                 *p = tolower((unsigned char) *p);
683
684         break;
685       case PERMdefdomain:
686         curauth->default_domain = xstrdup(tok->name);
687         SET_CONFIG(PERMdefdomain);
688         break;
689       case PERMdefuser:
690         curauth->default_user = xstrdup(tok->name);
691         SET_CONFIG(PERMdefuser);
692         break;
693       case PERMresolv:
694       case PERMresprog:
695         m = xcalloc(1, sizeof(METHOD));
696         memset(ConfigBit, '\0', ConfigBitsize);
697         GrowArray((void***) &curauth->res_methods, (void*) m);
698
699         if (oldtype == PERMresprog)
700             m->program = xstrdup(tok->name);
701         else {
702             m->name = xstrdup(tok->name);
703             tok = CONFgettoken(PERMtoks, f);
704             if (tok == NULL || tok->type != PERMlbrace) {
705                 ReportError(f, "Expected '{' after 'res'");
706             }
707
708             tok = CONFgettoken(PERMtoks, f);
709
710             while (tok != NULL && tok->type != PERMrbrace) {
711                 method_parse(m, f, tok, 0);
712                 tok = CONFgettoken(PERMtoks, f);
713             }
714
715             if (tok == NULL) {
716                 ReportError(f, "Unexpected EOF.");
717             }
718         }
719         break;
720       case PERMauth:
721       case PERMperl_auth:
722       case PERMpython_auth:
723       case PERMauthprog:
724         m = xcalloc(1, sizeof(METHOD));
725         memset(ConfigBit, '\0', ConfigBitsize);
726         GrowArray((void***) &curauth->auth_methods, (void*) m);
727         if (oldtype == PERMauthprog) {
728             m->type = PERMauthprog;
729             m->program = xstrdup(tok->name);
730         } else if (oldtype == PERMperl_auth) {
731 #ifdef DO_PERL
732             m->type = PERMperl_auth;
733             m->program = xstrdup(tok->name);
734 #else
735             ReportError(f, "perl_auth can not be used in readers.conf: inn not compiled with perl support enabled.");
736 #endif
737         } else if (oldtype == PERMpython_auth) {
738 #ifdef DO_PYTHON
739             m->type = PERMpython_auth;
740             m->program = xstrdup(tok->name);
741 #else
742             ReportError(f, "python_auth can not be used in readers.conf: inn not compiled with python support enabled.");
743 #endif
744         } else {
745             m->name = xstrdup(tok->name);
746             tok = CONFgettoken(PERMtoks, f);
747
748             if (tok == NULL || tok->type != PERMlbrace) {
749                 ReportError(f, "Expected '{' after 'auth'");
750             }
751
752             tok = CONFgettoken(PERMtoks, f);
753
754             while (tok != NULL && tok->type != PERMrbrace) {
755                 method_parse(m, f, tok, 1);
756                 tok = CONFgettoken(PERMtoks, f);
757             }
758
759             if (tok == NULL) {
760                 ReportError(f, "Unexpected EOF.");
761             }
762         }
763         break;
764       case PERMperl_access:
765 #ifdef DO_PERL
766         curauth->access_script = xstrdup(tok->name);
767         curauth->access_type = PERMperl_access;
768 #else
769         ReportError(f, "perl_access can not be used in readers.conf: inn not compiled with perl support enabled.");
770 #endif
771         break;
772       case PERMpython_access:
773 #ifdef DO_PYTHON
774         curauth->access_script = xstrdup(tok->name);
775         curauth->access_type = PERMpython_access;
776 #else
777         ReportError(f, "python_access can not be used in readers.conf: inn not compiled with python support enabled.");
778 #endif
779         break;
780       case PERMpython_dynamic:
781 #ifdef DO_PYTHON
782         curauth->dynamic_script = xstrdup(tok->name);
783         curauth->dynamic_type = PERMpython_dynamic;
784 #else
785         ReportError(f, "python_dynamic can not be used in readers.conf: inn not compiled with python support enabled.");
786 #endif
787        break;
788       case PERMlocaladdress:
789         curauth->localaddress = xstrdup(tok->name);
790         CompressList(curauth->localaddress);
791         SET_CONFIG(PERMlocaladdress);
792         break;
793       default:
794         snprintf(buff, sizeof(buff), "Unexpected token: %s", tok->name);
795         ReportError(f, buff);
796         break;
797     }
798 }
799
800 static void accessdecl_parse(ACCESSGROUP *curaccess, CONFFILE *f, CONFTOKEN *tok)
801 {
802     int oldtype, boolval;
803     bool bit;
804     char buff[SMBUF], *oldname;
805
806     oldtype = tok->type;
807     oldname = tok->name;
808
809     tok = CONFgettoken(0, f);
810
811     if (tok == NULL) {
812         ReportError(f, "Expected value.");
813     }
814     TEST_CONFIG(oldtype, bit);
815     if (bit) {
816         snprintf(buff, sizeof(buff), "Duplicated '%s' field in accessgroup.",
817                  oldname);
818         ReportError(f, buff);
819     }
820     if (strcasecmp(tok->name, "on") == 0
821         || strcasecmp(tok->name, "true") == 0
822         || strcasecmp(tok->name, "yes") == 0)
823         boolval = true;
824     else if (strcasecmp(tok->name, "off") == 0
825              || strcasecmp(tok->name, "false") == 0
826              || strcasecmp(tok->name, "no") == 0)
827         boolval = false;
828     else
829         boolval = -1;
830
831     switch (oldtype) {
832       case PERMkey:
833         curaccess->key = xstrdup(tok->name);
834         SET_CONFIG(oldtype);
835         break;
836       case PERMusers:
837         curaccess->users = xstrdup(tok->name);
838         CompressList(curaccess->users);
839         SET_CONFIG(oldtype);
840         break;
841       case PERMrejectwith:
842         curaccess->rejectwith = xstrdup(tok->name);
843         SET_CONFIG(oldtype);
844         break;
845       case PERMnewsgroups:
846         TEST_CONFIG(PERMread, bit);
847         if (bit) {
848             /* syntax error..  can't set read: or post: _and_ use
849              * newsgroups: */
850             ReportError(f, "read: newsgroups already set.");
851         }
852         TEST_CONFIG(PERMpost, bit);
853         if (bit) {
854             /* syntax error..  can't set read: or post: _and_ use
855              * newsgroups: */
856             ReportError(f, "post: newsgroups already set.");
857         }
858
859         curaccess->read = xstrdup(tok->name);
860         CompressList(curaccess->read);
861         curaccess->post = xstrdup(tok->name);
862         CompressList(curaccess->post);
863         SET_CONFIG(oldtype);
864         SET_CONFIG(PERMread);
865         SET_CONFIG(PERMpost);
866         break;
867       case PERMread:
868         curaccess->read = xstrdup(tok->name);
869         CompressList(curaccess->read);
870         SET_CONFIG(oldtype);
871         break;
872       case PERMpost:
873         curaccess->post = xstrdup(tok->name);
874         CompressList(curaccess->post);
875         SET_CONFIG(oldtype);
876         break;
877       case PERMaccessrp:
878         TEST_CONFIG(PERMread, bit);
879         if (bit && strchr(tok->name, 'R') == NULL) {
880             free(curaccess->read);
881             curaccess->read = 0;
882             CLEAR_CONFIG(PERMread);
883         }
884         TEST_CONFIG(PERMpost, bit);
885         if (bit && strchr(tok->name, 'P') == NULL) {
886             free(curaccess->post);
887             curaccess->post = 0;
888             CLEAR_CONFIG(PERMpost);
889         }
890         curaccess->allowapproved = (strchr(tok->name, 'A') != NULL);
891         curaccess->allownewnews = (strchr(tok->name, 'N') != NULL);
892         curaccess->allowihave = (strchr(tok->name, 'I') != NULL);
893         curaccess->locpost = (strchr(tok->name, 'L') != NULL);
894         SET_CONFIG(oldtype);
895         break;
896       case PERMlocaltime:
897         if (boolval != -1) curaccess->localtime = boolval;
898         SET_CONFIG(oldtype);
899         break;
900       case PERMstrippath:
901         if (boolval != -1) curaccess->strippath = boolval;
902         SET_CONFIG(oldtype);
903         break;
904       case PERMnnrpdperlfilter:
905         if (boolval != -1) curaccess->nnrpdperlfilter = boolval;
906         SET_CONFIG(oldtype);
907         break;
908       case PERMnnrpdpythonfilter:
909         if (boolval != -1) curaccess->nnrpdpythonfilter = boolval;
910         SET_CONFIG(oldtype);
911         break;
912       case PERMfromhost:
913         if (curaccess->fromhost)
914             free(curaccess->fromhost);
915         curaccess->fromhost = xstrdup(tok->name);
916         SET_CONFIG(oldtype);
917         break;
918       case PERMpathhost:
919         if (curaccess->pathhost)
920             free(curaccess->pathhost);
921         curaccess->pathhost = xstrdup(tok->name);
922         SET_CONFIG(oldtype);
923         break;
924       case PERMorganization:
925         if (curaccess->organization)
926             free(curaccess->organization);
927         curaccess->organization = xstrdup(tok->name);
928         SET_CONFIG(oldtype);
929         break;
930       case PERMmoderatormailer:
931         if (curaccess->moderatormailer)
932             free(curaccess->moderatormailer);
933         curaccess->moderatormailer = xstrdup(tok->name);
934         SET_CONFIG(oldtype);
935         break;
936       case PERMdomain:
937         if (curaccess->domain)
938             free(curaccess->domain);
939         curaccess->domain = xstrdup(tok->name);
940         SET_CONFIG(oldtype);
941         break;
942       case PERMcomplaints:
943         if (curaccess->complaints)
944             free(curaccess->complaints);
945         curaccess->complaints = xstrdup(tok->name);
946         SET_CONFIG(oldtype);
947         break;
948       case PERMspoolfirst:
949         if (boolval != -1) curaccess->spoolfirst = boolval;
950         SET_CONFIG(oldtype);
951         break;
952       case PERMcheckincludedtext:
953         if (boolval != -1) curaccess->checkincludedtext = boolval;
954         SET_CONFIG(oldtype);
955         break;
956       case PERMclienttimeout:
957         curaccess->clienttimeout = atoi(tok->name);
958         SET_CONFIG(oldtype);
959         break;
960       case PERMlocalmaxartsize:
961         curaccess->localmaxartsize = atol(tok->name);
962         SET_CONFIG(oldtype);
963         break;
964       case PERMreadertrack:
965         if (boolval != -1) curaccess->readertrack = boolval;
966         SET_CONFIG(oldtype);
967         break;
968       case PERMstrippostcc:
969         if (boolval != -1) curaccess->strippostcc = boolval;
970         SET_CONFIG(oldtype);
971         break;
972       case PERMaddnntppostinghost:
973         if (boolval != -1) curaccess->addnntppostinghost = boolval;
974         SET_CONFIG(oldtype);
975         break;
976       case PERMaddnntppostingdate:
977         if (boolval != -1) curaccess->addnntppostingdate = boolval;
978         SET_CONFIG(oldtype);
979         break;
980       case PERMnnrpdposthost:
981         if (curaccess->nnrpdposthost)
982             free(curaccess->nnrpdposthost);
983         curaccess->nnrpdposthost = xstrdup(tok->name);
984         SET_CONFIG(oldtype);
985         break;
986       case PERMnnrpdpostport:
987         curaccess->nnrpdpostport = atoi(tok->name);
988         SET_CONFIG(oldtype);
989         break;
990       case PERMnnrpdoverstats:
991         if (boolval != -1) curaccess->nnrpdoverstats = boolval;
992         SET_CONFIG(oldtype);
993         break;
994       case PERMbackoff_auth:
995         if (boolval != -1) curaccess->backoff_auth = boolval;
996         SET_CONFIG(oldtype);
997         break;
998       case PERMbackoff_db:
999         if (curaccess->backoff_db)
1000             free(curaccess->backoff_db);
1001         curaccess->backoff_db = xstrdup(tok->name);
1002         SET_CONFIG(oldtype);
1003         break;
1004       case PERMbackoff_k:
1005         curaccess->backoff_k = atol(tok->name);
1006         SET_CONFIG(oldtype);
1007         break;
1008       case PERMbackoff_postfast:
1009         curaccess->backoff_postfast = atol(tok->name);
1010         SET_CONFIG(oldtype);
1011         break;
1012       case PERMbackoff_postslow:
1013         curaccess->backoff_postslow = atol(tok->name);
1014         SET_CONFIG(oldtype);
1015         break;
1016       case PERMbackoff_trigger:
1017         curaccess->backoff_trigger = atol(tok->name);
1018         SET_CONFIG(oldtype);
1019         break;
1020       case PERMnnrpdcheckart:
1021         if (boolval != -1) curaccess->nnrpdcheckart = boolval;
1022         SET_CONFIG(oldtype);
1023         break;
1024       case PERMnnrpdauthsender:
1025         if (boolval != -1) curaccess->nnrpdauthsender = boolval;
1026         SET_CONFIG(oldtype);
1027         break;
1028       case PERMvirtualhost:
1029         if (boolval != -1) curaccess->virtualhost = boolval;
1030         SET_CONFIG(oldtype);
1031         break;
1032       case PERMnewsmaster:
1033         if (curaccess->newsmaster)
1034             free(curaccess->newsmaster);
1035         curaccess->newsmaster = xstrdup(tok->name);
1036         SET_CONFIG(oldtype);
1037         break;
1038       case PERMmaxbytespersecond:
1039         curaccess->maxbytespersecond = atol(tok->name);
1040         SET_CONFIG(oldtype);
1041         break;
1042       default:
1043         snprintf(buff, sizeof(buff), "Unexpected token: %s", tok->name);
1044         ReportError(f, buff);
1045         break;
1046     }
1047 }
1048
1049 static void PERMvectortoaccess(ACCESSGROUP *acc, const char *name, struct vector *access_vec) {
1050     CONFTOKEN   *tok        = NULL;
1051     CONFFILE    *file;
1052     char        *str;
1053     unsigned int i;
1054
1055     file = xcalloc(1, sizeof(CONFFILE));
1056     file->array = access_vec->strings;
1057     file->array_len = access_vec->count;
1058  
1059     memset(ConfigBit, '\0', ConfigBitsize);
1060
1061     SetDefaultAccess(acc);
1062     str = xstrdup(name);
1063     acc->name = str;
1064
1065     for (i = 0; i <= access_vec->count; i++) {
1066       tok = CONFgettoken(PERMtoks, file);
1067
1068       if (tok != NULL) {
1069         accessdecl_parse(acc, file, tok);
1070       }
1071     }
1072     free(file);
1073     return;       
1074 }
1075
1076 static void PERMreadfile(char *filename)
1077 {
1078     CONFCHAIN   *cf         = NULL,
1079                 *hold       = NULL;
1080     CONFTOKEN   *tok        = NULL;
1081     int         inwhat;
1082     GROUP       *curgroup   = NULL,
1083                 *newgroup   = NULL;
1084     ACCESSGROUP *curaccess  = NULL;
1085     AUTHGROUP   *curauth    = NULL;
1086     int         oldtype;
1087     char        *str        = NULL;
1088     char        *path       = NULL;
1089     char buff[SMBUF];
1090
1091     if(filename != NULL) {
1092         syslog(L_TRACE, "Reading access from %s", 
1093                filename == NULL ? "(NULL)" : filename);
1094     }
1095
1096     cf          = xmalloc(sizeof(CONFCHAIN));
1097     if ((cf->f = CONFfopen(filename)) == NULL) {
1098         syslog(L_ERROR, "%s cannot open %s: %m", ClientHost, filename);
1099         Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
1100         ExitWithStats(1, true);
1101     }
1102     cf->parent  = 0;
1103
1104     /* are we editing an AUTH or ACCESS group? */
1105
1106     inwhat      = 0;
1107     newgroup    = curgroup = 0;
1108
1109     tok         = CONFgettoken(PERMtoks, cf->f);
1110
1111     while (tok != NULL) {
1112         if (inwhat == 0) {
1113             /* top-level parser */
1114
1115             switch (tok->type) {
1116                 /* include a child file */
1117
1118               case PERMinclude:
1119                 tok = CONFgettoken(0, cf->f);
1120
1121                 if (tok == NULL) {
1122                     ReportError(cf->f, "Expected filename after 'include'.");
1123                 }
1124
1125                 hold            = xmalloc(sizeof(CONFCHAIN));
1126                 hold->parent    = cf;
1127
1128                 /* unless the filename's path is fully qualified, open it
1129                  * relative to /news/etc */
1130
1131                 path = concatpath(innconf->pathetc, tok->name);
1132                 hold->f = CONFfopen(path);
1133                 free(path);
1134
1135                 if (hold->f == NULL) {
1136                     ReportError(cf->f, "Couldn't open 'include' filename.");
1137                 }
1138
1139                 cf = hold;
1140                 goto again;
1141                 break;
1142
1143                 /* nested group declaration. */
1144               case PERMgroup:
1145                 tok = CONFgettoken(PERMtoks, cf->f);
1146
1147                 if (tok == NULL) {
1148                     ReportError(cf->f, "Unexpected EOF at group name");
1149                 }
1150
1151                 newgroup        = xmalloc(sizeof(GROUP));
1152                 newgroup->above = curgroup;
1153                 newgroup->name  = xstrdup(tok->name);
1154                 memset(ConfigBit, '\0', ConfigBitsize);
1155
1156                 tok = CONFgettoken(PERMtoks, cf->f);
1157
1158                 if (tok == NULL || tok->type != PERMlbrace) {
1159                     ReportError(cf->f, "Expected '{' after group name");
1160                 }
1161
1162                 /* nested group declaration */
1163                 if (curgroup) {
1164                     newgroup->auth = copy_authgroup(curgroup->auth);
1165                     newgroup->access = copy_accessgroup(curgroup->access);
1166                 } else {
1167                     newgroup->auth = 0;
1168                     newgroup->access = 0;
1169                 }
1170
1171                 curgroup = newgroup;
1172                 break;
1173
1174                 /* beginning of an auth or access group decl */
1175               case PERMauth:
1176               case PERMaccess:
1177                 oldtype = tok->type;
1178
1179                 if ((tok = CONFgettoken(PERMtoks, cf->f)) == NULL) {
1180                     ReportError(cf->f, "Expected identifier.");
1181                 }
1182
1183                 str = xstrdup(tok->name);
1184
1185                 tok = CONFgettoken(PERMtoks, cf->f);
1186
1187                 if (tok == NULL || tok->type != PERMlbrace) {
1188                     ReportError(cf->f, "Expected '{'");
1189                 }
1190
1191                 switch (oldtype) {
1192                   case PERMauth:
1193                     if (curgroup && curgroup->auth)
1194                         curauth = copy_authgroup(curgroup->auth);
1195                     else {
1196                         curauth = xcalloc(1, sizeof(AUTHGROUP));
1197                         memset(ConfigBit, '\0', ConfigBitsize);
1198                         SetDefaultAuth(curauth);
1199                     }
1200
1201                     curauth->name = str;
1202                     inwhat = 1;
1203                     break;
1204
1205                   case PERMaccess:
1206                     if (curgroup && curgroup->access)
1207                         curaccess = copy_accessgroup(curgroup->access);
1208                     else {
1209                         curaccess = xcalloc(1, sizeof(ACCESSGROUP));
1210                         memset(ConfigBit, '\0', ConfigBitsize);
1211                         SetDefaultAccess(curaccess);
1212                     }
1213                     curaccess->name = str;
1214                     inwhat = 2;
1215                     break;
1216                 }
1217
1218                 break;
1219
1220                 /* end of a group declaration */
1221
1222               case PERMrbrace:
1223                 if (curgroup == NULL) {
1224                     ReportError(cf->f, "Unmatched '}'");
1225                 }
1226
1227                 newgroup = curgroup;
1228                 curgroup = curgroup->above;
1229                 if (newgroup->auth)
1230                     free_authgroup(newgroup->auth);
1231                 if (newgroup->access)
1232                     free_accessgroup(newgroup->access);
1233                 free(newgroup->name);
1234                 free(newgroup);
1235                 break;
1236
1237                 /* stuff that belongs in an authgroup */
1238               case PERMhost:
1239 #ifdef HAVE_SSL
1240               case PERMrequire_ssl:
1241 #endif
1242               case PERMauthprog:
1243               case PERMresprog:
1244               case PERMdefuser:
1245               case PERMdefdomain:
1246                 if (curgroup == NULL) {
1247                     curgroup = xcalloc(1, sizeof(GROUP));
1248                     memset(ConfigBit, '\0', ConfigBitsize);
1249                 }
1250                 if (curgroup->auth == NULL) {
1251                     curgroup->auth = xcalloc(1, sizeof(AUTHGROUP));
1252                     memset(ConfigBit, '\0', ConfigBitsize);
1253                     SetDefaultAuth(curgroup->auth);
1254                 }
1255
1256                 authdecl_parse(curgroup->auth, cf->f, tok);
1257                 break;
1258
1259                 /* stuff that belongs in an accessgroup */
1260               case PERMusers:
1261               case PERMrejectwith:
1262               case PERMnewsgroups:
1263               case PERMread:
1264               case PERMpost:
1265               case PERMaccessrp:
1266               case PERMlocaltime:
1267               case PERMstrippath:
1268               case PERMnnrpdperlfilter:
1269               case PERMnnrpdpythonfilter:
1270               case PERMfromhost:
1271               case PERMpathhost:
1272               case PERMorganization:
1273               case PERMmoderatormailer:
1274               case PERMdomain:
1275               case PERMcomplaints:
1276               case PERMspoolfirst:
1277               case PERMcheckincludedtext:
1278               case PERMclienttimeout:
1279               case PERMlocalmaxartsize:
1280               case PERMreadertrack:
1281               case PERMstrippostcc:
1282               case PERMaddnntppostinghost:
1283               case PERMaddnntppostingdate:
1284               case PERMnnrpdposthost:
1285               case PERMnnrpdpostport:
1286               case PERMnnrpdoverstats:
1287               case PERMbackoff_auth:
1288               case PERMbackoff_db:
1289               case PERMbackoff_k:
1290               case PERMbackoff_postfast:
1291               case PERMbackoff_postslow:
1292               case PERMbackoff_trigger:
1293               case PERMnnrpdcheckart:
1294               case PERMnnrpdauthsender:
1295               case PERMvirtualhost:
1296               case PERMnewsmaster:
1297                 if (!curgroup) {
1298                     curgroup = xcalloc(1, sizeof(GROUP));
1299                     memset(ConfigBit, '\0', ConfigBitsize);
1300                 }
1301                 if (!curgroup->access) {
1302                     curgroup->access = xcalloc(1, sizeof(ACCESSGROUP));
1303                     memset(ConfigBit, '\0', ConfigBitsize);
1304                     SetDefaultAccess(curgroup->access);
1305                 }
1306                 accessdecl_parse(curgroup->access, cf->f, tok);
1307                 break;
1308               default:
1309                 snprintf(buff, sizeof(buff), "Unexpected token: %s", tok->name);
1310                 ReportError(cf->f, buff);
1311                 break;
1312             }
1313         } else if (inwhat == 1) {
1314             /* authgroup parser */
1315             if (tok->type == PERMrbrace) {
1316                 inwhat = 0;
1317
1318                 if (curauth->name
1319 #ifdef HAVE_SSL
1320                     && ((curauth->require_ssl == false) || (ClientSSL == true))
1321 #endif
1322                     && MatchHost(curauth->hosts, ClientHost, ClientIpString)) {
1323                     if (!MatchHost(curauth->localaddress, ServerHost, ServerIpString)) {
1324                         syslog(L_TRACE, "Auth strategy '%s' does not match localhost.  Removing.",
1325                            curauth->name == NULL ? "(NULL)" : curauth->name);
1326                         free_authgroup(curauth);
1327                     } else
1328                         add_authgroup(curauth);
1329                 } else {
1330                     syslog(L_TRACE, "Auth strategy '%s' does not match client.  Removing.",
1331                            curauth->name == NULL ? "(NULL)" : curauth->name);
1332                     free_authgroup(curauth);
1333                 }
1334                 curauth = NULL;
1335                 goto again;
1336             }
1337
1338             authdecl_parse(curauth, cf->f, tok);
1339         } else if (inwhat == 2) {
1340             /* accessgroup parser */
1341             if (tok->type == PERMrbrace) {
1342                 inwhat = 0;
1343
1344                 if (curaccess->name)
1345                     add_accessgroup(curaccess);
1346                 else
1347                     free_accessgroup(curaccess);
1348                 curaccess = NULL;
1349                 goto again;
1350             }
1351
1352             accessdecl_parse(curaccess, cf->f, tok);
1353         } else {
1354             /* should never happen */
1355             syslog(L_TRACE, "SHOULD NEVER HAPPEN!");
1356         }
1357 again:
1358         /* go back up the 'include' chain. */
1359         tok = CONFgettoken(PERMtoks, cf->f);
1360
1361         while (tok == NULL && cf) {
1362             hold = cf;
1363             cf = hold->parent;
1364             CONFfclose(hold->f);
1365             free(hold);
1366             if (cf) {
1367                 tok = CONFgettoken(PERMtoks, cf->f);
1368             }
1369         }
1370     }
1371
1372     return;
1373 }
1374
1375 void PERMgetaccess(char *nnrpaccess)
1376 {
1377     int i;
1378     char *uname;
1379     int canauthenticate;
1380
1381     auth_realms     = NULL;
1382     access_realms   = NULL;
1383     success_auth    = NULL;
1384
1385     PERMcanread     = PERMcanpost   = false;
1386     PERMreadlist    = PERMpostlist  = false;
1387     PERMaccessconf = NULL;
1388
1389     if (ConfigBit == NULL) {
1390         if (PERMMAX % 8 == 0)
1391             ConfigBitsize = PERMMAX/8;
1392         else
1393             ConfigBitsize = (PERMMAX - (PERMMAX % 8))/8 + 1;
1394         ConfigBit = xcalloc(ConfigBitsize, 1);
1395     }
1396     PERMreadfile(nnrpaccess);
1397
1398     strip_accessgroups();
1399
1400     if (auth_realms == NULL) {
1401         /* no one can talk, empty file */
1402         syslog(L_NOTICE, "%s no_permission", ClientHost);
1403         Printf("%d You have no permission to talk.  Goodbye.\r\n",
1404           NNTP_ACCESS_VAL);
1405         ExitWithStats(1, true);
1406     }
1407
1408     /* auth_realms are all expected to match the user. */
1409     canauthenticate = 0;
1410     for (i = 0; auth_realms[i]; i++)
1411         if (auth_realms[i]->auth_methods)
1412             canauthenticate = 1;
1413     uname = 0;
1414     while (!uname && i--) {
1415         if ((uname = ResolveUser(auth_realms[i])) != NULL)
1416             PERMauthorized = true;
1417         if (!uname && auth_realms[i]->default_user)
1418             uname = auth_realms[i]->default_user;
1419     }
1420     if (uname) {
1421         strlcpy(PERMuser, uname, sizeof(PERMuser));
1422         uname = strchr(PERMuser, '@');
1423         if (!uname && auth_realms[i]->default_domain) {
1424             /* append the default domain to the username */
1425             strlcat(PERMuser, "@", sizeof(PERMuser));
1426             strlcat(PERMuser, auth_realms[i]->default_domain,
1427                     sizeof(PERMuser));
1428         }
1429         PERMneedauth = false;
1430         success_auth = auth_realms[i];
1431         syslog(L_TRACE, "%s res %s", ClientHost, PERMuser);
1432     } else if (!canauthenticate) {
1433         /* couldn't resolve the user. */
1434         syslog(L_NOTICE, "%s no_user", ClientHost);
1435         Printf("%d Could not get your access name.  Goodbye.\r\n",
1436           NNTP_ACCESS_VAL);
1437         ExitWithStats(1, true);
1438     } else {
1439         PERMneedauth = true;
1440     }
1441     /* check maximum allowed permissions for any host that matches (for
1442      * the greeting string) */
1443     for (i = 0; access_realms[i]; i++) {
1444         if (!PERMcanread)
1445             PERMcanread = (access_realms[i]->read != NULL);
1446         if (!PERMcanpost)
1447             PERMcanpost = (access_realms[i]->post != NULL);
1448     }
1449     if (!i) {
1450         /* no applicable access groups. Zeroing all these makes INN 
1451          * return permission denied to client. */
1452         PERMcanread = PERMcanpost = PERMneedauth = false;
1453     }
1454 }
1455
1456 void PERMlogin(char *uname, char *pass, char *errorstr)
1457 {
1458     int i   = 0;
1459     char *runame;
1460
1461     if (ConfigBit == NULL) {
1462         if (PERMMAX % 8 == 0)
1463             ConfigBitsize = PERMMAX/8;
1464         else
1465             ConfigBitsize = (PERMMAX - (PERMMAX % 8))/8 + 1;
1466         ConfigBit = xcalloc(ConfigBitsize, 1);
1467     }
1468     /* The check in CMDauthinfo uses the value of PERMneedauth to know if
1469      * authentication succeeded or not.  By default, authentication doesn't
1470      * succeed. */
1471     PERMneedauth = true;
1472
1473     if(auth_realms != NULL) {
1474         for (i = 0; auth_realms[i]; i++) {
1475             ;
1476         }
1477     }
1478
1479     runame  = NULL;
1480
1481     while (runame == NULL && i--)
1482         runame = AuthenticateUser(auth_realms[i], uname, pass, errorstr);
1483     if (runame) {
1484         strlcpy(PERMuser, runame, sizeof(PERMuser));
1485         uname = strchr(PERMuser, '@');
1486         if (!uname && auth_realms[i]->default_domain) {
1487             /* append the default domain to the username */
1488             strlcat(PERMuser, "@", sizeof(PERMuser));
1489             strlcat(PERMuser, auth_realms[i]->default_domain,
1490                     sizeof(PERMuser));
1491         }
1492         PERMneedauth = false;
1493         PERMauthorized = true;
1494         success_auth = auth_realms[i];
1495     }
1496 }
1497
1498 static int MatchUser(char *pat, char *user)
1499 {
1500     char *cp, **list;
1501     char *userlist[2];
1502     int ret;
1503
1504     if (!pat)
1505         return(1);
1506     if (!user || !*user)
1507         return(0);
1508     cp = xstrdup(pat);
1509     list = 0;
1510     NGgetlist(&list, cp);
1511     userlist[0] = user;
1512     userlist[1] = 0;
1513     ret = PERMmatch(list, userlist);
1514     free(cp);
1515     free(list[0]);
1516     free(list);
1517     return(ret);
1518 }
1519
1520 void PERMgetpermissions()
1521 {
1522     int i;
1523     char *cp, **list;
1524     char *user[2];
1525     static ACCESSGROUP *noaccessconf;
1526     char *uname;
1527     char *cpp, *script_path;
1528     char **args;
1529     struct vector *access_vec;
1530
1531     if (ConfigBit == NULL) {
1532         if (PERMMAX % 8 == 0)
1533             ConfigBitsize = PERMMAX/8;
1534         else
1535             ConfigBitsize = (PERMMAX - (PERMMAX % 8))/8 + 1;
1536         ConfigBit = xcalloc(ConfigBitsize, 1);
1537     }
1538     if (!success_auth) {
1539         /* if we haven't successfully authenticated, we can't do anything. */
1540         syslog(L_TRACE, "%s no_success_auth", ClientHost);
1541         if (!noaccessconf)
1542             noaccessconf = xmalloc(sizeof(ACCESSGROUP));
1543         PERMaccessconf = noaccessconf;
1544         SetDefaultAccess(PERMaccessconf);
1545         return;
1546 #ifdef DO_PERL
1547     } else if ((success_auth->access_script != NULL) && (success_auth->access_type == PERMperl_access)) {
1548       i = 0;
1549       cpp = xstrdup(success_auth->access_script);
1550       args = 0;
1551       Argify(cpp, &args);
1552       script_path = concat(args[0], (char *) 0);
1553       if ((script_path != NULL) && (strlen(script_path) > 0)) {
1554         if(!PerlLoaded) {
1555           loadPerl();
1556         }
1557         PERLsetup(NULL, script_path, "access");
1558         free(script_path);
1559
1560         uname = xstrdup(PERMuser);
1561         
1562         access_vec = vector_new();
1563
1564         perlAccess(uname, access_vec);
1565         free(uname);
1566
1567         access_realms[0] = xcalloc(1, sizeof(ACCESSGROUP));
1568
1569         PERMvectortoaccess(access_realms[0], "perl-dynamic", access_vec);
1570
1571         vector_free(access_vec);
1572       } else {
1573         syslog(L_ERROR, "No script specified in perl_access method.\n");
1574         Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
1575         ExitWithStats(1, true);
1576       }
1577       free(cpp);
1578       free(args);
1579 #endif /* DO_PERL */
1580 #ifdef DO_PYTHON
1581     } else if ((success_auth->access_script != NULL) && (success_auth->access_type == PERMpython_access)) {
1582         i = 0;
1583         cpp = xstrdup(success_auth->access_script);
1584         args = 0;
1585         Argify(cpp, &args);
1586         script_path = concat(args[0], (char *) 0);
1587         if ((script_path != NULL) && (strlen(script_path) > 0)) {
1588             uname = xstrdup(PERMuser);
1589             access_vec = vector_new();
1590
1591             PY_access(script_path, access_vec, uname);
1592             free(script_path);
1593             free(uname);
1594             free(args);
1595             
1596             access_realms[0] = xcalloc(1, sizeof(ACCESSGROUP));
1597             memset(access_realms[0], 0, sizeof(ACCESSGROUP));
1598             
1599             PERMvectortoaccess(access_realms[0], "python-dynamic", access_vec);
1600             
1601             vector_free(access_vec);
1602         } else {
1603             syslog(L_ERROR, "No script specified in python_access method.\n");
1604             Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
1605             ExitWithStats(1, true);
1606         }
1607         free(cpp);
1608 #endif /* DO_PYTHON */
1609     } else {
1610       for (i = 0; access_realms[i]; i++)
1611         ;
1612       user[0] = PERMuser;
1613       user[1] = 0;
1614       while (i--) {
1615         if ((!success_auth->key && !access_realms[i]->key) ||
1616             (access_realms[i]->key && success_auth->key &&
1617              strcmp(access_realms[i]->key, success_auth->key) == 0)) {
1618           if (!access_realms[i]->users)
1619             break;
1620           else if (!*PERMuser)
1621             continue;
1622           cp = xstrdup(access_realms[i]->users);
1623           list = 0;
1624           NGgetlist(&list, cp);
1625           if (PERMmatch(list, user)) {
1626             syslog(L_TRACE, "%s match_user %s %s", ClientHost,
1627                    PERMuser, access_realms[i]->users);
1628             free(cp);
1629             free(list[0]);
1630             free(list);
1631             break;
1632           } else
1633             syslog(L_TRACE, "%s no_match_user %s %s", ClientHost,
1634                    PERMuser, access_realms[i]->users);
1635           free(cp);
1636           free(list[0]);
1637           free(list);
1638         }
1639       }
1640     }
1641     if (i >= 0) {
1642         /* found the right access group */
1643         if (access_realms[i]->rejectwith) {
1644             syslog(L_ERROR, "%s rejected by rule (%s)",
1645                 ClientHost, access_realms[i]->rejectwith);
1646             Reply("%d Permission denied:  %s\r\n",
1647                 NNTP_ACCESS_VAL, access_realms[i]->rejectwith);
1648             ExitWithStats(1, true);
1649         }
1650         if (access_realms[i]->read) {
1651             cp = xstrdup(access_realms[i]->read);
1652             PERMspecified = NGgetlist(&PERMreadlist, cp);
1653             free(cp);
1654             PERMcanread = true;
1655         } else {
1656             syslog(L_TRACE, "%s no_read %s", ClientHost, access_realms[i]->name);
1657             PERMcanread = false;
1658         }
1659         if (access_realms[i]->post) {
1660             cp = xstrdup(access_realms[i]->post);
1661             NGgetlist(&PERMpostlist, cp);
1662             free(cp);
1663             PERMcanpost = true;
1664         } else {
1665             syslog(L_TRACE, "%s no_post %s", ClientHost, access_realms[i]->name);
1666             PERMcanpost = false;
1667         }
1668         PERMaccessconf = access_realms[i];
1669         MaxBytesPerSecond = PERMaccessconf->maxbytespersecond;
1670         if (PERMaccessconf->virtualhost) {
1671             if (PERMaccessconf->domain == NULL) {
1672                 syslog(L_ERROR, "%s virtualhost needs domain parameter(%s)",
1673                     ClientHost, PERMaccessconf->name);
1674                 Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
1675                 ExitWithStats(1, true);
1676             }
1677             if (VirtualPath)
1678                 free(VirtualPath);
1679             if (strcmp(innconf->pathhost, PERMaccessconf->pathhost) == 0) {
1680                 /* use domain, if pathhost in access relm matches one in
1681                    inn.conf to differentiate virtual host */
1682                 if (innconf->domain != NULL && strcmp(innconf->domain, PERMaccessconf->domain) == 0) {
1683                     syslog(L_ERROR, "%s domain parameter(%s) in readers.conf must be different from the one in inn.conf",
1684                         ClientHost, PERMaccessconf->name);
1685                     Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
1686                     ExitWithStats(1, true);
1687                 }
1688                 VirtualPath = concat(PERMaccessconf->domain, "!", (char *) 0);
1689             } else {
1690                 VirtualPath = concat(PERMaccessconf->pathhost, "!",
1691                                      (char *) 0);
1692             }
1693             VirtualPathlen = strlen(VirtualPath);
1694         } else
1695             VirtualPathlen = 0;
1696     } else {
1697         if (!noaccessconf)
1698             noaccessconf = xmalloc(sizeof(ACCESSGROUP));
1699         PERMaccessconf = noaccessconf;
1700         SetDefaultAccess(PERMaccessconf);
1701         syslog(L_TRACE, "%s no_access_realm", ClientHost);
1702     }
1703     /* check if dynamic access control is enabled, if so init it */
1704 #ifdef DO_PYTHON
1705     if ((success_auth->dynamic_type == PERMpython_dynamic) && success_auth->dynamic_script) {
1706       PY_dynamic_init(success_auth->dynamic_script);
1707     }
1708 #endif /* DO_PYTHON */
1709 }
1710
1711 /* strip blanks out of a string */
1712 static void CompressList(char *list)
1713 {
1714     char *cpto;
1715     bool inword = false;
1716
1717     for (cpto = list; *list; ) {
1718         if (strchr("\n \t,", *list) != NULL) {
1719             list++;
1720             if(inword) {
1721                 *cpto++ = ',';
1722                 inword = false;
1723             }
1724         } else {
1725             *cpto++ = *list++;
1726             inword = true;
1727         }
1728     }
1729     *cpto = '\0';
1730 }
1731
1732 static bool MatchHost(char *hostlist, char *host, char *ip)
1733 {
1734     char    **list;
1735     bool    ret = false;
1736     char    *cp;
1737     int     iter;
1738     char    *pat, 
1739             *p;
1740
1741     /*  If no hostlist are specified, by default they match.   */
1742
1743     if (hostlist == NULL) {
1744         return(true);
1745     }
1746
1747     list    = 0;
1748     cp      = xstrdup(hostlist);
1749
1750     NGgetlist(&list, cp);
1751
1752     /* default is no access */
1753     for (iter = 0; list[iter]; iter++) {
1754         ;
1755     }
1756
1757     while (iter-- > 0) {
1758         pat = list[iter];
1759         if (*pat == '!')
1760             pat++;
1761         ret = uwildmat(host, pat);
1762         if (!ret && *ip) {
1763             ret = uwildmat(ip, pat);
1764             if (!ret && (p = strchr(pat, '/')) != (char *)NULL) {
1765                 unsigned int bits, c;
1766                 struct in_addr ia, net, tmp;
1767 #ifdef HAVE_INET6
1768                 struct in6_addr ia6, net6;
1769                 unsigned char bits8;
1770 #endif
1771                 unsigned int mask;
1772
1773                 *p = '\0';
1774                 if (inet_aton(ip, &ia) && inet_aton(pat, &net)) {
1775                     if (strchr(p+1, '.') == (char *)NULL) {
1776                         /* string following / is a masklength */
1777                         mask = atoi(p+1);
1778                         for (bits = c = 0; c < mask && c < 32; c++)
1779                             bits |= (1 << (31 - c));
1780                         mask = htonl(bits);
1781                     } else {    /* or it may be a dotted quad bitmask */
1782                         if (inet_aton(p+1, &tmp))
1783                             mask = tmp.s_addr;
1784                         else    /* otherwise skip it */
1785                             continue;
1786                     }
1787                     if ((ia.s_addr & mask) == (net.s_addr & mask))
1788                         ret = true;
1789                 }
1790 #ifdef HAVE_INET6
1791                 else if (inet_pton(AF_INET6, ip, &ia6) && 
1792                          inet_pton(AF_INET6, pat, &net6)) {
1793                     mask = atoi(p+1);
1794                     ret = true;
1795                     /* do a prefix match byte by byte */
1796                     for (c = 0; c*8 < mask && c < sizeof(ia6); c++) {
1797                         if ( (c+1)*8 <= mask &&
1798                             ia6.s6_addr[c] != net6.s6_addr[c] ) {
1799                             ret = false;
1800                             break;
1801                         } else if ( (c+1)*8 > mask ) {
1802                             unsigned int b;
1803
1804                             for (bits8 = b = 0; b < (mask % 8); b++)
1805                                 bits8 |= (1 << (7 - b));
1806                             if ((ia6.s6_addr[c] & bits8) !=
1807                                 (net6.s6_addr[c] & bits8) ) {
1808                                 ret = false;
1809                                 break;
1810                             }
1811                         }
1812                     }
1813                 }
1814 #endif
1815             }
1816         }
1817         if (ret)
1818             break;
1819     }
1820     if (ret && list[iter][0] == '!')
1821         ret = false;
1822     free(list[0]);
1823     free(list);
1824     free(cp);
1825     return(ret);
1826 }
1827
1828 static void add_authgroup(AUTHGROUP *group)
1829 {
1830     int i;
1831
1832     if (auth_realms == NULL) {
1833         i = 0;
1834         auth_realms = xmalloc(2 * sizeof(AUTHGROUP *));
1835     } else {
1836         for (i = 0; auth_realms[i]; i++)
1837             ;
1838         auth_realms = xrealloc(auth_realms, (i + 2) * sizeof(AUTHGROUP *));
1839     }
1840     auth_realms[i] = group;
1841     auth_realms[i+1] = 0;
1842 }
1843
1844 static void add_accessgroup(ACCESSGROUP *group)
1845 {
1846     int i;
1847
1848     if (access_realms == NULL) {
1849         i = 0;
1850         access_realms = xmalloc(2 * sizeof(ACCESSGROUP *));
1851     } else {
1852         for (i = 0; access_realms[i]; i++)
1853             ;
1854         access_realms = xrealloc(access_realms, (i + 2) * sizeof(ACCESSGROUP *));
1855     }
1856     access_realms[i] = group;
1857     access_realms[i+1] = 0;
1858 }
1859
1860 /* clean out access groups that don't apply to any of our auth groups. */
1861
1862 static void strip_accessgroups(void)
1863 {
1864     int i, j;
1865
1866     /* flag the access group as used or not */
1867
1868     if(access_realms != NULL) {
1869         for (j = 0; access_realms[j] != NULL; j++) {
1870             access_realms[j]->used = 0;
1871         }
1872     } else {
1873         syslog(L_TRACE, "No access realms to check!");
1874     }
1875
1876     /*  If there are auth realms to check...    */
1877
1878     if(auth_realms != NULL) {
1879         /*  ... Then for each auth realm...     */
1880
1881         for (i = 0; auth_realms[i] != NULL; i++) {
1882
1883             /*  ... for each access realm...    */
1884
1885             for (j = 0; access_realms[j] != NULL; j++) {
1886
1887                 /*  If the access realm isn't already in use... */
1888
1889                 if (! access_realms[j]->used) {
1890                     /*  Check to see if both the access_realm key and 
1891                         auth_realm key are NULL...                      */
1892
1893                     if (!access_realms[j]->key && !auth_realms[i]->key) {
1894                         /*  If so, mark the realm in use and continue on... */
1895
1896                         access_realms[j]->used = 1;
1897                     } else { 
1898                         /*  If not, check to see if both the access_realm and 
1899                             auth_realm are NOT _both_ NULL, and see if they are
1900                             equal...                                            */
1901
1902                         if (access_realms[j]->key && auth_realms[i]->key &&
1903                             strcmp(access_realms[j]->key, auth_realms[i]->key) == 0) {
1904
1905                             /*  And if so, mark the realm in use.   */
1906
1907                             access_realms[j]->used = 1;
1908                         }
1909                     }
1910                 }
1911             }
1912         }
1913     } else {
1914         syslog(L_TRACE, "No auth realms to check!");
1915     }
1916
1917     /* strip out unused access groups */
1918     i = j = 0;
1919
1920     while (access_realms[i] != NULL) {
1921         if (access_realms[i]->used)
1922             access_realms[j++] = access_realms[i];
1923         else
1924             syslog(L_TRACE, "%s removing irrelevant access group %s", 
1925                    ClientHost, access_realms[i]->name);
1926         i++;
1927     }
1928     access_realms[j] = 0;
1929 }
1930
1931 typedef struct _EXECSTUFF {
1932     pid_t pid;
1933     int rdfd, errfd, wrfd;
1934 } EXECSTUFF;
1935
1936 static EXECSTUFF *ExecProg(char *arg0, char **args)
1937 {
1938     EXECSTUFF *ret;
1939     int rdfd[2], errfd[2], wrfd[2];
1940     pid_t pid;
1941     struct stat stb;
1942
1943 #if !defined(S_IXUSR) && defined(_S_IXUSR)
1944 #define S_IXUSR _S_IXUSR
1945 #endif /* !defined(S_IXUSR) && defined(_S_IXUSR) */
1946
1947 #if !defined(S_IXUSR) && defined(S_IEXEC)
1948 #define S_IXUSR S_IEXEC
1949 #endif  /* !defined(S_IXUSR) && defined(S_IEXEC) */
1950
1951     if (stat(arg0, &stb) || !(stb.st_mode&S_IXUSR))
1952         return(0);
1953
1954     pipe(rdfd);
1955     pipe(errfd);
1956     pipe(wrfd);
1957     switch (pid = fork()) {
1958       case -1:
1959         close(rdfd[0]);
1960         close(rdfd[1]);
1961         close(errfd[0]);
1962         close(errfd[1]);
1963         close(wrfd[0]);
1964         close(wrfd[1]);
1965         return(0);
1966       case 0:
1967         close(rdfd[0]);
1968         dup2(rdfd[1], 1);
1969         close(errfd[0]);
1970         dup2(errfd[1], 2);
1971         close(wrfd[1]);
1972         dup2(wrfd[0], 0);
1973         execv(arg0, args);
1974         /* if we got here, there was an error */
1975         syslog(L_ERROR, "%s perm could not exec %s: %m", ClientHost, arg0);
1976         exit(1);
1977     }
1978     close(rdfd[1]);
1979     close(errfd[1]);
1980     close(wrfd[0]);
1981     ret = xmalloc(sizeof(EXECSTUFF));
1982     ret->pid = pid;
1983     ret->rdfd = rdfd[0];
1984     ret->errfd = errfd[0];
1985     ret->wrfd = wrfd[1];
1986     return(ret);
1987 }
1988
1989 static void GetConnInfo(METHOD *method, char *buf)
1990 {
1991     int i;
1992
1993     buf[0] = '\0';
1994     if (*ClientHost)
1995         sprintf(buf, "ClientHost: %s\r\n", ClientHost);
1996     if (*ClientIpString)
1997         sprintf(buf+strlen(buf), "ClientIP: %s\r\n", ClientIpString);
1998     if (ClientPort)
1999         sprintf(buf+strlen(buf), "ClientPort: %d\r\n", ClientPort);
2000     if (*ServerIpString)
2001         sprintf(buf+strlen(buf), "LocalIP: %s\r\n", ServerIpString);
2002     if (ServerPort)
2003         sprintf(buf+strlen(buf), "LocalPort: %d\r\n", ServerPort);
2004     /* handle this here, since we only get here when we're about to exec
2005      * something. */
2006     if (method->extra_headers) {
2007         for (i = 0; method->extra_headers[i]; i++)
2008             sprintf(buf+strlen(buf), "%s\r\n", method->extra_headers[i]);
2009     }
2010 }
2011
2012 static char ubuf[SMBUF];
2013
2014 typedef void (*LineFunc)(char*);
2015
2016 /* messages from a program's stdout */
2017 static void HandleProgLine(char *ln)
2018 {
2019     if (strncasecmp(ln, "User:", strlen("User:")) == 0)
2020         strlcpy(ubuf, ln + strlen("User:"), sizeof(ubuf));
2021 }
2022
2023 /* messages from a programs stderr */
2024 static void HandleErrorLine(char *ln)
2025 {
2026     syslog(L_NOTICE, "%s auth_err %s", ClientHost, ln);
2027 }
2028
2029 static bool
2030 HandleProgInput(int fd, char *buf, int buflen, LineFunc f)
2031 {
2032     char *nl;
2033     char *start;
2034     int curpos, got;
2035
2036     /* read the data */
2037     curpos = strlen(buf);
2038     if (curpos >= buflen-1) {
2039         /* data overflow (on one line!) */
2040         return false;
2041     }
2042     got = read(fd, buf+curpos, buflen-curpos-1);
2043     if (got <= 0)
2044         return false;
2045     buf[curpos+got] = '\0';
2046
2047     /* break what we got up into lines */
2048     start = nl = buf;
2049     while ((nl = strchr(nl, '\n')) != NULL) {
2050         if (nl != buf && *(nl-1) == '\r')
2051             *(nl-1) = '\0';
2052         *nl++ = '\0';
2053         f(start);
2054         start = nl;
2055     }
2056
2057     /* delete all the lines we've read from the buffer. */
2058     /* 'start' points to the end of the last unterminated string */
2059     nl = start;
2060     start = buf;
2061     if (nl == start) {
2062         return true;
2063     }
2064
2065     while (*nl) {
2066         *start++ = *nl++;
2067     }
2068
2069     *start = '\0';
2070
2071     return true;
2072 }
2073
2074 static void GetProgInput(EXECSTUFF *prog)
2075 {
2076     fd_set rfds, tfds;
2077     int maxfd;
2078     int got;
2079     bool okay;
2080     struct timeval tmout;
2081     pid_t tmp;
2082     int status;
2083     char rdbuf[BIG_BUFFER], errbuf[BIG_BUFFER];
2084     double start, end;
2085
2086     FD_ZERO(&rfds);
2087     FD_SET(prog->rdfd, &rfds);
2088     FD_SET(prog->errfd, &rfds);
2089     tfds = rfds;
2090     maxfd = prog->rdfd > prog->errfd ? prog->rdfd : prog->errfd;
2091     tmout.tv_sec = 5;
2092     tmout.tv_usec = 0;
2093     rdbuf[0] = errbuf[0] = '\0';
2094     start = TMRnow_double();
2095     while ((got = select(maxfd+1, &tfds, 0, 0, &tmout)) >= 0) {
2096         end = TMRnow_double();
2097         IDLEtime += end - start;
2098         start = end;
2099         tmout.tv_sec = 5;
2100         tmout.tv_usec = 0;
2101         if (got > 0) {
2102             if (FD_ISSET(prog->rdfd, &tfds)) {
2103                 okay = HandleProgInput(prog->rdfd, rdbuf, sizeof(rdbuf), HandleProgLine);
2104                 if (!okay) {
2105                     close(prog->rdfd);
2106                     FD_CLR(prog->rdfd, &tfds);
2107                     kill(prog->pid, SIGTERM);
2108                 }
2109             }
2110             if (FD_ISSET(prog->errfd, &tfds)) {
2111                 okay = HandleProgInput(prog->errfd, errbuf, sizeof(errbuf), HandleErrorLine);
2112                 if (!okay) {
2113                     close(prog->errfd);
2114                     FD_CLR(prog->errfd, &tfds);
2115                     kill(prog->pid, SIGTERM);
2116                 }
2117             }
2118         }
2119         tfds = rfds;
2120     }
2121     end = TMRnow_double();
2122     IDLEtime += end - start;
2123     /* wait for it if he's toast. */
2124     do {
2125         tmp = waitpid(prog->pid, &status, 0);
2126     } while ((tmp >= 0 || (tmp < 0 && errno == EINTR)) &&
2127       !WIFEXITED(status) && !WIFSIGNALED(status));
2128     if (WIFSIGNALED(status)) {
2129         ubuf[0] = '\0';
2130         syslog(L_NOTICE, "%s bad_hook program caught signal %d", ClientHost,
2131           WTERMSIG(status));
2132     } else if (WIFEXITED(status)) {
2133         if (WEXITSTATUS(status) != 0) {
2134             ubuf[0] = '\0';
2135             syslog(L_TRACE, "%s bad_hook program exited with status %d",
2136               ClientHost, WEXITSTATUS(status));
2137         }
2138     } else {
2139         syslog(L_ERROR, "%s bad_hook waitpid failed: %m", ClientHost);
2140         ubuf[0] = '\0';
2141     }
2142 }
2143
2144 /* execute a series of resolvers to get the remote username */
2145 static char *ResolveUser(AUTHGROUP *auth)
2146 {
2147     int i, j;
2148     char *cp;
2149     char **args;
2150     char *arg0;
2151     char *resdir;
2152     char *tmp;
2153     EXECSTUFF *foo;
2154     int done        = 0;
2155     char buf[BIG_BUFFER];
2156
2157     if (!auth->res_methods)
2158         return(0);
2159
2160     tmp = concatpath(innconf->pathbin, _PATH_AUTHDIR);
2161     resdir = concatpath(tmp, _PATH_AUTHDIR_NOPASS);
2162     free(tmp);
2163
2164     ubuf[0] = '\0';
2165     for (i = 0; auth->res_methods[i]; i++) {
2166         /* build the command line */
2167         syslog(L_TRACE, "%s res starting resolver %s", ClientHost, auth->res_methods[i]->program);
2168         if (auth->res_methods[i]->extra_logs) {
2169             for (j = 0; auth->res_methods[i]->extra_logs[j]; j++)
2170                 syslog(L_NOTICE, "%s res also-log: %s", ClientHost,
2171                   auth->res_methods[i]->extra_logs[j]);
2172         }
2173         cp = xstrdup(auth->res_methods[i]->program);
2174         args = 0;
2175         Argify(cp, &args);
2176         arg0 = args[0];
2177         if (args[0][0] != '/')
2178             arg0 = concat(resdir, "/", arg0, (char *) 0);
2179         /* exec the resolver */
2180         foo = ExecProg(arg0, args);
2181         if (foo) {
2182             GetConnInfo(auth->res_methods[i], buf);
2183             strlcat(buf, ".\r\n", sizeof(buf));
2184             xwrite(foo->wrfd, buf, strlen(buf));
2185             close(foo->wrfd);
2186
2187             GetProgInput(foo);
2188             done = (ubuf[0] != '\0');
2189             if (done)
2190                 syslog(L_TRACE, "%s res resolver successful, user %s", ClientHost, ubuf);
2191             else
2192                 syslog(L_TRACE, "%s res resolver failed", ClientHost);
2193             free(foo);
2194         } else
2195             syslog(L_ERROR, "%s res couldnt start resolver: %m", ClientHost);
2196         /* clean up */
2197         if (args[0][0] != '/') {
2198             free(arg0);
2199         }
2200         free(args);
2201         free(cp);
2202         if (done)
2203             /* this resolver succeeded */
2204             break;
2205     }
2206     free(resdir);
2207     if (ubuf[0])
2208         return(ubuf);
2209     return(0);
2210 }
2211
2212 /* execute a series of authenticators to get the remote username */
2213 static char *AuthenticateUser(AUTHGROUP *auth, char *username, char *password, char *errorstr)
2214 {
2215     int i, j;
2216     char *cp;
2217     char **args;
2218     char *arg0;
2219     char *resdir;
2220     char *tmp;
2221     char *script_path;
2222     char newUser[BIG_BUFFER];
2223     EXECSTUFF *foo;
2224     int done        = 0;
2225     int code;
2226     char buf[BIG_BUFFER];
2227
2228     if (!auth->auth_methods)
2229         return(0);
2230
2231     tmp = concatpath(innconf->pathbin, _PATH_AUTHDIR);
2232     resdir = concatpath(tmp, _PATH_AUTHDIR_PASSWD);
2233     free(tmp);
2234
2235     ubuf[0] = '\0';
2236     newUser[0] = '\0';
2237     for (i = 0; auth->auth_methods[i]; i++) {
2238       if (auth->auth_methods[i]->type == PERMperl_auth) {
2239 #ifdef DO_PERL
2240             cp = xstrdup(auth->auth_methods[i]->program);
2241             args = 0;
2242             Argify(cp, &args);
2243             script_path = concat(args[0], (char *) 0);
2244             if ((script_path != NULL) && (strlen(script_path) > 0)) {
2245                 if(!PerlLoaded) {
2246                     loadPerl();
2247                 }
2248                 PERLsetup(NULL, script_path, "authenticate");
2249                 free(script_path);
2250                 perlAuthInit();
2251           
2252                 code = perlAuthenticate(username, password, errorstr, newUser);
2253                 if (code == NNTP_AUTH_OK_VAL) {
2254                     /* Set the value of ubuf to the right username */
2255                     if (newUser[0] != '\0') {
2256                       strlcpy(ubuf, newUser, sizeof(ubuf));
2257                     } else {
2258                       strlcpy(ubuf, username, sizeof(ubuf));
2259                     }
2260
2261                     syslog(L_NOTICE, "%s user %s", ClientHost, ubuf);
2262                     if (LLOGenable) {
2263                         fprintf(locallog, "%s user %s\n", ClientHost, ubuf);
2264                         fflush(locallog);
2265                     }
2266                     break;
2267                 } else {
2268                     syslog(L_NOTICE, "%s bad_auth", ClientHost);
2269                 }            
2270             } else {
2271               syslog(L_ERROR, "No script specified in auth method.\n");
2272             }
2273 #endif  /* DO_PERL */    
2274       } else if (auth->auth_methods[i]->type == PERMpython_auth) {
2275 #ifdef DO_PYTHON
2276         cp = xstrdup(auth->auth_methods[i]->program);
2277         args = 0;
2278         Argify(cp, &args);
2279         script_path = concat(args[0], (char *) 0);
2280         if ((script_path != NULL) && (strlen(script_path) > 0)) {
2281           code = PY_authenticate(script_path, username, password, errorstr, newUser);
2282           free(script_path);
2283           if (code < 0) {
2284             syslog(L_NOTICE, "PY_authenticate(): authentication skipped due to no Python authentication method defined.");
2285           } else {
2286             if (code == NNTP_AUTH_OK_VAL) {
2287               /* Set the value of ubuf to the right username */
2288               if (newUser[0] != '\0') {
2289                   strlcpy(ubuf, newUser, sizeof(ubuf));
2290               } else {
2291                   strlcpy(ubuf, username, sizeof(ubuf));
2292               }
2293               
2294               syslog(L_NOTICE, "%s user %s", ClientHost, ubuf);
2295               if (LLOGenable) {
2296                 fprintf(locallog, "%s user %s\n", ClientHost, ubuf);
2297                 fflush(locallog);
2298               }
2299               break;
2300             } else {
2301               syslog(L_NOTICE, "%s bad_auth", ClientHost);
2302             }
2303           }
2304         } else {
2305           syslog(L_ERROR, "No script specified in auth method.\n");
2306         }
2307 #endif /* DO_PYTHON */
2308       } else {
2309         if (auth->auth_methods[i]->users &&
2310           !MatchUser(auth->auth_methods[i]->users, username))
2311             continue;
2312
2313         /* build the command line */
2314         syslog(L_TRACE, "%s auth starting authenticator %s", ClientHost, auth->auth_methods[i]->program);
2315         if (auth->auth_methods[i]->extra_logs) {
2316             for (j = 0; auth->auth_methods[i]->extra_logs[j]; j++)
2317                 syslog(L_NOTICE, "%s auth also-log: %s", ClientHost,
2318                   auth->auth_methods[i]->extra_logs[j]);
2319         }
2320         cp = xstrdup(auth->auth_methods[i]->program);
2321         args = 0;
2322         Argify(cp, &args);
2323         arg0 = args[0];
2324         if (args[0][0] != '/')
2325             arg0 = concat(resdir, "/", arg0, (char *) 0);
2326         /* exec the authenticator */
2327         foo = ExecProg(arg0, args);
2328         if (foo) {
2329             GetConnInfo(auth->auth_methods[i], buf);
2330             snprintf(buf+strlen(buf), sizeof(buf) - strlen(buf) - 3,
2331                      "ClientAuthname: %s\r\n", username);
2332             snprintf(buf+strlen(buf), sizeof(buf) - strlen(buf) - 3,
2333                      "ClientPassword: %s\r\n", password);
2334             strlcat(buf, ".\r\n", sizeof(buf));
2335             xwrite(foo->wrfd, buf, strlen(buf));
2336             close(foo->wrfd);
2337
2338             GetProgInput(foo);
2339             done = (ubuf[0] != '\0');
2340             if (done)
2341                 syslog(L_TRACE, "%s auth authenticator successful, user %s", ClientHost, ubuf);
2342             else
2343                 syslog(L_TRACE, "%s auth authenticator failed", ClientHost);
2344             free(foo);
2345         } else
2346             syslog(L_ERROR, "%s auth couldnt start authenticator: %m", ClientHost);
2347         /* clean up */
2348         if (args[0][0] != '/') {
2349             free(arg0);
2350         }
2351         free(args);
2352         free(cp);
2353         if (done)
2354             /* this authenticator succeeded */
2355             break;
2356       }
2357     }
2358     free(resdir);
2359     if (ubuf[0])
2360         return(ubuf);
2361     return(0);
2362 }