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