1 /* deliver.c -- deliver shell - just calls lmtpd
2  * Tim Martin
3  *
4  * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * 3. The name "Carnegie Mellon University" must not be used to
19  *    endorse or promote products derived from this software without
20  *    prior written permission. For permission or any legal
21  *    details, please contact
22  *      Carnegie Mellon University
23  *      Center for Technology Transfer and Enterprise Creation
24  *      4615 Forbes Avenue
25  *      Suite 302
26  *      Pittsburgh, PA  15213
27  *      (412) 268-7393, fax: (412) 268-7395
28  *      innovation@andrew.cmu.edu
29  *
30  * 4. Redistributions of any form whatsoever must retain the following
31  *    acknowledgment:
32  *    "This product includes software developed by Computing Services
33  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
34  *
35  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
36  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
37  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
38  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
39  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
40  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
41  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42  */
43 
44 #include <config.h>
45 
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sysexits.h>
54 #include <fcntl.h>
55 #include <sys/stat.h>
56 #include <errno.h>
57 #include <pwd.h>
58 #include <sys/types.h>
59 
60 #include <netdb.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include <sys/un.h>
65 
66 #include "global.h"
67 #include "xmalloc.h"
68 #include "xstrlcpy.h"
69 #include "xstrlcat.h"
70 #include "lmtpengine.h"
71 #include "prot.h"
72 #include "proxy.h"
73 #include "version.h"
74 
75 /* generated headers are not necessarily in current directory */
76 #include "imap/imap_err.h"
77 
78 extern int optind;
79 extern char *optarg;
80 
81 static int logdebug = 0;
82 
83 static struct protstream *deliver_out, *deliver_in;
84 
85 static const char *sockaddr;
86 
87 static struct protocol_t lmtp_protocol =
88 { "lmtp", "lmtp", TYPE_STD,
89   { { { 0, "220 " },
90       { "LHLO", "deliver", "250 ", NULL,
91         CAPAF_ONE_PER_LINE|CAPAF_SKIP_FIRST_WORD|CAPAF_DASH_STUFFING,
92         { { "AUTH", CAPA_AUTH },
93           { "STARTTLS", CAPA_STARTTLS },
94           { "PIPELINING", CAPA_PIPELINING },
95           { "IGNOREQUOTA", CAPA_IGNOREQUOTA },
96           { NULL, 0 } } },
97       { "STARTTLS", "220", "454", 0 },
98       { "AUTH", 512, 0, "235", "5", "334 ", "*", NULL, 0 },
99       { NULL, NULL, NULL },
100       { "NOOP", NULL, "250" },
101       { "QUIT", NULL, "221" } } }
102 };
103 
104 /* unused for deliver.c, but needed to make lmtpengine.c happy */
105 int deliver_logfd = -1;
106 
107 /* forward declarations */
108 
109 static int deliver_msg(char *return_path, char *authuser, int ignorequota,
110                        char **users, int numusers, char *mailbox);
111 static struct backend *init_net(const char *sockaddr);
112 
usage(void)113 static void usage(void)
114 {
115     fprintf(stderr,
116             "421-4.3.0 usage: deliver [-C <alt_config> ] [-m mailbox]"
117             " [-a auth] [-r return_path] [-l] [-D]\r\n");
118     fprintf(stderr, "421 4.3.0 %s\n", CYRUS_VERSION);
119     exit(EX_USAGE);
120 }
121 
fatal(const char * s,int code)122 EXPORTED void fatal(const char* s, int code)
123 {
124     static int recurse_code = 0;
125 
126     if(recurse_code) exit(code);
127     else recurse_code = 0;
128 
129     prot_printf(deliver_out,"421 4.3.0 deliver: %s\r\n", s);
130     prot_flush(deliver_out);
131     cyrus_done();
132     exit(code);
133 }
134 
135 /*
136  * Here we're just an intermediatory piping stdin to lmtp socket
137  * and lmtp socket to stdout
138  */
pipe_through(struct backend * conn)139 void pipe_through(struct backend *conn)
140 {
141     struct protgroup *protin = protgroup_new(2);
142 
143     protgroup_insert(protin, deliver_in);
144     protgroup_insert(protin, conn->in);
145 
146     do {
147         /* Flush any buffered output */
148         prot_flush(deliver_out);
149         prot_flush(conn->out);
150 
151     } while (!proxy_check_input(protin, deliver_in, deliver_out,
152                                 conn->in, conn->out, 0));
153 
154     /* ok, we're done. */
155     protgroup_free(protin);
156 
157     return;
158 }
159 
main(int argc,char ** argv)160 int main(int argc, char **argv)
161 {
162     int r = 0;
163     int opt;
164     int lmtpflag = 0;
165     int ignorequota = 0;
166     char *mailboxname = NULL;
167     char *authuser = NULL;
168     char *return_path = NULL;
169     char buf[1024];
170     char *alt_config = NULL;
171 
172     while ((opt = getopt(argc, argv, "C:df:r:m:a:F:eE:lqD")) != EOF) {
173         switch(opt) {
174         case 'C': /* alt config file */
175             alt_config = optarg;
176             break;
177 
178         case 'd':
179             /* Ignore -- /bin/mail compatibility flags */
180             break;
181 
182         case 'D':
183             logdebug = 1;
184             break;
185 
186         case 'r':
187         case 'f':
188             return_path = optarg;
189             break;
190 
191         case 'm':
192             if (mailboxname) {
193                 fprintf(stderr, "deliver: multiple -m options\n");
194                 usage();
195                 /* NOTREACHED */
196             }
197             if (*optarg) mailboxname = optarg;
198             break;
199 
200         case 'a':
201             if (authuser) {
202                 fprintf(stderr, "deliver: multiple -a options\n");
203                 usage();
204                 /* NOTREACHED */
205             }
206             authuser = optarg;
207             break;
208 
209         case 'F': /* set IMAP flag. we no longer support this */
210             fprintf(stderr,"deliver: 'F' option no longer supported\n");
211             usage();
212             break;
213 
214         case 'e':
215             /* duplicate delivery. ignore */
216             break;
217 
218         case 'E':
219             fprintf(stderr,"deliver: 'E' option no longer supported\n");
220             usage();
221             break;
222 
223         case 'l':
224             lmtpflag = 1;
225             break;
226 
227         case 'q':
228             ignorequota = 1;
229             break;
230 
231         default:
232             usage();
233             /* NOTREACHED */
234         }
235     }
236 
237     deliver_in = prot_new(0, 0);
238     deliver_out = prot_new(1, 1);
239     prot_setflushonread(deliver_in, deliver_out);
240     prot_settimeout(deliver_in, 300);
241 
242     cyrus_init(alt_config, "deliver", CYRUSINIT_NODB, CONFIG_NEED_PARTITION_DATA);
243 
244     sockaddr = config_getstring(IMAPOPT_LMTPSOCKET);
245     if (!sockaddr) {
246         strlcpy(buf, config_dir, sizeof(buf));
247         strlcat(buf, "/socket/lmtp", sizeof(buf));
248         sockaddr = buf;
249     }
250 
251     if (lmtpflag == 1) {
252         struct backend *conn = init_net(sockaddr);
253 
254         pipe_through(conn);
255 
256         backend_disconnect(conn);
257         free(conn);
258     }
259     else {
260         if (return_path == NULL) {
261             uid_t me = getuid();
262             struct passwd *p = getpwuid(me);
263             return_path = p->pw_name;
264         }
265 
266         /* deliver to users or global mailbox */
267         r = deliver_msg(return_path,authuser, ignorequota,
268                         argv+optind, argc - optind, mailboxname);
269     }
270 
271     cyrus_done();
272 
273     return r;
274 }
275 
just_exit(const char * msg)276 static void just_exit(const char *msg)
277 {
278     com_err(msg, 0, "%s", error_message(errno));
279 
280     fatal(msg, EX_CONFIG);
281 }
282 
283 /* initialize the network
284  * we talk on unix sockets
285  */
init_net(const char * unixpath)286 static struct backend *init_net(const char *unixpath)
287 {
288   int lmtpdsock;
289   struct sockaddr_un addr;
290   struct backend *conn;
291 
292   if ((lmtpdsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
293       just_exit("socket failed");
294   }
295 
296   addr.sun_family = AF_UNIX;
297   strlcpy(addr.sun_path, unixpath, sizeof(addr.sun_path));
298 
299   if (connect(lmtpdsock, (struct sockaddr *) &addr,
300               sizeof(addr.sun_family) + strlen(addr.sun_path) + 1) < 0) {
301       just_exit("connect failed");
302   }
303 
304   conn = xzmalloc(sizeof(struct backend));
305   conn->timeout = NULL;
306   conn->in = prot_new(lmtpdsock, 0);
307   conn->out = prot_new(lmtpdsock, 1);
308   conn->sock = lmtpdsock;
309   prot_setflushonread(conn->in, conn->out);
310   conn->prot = &lmtp_protocol;
311 
312   return conn;
313 }
314 
deliver_msg(char * return_path,char * authuser,int ignorequota,char ** users,int numusers,char * mailbox)315 static int deliver_msg(char *return_path, char *authuser, int ignorequota,
316                        char **users, int numusers, char *mailbox)
317 {
318     int r;
319     struct backend *conn;
320     struct lmtp_txn *txn = LMTP_TXN_ALLOC(numusers ? numusers : 1);
321     int j;
322     int ml = 0;
323 
324     /* must have either some users or a mailbox */
325     if (!numusers && !mailbox) {
326         usage();
327     }
328 
329     /* connect */
330     conn = backend_connect(NULL, sockaddr, &lmtp_protocol,
331                            "", NULL, NULL, -1);
332     if (!conn) {
333         just_exit("couldn't connect to lmtpd");
334     }
335 
336     /* setup txn */
337     txn->from = return_path;
338     txn->auth = authuser;
339     txn->data = deliver_in;
340     txn->isdotstuffed = 0;
341     txn->tempfail_unknown_mailbox = 0;
342     txn->rcpt_num = numusers ? numusers : 1;
343     if (mailbox) ml = strlen(mailbox);
344     if (numusers == 0) {
345         /* just deliver to mailbox 'mailbox' */
346         const char *BB = config_getstring(IMAPOPT_POSTUSER);
347         txn->rcpt[0].addr = (char *) xmalloc(ml + strlen(BB) + 2); /* xxx leaks! */
348         sprintf(txn->rcpt[0].addr, "%s+%s", BB, mailbox);
349         txn->rcpt[0].ignorequota = ignorequota;
350     } else {
351         /* setup each recipient */
352         for (j = 0; j < numusers; j++) {
353             if (mailbox) {
354                 size_t ulen;
355 
356                 txn->rcpt[j].addr =
357                     (char *) xmalloc(strlen(users[j]) + ml + 2);
358 
359                 /* find the length of the userid minus the domain */
360                 ulen = strcspn(users[j], "@");
361                 sprintf(txn->rcpt[j].addr, "%.*s+%s",
362                         (int) ulen, users[j], mailbox);
363 
364                 /* add the domain if we have one */
365                 if (ulen < strlen(users[j]))
366                     strcat(txn->rcpt[j].addr, users[j]+ulen);
367             } else {
368                 txn->rcpt[j].addr = xstrdup(users[j]);
369             }
370             txn->rcpt[j].ignorequota = ignorequota;
371         }
372     }
373 
374     /* run txn */
375     r = lmtp_runtxn(conn, txn);
376 
377     /* disconnect */
378     backend_disconnect(conn);
379     free(conn);
380 
381     /* examine txn for error state */
382     r = 0;
383     for (j = 0; j < txn->rcpt_num; j++) {
384         switch (txn->rcpt[j].result) {
385         case RCPT_GOOD:
386             break;
387 
388         case RCPT_TEMPFAIL:
389             r = EX_TEMPFAIL;
390             break;
391 
392         case RCPT_PERMFAIL:
393             /* we just need any permanent failure, though we should
394                probably return data from the client-side LMTP info */
395             printf("%s: %s\n",
396                    txn->rcpt[j].addr, error_message(txn->rcpt[j].r));
397             if (r != EX_TEMPFAIL) {
398                 r = EX_DATAERR;
399             }
400             break;
401         }
402         free(txn->rcpt[j].addr);
403         strarray_free(txn->rcpt[j].resp);
404     }
405 
406     free(txn);
407 
408     /* return appropriately */
409     return r;
410 }
411