Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120826022
lmtpd.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
16 KB
Referenced Files
None
Subscribers
None
lmtpd.c
View Options
/* lmtpd.c -- Program to deliver mail to a mailbox
*
* $Id: lmtpd.c,v 1.135 2004/05/19 14:52:55 rjs3 Exp $
* Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*
*/
#include
<config.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
<signal.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<ctype.h>
#include
<fcntl.h>
#include
<sys/stat.h>
#include
<syslog.h>
#include
<com_err.h>
#include
<errno.h>
#include
<sys/types.h>
#include
<sys/wait.h>
#include
<netdb.h>
#include
<sys/socket.h>
#include
<netinet/in.h>
#include
<arpa/inet.h>
#include
<sasl/sasl.h>
#include
<sasl/saslutil.h>
#include
"acl.h"
#include
"assert.h"
#include
"util.h"
#include
"auth.h"
#include
"prot.h"
#include
"imparse.h"
#include
"lock.h"
#include
"global.h"
#include
"exitcodes.h"
#include
"imap_err.h"
#include
"mailbox.h"
#include
"xmalloc.h"
#include
"version.h"
#include
"duplicate.h"
#include
"append.h"
#include
"mboxlist.h"
#include
"notify.h"
#include
"idle.h"
#include
"tls.h"
#include
"lmtpengine.h"
#include
"lmtpstats.h"
#ifdef USE_SIEVE
#include
"lmtp_sieve.h"
static
sieve_interp_t
*
sieve_interp
=
NULL
;
#endif
/* forward declarations */
static
int
deliver
(
message_data_t
*
msgdata
,
char
*
authuser
,
struct
auth_state
*
authstate
);
static
int
verify_user
(
const
char
*
user
,
const
char
*
domain
,
const
char
*
mailbox
,
long
quotacheck
,
struct
auth_state
*
authstate
);
static
char
*
generate_notify
(
message_data_t
*
m
);
void
shut_down
(
int
code
);
static
FILE
*
spoolfile
(
message_data_t
*
msgdata
);
static
void
removespool
(
message_data_t
*
msgdata
);
/* current namespace */
static
struct
namespace
lmtpd_namespace
;
struct
lmtp_func
mylmtp
=
{
&
deliver
,
&
verify_user
,
&
shut_down
,
&
spoolfile
,
&
removespool
,
&
lmtpd_namespace
,
0
,
1
,
0
};
static
void
usage
();
/* global state */
const
int
config_need_data
=
CONFIG_NEED_PARTITION_DATA
;
extern
int
optind
;
extern
char
*
optarg
;
static
int
dupelim
=
1
;
/* eliminate duplicate messages with
same message-id */
static
int
singleinstance
=
1
;
/* attempt single instance store */
const
char
*
BB
=
""
;
/* per-user/session state */
static
struct
protstream
*
deliver_out
,
*
deliver_in
;
int
deliver_logfd
=
-1
;
/* used in lmtpengine.c */
static
struct
sasl_callback
mysasl_cb
[]
=
{
{
SASL_CB_GETOPT
,
&
mysasl_config
,
NULL
},
{
SASL_CB_PROXY_POLICY
,
&
mysasl_proxy_policy
,
NULL
},
{
SASL_CB_CANON_USER
,
&
mysasl_canon_user
,
NULL
},
{
SASL_CB_LIST_END
,
NULL
,
NULL
}
};
int
service_init
(
int
argc
__attribute__
((
unused
)),
char
**
argv
__attribute__
((
unused
)),
char
**
envp
__attribute__
((
unused
)))
{
int
r
;
if
(
geteuid
()
==
0
)
return
1
;
signals_set_shutdown
(
&
shut_down
);
signals_add_handlers
();
signal
(
SIGPIPE
,
SIG_IGN
);
singleinstance
=
config_getswitch
(
IMAPOPT_SINGLEINSTANCESTORE
);
BB
=
config_getstring
(
IMAPOPT_POSTUSER
);
global_sasl_init
(
0
,
1
,
mysasl_cb
);
dupelim
=
config_getswitch
(
IMAPOPT_DUPLICATESUPPRESSION
);
#ifdef USE_SIEVE
mylmtp
.
addheaders
=
xzmalloc
(
2
*
sizeof
(
struct
addheader
));
mylmtp
.
addheaders
[
0
].
name
=
"X-Sieve"
;
mylmtp
.
addheaders
[
0
].
body
=
SIEVE_VERSION
;
/* setup sieve support */
sieve_interp
=
setup_sieve
();
#else
if
(
dupelim
)
#endif
{
/* initialize duplicate delivery database */
if
(
duplicate_init
(
NULL
,
0
)
!=
0
)
{
fatal
(
"lmtpd: unable to init duplicate delivery database"
,
EC_SOFTWARE
);
}
}
/* so we can do mboxlist operations */
mboxlist_init
(
0
);
mboxlist_open
(
NULL
);
/* so we can do quota operations */
quotadb_init
(
0
);
quotadb_open
(
NULL
);
/* setup for sending IMAP IDLE notifications */
idle_enabled
();
/* Set namespace */
if
((
r
=
mboxname_init_namespace
(
&
lmtpd_namespace
,
0
))
!=
0
)
{
syslog
(
LOG_ERR
,
error_message
(
r
));
fatal
(
error_message
(
r
),
EC_CONFIG
);
}
/* create connection to the SNMP listener, if available. */
snmp_connect
();
/* ignore return code */
snmp_set_str
(
SERVER_NAME_VERSION
,
CYRUS_VERSION
);
return
0
;
}
/*
* run for each accepted connection
*/
int
service_main
(
int
argc
,
char
**
argv
,
char
**
envp
__attribute__
((
unused
)))
{
int
opt
;
deliver_in
=
prot_new
(
0
,
0
);
deliver_out
=
prot_new
(
1
,
1
);
prot_setflushonread
(
deliver_in
,
deliver_out
);
prot_settimeout
(
deliver_in
,
360
);
while
((
opt
=
getopt
(
argc
,
argv
,
"a"
))
!=
EOF
)
{
switch
(
opt
)
{
case
'a'
:
mylmtp
.
preauth
=
1
;
break
;
default
:
usage
();
}
}
snmp_increment
(
TOTAL_CONNECTIONS
,
1
);
snmp_increment
(
ACTIVE_CONNECTIONS
,
1
);
lmtpmode
(
&
mylmtp
,
deliver_in
,
deliver_out
,
0
);
/* free session state */
if
(
deliver_in
)
prot_free
(
deliver_in
);
if
(
deliver_out
)
prot_free
(
deliver_out
);
deliver_in
=
deliver_out
=
NULL
;
if
(
deliver_logfd
!=
-1
)
{
close
(
deliver_logfd
);
deliver_logfd
=
-1
;
}
cyrus_close_sock
(
0
);
cyrus_close_sock
(
1
);
cyrus_close_sock
(
2
);
return
0
;
}
/* Called by service API to shut down the service */
void
service_abort
(
int
error
)
{
shut_down
(
error
);
}
static
void
usage
()
{
fprintf
(
stderr
,
"421-4.3.0 usage: lmtpd [-C <alt_config>] [-a]
\r\n
"
);
fprintf
(
stderr
,
"421 4.3.0 %s
\n
"
,
CYRUS_VERSION
);
exit
(
EC_USAGE
);
}
/* places msg in mailbox mailboxname.
* if you wish to use single instance store, pass stage as non-NULL
* if you want to deliver message regardless of duplicates, pass id as NULL
* if you want to notify, pass user
* if you want to force delivery (to force delivery to INBOX, for instance)
* pass acloverride
*/
int
deliver_mailbox
(
struct
protstream
*
msg
,
struct
stagemsg
*
stage
,
unsigned
size
__attribute__
((
unused
)),
char
**
flag
,
int
nflags
,
char
*
authuser
,
struct
auth_state
*
authstate
,
char
*
id
,
const
char
*
user
,
char
*
notifyheader
,
const
char
*
mailboxname
,
int
quotaoverride
,
int
acloverride
)
{
int
r
;
struct
appendstate
as
;
time_t
now
=
time
(
NULL
);
unsigned
long
uid
;
const
char
*
notifier
;
if
(
dupelim
&&
id
&&
duplicate_check
(
id
,
strlen
(
id
),
mailboxname
,
strlen
(
mailboxname
)))
{
/* duplicate message */
duplicate_log
(
id
,
mailboxname
,
"delivery"
);
return
0
;
}
r
=
append_setup
(
&
as
,
mailboxname
,
MAILBOX_FORMAT_NORMAL
,
authuser
,
authstate
,
acloverride
?
0
:
ACL_POST
,
quotaoverride
?
-1
:
0
);
if
(
!
r
)
{
prot_rewind
(
msg
);
r
=
append_fromstage
(
&
as
,
stage
,
now
,
(
const
char
**
)
flag
,
nflags
,
!
singleinstance
);
if
(
!
r
)
append_commit
(
&
as
,
quotaoverride
?
-1
:
0
,
NULL
,
&
uid
,
NULL
);
else
append_abort
(
&
as
);
}
if
(
!
r
&&
user
&&
(
notifier
=
config_getstring
(
IMAPOPT_MAILNOTIFIER
)))
{
char
inbox
[
MAX_MAILBOX_NAME
+
1
];
char
namebuf
[
MAX_MAILBOX_NAME
+
1
];
char
userbuf
[
MAX_MAILBOX_NAME
+
1
];
const
char
*
notify_mailbox
=
mailboxname
;
int
r2
;
/* translate user.foo to INBOX */
if
(
!
(
*
lmtpd_namespace
.
mboxname_tointernal
)(
&
lmtpd_namespace
,
"INBOX"
,
user
,
inbox
))
{
int
inboxlen
=
strlen
(
inbox
);
if
(
strlen
(
mailboxname
)
>=
inboxlen
&&
!
strncmp
(
mailboxname
,
inbox
,
inboxlen
)
&&
(
!
mailboxname
[
inboxlen
]
||
mailboxname
[
inboxlen
]
==
'.'
))
{
strlcpy
(
inbox
,
"INBOX"
,
sizeof
(
inbox
));
strlcat
(
inbox
,
mailboxname
+
inboxlen
,
sizeof
(
inbox
));
notify_mailbox
=
inbox
;
}
}
/* translate mailboxname */
r2
=
(
*
lmtpd_namespace
.
mboxname_toexternal
)(
&
lmtpd_namespace
,
notify_mailbox
,
user
,
namebuf
);
if
(
!
r2
)
{
strlcpy
(
userbuf
,
user
,
sizeof
(
userbuf
));
/* translate any separators in user */
mboxname_hiersep_toexternal
(
&
lmtpd_namespace
,
userbuf
,
config_virtdomains
?
strcspn
(
userbuf
,
"@"
)
:
0
);
notify
(
notifier
,
"MAIL"
,
NULL
,
userbuf
,
namebuf
,
0
,
NULL
,
notifyheader
?
notifyheader
:
""
);
}
}
if
(
!
r
&&
dupelim
&&
id
)
duplicate_mark
(
id
,
strlen
(
id
),
mailboxname
,
strlen
(
mailboxname
),
now
,
uid
);
return
r
;
}
int
deliver
(
message_data_t
*
msgdata
,
char
*
authuser
,
struct
auth_state
*
authstate
)
{
int
n
,
nrcpts
;
struct
stagemsg
*
stage
;
char
*
notifyheader
;
#ifdef USE_SIEVE
sieve_msgdata_t
mydata
;
#endif
assert
(
msgdata
);
nrcpts
=
msg_getnumrcpt
(
msgdata
);
assert
(
nrcpts
);
stage
=
(
struct
stagemsg
*
)
msg_getrock
(
msgdata
);
notifyheader
=
generate_notify
(
msgdata
);
#ifdef USE_SIEVE
/* create 'mydata', our per-delivery data */
mydata
.
m
=
msgdata
;
mydata
.
stage
=
stage
;
mydata
.
notifyheader
=
notifyheader
;
mydata
.
namespace
=
&
lmtpd_namespace
;
mydata
.
authuser
=
authuser
;
mydata
.
authstate
=
authstate
;
#endif
/* loop through each recipient, attempting delivery for each */
for
(
n
=
0
;
n
<
nrcpts
;
n
++
)
{
char
namebuf
[
MAX_MAILBOX_NAME
+
1
]
=
""
;
const
char
*
user
,
*
domain
,
*
mailbox
;
int
quotaoverride
=
msg_getrcpt_ignorequota
(
msgdata
,
n
);
int
r
=
0
;
msg_getrcpt
(
msgdata
,
n
,
&
user
,
&
domain
,
&
mailbox
);
if
(
domain
)
snprintf
(
namebuf
,
sizeof
(
namebuf
),
"%s!"
,
domain
);
/* case 1: shared mailbox request */
if
(
!
user
)
{
strlcat
(
namebuf
,
mailbox
,
sizeof
(
namebuf
));
r
=
deliver_mailbox
(
msgdata
->
data
,
stage
,
msgdata
->
size
,
NULL
,
0
,
authuser
,
authstate
,
msgdata
->
id
,
NULL
,
notifyheader
,
namebuf
,
quotaoverride
,
0
);
}
/* case 2: ordinary user, might have Sieve script */
else
{
char
userbuf
[
MAX_MAILBOX_NAME
+
1
];
char
*
tail
;
strlcat
(
namebuf
,
"user."
,
sizeof
(
namebuf
));
strlcat
(
namebuf
,
user
,
sizeof
(
namebuf
));
tail
=
namebuf
+
strlen
(
namebuf
);
strlcpy
(
userbuf
,
user
,
sizeof
(
userbuf
));
if
(
domain
)
{
strlcat
(
userbuf
,
"@"
,
sizeof
(
userbuf
));
strlcat
(
userbuf
,
domain
,
sizeof
(
userbuf
));
}
#ifdef USE_SIEVE
mydata
.
cur_rcpt
=
n
;
r
=
run_sieve
(
user
,
domain
,
mailbox
,
sieve_interp
,
&
mydata
);
/* if there was no sieve script, or an error during execution,
r is non-zero and we'll do normal delivery */
#else
r
=
1
;
/* normal delivery */
#endif
if
(
r
&&
mailbox
)
{
/* normal delivery to + mailbox */
strlcat
(
namebuf
,
"."
,
sizeof
(
namebuf
));
strlcat
(
namebuf
,
mailbox
,
sizeof
(
namebuf
));
r
=
deliver_mailbox
(
msgdata
->
data
,
stage
,
msgdata
->
size
,
NULL
,
0
,
authuser
,
authstate
,
msgdata
->
id
,
userbuf
,
notifyheader
,
namebuf
,
quotaoverride
,
0
);
}
if
(
r
)
{
/* normal delivery to INBOX */
*
tail
=
'\0'
;
/* ignore ACL's trying to deliver to INBOX */
r
=
deliver_mailbox
(
msgdata
->
data
,
stage
,
msgdata
->
size
,
NULL
,
0
,
authuser
,
authstate
,
msgdata
->
id
,
userbuf
,
notifyheader
,
namebuf
,
quotaoverride
,
1
);
}
}
msg_setrcpt_status
(
msgdata
,
n
,
r
);
}
append_removestage
(
stage
);
if
(
notifyheader
)
free
(
notifyheader
);
return
0
;
}
void
fatal
(
const
char
*
s
,
int
code
)
{
static
int
recurse_code
=
0
;
if
(
recurse_code
)
{
/* We were called recursively. Just give up */
snmp_increment
(
ACTIVE_CONNECTIONS
,
-1
);
exit
(
recurse_code
);
}
recurse_code
=
code
;
if
(
deliver_out
)
{
prot_printf
(
deliver_out
,
"421 4.3.0 lmtpd: %s
\r\n
"
,
s
);
prot_flush
(
deliver_out
);
}
syslog
(
LOG_ERR
,
"FATAL: %s"
,
s
);
/* shouldn't return */
shut_down
(
code
);
exit
(
code
);
}
/*
* Cleanly shut down and exit
*/
void
shut_down
(
int
code
)
__attribute__
((
noreturn
));
void
shut_down
(
int
code
)
{
#ifdef USE_SIEVE
sieve_interp_free
(
&
sieve_interp
);
#else
if
(
dupelim
)
#endif
duplicate_done
();
mboxlist_close
();
mboxlist_done
();
quotadb_close
();
quotadb_done
();
#ifdef HAVE_SSL
tls_shutdown_serverengine
();
#endif
if
(
deliver_out
)
{
prot_flush
(
deliver_out
);
/* one less active connection */
snmp_increment
(
ACTIVE_CONNECTIONS
,
-1
);
}
cyrus_done
();
exit
(
code
);
}
static
int
verify_user
(
const
char
*
user
,
const
char
*
domain
,
const
char
*
mailbox
,
long
quotacheck
,
struct
auth_state
*
authstate
)
{
char
namebuf
[
MAX_MAILBOX_NAME
+
1
]
=
""
;
int
r
=
0
;
if
((
!
user
&&
!
mailbox
)
||
(
domain
&&
(
strlen
(
domain
)
+
1
>
sizeof
(
namebuf
))))
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
else
{
/* construct the mailbox that we will verify */
if
(
domain
)
snprintf
(
namebuf
,
sizeof
(
namebuf
),
"%s!"
,
domain
);
if
(
!
user
)
{
/* shared folder */
if
(
strlen
(
namebuf
)
+
strlen
(
mailbox
)
>
sizeof
(
namebuf
))
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
else
{
strlcat
(
namebuf
,
mailbox
,
sizeof
(
namebuf
));
}
}
else
{
/* ordinary user -- check INBOX */
if
(
strlen
(
namebuf
)
+
5
+
strlen
(
user
)
>
sizeof
(
namebuf
))
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
else
{
strlcat
(
namebuf
,
"user."
,
sizeof
(
namebuf
));
strlcat
(
namebuf
,
user
,
sizeof
(
namebuf
));
}
}
}
if
(
!
r
)
{
/*
* check to see if mailbox exists and we can append to it:
*
* - must have posting privileges on shared folders
* - don't care about ACL on INBOX (always allow post)
* - don't care about message size (1 msg over quota allowed)
*/
r
=
append_check
(
namebuf
,
MAILBOX_FORMAT_NORMAL
,
authstate
,
!
user
?
ACL_POST
:
0
,
quotacheck
>
0
?
0
:
quotacheck
);
}
if
(
r
)
syslog
(
LOG_DEBUG
,
"verify_user(%s) failed: %s"
,
namebuf
,
error_message
(
r
));
return
r
;
}
const
char
*
notifyheaders
[]
=
{
"From"
,
"Subject"
,
"To"
,
0
};
/* returns a malloc'd string that should be sent to users for successful
delivery of 'm'. */
char
*
generate_notify
(
message_data_t
*
m
)
{
const
char
**
body
;
char
*
ret
=
NULL
;
unsigned
int
len
=
0
;
unsigned
int
pos
=
0
;
int
i
;
for
(
i
=
0
;
notifyheaders
[
i
];
i
++
)
{
const
char
*
h
=
notifyheaders
[
i
];
body
=
msg_getheader
(
m
,
h
);
if
(
body
)
{
int
j
;
for
(
j
=
0
;
body
[
j
]
!=
NULL
;
j
++
)
{
/* put the header */
/* need: length + ": " + '\0'*/
while
(
pos
+
strlen
(
h
)
+
3
>
len
)
{
ret
=
xrealloc
(
ret
,
len
+=
1024
);
}
pos
+=
sprintf
(
ret
+
pos
,
"%s: "
,
h
);
/* put the header body.
xxx it would be nice to linewrap.*/
/* need: length + '\n' + '\0' */
while
(
pos
+
strlen
(
body
[
j
])
+
2
>
len
)
{
ret
=
xrealloc
(
ret
,
len
+=
1024
);
}
pos
+=
sprintf
(
ret
+
pos
,
"%s
\n
"
,
body
[
j
]);
}
}
}
return
ret
;
}
FILE
*
spoolfile
(
message_data_t
*
msgdata
)
{
int
i
,
n
;
time_t
now
=
time
(
NULL
);
FILE
*
f
=
NULL
;
/* spool to the stage of one of the recipients */
n
=
msg_getnumrcpt
(
msgdata
);
for
(
i
=
0
;
!
f
&&
(
i
<
n
);
i
++
)
{
int
r
=
0
;
char
namebuf
[
MAX_MAILBOX_NAME
+
1
]
=
""
;
const
char
*
user
,
*
domain
,
*
mailbox
;
/* build the mailboxname from the recipient address */
msg_getrcpt
(
msgdata
,
i
,
&
user
,
&
domain
,
&
mailbox
);
if
(
domain
)
snprintf
(
namebuf
,
sizeof
(
namebuf
),
"%s!"
,
domain
);
/* case 1: shared mailbox request */
if
(
!
user
)
{
strlcat
(
namebuf
,
mailbox
,
sizeof
(
namebuf
));
}
/* case 2: ordinary user */
else
{
/* assume delivery to INBOX for now */
strlcat
(
namebuf
,
"user."
,
sizeof
(
namebuf
));
strlcat
(
namebuf
,
user
,
sizeof
(
namebuf
));
}
#if 0
/* case 3: unable to handle rcpt */
else {
/* force error and we'll fallback to using /tmp */
r = 1;
}
#endif
if
(
!
r
)
{
struct
stagemsg
*
stage
=
NULL
;
/* setup stage for later use by deliver() */
f
=
append_newstage
(
namebuf
,
now
,
0
,
&
stage
);
msg_setrock
(
msgdata
,
(
void
*
)
stage
);
}
}
return
f
;
}
void
removespool
(
message_data_t
*
msgdata
)
{
struct
stagemsg
*
stage
=
(
struct
stagemsg
*
)
msg_getrock
(
msgdata
);
append_removestage
(
stage
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Apr 24, 10:47 AM (5 d, 6 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18855638
Default Alt Text
lmtpd.c (16 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline