]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
Prevent pathological circular lists.
[~andy/fetchmail] / rcfile_y.y
1 %{
2 /*
3  * rcfile_y.y -- Run control file parser for fetchmail
4  *
5  * For license terms, see the file COPYING in this directory.
6  */
7
8 #include "config.h"
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <sys/file.h>
12 #if defined(HAVE_SYS_WAIT_H)
13 #include <sys/wait.h>
14 #endif
15 #include <sys/stat.h>
16 #include <errno.h>
17 #if defined(STDC_HEADERS)
18 #include <stdlib.h>
19 #endif
20 #if defined(HAVE_UNISTD_H)
21 #include <unistd.h>
22 #endif
23 #include <string.h>
24
25 #if NET_SECURITY
26 #include <net/security.h>
27 #endif /* NET_SECURITY */
28
29 #include "fetchmail.h"
30
31 /* parser reads these */
32 char *rcfile;                   /* path name of rc file */
33 struct query cmd_opts;          /* where to put command-line info */
34
35 /* parser sets these */
36 int poll_interval;              /* poll interval in seconds */
37 char *logfile;                  /* log file for daemon mode */
38 flag errors_to_syslog;          /* if syslog was set */
39 flag use_invisible;             /* if invisible was set */
40 struct query *querylist;        /* head of server list (globally visible) */
41
42 int yydebug;                    /* in case we didn't generate with -- debug */
43
44 static struct query current;    /* current server record */
45 static int prc_errflag;
46 static struct hostdata *leadentry;
47 static flag trailer;
48
49 static void record_current();
50 static void user_reset();
51 static void reset_server(char *name, int skip);
52
53 /* using Bison, this arranges that yydebug messages will show actual tokens */
54 extern char * yytext;
55 #define YYPRINT(fp, type, val)  fprintf(fp, " = \"%s\"", yytext)
56 %}
57
58 %union {
59   int proto;
60   int number;
61   char *sval;
62 }
63
64 %token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL
65 %token AUTHENTICATE TIMEOUT KPOP KERBEROS4 KERBEROS5 KERBEROS
66 %token ENVELOPE QVIRTUAL USERNAME PASSWORD FOLDER SMTPHOST MDA SMTPADDRESS
67 %token SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT
68 %token IS HERE THERE TO MAP WILDCARD
69 %token BATCHLIMIT FETCHLIMIT EXPUNGE
70 %token SET LOGFILE DAEMON SYSLOG INVISIBLE NETSEC INTERFACE MONITOR
71 %token <proto> PROTO
72 %token <sval>  STRING
73 %token <number> NUMBER
74 %token NO KEEP FLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS DROPSTATUS
75 %token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE
76
77 %%
78
79 rcfile          : /* empty */
80                 | statement_list
81                 ;
82
83 statement_list  : statement
84                 | statement_list statement
85                 ;
86
87 optmap          : MAP | /* EMPTY */;
88
89 /* future global options should also have the form SET <name> optmap <value> */
90 statement       : SET LOGFILE optmap STRING     {logfile = xstrdup($4);}
91                 | SET DAEMON optmap NUMBER      {poll_interval = $4;}
92                 | SET SYSLOG                    {errors_to_syslog = TRUE;}
93                 | SET INVISIBLE                 {use_invisible = TRUE;}
94
95 /* 
96  * The way the next two productions are written depends on the fact that
97  * userspecs cannot be empty.  It's a kluge to deal with files that set
98  * up a load of defaults and then have poll statements following with no
99  * user options at all. 
100  */
101                 | define_server serverspecs             {record_current();}
102                 | define_server serverspecs userspecs
103
104 /* detect and complain about the most common user error */
105                 | define_server serverspecs userspecs serv_option
106                         {yyerror("server option after user options");}
107                 ;
108
109 define_server   : POLL STRING           {reset_server($2, FALSE);}
110                 | SKIP STRING           {reset_server($2, TRUE);}
111                 | DEFAULTS              {reset_server("defaults", FALSE);}
112                 ;
113
114 serverspecs     : /* EMPTY */
115                 | serverspecs serv_option
116                 ;
117
118 alias_list      : STRING                {save_str(&current.server.akalist,$1,0);}
119                 | alias_list STRING     {save_str(&current.server.akalist,$2,0);}
120                 ;
121
122 domain_list     : STRING                {save_str(&current.server.localdomains,$1,0);}
123                 | domain_list STRING    {save_str(&current.server.localdomains,$2,0);}
124                 ;
125
126 serv_option     : AKA alias_list
127                 | VIA STRING            {current.server.via = xstrdup($2);}
128                 | LOCALDOMAINS domain_list
129                 | PROTOCOL PROTO        {current.server.protocol = $2;}
130                 | PROTOCOL KPOP         {
131                                             current.server.protocol = P_POP3;
132 #ifdef KERBEROS_V5
133                                             current.server.preauthenticate = A_KERBEROS_V5;
134 #else
135                                             current.server.preauthenticate = A_KERBEROS_V4;
136 #endif /* KERBEROS_V5 */
137 #if INET6
138                                             current.server.service = KPOP_PORT;
139 #else /* INET6 */
140                                             current.server.port = KPOP_PORT;
141 #endif /* INET6 */
142                                         }
143                 | UIDL                  {current.server.uidl = FLAG_TRUE;}
144                 | NO UIDL               {current.server.uidl  = FLAG_FALSE;}
145                 | SERVICE STRING        {
146 #if INET6
147                                         current.server.service = $2;
148 #endif /* INET6 */
149                                         }
150                 | PORT NUMBER           {
151 #if !INET6
152                                         current.server.port = $2;
153 #endif /* !INET6 */
154                                         }
155                 | INTERVAL NUMBER               {current.server.interval = $2;}
156                 | AUTHENTICATE PASSWORD {current.server.preauthenticate = A_PASSWORD;}
157                 | AUTHENTICATE KERBEROS4        {current.server.preauthenticate = A_KERBEROS_V4;}
158                 | AUTHENTICATE KERBEROS5        {current.server.preauthenticate = A_KERBEROS_V5;}
159                 | AUTHENTICATE KERBEROS         {
160 #ifdef KERBEROS_V5
161                     current.server.preauthenticate = A_KERBEROS_V5;
162 #else
163                     current.server.preauthenticate = A_KERBEROS_V4;
164 #endif /* KERBEROS_V5 */
165                 }
166                 | TIMEOUT NUMBER        {current.server.timeout = $2;}
167
168                 | ENVELOPE NUMBER STRING 
169                                         {
170                                             current.server.envelope = 
171                                                 xstrdup($3);
172                                             current.server.envskip = $2;
173                                         }
174                 | ENVELOPE STRING
175                                         {
176                                             current.server.envelope = 
177                                                 xstrdup($2);
178                                             current.server.envskip = 0;
179                                         }
180
181                 | QVIRTUAL STRING       {current.server.qvirtual=xstrdup($2);}
182                 | NETSEC STRING         {
183 #ifdef NET_SECURITY
184                                             void *request;
185                                             int requestlen;
186
187                                             if (net_security_strtorequest($2, &request, &requestlen))
188                                                 yyerror("invalid security request");
189                                             else {
190                                                 current.server.netsec = xstrdup($2);
191                                                 free(request);
192                                             }
193 #else
194                                             yyerror("network-security support disabled");
195 #endif /* NET_SECURITY */
196                                         }
197                 | INTERFACE STRING      {
198 #if defined(linux) && !defined(INET6)
199                                         interface_parse($2, &current.server);
200 #else /* defined(linux) && !defined(INET6) */
201                                         fprintf(stderr, "fetchmail: interface option is only supported under Linux\n");
202 #endif /* defined(linux) && !defined(INET6) */
203                                         }
204                 | MONITOR STRING        {
205 #if defined(linux) && !defined(INET6)
206                                         current.server.monitor = xstrdup($2);
207 #else /* defined(linux) && !defined(INET6) */
208                                         fprintf(stderr, "fetchmail: monitor option is only supported under Linux\n");
209 #endif /* defined(linux) && !defined(INET6) */
210                                         }
211                 | DNS                   {current.server.dns = FLAG_TRUE;}
212                 | NO DNS                {current.server.dns = FLAG_FALSE;}
213                 | NO ENVELOPE           {current.server.envelope = STRING_DISABLED;}
214                 ;
215
216 userspecs       : user1opts             {record_current(); user_reset();}
217                 | explicits
218                 ;
219
220 explicits       : explicitdef           {record_current(); user_reset();}
221                 | explicits explicitdef {record_current(); user_reset();}
222                 ;
223
224 explicitdef     : userdef user0opts
225                 ;
226
227 userdef         : USERNAME STRING       {current.remotename = xstrdup($2);}
228                 | USERNAME mapping_list HERE
229                 | USERNAME STRING THERE {current.remotename = xstrdup($2);}
230                 ;
231
232 user0opts       : /* EMPTY */
233                 | user0opts user_option
234                 ;
235
236 user1opts       : user_option
237                 | user1opts user_option
238                 ;
239
240 localnames      : WILDCARD              {current.wildcard =  TRUE;}
241                 | mapping_list          {current.wildcard =  FALSE;}
242                 | mapping_list WILDCARD {current.wildcard =  TRUE;}
243                 ;
244
245 mapping_list    : mapping               
246                 | mapping_list mapping
247                 ;
248
249 mapping         : STRING        
250                                 {save_str_pair(&current.localnames, $1, NULL);}
251                 | STRING MAP STRING
252                                 {save_str_pair(&current.localnames, $1, $3);}
253                 ;
254
255 folder_list     : STRING                {save_str(&current.mailboxes,$1,0);}
256                 | folder_list STRING    {save_str(&current.mailboxes,$2,0);}
257                 ;
258
259 smtp_list       : STRING                {save_str(&current.smtphunt, $1,TRUE);}
260                 | smtp_list STRING      {save_str(&current.smtphunt, $2,TRUE);}
261                 ;
262
263 user_option     : TO localnames HERE
264                 | TO localnames
265                 | IS localnames HERE
266                 | IS localnames
267
268                 | IS STRING THERE       {current.remotename = xstrdup($2);}
269                 | PASSWORD STRING       {current.password   = xstrdup($2);}
270                 | FOLDER folder_list
271                 | SMTPHOST smtp_list
272                 | SMTPADDRESS STRING    {current.smtpaddress = xstrdup($2);}
273                 | SPAMRESPONSE NUMBER   {current.antispam = $2;}
274                 | MDA STRING            {current.mda        = xstrdup($2);}
275                 | PRECONNECT STRING     {current.preconnect = xstrdup($2);}
276                 | POSTCONNECT STRING    {current.postconnect = xstrdup($2);}
277
278                 | KEEP                  {current.keep       = FLAG_TRUE;}
279                 | FLUSH                 {current.flush      = FLAG_TRUE;}
280                 | FETCHALL              {current.fetchall   = FLAG_TRUE;}
281                 | REWRITE               {current.rewrite    = FLAG_TRUE;}
282                 | FORCECR               {current.forcecr    = FLAG_TRUE;}
283                 | STRIPCR               {current.stripcr    = FLAG_TRUE;}
284                 | PASS8BITS             {current.pass8bits  = FLAG_TRUE;}
285                 | DROPSTATUS            {current.dropstatus = FLAG_TRUE;}
286                 | MIMEDECODE            {current.mimedecode = FLAG_TRUE;}
287
288                 | NO KEEP               {current.keep       = FLAG_FALSE;}
289                 | NO FLUSH              {current.flush      = FLAG_FALSE;}
290                 | NO FETCHALL           {current.fetchall   = FLAG_FALSE;}
291                 | NO REWRITE            {current.rewrite    = FLAG_FALSE;}
292                 | NO FORCECR            {current.forcecr    = FLAG_FALSE;}
293                 | NO STRIPCR            {current.stripcr    = FLAG_FALSE;}
294                 | NO PASS8BITS          {current.pass8bits  = FLAG_FALSE;}
295                 | NO DROPSTATUS         {current.dropstatus = FLAG_FALSE;}
296                 | NO MIMEDECODE         {current.mimedecode = FLAG_FALSE;}
297
298                 | LIMIT NUMBER          {current.limit      = NUM_VALUE($2);}
299                 | FETCHLIMIT NUMBER     {current.fetchlimit = NUM_VALUE($2);}
300                 | BATCHLIMIT NUMBER     {current.batchlimit = NUM_VALUE($2);}
301                 | EXPUNGE NUMBER        {current.expunge    = NUM_VALUE($2);}
302                 ;
303 %%
304
305 /* lexer interface */
306 extern char *rcfile;
307 extern int prc_lineno;
308 extern char *yytext;
309 extern FILE *yyin;
310
311 static struct query *hosttail;  /* where to add new elements */
312
313 void yyerror (const char *s)
314 /* report a syntax error */
315 {
316     error_at_line( 0, 0, rcfile, prc_lineno, "%s at %s", s, 
317                    (yytext && yytext[0]) ? yytext : "end of input");
318     prc_errflag++;
319 }
320
321 int prc_filecheck(pathname)
322 /* check that a configuration file is secure */
323 const char *pathname;           /* pathname for the configuration file */
324 {
325 #ifndef __EMX__
326     struct stat statbuf;
327
328     errno = 0;
329
330     /* special cases useful for debugging purposes */
331     if (strcmp("/dev/null", pathname) == 0)
332         return(0);
333
334     /* the run control file must have the same uid as the REAL uid of this 
335        process, it must have permissions no greater than 600, and it must not 
336        be a symbolic link.  We check these conditions here. */
337
338     if (lstat(pathname, &statbuf) < 0) {
339         if (errno == ENOENT) 
340             return(0);
341         else {
342             error(0, errno, "lstat: %s", pathname);
343             return(PS_IOERR);
344         }
345     }
346
347     if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
348         fprintf(stderr, "File %s must not be a symbolic link.\n", pathname);
349         return(PS_AUTHFAIL);
350     }
351
352     if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) {
353         fprintf(stderr, "File %s must have no more than -rw------ (0600) permissions.\n", 
354                 pathname);
355         return(PS_AUTHFAIL);
356     }
357
358     if (statbuf.st_uid != getuid()) {
359         fprintf(stderr, "File %s must be owned by you.\n", pathname);
360         return(PS_AUTHFAIL);
361     }
362 #endif
363     return(0);
364 }
365
366 int prc_parse_file (const char *pathname, const flag securecheck)
367 /* digest the configuration into a linked list of host records */
368 {
369     prc_errflag = 0;
370     querylist = hosttail = (struct query *)NULL;
371
372     errno = 0;
373
374     /* Check that the file is secure */
375     if (securecheck && (prc_errflag = prc_filecheck(pathname)) != 0)
376         return(prc_errflag);
377
378     if (errno == ENOENT)
379         return(0);
380
381     /* Open the configuration and feed it to the lexer. */
382     if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
383         error(0, errno, "open: %s", pathname);
384         return(PS_IOERR);
385     }
386
387     yyparse();          /* parse entire file */
388
389     fclose(yyin);
390
391     if (prc_errflag) 
392         return(PS_SYNTAX);
393     else
394         return(0);
395 }
396
397 static void reset_server(char *name, int skip)
398 /* clear the entire global record and initialize it with a new name */
399 {
400     trailer = FALSE;
401     memset(&current,'\0',sizeof(current));
402     current.smtp_socket = -1;
403     current.server.pollname = xstrdup(name);
404     current.server.skip = skip;
405 }
406
407
408 static void user_reset(void)
409 /* clear the global current record (user parameters) used by the parser */
410 {
411     struct hostdata save;
412
413     /*
414      * Purpose of this code is to initialize the new server block, but
415      * preserve whatever server name was previously set.  Also
416      * preserve server options unless the command-line explicitly
417      * overrides them.
418      */
419     save = current.server;
420
421     memset(&current, '\0', sizeof(current));
422     current.smtp_socket = -1;
423
424     current.server = save;
425 }
426
427 struct query *hostalloc(init)
428 /* append a host record to the host list */
429 struct query *init;     /* pointer to block containing initial values */
430 {
431     struct query *node;
432
433     /* allocate new node */
434     node = (struct query *) xmalloc(sizeof(struct query));
435
436     /* initialize it */
437     if (init)
438         memcpy(node, init, sizeof(struct query));
439
440     /* append to end of list */
441     if (hosttail != (struct query *) 0)
442         hosttail->next = node;  /* list contains at least one element */
443     else
444         querylist = node;       /* list is empty */
445     hosttail = node;
446
447     if (trailer)
448         node->server.lead_server = leadentry;
449     else
450     {
451         node->server.lead_server = NULL;
452         leadentry = &node->server;
453     }
454
455     return(node);
456 }
457
458 static void record_current(void)
459 /* register current parameters and append to the host list */
460 {
461 #define FLAG_FORCE(fld) if (cmd_opts.fld) current.fld = cmd_opts.fld
462     FLAG_FORCE(server.localdomains);
463     FLAG_FORCE(localnames);
464     FLAG_FORCE(mailboxes);
465     FLAG_FORCE(smtphunt);
466
467     FLAG_FORCE(server.via);
468     FLAG_FORCE(server.protocol);
469 #if INET6
470     FLAG_FORCE(server.service);
471     FLAG_FORCE(server.netsec);
472 #else /* INET6 */
473     FLAG_FORCE(server.port);
474 #endif /* INET6 */
475     FLAG_FORCE(server.interval);
476     FLAG_FORCE(server.preauthenticate);
477     FLAG_FORCE(server.timeout);
478     FLAG_FORCE(server.envelope);
479     FLAG_FORCE(server.envskip);
480     FLAG_FORCE(server.qvirtual);
481     FLAG_FORCE(server.skip);
482     FLAG_FORCE(server.dns);
483     FLAG_FORCE(server.uidl);
484
485 #ifdef linux
486     FLAG_FORCE(server.interface);
487     FLAG_FORCE(server.monitor);
488     FLAG_FORCE(server.interface_pair);
489 #endif /* linux */
490
491     FLAG_FORCE(remotename);
492     FLAG_FORCE(password);
493     FLAG_FORCE(mda);
494     FLAG_FORCE(smtpaddress);
495     FLAG_FORCE(antispam);
496     FLAG_FORCE(preconnect);
497     FLAG_FORCE(postconnect);
498
499     FLAG_FORCE(keep);
500     FLAG_FORCE(flush);
501     FLAG_FORCE(fetchall);
502     FLAG_FORCE(rewrite);
503     FLAG_FORCE(forcecr);
504     FLAG_FORCE(stripcr);
505     FLAG_FORCE(pass8bits);
506     FLAG_FORCE(dropstatus);
507     FLAG_FORCE(mimedecode);
508     FLAG_FORCE(limit);
509     FLAG_FORCE(fetchlimit);
510     FLAG_FORCE(batchlimit);
511     FLAG_FORCE(expunge);
512
513 #undef FLAG_FORCE
514
515     (void) hostalloc(&current);
516
517     trailer = TRUE;
518 }
519
520 void optmerge(struct query *h2, struct query *h1)
521 /* merge two options records; empty fields in h2 are filled in from h1 */
522 {
523     append_str_list(&h2->server.localdomains, &h1->server.localdomains);
524     append_str_list(&h2->localnames, &h1->localnames);
525     append_str_list(&h2->mailboxes, &h1->mailboxes);
526     append_str_list(&h2->smtphunt, &h1->smtphunt);
527
528 #define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld
529     FLAG_MERGE(server.via);
530     FLAG_MERGE(server.protocol);
531 #if INET6
532     FLAG_MERGE(server.service);
533     FLAG_MERGE(server.netsec);
534 #else /* INET6 */
535     FLAG_MERGE(server.port);
536 #endif /* INET6 */
537     FLAG_MERGE(server.interval);
538     FLAG_MERGE(server.preauthenticate);
539     FLAG_MERGE(server.timeout);
540     FLAG_MERGE(server.envelope);
541     FLAG_MERGE(server.envskip);
542     FLAG_MERGE(server.qvirtual);
543     FLAG_MERGE(server.skip);
544     FLAG_MERGE(server.dns);
545     FLAG_MERGE(server.uidl);
546
547 #ifdef linux
548     FLAG_MERGE(server.interface);
549     FLAG_MERGE(server.monitor);
550     FLAG_MERGE(server.interface_pair);
551 #endif /* linux */
552
553     FLAG_MERGE(remotename);
554     FLAG_MERGE(password);
555     FLAG_MERGE(mda);
556     FLAG_MERGE(smtpaddress);
557     FLAG_MERGE(antispam);
558     FLAG_MERGE(preconnect);
559
560     FLAG_MERGE(keep);
561     FLAG_MERGE(flush);
562     FLAG_MERGE(fetchall);
563     FLAG_MERGE(rewrite);
564     FLAG_MERGE(forcecr);
565     FLAG_MERGE(stripcr);
566     FLAG_MERGE(pass8bits);
567     FLAG_MERGE(dropstatus);
568     FLAG_MERGE(mimedecode);
569     FLAG_MERGE(limit);
570     FLAG_MERGE(fetchlimit);
571     FLAG_MERGE(batchlimit);
572     FLAG_MERGE(expunge);
573 #undef FLAG_MERGE
574 }
575
576 /* easier to do this than cope with variations in where the library lives */
577 int yywrap(void) {return 1;}
578
579 /* rcfile_y.y ends here */
580
581