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