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