summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--esmtprc.523
-rw-r--r--lexer.l5
-rw-r--r--main.c42
-rw-r--r--main.h4
-rw-r--r--parser.y6
-rw-r--r--smtp.c164
-rw-r--r--smtp.h12
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
@@ -69,6 +69,29 @@ It can be one of \fBenabled\fR, \fBdisabled\fR or \fBrequired\fR. It defaults to
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 <stdio.h>
#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
#include <string.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
@@ -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 <signal.h>
#include <errno.h>
#include <sys/wait.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
#include <auth-client.h>
#include <libesmtp.h>
@@ -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