From 583741ac138fa6983ae8b201b3c51bad9ef2ef05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Fonseca?= Date: Tue, 15 Feb 2005 19:22:50 +0000 Subject: Allow to set helo-name. Allow to send out local mail as remote mail by adding a qualifying domain name. Allow forcing envelope-from and Sender headers. Drop possible admin-added sgid priveleges to be dropped after reading the config. (Bernhard Link) --- NEWS | 5 ++ esmtprc.5 | 23 +++++++++ lexer.l | 5 ++ main.c | 42 ++++++++++++++-- main.h | 4 +- parser.y | 6 ++- smtp.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- smtp.h | 12 ++++- 8 files changed, 244 insertions(+), 17 deletions(-) diff --git a/NEWS b/NEWS index 729de80..cd698ae 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,11 @@ News * Several configuration fixes/enhancements (SASAJIMA and Dirk Tilger). * Enforce strict permissions on configuration file (Tiago Macambira). + + * Allow to set helo-name. Allow to send out local mail as remote mail + by adding a qualifying domain name. Allow forcing envelope-from and Sender + headers. Drop possible admin-added sgid priveleges to be dropped after + reading the config. (Bernhard Link) * Version 0.5.0 (2003-11-14): diff --git a/esmtprc.5 b/esmtprc.5 index 6deebd6..1bca9e9 100644 --- a/esmtprc.5 +++ b/esmtprc.5 @@ -68,6 +68,29 @@ It can be one of \fBenabled\fR, \fBdisabled\fR or \fBrequired\fR. It defaults to \fBcertificate_passphrase\fR Set the certificate passphrase for the StartTLS extension. +.TP +\fBhelo\fR +Set the hostname to identify as when sending HELO or EHLO commands. +(This is a per identity option, as it should be the name you are +seen as from the connected host, which may very with host to host +due to NAT or different naming schemes). + +.TP +\fBqualifydomain\fR +Make all local addresses to remote ones by adding @ and this +name. + +.TP +\fBforce sender\fR +Set a "Sender:" header and ignore those in the message. "%u" will +be replaced with the username. "%%" by "%". + +.TP +\fBforce reverse_path\fR +Set the envelope from address. The address given to -f will only +be used as "From:" when the message contains none. "%u" will +be repalced with the username. "%%" by "%". + .TP \fBpreconnect\fR Shell command to execute prior to opening an SMTP connection. diff --git a/lexer.l b/lexer.l index 4563c9d..0743743 100644 --- a/lexer.l +++ b/lexer.l @@ -62,6 +62,11 @@ pass(word)? { BEGIN(NAME); return PASSWORD; } (certificate_)?passphrase { return CERTIFICATE_PASSPHRASE; } preconnect { return PRECONNECT; } postconnect { return POSTCONNECT; } +qualifydomain { return QUALIFYDOMAIN; } +helo { return HELO; } +force { return FORCE; } +reverse_path { return REVERSE_PATH; } +sender { return SENDER; } mda { return MDA; } = { return MAP; } diff --git a/main.c b/main.c index d1f47e6..7b9ebc1 100644 --- a/main.c +++ b/main.c @@ -8,6 +8,9 @@ #include #include +#include +#include +#include #include #ifdef HAVE_GETOPT_H #include @@ -20,6 +23,21 @@ #include "rcfile.h" +static void drop_sgids( void ) +{ + gid_t real_gid, effective_gid; + + real_gid = getgid(); + effective_gid = getegid(); + + if( setregid(real_gid,real_gid) != 0 ) + { + fprintf (stderr, "Could not drop setgid: %m!\n"); + exit(EX_DROPPERM); + } +} + + /** Modes of operation. */ typedef enum { ENQUEUE, /**< delivery mode */ @@ -33,16 +51,28 @@ int verbose = 0; FILE *log_fp = NULL; - static void message_send(message_t *message) { int local, remote; + identity_t *identity; + + /* Lookup the identity already here */ + identity = identity_lookup(message->reverse_path); + assert(identity); - local = !list_empty(&message->local_recipients); - remote = !list_empty(&message->remote_recipients); + if( identity->qualifydomain ) + { + local = 0; + remote = 1; + } + else + { + local = !list_empty(&message->local_recipients); + remote = !list_empty(&message->remote_recipients); + } if(remote && !local) - smtp_send(message); + smtp_send(message,identity); else if(!remote && local) { local_init(message); @@ -51,7 +81,7 @@ static void message_send(message_t *message) else { local_init(message); - smtp_send(message); + smtp_send(message,identity); local_flush(message); } @@ -349,6 +379,8 @@ int main (int argc, char **argv) /* Parse the rc file. */ rcfile_parse(rcfile); + drop_sgids(); + message_send(message); identities_cleanup(); diff --git a/main.h b/main.h index 6686bc7..5ec63ba 100644 --- a/main.h +++ b/main.h @@ -29,7 +29,9 @@ enum { EX_TEMPFAIL = 75, /**< temp failure; user is invited to retry */ EX_PROTOCOL = 76, /**< remote error in protocol */ EX_NOPERM = 77, /**< permission denied */ - EX_CONFIG = 78 /**< configuration error */ + EX_CONFIG = 78, /**< configuration error */ + EX_DROPPERM = 79, /**< cannot drop setgid */ + EX_NOUSERNAME = 80 /**< cannot determine username */ }; extern FILE *log_fp; diff --git a/parser.y b/parser.y index a13a200..dd6a9dd 100644 --- a/parser.y +++ b/parser.y @@ -64,7 +64,7 @@ void yyerror (const char *s); char *sval; } -%token IDENTITY DEFAULT HOSTNAME USERNAME PASSWORD STARTTLS CERTIFICATE_PASSPHRASE PRECONNECT POSTCONNECT MDA +%token IDENTITY DEFAULT HOSTNAME USERNAME PASSWORD STARTTLS CERTIFICATE_PASSPHRASE PRECONNECT POSTCONNECT MDA QUALIFYDOMAIN HELO FORCE SENDER REVERSE_PATH %token MAP @@ -112,6 +112,10 @@ statement : HOSTNAME map STRING { identity->host = xstrdup($3); SET_DEFAULT_IDEN | CERTIFICATE_PASSPHRASE map STRING { identity->certificate_passphrase = xstrdup($3); SET_DEFAULT_IDENTITY; } | PRECONNECT map STRING { identity->preconnect = xstrdup($3); SET_DEFAULT_IDENTITY; } | POSTCONNECT map STRING { identity->postconnect = xstrdup($3); SET_DEFAULT_IDENTITY; } + | QUALIFYDOMAIN map STRING { identity->qualifydomain = xstrdup($3); SET_DEFAULT_IDENTITY; } + | HELO map STRING { identity->helo = xstrdup($3); SET_DEFAULT_IDENTITY; } + | FORCE REVERSE_PATH map STRING { identity->force_reverse_path = xstrdup($4); SET_DEFAULT_IDENTITY; } + | FORCE SENDER map STRING { identity->force_sender = xstrdup($4); SET_DEFAULT_IDENTITY; } | MDA map STRING { mda = xstrdup($3); } | DEFAULT { default_identity = identity; } ; diff --git a/smtp.c b/smtp.c index 116c5a1..6fee9d7 100644 --- a/smtp.c +++ b/smtp.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include #include @@ -68,6 +71,18 @@ void identity_free(identity_t *identity) if(identity->postconnect) free(identity->postconnect); + if(identity->qualifydomain) + free(identity->qualifydomain); + + if(identity->helo) + free(identity->helo); + + if(identity->force_reverse_path) + free(identity->force_reverse_path); + + if(identity->force_sender) + free(identity->force_sender); + free(identity); } @@ -380,7 +395,78 @@ void print_recipient_status (smtp_recipient_t recipient, const char *mailbox, fprintf (stderr, "%s: %d %s\n", mailbox, status->code, status->text); } -void smtp_send(message_t *msg) +/** + * Escape a forced fields: %u by username %% by % + */ + +static char *escape_forced_address (char *mask) +{ + uid_t uid; + char *escaped, *e; + const char *p; + int len; + struct passwd *user; + + uid = getuid(); + user = getpwuid(uid); + + len = 0; + for (p=mask ; *p ; p++) + { + if ( *p == '%' ) + { + p++; + if( !*p ) + break; + switch( *p ) { + case '%': + len++; + break; + case 'u': + if (user) + { + len += strlen(user->pw_name); + } + else + { + fprintf (stderr, "Could not determine the username of uid %d!\n",(int)uid); + exit (EX_NOUSERNAME); + } + break; + } + } + else + len++; + } + + e = escaped = xmalloc(len+1); + for (p=mask ; *p ; p++) + { + if (*p == '%') + { + p++; + if (!*p) + break; + if (*p == '%') + *(e++) = *p; + else if (*p == 'u') + { + strcpy(e,user->pw_name); + e += strlen(user->pw_name); + } + } + else + { + *(e++) = *p; + continue; + } + } + *e = '\0'; + return escaped; + +} + +void smtp_send(message_t *msg, identity_t *identity) { smtp_session_t session; smtp_message_t message; @@ -388,9 +474,8 @@ void smtp_send(message_t *msg) auth_context_t authctx; const smtp_status_t *status; struct sigaction sa; - identity_t *identity; struct list_head *ptr; - + /* This program sends only one message at a time. Create an SMTP * session. */ @@ -403,10 +488,6 @@ void smtp_send(message_t *msg) if(!smtp_set_monitorcb (session, monitor_cb, NULL, 1)) goto failure; - /* Lookup the identity */ - identity = identity_lookup(msg->reverse_path); - assert(identity); - /* Set the event callback. */ if(!smtp_set_eventcb (session, event_cb, NULL)) goto failure; @@ -420,6 +501,13 @@ void smtp_send(message_t *msg) sa.sa_handler = SIG_IGN; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (SIGPIPE, &sa, NULL); + /* Set the hostname of this computer to be used for HELO names: */ + if(identity->helo) + { + if(!smtp_set_hostname (session, identity->helo)) + goto failure; + } + /* Set the host running the SMTP server. LibESMTP has a default port * number of 587, however this is not widely deployed so the port is * specified as 25 along with the default MTA host. @@ -463,7 +551,26 @@ void smtp_send(message_t *msg) goto failure; /* Set the reverse path for the mail envelope. (NULL is ok) */ - if(msg->reverse_path) + if(identity->force_reverse_path) + { + char *value; + + value = escape_forced_address(identity->force_reverse_path); + if(!smtp_set_reverse_path (message, value)) + { + free(value); + goto failure; + } + free(value); + /* Allow -f to set an default From: address though */ + if(msg->reverse_path) + { + if(!smtp_set_header (message, "From", NULL, msg->reverse_path)) + goto failure; + + } + } + else if(msg->reverse_path) { if(!smtp_set_reverse_path (message, msg->reverse_path)) goto failure; @@ -478,6 +585,22 @@ void smtp_send(message_t *msg) if(!smtp_set_messagecb (message, message_cb, msg)) goto failure; + /* Overwrite Sender:-Header if force sender is specified */ + if(identity->force_sender) + { + char *value; + + value = escape_forced_address(identity->force_sender); + if(!smtp_set_header (message, "Sender", NULL, value)) + { + free(value); + goto failure; + } + free(value); + if(!smtp_set_header_option (message, "Sender", Hdr_OVERRIDE, (int)1)) + goto failure; + } + /* DSN options */ if(!smtp_dsn_set_ret(message, msg->ret)) goto failure; @@ -489,7 +612,7 @@ void smtp_send(message_t *msg) if(!smtp_8bitmime_set_body(message, msg->body)) goto failure; - /* Add remaining program arguments as message recipients. */ + /* Add remote message recipients. */ list_for_each(ptr, &msg->remote_recipients) { recipient_t *entry = list_entry(ptr, recipient_t, list); @@ -505,6 +628,29 @@ void smtp_send(message_t *msg) goto failure; } + /* Add local message recipients if qualifydomain is set */ + if (identity->qualifydomain) list_for_each(ptr, &msg->local_recipients) + { + recipient_t *entry = list_entry(ptr, recipient_t, list); + char *qualifiedaddress; + + assert(entry->address); + + qualifiedaddress = xmalloc(strlen(identity->qualifydomain) + strlen(entry->address) + 2); + strcpy(qualifiedaddress, entry->address); + strcat(qualifiedaddress, "@"); + strcat(qualifiedaddress, identity->qualifydomain); + + if(!(recipient = smtp_add_recipient (message, qualifiedaddress))) + goto failure; + free(qualifiedaddress); + + /* Recipient options set here */ + if (msg->notify != Notify_NOTSET) + if(!smtp_dsn_set_notify (recipient, msg->notify)) + goto failure; + } + /* Execute pre-connect command if one was specified. */ if (identity->preconnect) { diff --git a/smtp.h b/smtp.h index f5800fc..0e2909d 100644 --- a/smtp.h +++ b/smtp.h @@ -44,6 +44,16 @@ typedef struct { char *preconnect; char *postconnect; /*@}*/ + + char *qualifydomain; /**< domain to qualify unqualified addresses with */ + + char *helo; /**< hostname to tell with helo */ + + /** \name Forcing options */ + /*@{*/ + char *force_reverse_path; + char *force_sender; + /*@}*/ } identity_t; /** @@ -70,6 +80,6 @@ void identities_cleanup(void); /** Send a message via a SMTP server */ -void smtp_send(message_t *msg); +void smtp_send(message_t *msg, identity_t *identity); #endif -- cgit v1.2.3