#include #include #include #include #include #include #include #include #include "internal.h" #include "../paths.h" #include #include #include "mailconf.h" #include "mailconf.m" #include extern MAILCONF_HELP_FILE help_mailconf; CONFIG_FILE f_sendmail (ETC_SENDMAIL_CF,help_mailconf ,CONFIGF_GENERATED|CONFIGF_OPTIONAL|CONFIGF_PROBED ,subsys_sendmail); CONFIG_FILE f_sendmail_ct (ETC_MAIL_SENDMAIL_CT,help_mailconf ,CONFIGF_OPTIONAL|CONFIGF_PROBED ,subsys_sendmail); CONFIG_FILE f_virtuser (ETC_MAIL_VIRTUSER,help_mailconf ,CONFIGF_OPTIONAL|CONFIGF_PROBED ,subsys_mail); CONFIG_FILE f_pophash (ETC_MAIL_POPHASH,help_mailconf ,CONFIGF_OPTIONAL|CONFIGF_PROBED ,subsys_mail); extern CONFIG_FILE f_mailtable; extern CONFIG_FILE f_sendmail_cw; static CONFIG_FILE f_sendmail_cV ("/etc/mail/sendmail.cV",help_nil ,CONFIGF_GENERATED|CONFIGF_NOARCH); /* Avoid repeating the same path over and over */ class MAILCONF_FILE: public CONFIG_FILE{ public: SSTRING refpath; // Path of the template in /usr/lib/linuxconf MAILCONF_FILE(const char *fname); }; PUBLIC MAILCONF_FILE::MAILCONF_FILE(const char *fname) : CONFIG_FILE (fname,help_mailconf,CONFIGF_OPTIONAL,subsys_mail) { char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/%s.cf",ETC_MAIL_MAILCONF,fname); setkey (path); refpath.setfromf("%s/mailconf/%s.cf",USR_LIB_LINUXCONF,fname); } static MAILCONF_FILE f_intro ("intro"); static MAILCONF_FILE f_copyright ("copyright"); static MAILCONF_FILE f_stdmacros ("stdmacros"); static MAILCONF_FILE f_stdoptions ("stdoptions"); static MAILCONF_FILE f_milteroptions ("milteroptions"); static MAILCONF_FILE f_rulesets ("rulesets"); static MAILCONF_FILE f_rulesets_s0_intro ("rulesets.s0.intro"); static MAILCONF_FILE f_rulesets_s0_90 ("rulesets.s0.90"); static MAILCONF_FILE f_rulesets_s0_local ("rulesets.s0.local"); static MAILCONF_FILE f_rulesets_s0_local_amavis ("rulesets.s0.local.amavis"); static MAILCONF_FILE f_rulesets_parse1_init ("rulesets.parse1.init"); static MAILCONF_FILE f_rulesets_parse1_virtuser ("rulesets.parse1.virtuser"); static MAILCONF_FILE f_rulesets_parse1_middle ("rulesets.parse1.middle"); static MAILCONF_FILE f_rulesets_parse1_mailtable ("rulesets.parse1.mailtable"); static MAILCONF_FILE f_rulesets_parse1_remote ("rulesets.parse1.remote"); static MAILCONF_FILE f_rulesets_s3 ("rulesets.s3"); static MAILCONF_FILE f_rulesets_s90 ("rulesets.s90"); static MAILCONF_FILE f_rulesets_s96_dns ("rulesets.s96.dns"); static MAILCONF_FILE f_rulesets_s96_nodns ("rulesets.s96.nodns"); static MAILCONF_FILE f_rulesets_s96_fewdns ("rulesets.s96.fewdns"); static MAILCONF_FILE f_rulesets_relay ("rulesets.relay"); static MAILCONF_FILE f_rulesets_junk ("rulesets.junk"); static MAILCONF_FILE f_rulesets_rbl ("rulesets.rbl"); static MAILCONF_FILE f_localmailer ("localmailer.std"); static MAILCONF_FILE f_faxmailer ("faxmailer"); static MAILCONF_FILE f_delivermailer ("localmailer.deliver"); static MAILCONF_FILE f_procmailmailer ("localmailer.procmail"); static MAILCONF_FILE f_amavismailer ("localmailer.amavis"); static MAILCONF_FILE f_progmailer ("progmailer"); static MAILCONF_FILE f_uucpmailer ("uucpmailer.std"); static MAILCONF_FILE f_virtmailer ("virtualmailer.std"); //static MAILCONF_FILE f_uucpnbmailer ("uucpmailer.nobatch"); static MAILCONF_FILE f_smtpmailer ("smtpmailer.std"); static MAILCONF_FILE f_localmailer_tst ("localmailer.tst"); static MAILCONF_FILE f_uucpmailer_tst ("uucpmailer.tst"); static MAILCONF_FILE f_smtpmailer_tst ("smtpmailer.tst"); const int TOKEN_VALLEN = 100; /* Copy a text file into fout with token replacement Return 0 if ok, -1 if any error. */ static int copyfile ( MAILCONF_FILE &filesrc, SSTREAM &out, const char *token[], const char repl[][TOKEN_VALLEN], int nbtoken) { int ret = -1; /* #Specification: sendmail.cf / strategy The sendmail.cf file is built by using templates in /usr/lib/linuxconf/mailconf. The user has tbe ability to provide its own copies in /etc/mail/mailconf. Linuxconf will use those, overriding its own. */ const char *path = filesrc.getpath(); if (!file_exist (path)){ path = filesrc.refpath.get(); } FILE_CFG *fin = filesrc.fopen(path,"r"); if (fin != NULL){ ret = 0; char buf[1000]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ for (int i=0; iget()); } } out.puts ("# Virtual email domain\n"); VDOMAINS vdomains; // Virtual domains out.printf ("FV%s\n",f_sendmail_cV.getpath()); FILE_CFG *fV = f_sendmail_cV.fopen ("w"); if (fV == NULL){ ret = -1; }else{ for (int i=0; idomain.get(); fprintf (fV,"%s\n",domname); out.printf ("#CV %s\n",domname); for (int o=0; oothers.size(); o++){ SSTRING *s = v->others.getitem(o); const char *ali = s->get(); fprintf (fV,"%s\n",ali); // We are still generated #CV lines, because the // sendmail.cf content is compared with the previous one // so we know if it has to be changed or not. out.printf ("#CV %s\n",ali); } } fclose (fV); } /* #specification: generating sendmail.cf / other module contribution When generating the sendmail.cf, the mailconf module allows other modules to participate in the generation. We are using the general purpose messaging facility. Various messages are sent at different stage of the sendmail.cf generation. THe messaging api is text only using an argc,argv[] pair. We encode a FILE pointer as the first argument. A strategy used by co-manager might be better. */ const char *message_argv[2]; message_argv[0] = (const char *)&out; message_argv[1] = NULL; module_sendmessage ("mailconf:defclass",1,message_argv); out.puts ("# who I masquerade as (null for no masquerading)\n"); if (!mailas.is_empty()) out.printf ("DM%s\n",mailas.get()); /* #Specification: mailconf / sendmail.cf / smarthost If we specify that we are our own smarthost, this is causing problem with sendmail. It could be argue that it makes sens (independant of sendmail). Given also that linuxconf mail configuration will be shareable one day, this makes sens. When we detect that the smarthost is this host we simply ignore it (The S macro of sendmail.cf remain empty). We do the same for the mailhost (Macro H of sendmail.cf) */ out.puts ("# Smart host\n"); if (smarthost.icmp(hname)==0){ out.puts ("DS\n"); }else if (ipnum_validip(smarthost.get(),false)){ out.printf ("DS[%s]\n",smarthost.get()); }else{ out.printf ("DS%s\n",smarthost.get()); } out.puts ("# Use this mailer to reach the Smart host\n"); if (ipnum_validip(smartmailer.get(),false)){ out.printf ("DN[%s]\n",smartmailer.get()); }else{ out.printf ("DN%s\n",smartmailer.get()); } out.puts ("# Central host for local mail\n"); if (mailhost.icmp(hname)==0){ out.puts ("DH\n"); }else{ out.printf ("DH%s\n",mailhost.get()); } out.puts ("# class L: names that should be delivered locally, even if we have a relay\n"); out.printf ("CL%s\n",users.deliverlocal.get()); out.puts ("# class E: names that should be exposed as from this host, even if we masquerade\n"); out.printf ("CE%s\n",users.dontmasque.get()); out.puts ("# Trust users\n"); { if (f_sendmail_ct.exist()){ out.printf ("Ft%s\n",f_sendmail_ct.getpath()); } if (!sendmail_ct){ const char *pt = users.trust.get(); while (1){ char word[100]; pt=str_copyword(word,pt,sizeof(word)-1); if (word[0] == '\0') break; out.printf ("T%s\n",word); } } } out.puts ("# Database for special routing\n"); if (features.mailertable){ out.printf ("Kmailertable %s %s\n" ,features.dbformat.get(),f_mailtable.getpath()); }else{ out.puts("# Not activated\n"); if (spcs.size()>0){ xconf_notice(MSG_U(E_MTABLEON ,"The /etc/sendmail.cf will be generated without\n" "special (domain) routing enabled. This system\n" "has already some special routing definition(s)\n" "and this sendmail.cf won't make use of them.\n" "Enable \"special routing database\" in the\n" "\"basic information\" dialog to fix this\n" "and regenerate the sendmail.cf.")); } } out.puts ("# Restrict DNS to those domain only\n"); out.puts ("CD "); int restricted_dns = 0; for (int i=0; iis_empty()) restricted_dns = 1; out.printf (" %s",item->get()); } out.puts ("\n"); ret |= copyfile (f_stdmacros,out); if (features.relayctrl){ if (f_spam_ip_allow.exist()){ out.puts ("# file containing IP numbers of machines which can use our relay\n"); out.printf ("F{LocalIP} %s\n",f_spam_ip_allow.getpath()); } if (f_spam_name_allow.exist()){ out.puts ("# file containing names of machines which can use our relay\n"); out.printf ("F{LocalNames} %s\n",f_spam_name_allow.getpath()); } if (f_spam_relay_allow.exist()){ out.puts ("# file containing names we relay to\n"); out.printf ("F{RelayTo} %s\n",f_spam_relay_allow.getpath()); } } if (f_spam_deny.exist()){ out.puts ("# file containing known spammers by email,domain,ip\n"); out.printf ("Kjunk %s -a@JUNK %s\n",features.dbformat.get() ,f_spam_deny.getpath()); } if (f_virtuser.exist()){ out.puts ("# Virtual user table (maps incoming users\n"); out.printf ("Kvirtuser %s %s\n",features.dbformat.get() ,f_virtuser.getpath()); } if (f_pophash.exist()){ out.puts ("# Roaming pop users accces\n"); out.printf ("Kpopauth %s %s\n",features.dbformat.get() ,f_pophash.getpath()); } out.puts ("# Deliver mail only in DNS is available\n"); out.puts (features.dnsneeded ? "OI\n" : "#OI\n"); out.puts ("# Match full user name when receiving\n"); out.puts (features.usegecos ? "OGTrue\n" : "OGFalse\n"); out.puts ("# maximum message size\n"); if (features.maxmsgsize > 0){ out.printf ("O MaxMessageSize=%d\n",features.maxmsgsize); }else{ out.puts ("#O MaxMessageSize=1000000\n"); } if (features.maxrecipients > 0){ out.printf ("O MaxRecipientsPerMessage=%d\n" ,features.maxrecipients); }else{ out.puts ("#O MaxRecipientsPerMessage=xxxxx\n"); } out.puts ("# delivery mode\n"); out.printf ("O DeliveryMode=%s\n" , features.deferdeliv ? "deferred" : "background"); if (features.bogushelo){ out.puts ("# Give a chance to broken mail client\n"); out.puts ("O AllowBogusHELO\n"); } { static const char *usertoken[]={ "$(MAILMAIL)","$(MQUEUE)","$(EXPN)","$(VERIFY)", "$(TIMEOUTIDENT)", "$(ETRN)","$(VERB)","$(MAILQ)","$(RUN)","$(RECEIPTS)","$(GOAWAY)", "$(PROBEINTERFACE)","$(AUTHWARNINGS)", "$(QUEUEWARN)","$(QUEUERETURN)", "$(PORT)","$(ADDR)","$(DOUBLE)","$(DOUBLEADDR)" }; char userrepl[19][TOKEN_VALLEN]; strcpy (userrepl[0],"mail:mail"); if (strcmp(linuxconf_getdistdir(),"suse")==0){ strcpy (userrepl[0],"bin:bin"); } strcpy (userrepl[1],configf_lookuppath("/var/spool/mqueue")); strcpy (userrepl[2],features.expn ? "# " : ""); strcpy (userrepl[3],features.verify ? "# " : ""); sprintf (userrepl[4],"%ds",features.tmoutident); strcpy (userrepl[5],features.etrn ? "# " : ""); strcpy (userrepl[6],features.verb ? "# " : ""); strcpy (userrepl[7],features.mailq ? "# " : ""); strcpy (userrepl[8],features.run ? "# " : ""); strcpy (userrepl[9],features.receipts ? "# " : ""); strcpy (userrepl[10],features.goaway ? "# " : ""); strcpy (userrepl[11],features.probeinterface ? "# " : ""); strcpy (userrepl[12],features.authwarnings ? "" : "# "); strcpy (userrepl[13],features.queuewarn.get()); strcpy (userrepl[14],features.queuereturn.get()); listen.port.copy (userrepl[15]); listen.addr.copy (userrepl[16]); if (features.doublebounce > 0){ strcpy (userrepl[17],""); if (features.doublebounce == 1){ strcpy (userrepl[18],""); }else{ strcpy (userrepl[18],features.doublebouncedest.get()); } }else{ strcpy (userrepl[17],"#"); strcpy (userrepl[18],""); } ret |= copyfile (f_stdoptions,out,usertoken,userrepl,19); } if (milters.size() > 0){ ret |= copyfile (f_milteroptions,out); out.puts ("# Input mail filters\n"); SSTRING mlist,mspecs; for (int i=0; i0) mlist.append (","); mlist.append (m->name); mspecs.appendf ("X%s, S=%s, F=%s\n",m->name.get() ,m->port.get(),m->flags.get()); } out.printf ("O InputMailFilters=%s\n",mlist.get()); out.puts (mspecs.get()); } ret |= copyfile (f_rulesets,out); if (features.mailertable) ret |= copyfile (f_rulesets_s90,out); if (features.relayctrl){ static const char *usertoken[]={ "$(POPHASH)" }; char userrepl[1][TOKEN_VALLEN]; if (f_pophash.exist()){ strcpy (userrepl[0],""); }else{ strcpy (userrepl[0],"#"); } ret |= copyfile (f_rulesets_relay,out,usertoken,userrepl,1); } if (f_spam_deny.exist()){ ret |= copyfile (f_rulesets_junk,out); }else{ out.puts ("#Empty junk rule\nSjunk\n\nSjunkIP\n\n"); } if (features.userbl){ ret |= copyfile (f_rulesets_rbl,out); }else{ out.puts ("# RBL not enabled, empty ruleset\n"); out.puts ("Scheck_rbl\n"); } ret |= copyfile (f_rulesets_s0_intro,out); // Let a module define complex/exceptional routing module_sendmessage ("mailconf:complex_s0",1,message_argv); COMPLEX_ROUTES cplx; cplx.rule0 (out,alias,vdomains); #if 0 // Alias for virtual email domain for (i=0; iothers.size(); o++){ SSTRING *s = v->others.getitem(o); out.printf ("R$*<@%s>\t$#virtual $@ %s $: $1\n" ,s->get(),v->domain.get()); out.printf ("R$*<@%s.>\t$#virtual $@ %s $: $1\n" ,s->get(),v->domain.get()); } } #endif if (mfax.enable){ const char *dom = thishost.getdomain(); if (dom == NULL) dom = ""; out.printf ("R$*<@fax.%s>\t$#fax $@ $1 $: _\n",dom); out.printf ("R$*<@fax.%s.>\t$#fax $@ $1 $: _\n",dom); out.printf ("R$*<@$*.fax.%s>\t$#fax $@ $2 $: $1\n",dom); out.printf ("R$*<@$*.fax.%s.>\t$#fax $@ $2 $: $1\n",dom); } if (deliver.cmp("amavis")==0){ ret |= copyfile (f_rulesets_s0_local_amavis,out); }else{ ret |= copyfile (f_rulesets_s0_local,out); } ret |= copyfile (f_rulesets_parse1_init,out); if (f_virtuser.exist()){ ret |= copyfile (f_rulesets_parse1_virtuser,out); } ret |= copyfile (f_rulesets_parse1_middle,out); if (features.mailertable) ret |= copyfile (f_rulesets_parse1_mailtable,out); ret |= copyfile (f_rulesets_parse1_remote,out); if (features.mailertable) ret |= copyfile (f_rulesets_s0_90,out); ret |= copyfile (f_rulesets_s3,out); if (features.nodns){ ret |= copyfile (f_rulesets_s96_nodns,out); }else if (restricted_dns){ ret |= copyfile (f_rulesets_s96_fewdns,out); }else{ ret |= copyfile (f_rulesets_s96_dns,out); } if (deliver.is_empty()){ if (file_exist ("/bin/mail.local")){ ret |= copyfile (f_localmailer,out); }else if (file_exist ("/usr/bin/deliver")){ ret |= copyfile (f_delivermailer,out); }else if (file_exist ("/usr/bin/procmail")){ ret |= copyfile (f_procmailmailer,out); }else if (file_exist ("/usr/sbin/scanmails")){ ret |= copyfile (f_amavismailer,out); } }else if (deliver.cmp("deliver")==0){ ret |= copyfile (f_delivermailer,out); }else if (deliver.cmp("procmail")==0){ ret |= copyfile (f_procmailmailer,out); }else if (deliver.cmp("amavis")==0){ ret |= copyfile (f_amavismailer,out); }else if (deliver.cmp("mail.local")==0){ ret |= copyfile (f_localmailer,out); } { static const char *progtoken[]={ "$(SHELL)" }; char progrepl[1][TOKEN_VALLEN]; strcpy (progrepl[0],"/bin/sh"); if (features.usesmrsh){ DAEMON_INTERNAL *dae = daemon_find ("smrsh"); if (dae != NULL){ const char *path = dae->getpath(); if (file_exist (path)){ strcpy (progrepl[0],path); }else if (!simul_ison()){ xconf_error (MSG_U(E_NOSMRSH ,"The smrsh shell is not installed\n" "on this system.\n" "Generating a sendmail.cf using /bin/sh")); } } } ret |= copyfile (f_progmailer,out,progtoken,progrepl,1); } ret |= copyfile (f_virtmailer,out); ret |= copyfile (f_faxmailer,out); { static const char *uucptoken[]={ "$(MAXSIZ)","$(UUXOPT)" }; char uucprepl[2][TOKEN_VALLEN]; uucprepl[0][0] = '\0'; if (features.uucpmax > 0){ sprintf (uucprepl[0],"M=%d,",features.uucpmax); } strcpy (uucprepl[1],features.uucpnobatch ? "" : "-r"); ret |= copyfile (f_uucpmailer,out ,uucptoken,uucprepl,2); } ret |= copyfile (f_smtpmailer,out); // Let modules record mailer module_sendmessage ("mailconf:defmailer",1,message_argv); ret |= masqs.rule1(out); return ret; } static const char K_SENDMAILSUM[]="sendmailsum"; /* Check if the sendmail.cf currently installed was generated by me Use the MD5 checksum to make sure. sendmail_sum[] will contain the MD5 checksum of the current sendmail.cf */ PRIVATE bool MAILCONF::generated_byme(char sendmail_sum[]) { bool ret = false; const char *gensum = confread_getval (K_SENDMAILSUM,""); if (f_sendmail.md5sum(sendmail_sum)!= -1 && strcmp(gensum,sendmail_sum)==0){ ret = true; } return ret; } PUBLIC int MAILCONF::generate_go(bool confirm) { int ret = -1; FILE_CFG *fout = f_sendmail.fopen ("w"); if (fout != NULL){ SSTREAM_FILE_CFG out (fout); ret = format (out); if (features.mailertable) spcs.build(); fclose (fout); char sum[100]; f_sendmail.md5sum(sum); linuxconf_setcursys(subsys_sendmail); confread_replace (K_SENDMAILSUM,sum); linuxconf_save(); if (ret != -1 && confirm){ xconf_notice (MSG_U(N_HASGEN,"%s has been regenerated!") ,f_sendmail.getpath()); } } return ret; } /* Produce the file /etc/sendmail.cf using the configuration. It mail generate the normal sendmail.cf or a temporary one for automated test purpose (If its argument is true). Not implemented yet. Return -1 if any error */ PUBLIC int MAILCONF::generate(bool confirm) { int ret = -1; net_prtlog (NETLOG_CMD,MSG_U(I_UPDSENDMAILCF ,"Generating %s\n"),f_sendmail.getpath()); bool go = true; char sum[100]; if (!generated_byme(sum)){ const char *path = f_sendmail.getpath(); char pathold[PATH_MAX]; sprintf (pathold,"%s.old",path); char bufmsg[1000]; snprintf (bufmsg,sizeof(bufmsg)-1,MSG_U(Q_NOTBYME ,"The file %s was not generated by Linuxconf.\n" "It was either never generated by Linuxconf\n" "or has been modified manually later.\n" "\n" "Linuxconf will overwrite it and produce a backup file\n" "named %s.\n" "\n" "\tOk ?") ,path,pathold); if (dialog_yesno (MSG_U(T_NOTBYME,"Attention"),bufmsg,help_nil) !=MENU_YES){ go = false; net_prtlog (NETLOG_VERB,MSG_U(I_USERSAYNO ,"Not done as confirmed by the admin\n")); }else{ path = f_sendmail.getpath(); net_prtlog (NETLOG_CMD,"mv %s %s\n",path,pathold); rename (path,pathold); } } if (go) ret = generate_go(confirm); return ret; } /* Return true if the sendmail.cf was generated once by Linuxconf */ bool mailconf_generated_once () { // No need to check that the configuration was ever edited // by linuxconf. We simply check that linuxconf did generate at // least once a sendmail.cf. This proves the user has visited // the sendmail configuration menu const char *gensum = confread_getval (K_SENDMAILSUM,""); return strcmp(gensum,"")!=0; } /* Generate a new sendmail.cf if needed. We compare the md5 checksum of sendmail.cf with the one we would get if the file was generated from linuxconf config. If the checksum differ, then sendmail.cf needs to be updated. Return 1 if the sendmail.cf was regenerated (or should be if run in simul mode) 0 if all is fine -1 if any error */ PUBLIC int MAILCONF::generate_if(bool confirm) { int ret = -1; if (mailconf_generated_once()){ POPEN pop ("md5sum",""); if (pop.isok()){ // Imitate the way CONFIG_FILE::md5sum works by sending this line SSTREAM_POPEN ss (pop); configf_sendexist (ss,true); FILE *fout = pop.getfout(); SSTREAM_FILE out (fout); ret = format (out); pop.close(); if (ret != -1){ char line[100]; if (pop.readout(line,sizeof(line)-1)==0){ char sum_file[100],sum_conf[100]; str_copyword (sum_conf,line,sizeof(sum_conf)); if (generated_byme (sum_file) && strcmp(sum_conf,sum_file)!=0){ net_prtlog (NETLOG_CMD,MSG_R(I_UPDSENDMAILCF) ,f_sendmail.getpath()); ret = 1; if (!simul_ison()){ ret = generate_go(confirm); if (ret != -1) ret = 1; } } } } } } return ret; }