xref: /openbsd/usr.sbin/smtpd/smtp_client.c (revision 3a50f0a9)
1*3a50f0a9Sjmc /*	$OpenBSD: smtp_client.c,v 1.17 2022/12/28 21:30:18 jmc Exp $	*/
2ef573529Seric 
3ef573529Seric /*
4ef573529Seric  * Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
5ef573529Seric  *
6ef573529Seric  * Permission to use, copy, modify, and distribute this software for any
7ef573529Seric  * purpose with or without fee is hereby granted, provided that the above
8ef573529Seric  * copyright notice and this permission notice appear in all copies.
9ef573529Seric  *
10ef573529Seric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11ef573529Seric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12ef573529Seric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13ef573529Seric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14ef573529Seric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15ef573529Seric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16ef573529Seric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17ef573529Seric  */
18ef573529Seric 
19ef573529Seric #include <sys/types.h>
20ef573529Seric #include <sys/socket.h>
21ef573529Seric 
22ef573529Seric #include <netinet/in.h>
23ef573529Seric 
24ef573529Seric #include <ctype.h>
25ef573529Seric #include <errno.h>
26ef573529Seric #include <limits.h>
27ef573529Seric #include <resolv.h>
28ef573529Seric #include <stdio.h>
29ef573529Seric #include <stdlib.h>
30ef573529Seric #include <string.h>
31ef573529Seric 
32ef573529Seric #include "log.h"
33ef573529Seric #include "ioev.h"
34ef573529Seric #include "smtp.h"
35ef573529Seric 
36ef573529Seric #define	TRACE_SMTPCLT	2
37ef573529Seric #define	TRACE_IO	3
38ef573529Seric 
39ef573529Seric enum {
40ef573529Seric 	STATE_INIT = 0,
41ef573529Seric 	STATE_BANNER,
42ef573529Seric 	STATE_EHLO,
43ef573529Seric 	STATE_HELO,
44ef573529Seric 	STATE_LHLO,
45ef573529Seric 	STATE_STARTTLS,
46ef573529Seric 	STATE_AUTH,
47ef573529Seric 	STATE_AUTH_PLAIN,
48ef573529Seric 	STATE_AUTH_LOGIN,
49ef573529Seric 	STATE_AUTH_LOGIN_USER,
50ef573529Seric 	STATE_AUTH_LOGIN_PASS,
51ef573529Seric 	STATE_READY,
52ef573529Seric 	STATE_MAIL,
53ef573529Seric 	STATE_RCPT,
54ef573529Seric 	STATE_DATA,
55ef573529Seric 	STATE_BODY,
56ef573529Seric 	STATE_EOM,
57ef573529Seric 	STATE_RSET,
58ef573529Seric 	STATE_QUIT,
59ef573529Seric 
60ef573529Seric 	STATE_LAST
61ef573529Seric };
62ef573529Seric 
63ef573529Seric #define base64_encode	__b64_ntop
64ef573529Seric #define base64_decode	__b64_pton
65ef573529Seric 
66ef573529Seric #define FLAG_TLS		0x01
67ef573529Seric #define FLAG_TLS_VERIFIED	0x02
68ef573529Seric 
69ef573529Seric #define SMTP_EXT_STARTTLS	0x01
70ef573529Seric #define SMTP_EXT_PIPELINING	0x02
71ef573529Seric #define SMTP_EXT_AUTH		0x04
72ef573529Seric #define SMTP_EXT_AUTH_PLAIN     0x08
73ef573529Seric #define SMTP_EXT_AUTH_LOGIN     0x10
74ef573529Seric #define SMTP_EXT_DSN		0x20
75fba2f2d7Seric #define SMTP_EXT_SIZE		0x40
76ef573529Seric 
77ef573529Seric struct smtp_client {
78ef573529Seric 	void			*tag;
79ef573529Seric 	struct smtp_params	 params;
80ef573529Seric 
81ef573529Seric 	int			 state;
82ef573529Seric 	int			 flags;
83ef573529Seric 	int			 ext;
84fba2f2d7Seric 	size_t			 ext_size;
85ef573529Seric 
86ef573529Seric 	struct io		*io;
87ef573529Seric 	char			*reply;
88ef573529Seric 	size_t			 replysz;
89ef573529Seric 
90ef573529Seric 	struct smtp_mail	*mail;
91ef573529Seric 	int			 rcptidx;
92ef573529Seric 	int			 rcptok;
93ef573529Seric };
94ef573529Seric 
950cef6e54Seric void log_trace_verbose(int);
960cef6e54Seric void log_trace(int, const char *, ...)
970cef6e54Seric     __attribute__((format (printf, 2, 3)));
980cef6e54Seric 
99ef573529Seric static void smtp_client_io(struct io *, int, void *);
100ef573529Seric static void smtp_client_free(struct smtp_client *);
101ef573529Seric static void smtp_client_state(struct smtp_client *, int);
102ef573529Seric static void smtp_client_abort(struct smtp_client *, int, const char *);
103ef573529Seric static void smtp_client_cancel(struct smtp_client *, int, const char *);
104ef573529Seric static void smtp_client_sendcmd(struct smtp_client *, char *, ...);
105ef573529Seric static void smtp_client_sendbody(struct smtp_client *);
106ef573529Seric static int smtp_client_readline(struct smtp_client *);
107ef573529Seric static int smtp_client_replycat(struct smtp_client *, const char *);
108ef573529Seric static void smtp_client_response(struct smtp_client *, const char *);
109ef573529Seric static void smtp_client_mail_abort(struct smtp_client *);
110ef573529Seric static void smtp_client_mail_status(struct smtp_client *, const char *);
111ef573529Seric static void smtp_client_rcpt_status(struct smtp_client *, struct smtp_rcpt *, const char *);
112ef573529Seric 
113ef573529Seric static const char *strstate[STATE_LAST] = {
114ef573529Seric 	"INIT",
115ef573529Seric 	"BANNER",
116ef573529Seric 	"EHLO",
117ef573529Seric 	"HELO",
118ef573529Seric 	"LHLO",
119ef573529Seric 	"STARTTLS",
120ef573529Seric 	"AUTH",
121ef573529Seric 	"AUTH_PLAIN",
122ef573529Seric 	"AUTH_LOGIN",
123ef573529Seric 	"AUTH_LOGIN_USER",
124ef573529Seric 	"AUTH_LOGIN_PASS",
125ef573529Seric 	"READY",
126ef573529Seric 	"MAIL",
127ef573529Seric 	"RCPT",
128ef573529Seric 	"DATA",
129ef573529Seric 	"BODY",
130ef573529Seric 	"EOM",
131ef573529Seric 	"RSET",
132ef573529Seric 	"QUIT",
133ef573529Seric };
134ef573529Seric 
135ef573529Seric struct smtp_client *
smtp_connect(const struct smtp_params * params,void * tag)136ef573529Seric smtp_connect(const struct smtp_params *params, void *tag)
137ef573529Seric {
138ef573529Seric 	struct smtp_client *proto;
139ef573529Seric 
140ef573529Seric 	proto = calloc(1, sizeof *proto);
141ef573529Seric 	if (proto == NULL)
142ef573529Seric 		return NULL;
143ef573529Seric 
144ef573529Seric 	memmove(&proto->params, params, sizeof(*params));
145ef573529Seric 	proto->tag = tag;
146ef573529Seric 	proto->io = io_new();
147ef573529Seric 	if (proto->io == NULL) {
148ef573529Seric 		free(proto);
149ef573529Seric 		return NULL;
150ef573529Seric 	}
151ef573529Seric 	io_set_callback(proto->io, smtp_client_io, proto);
152ef573529Seric 	io_set_timeout(proto->io, proto->params.timeout);
153ef573529Seric 
154ef573529Seric 	if (io_connect(proto->io, proto->params.dst, proto->params.src) == -1) {
155ef573529Seric 		smtp_client_abort(proto, FAIL_CONN, io_error(proto->io));
156ef573529Seric 		return NULL;
157ef573529Seric 	}
158ef573529Seric 
159ef573529Seric 	return proto;
160ef573529Seric }
161ef573529Seric 
162ef573529Seric void
smtp_cert_verified(struct smtp_client * proto,int verified)163ef573529Seric smtp_cert_verified(struct smtp_client *proto, int verified)
164ef573529Seric {
165ef573529Seric 	if (verified == CERT_OK)
166ef573529Seric 		proto->flags |= FLAG_TLS_VERIFIED;
167ef573529Seric 
168ef573529Seric 	else if (proto->params.tls_verify) {
169ef573529Seric 		errno = EAUTH;
1702d0f6bd5Seric 		smtp_client_abort(proto, FAIL_CONN,
171ef573529Seric 		    "Invalid server certificate");
172ef573529Seric 		return;
173ef573529Seric 	}
174ef573529Seric 
175ef573529Seric 	io_resume(proto->io, IO_IN);
176ef573529Seric 
177ef573529Seric 	if (proto->state == STATE_INIT)
178ef573529Seric 		smtp_client_state(proto, STATE_BANNER);
179ef573529Seric 	else {
180ef573529Seric 		/* Clear extensions before re-issueing an EHLO command. */
181ef573529Seric 		proto->ext = 0;
182ef573529Seric 		smtp_client_state(proto, STATE_EHLO);
183ef573529Seric 	}
184ef573529Seric }
185ef573529Seric 
186ef573529Seric void
smtp_set_tls(struct smtp_client * proto,void * ctx)187cde53503Seric smtp_set_tls(struct smtp_client *proto, void *ctx)
188cde53503Seric {
189eed85469Seric 	io_connect_tls(proto->io, ctx, proto->params.tls_servname);
190cde53503Seric }
191cde53503Seric 
192cde53503Seric void
smtp_quit(struct smtp_client * proto)193ef573529Seric smtp_quit(struct smtp_client *proto)
194ef573529Seric {
195ef573529Seric 	if (proto->state != STATE_READY)
196b968de35Seric 		fatalx("connection is not ready");
197ef573529Seric 
198ef573529Seric 	smtp_client_state(proto, STATE_QUIT);
199ef573529Seric }
200ef573529Seric 
201ef573529Seric void
smtp_sendmail(struct smtp_client * proto,struct smtp_mail * mail)202ef573529Seric smtp_sendmail(struct smtp_client *proto, struct smtp_mail *mail)
203ef573529Seric {
204ef573529Seric 	if (proto->state != STATE_READY)
205b968de35Seric 		fatalx("connection is not ready");
206ef573529Seric 
207ef573529Seric 	proto->mail = mail;
208ef573529Seric 	smtp_client_state(proto, STATE_MAIL);
209ef573529Seric }
210ef573529Seric 
211ef573529Seric static void
smtp_client_free(struct smtp_client * proto)212ef573529Seric smtp_client_free(struct smtp_client *proto)
213ef573529Seric {
214ef573529Seric 	if (proto->mail)
215ef573529Seric 		fatalx("current task should have been deleted already");
216ef573529Seric 
217538492c2Seric 	smtp_closed(proto->tag, proto);
218538492c2Seric 
219ef573529Seric 	if (proto->io)
220ef573529Seric 		io_free(proto->io);
221ef573529Seric 
222ef573529Seric 	free(proto->reply);
223ef573529Seric 	free(proto);
224ef573529Seric }
225ef573529Seric 
226ef573529Seric /*
227*3a50f0a9Sjmc  * End the session immediately.
228ef573529Seric  */
229ef573529Seric static void
smtp_client_abort(struct smtp_client * proto,int err,const char * reason)230ef573529Seric smtp_client_abort(struct smtp_client *proto, int err, const char *reason)
231ef573529Seric {
232ef573529Seric 	smtp_failed(proto->tag, proto, err, reason);
233ef573529Seric 
234ef573529Seric 	if (proto->mail)
235ef573529Seric 		smtp_client_mail_abort(proto);
236ef573529Seric 
237ef573529Seric 	smtp_client_free(proto);
238ef573529Seric }
239ef573529Seric 
240ef573529Seric /*
241ef573529Seric  * Properly close the session.
242ef573529Seric  */
243ef573529Seric static void
smtp_client_cancel(struct smtp_client * proto,int err,const char * reason)244ef573529Seric smtp_client_cancel(struct smtp_client *proto, int err, const char *reason)
245ef573529Seric {
246ef573529Seric 	if (proto->mail)
247ef573529Seric 		fatal("not supposed to have a mail");
248ef573529Seric 
249ef573529Seric 	smtp_failed(proto->tag, proto, err, reason);
250ef573529Seric 
251ef573529Seric 	smtp_client_state(proto, STATE_QUIT);
252ef573529Seric }
253ef573529Seric 
254ef573529Seric static void
smtp_client_state(struct smtp_client * proto,int newstate)255ef573529Seric smtp_client_state(struct smtp_client *proto, int newstate)
256ef573529Seric {
257ef573529Seric 	struct smtp_rcpt *rcpt;
258ef573529Seric 	char ibuf[LINE_MAX], obuf[LINE_MAX];
25943ddfbdbSeric 	size_t n;
26043ddfbdbSeric 	int oldstate;
261ef573529Seric 
262ef573529Seric 	if (proto->reply)
263ef573529Seric 		proto->reply[0] = '\0';
264ef573529Seric 
265ef573529Seric     again:
266ef573529Seric 	oldstate = proto->state;
267ef573529Seric 	proto->state = newstate;
268ef573529Seric 
269ef573529Seric 	log_trace(TRACE_SMTPCLT, "%p: %s -> %s", proto,
270ef573529Seric 	    strstate[oldstate],
271ef573529Seric 	    strstate[newstate]);
272ef573529Seric 
273ef573529Seric 	/* don't try this at home! */
274ef573529Seric #define smtp_client_state(_s, _st) do { newstate = _st; goto again; } while (0)
275ef573529Seric 
276ef573529Seric 	switch (proto->state) {
277ef573529Seric 	case STATE_BANNER:
278ef573529Seric 		io_set_read(proto->io);
279ef573529Seric 		break;
280ef573529Seric 
281ef573529Seric 	case STATE_EHLO:
282ef573529Seric 		smtp_client_sendcmd(proto, "EHLO %s", proto->params.helo);
283ef573529Seric 		break;
284ef573529Seric 
285ef573529Seric 	case STATE_HELO:
286ef573529Seric 		smtp_client_sendcmd(proto, "HELO %s", proto->params.helo);
287ef573529Seric 		break;
288ef573529Seric 
289ef573529Seric 	case STATE_LHLO:
290ef573529Seric 		smtp_client_sendcmd(proto, "LHLO %s", proto->params.helo);
291ef573529Seric 		break;
292ef573529Seric 
293ef573529Seric 	case STATE_STARTTLS:
294ef573529Seric 		if (proto->params.tls_req == TLS_NO || proto->flags & FLAG_TLS)
295ef573529Seric 			smtp_client_state(proto, STATE_AUTH);
296ef573529Seric 		else if (proto->ext & SMTP_EXT_STARTTLS)
297ef573529Seric 			smtp_client_sendcmd(proto, "STARTTLS");
298ef573529Seric 		else if (proto->params.tls_req == TLS_FORCE)
299ef573529Seric 			smtp_client_cancel(proto, FAIL_IMPL,
300ef573529Seric 			    "TLS not supported by remote host");
301ef573529Seric 		else
302ef573529Seric 			smtp_client_state(proto, STATE_AUTH);
303ef573529Seric 		break;
304ef573529Seric 
305ef573529Seric 	case STATE_AUTH:
30643ddfbdbSeric 		if (!proto->params.auth_user)
307ef573529Seric 			smtp_client_state(proto, STATE_READY);
308ef573529Seric 		else if ((proto->flags & FLAG_TLS) == 0)
309ef573529Seric 			smtp_client_cancel(proto, FAIL_IMPL,
310ef573529Seric 			    "Authentication requires TLS");
311ef573529Seric 		else if ((proto->ext & SMTP_EXT_AUTH) == 0)
312ef573529Seric 			smtp_client_cancel(proto, FAIL_IMPL,
313ef573529Seric 			    "AUTH not supported by remote host");
314ef573529Seric 		else if (proto->ext & SMTP_EXT_AUTH_PLAIN)
315ef573529Seric 			smtp_client_state(proto, STATE_AUTH_PLAIN);
316ef573529Seric 		else if (proto->ext & SMTP_EXT_AUTH_LOGIN)
317ef573529Seric 			smtp_client_state(proto, STATE_AUTH_LOGIN);
318ef573529Seric 		else
319ef573529Seric 			smtp_client_cancel(proto, FAIL_IMPL,
320ef573529Seric 			    "No supported AUTH method");
321ef573529Seric 		break;
322ef573529Seric 
323ef573529Seric 	case STATE_AUTH_PLAIN:
32443ddfbdbSeric 		(void)strlcpy(ibuf, "-", sizeof(ibuf));
32543ddfbdbSeric 		(void)strlcat(ibuf, proto->params.auth_user, sizeof(ibuf));
32643ddfbdbSeric 		if (strlcat(ibuf, ":", sizeof(ibuf)) >= sizeof(ibuf)) {
32743ddfbdbSeric 			errno = EMSGSIZE;
32843ddfbdbSeric 			smtp_client_cancel(proto, FAIL_INTERNAL,
32943ddfbdbSeric 			    "credentials too large");
33043ddfbdbSeric 			break;
33143ddfbdbSeric 		}
33243ddfbdbSeric 		n = strlcat(ibuf, proto->params.auth_pass, sizeof(ibuf));
33343ddfbdbSeric 		if (n >= sizeof(ibuf)) {
33443ddfbdbSeric 			errno = EMSGSIZE;
33543ddfbdbSeric 			smtp_client_cancel(proto, FAIL_INTERNAL,
33643ddfbdbSeric 			    "credentials too large");
33743ddfbdbSeric 			break;
33843ddfbdbSeric 		}
33943ddfbdbSeric 		*strchr(ibuf, ':') = '\0';
34043ddfbdbSeric 		ibuf[0] = '\0';
34143ddfbdbSeric 		if (base64_encode(ibuf, n, obuf, sizeof(obuf)) == -1) {
34243ddfbdbSeric 			errno = EMSGSIZE;
34343ddfbdbSeric 			smtp_client_cancel(proto, FAIL_INTERNAL,
34443ddfbdbSeric 			    "credentials too large");
34543ddfbdbSeric 			break;
34643ddfbdbSeric 		}
34743ddfbdbSeric 		smtp_client_sendcmd(proto, "AUTH PLAIN %s", obuf);
34843ddfbdbSeric 		explicit_bzero(ibuf, sizeof ibuf);
34943ddfbdbSeric 		explicit_bzero(obuf, sizeof obuf);
350ef573529Seric 		break;
351ef573529Seric 
352ef573529Seric 	case STATE_AUTH_LOGIN:
353ef573529Seric 		smtp_client_sendcmd(proto, "AUTH LOGIN");
354ef573529Seric 		break;
355ef573529Seric 
356ef573529Seric 	case STATE_AUTH_LOGIN_USER:
35743ddfbdbSeric 		if (base64_encode(proto->params.auth_user,
35843ddfbdbSeric 		    strlen(proto->params.auth_user), obuf,
35943ddfbdbSeric 		    sizeof(obuf)) == -1) {
36043ddfbdbSeric 			errno = EMSGSIZE;
361ef573529Seric 			smtp_client_cancel(proto, FAIL_INTERNAL,
36243ddfbdbSeric 			    "credentials too large");
363ef573529Seric 			break;
364ef573529Seric 		}
365ef573529Seric 		smtp_client_sendcmd(proto, "%s", obuf);
366ef573529Seric 		explicit_bzero(obuf, sizeof obuf);
367ef573529Seric 		break;
368ef573529Seric 
369ef573529Seric 	case STATE_AUTH_LOGIN_PASS:
37043ddfbdbSeric 		if (base64_encode(proto->params.auth_pass,
37143ddfbdbSeric 		    strlen(proto->params.auth_pass), obuf,
37243ddfbdbSeric 		    sizeof(obuf)) == -1) {
37343ddfbdbSeric 			errno = EMSGSIZE;
374ef573529Seric 			smtp_client_cancel(proto, FAIL_INTERNAL,
37543ddfbdbSeric 			    "credentials too large");
376ef573529Seric 			break;
377ef573529Seric 		}
378ef573529Seric 		smtp_client_sendcmd(proto, "%s", obuf);
379ef573529Seric 		explicit_bzero(obuf, sizeof obuf);
380ef573529Seric 		break;
381ef573529Seric 
382ef573529Seric 	case STATE_READY:
383ef573529Seric 		smtp_ready(proto->tag, proto);
384ef573529Seric 		break;
385ef573529Seric 
386ef573529Seric 	case STATE_MAIL:
387ef573529Seric 		if (proto->ext & SMTP_EXT_DSN)
388ef573529Seric 			smtp_client_sendcmd(proto, "MAIL FROM:<%s>%s%s%s%s",
389ef573529Seric 			    proto->mail->from,
390ef573529Seric 			    proto->mail->dsn_ret ? " RET=" : "",
391ef573529Seric 			    proto->mail->dsn_ret ? proto->mail->dsn_ret : "",
392ef573529Seric 			    proto->mail->dsn_envid ? " ENVID=" : "",
393ef573529Seric 			    proto->mail->dsn_envid ? proto->mail->dsn_envid : "");
394ef573529Seric 		else
395ef573529Seric 			smtp_client_sendcmd(proto, "MAIL FROM:<%s>",
396ef573529Seric 			    proto->mail->from);
397ef573529Seric 		break;
398ef573529Seric 
399ef573529Seric 	case STATE_RCPT:
400ef573529Seric 		if (proto->rcptidx == proto->mail->rcptcount) {
401ef573529Seric 			smtp_client_state(proto, STATE_DATA);
402ef573529Seric 			break;
403ef573529Seric 		}
404ef573529Seric 		rcpt = &proto->mail->rcpt[proto->rcptidx];
405ef573529Seric 		if (proto->ext & SMTP_EXT_DSN)
406ef573529Seric 			smtp_client_sendcmd(proto, "RCPT TO:<%s>%s%s%s%s",
407ef573529Seric 			    rcpt->to,
408ef573529Seric 			    rcpt->dsn_notify ? " NOTIFY=" : "",
409ef573529Seric 			    rcpt->dsn_notify ? rcpt->dsn_notify : "",
410ef573529Seric 			    rcpt->dsn_orcpt ? " ORCPT=" : "",
411ef573529Seric 			    rcpt->dsn_orcpt ? rcpt->dsn_orcpt : "");
412ef573529Seric 		else
413ef573529Seric 			smtp_client_sendcmd(proto, "RCPT TO:<%s>", rcpt->to);
414ef573529Seric 		break;
415ef573529Seric 
416ef573529Seric 	case STATE_DATA:
417ef573529Seric 		if (proto->rcptok == 0) {
418ef573529Seric 			smtp_client_mail_abort(proto);
419ef573529Seric 			smtp_client_state(proto, STATE_RSET);
420ef573529Seric 		}
421ef573529Seric 		else
422ef573529Seric 			smtp_client_sendcmd(proto, "DATA");
423ef573529Seric 		break;
424ef573529Seric 
425ef573529Seric 	case STATE_BODY:
426ef573529Seric 		fseek(proto->mail->fp, 0, SEEK_SET);
427ef573529Seric 		smtp_client_sendbody(proto);
428ef573529Seric 		break;
429ef573529Seric 
430ef573529Seric 	case STATE_EOM:
431ef573529Seric 		smtp_client_sendcmd(proto, ".");
432ef573529Seric 		break;
433ef573529Seric 
434ef573529Seric 	case STATE_RSET:
435ef573529Seric 		smtp_client_sendcmd(proto, "RSET");
436ef573529Seric 		break;
437ef573529Seric 
438ef573529Seric 	case STATE_QUIT:
439ef573529Seric 		smtp_client_sendcmd(proto, "QUIT");
440ef573529Seric 		break;
441ef573529Seric 
442ef573529Seric 	default:
443fa1463c8Smiko 		fatalx("%s: bad state %d", __func__, proto->state);
444ef573529Seric 	}
445ef573529Seric #undef smtp_client_state
446ef573529Seric }
447ef573529Seric 
448ef573529Seric /*
449ef573529Seric  * Handle a response to an SMTP command
450ef573529Seric  */
451ef573529Seric static void
smtp_client_response(struct smtp_client * proto,const char * line)452ef573529Seric smtp_client_response(struct smtp_client *proto, const char *line)
453ef573529Seric {
454ef573529Seric 	struct smtp_rcpt *rcpt;
455ef573529Seric 	int i, seen;
456ef573529Seric 
457ef573529Seric 	switch (proto->state) {
458ef573529Seric 	case STATE_BANNER:
459ef573529Seric 		if (line[0] != '2')
460ef573529Seric 			smtp_client_abort(proto, FAIL_RESP, line);
461ef573529Seric 		else if (proto->params.lmtp)
462ef573529Seric 			smtp_client_state(proto, STATE_LHLO);
463ef573529Seric 		else
464ef573529Seric 			smtp_client_state(proto, STATE_EHLO);
465ef573529Seric 		break;
466ef573529Seric 
467ef573529Seric 	case STATE_EHLO:
468ef573529Seric 		if (line[0] != '2') {
469ef573529Seric 			/*
470ef573529Seric 			 * Either rejected or not implemented.  If we want to
471ef573529Seric 			 * use EHLO extensions, report an SMTP error.
472ef573529Seric 			 * Otherwise, fallback to using HELO.
473ef573529Seric 			 */
474ef573529Seric 			if ((proto->params.tls_req == TLS_FORCE) ||
47543ddfbdbSeric 			    (proto->params.auth_user))
476ef573529Seric 				smtp_client_cancel(proto, FAIL_RESP, line);
477ef573529Seric 			else
478ef573529Seric 				smtp_client_state(proto, STATE_HELO);
479ef573529Seric 			break;
480ef573529Seric 		}
481ef573529Seric 		smtp_client_state(proto, STATE_STARTTLS);
482ef573529Seric 		break;
483ef573529Seric 
484ef573529Seric 	case STATE_HELO:
485ef573529Seric 		if (line[0] != '2')
486ef573529Seric 			smtp_client_cancel(proto, FAIL_RESP, line);
487ef573529Seric 		else
488ef573529Seric 			smtp_client_state(proto, STATE_READY);
489ef573529Seric 		break;
490ef573529Seric 
491ef573529Seric 	case STATE_LHLO:
492ef573529Seric 		if (line[0] != '2')
493ef573529Seric 			smtp_client_cancel(proto, FAIL_RESP, line);
494ef573529Seric 		else
495ef573529Seric 			smtp_client_state(proto, STATE_READY);
496ef573529Seric 		break;
497ef573529Seric 
498ef573529Seric 	case STATE_STARTTLS:
499ef573529Seric 		if (line[0] != '2') {
500ef573529Seric 			if ((proto->params.tls_req == TLS_FORCE) ||
50143ddfbdbSeric 			    (proto->params.auth_user)) {
502ef573529Seric 				smtp_client_cancel(proto, FAIL_RESP, line);
503ef573529Seric 				break;
504ef573529Seric 			}
505ef573529Seric 			smtp_client_state(proto, STATE_AUTH);
506ef573529Seric 		}
507ef573529Seric 		else
508cde53503Seric 			smtp_require_tls(proto->tag, proto);
509ef573529Seric 		break;
510ef573529Seric 
511ef573529Seric 	case STATE_AUTH_PLAIN:
512ef573529Seric 		if (line[0] != '2')
513ef573529Seric 			smtp_client_cancel(proto, FAIL_RESP, line);
514ef573529Seric 		else
515ef573529Seric 			smtp_client_state(proto, STATE_READY);
516ef573529Seric 		break;
517ef573529Seric 
518ef573529Seric 	case STATE_AUTH_LOGIN:
519ef573529Seric 		if (strncmp(line, "334 ", 4))
520ef573529Seric 			smtp_client_cancel(proto, FAIL_RESP, line);
521ef573529Seric 		else
522ef573529Seric 			smtp_client_state(proto, STATE_AUTH_LOGIN_USER);
523ef573529Seric 		break;
524ef573529Seric 
525ef573529Seric 	case STATE_AUTH_LOGIN_USER:
526ef573529Seric 		if (strncmp(line, "334 ", 4))
527ef573529Seric 			smtp_client_cancel(proto, FAIL_RESP, line);
528ef573529Seric 		else
529ef573529Seric 			smtp_client_state(proto, STATE_AUTH_LOGIN_PASS);
530ef573529Seric 		break;
531ef573529Seric 
532ef573529Seric 	case STATE_AUTH_LOGIN_PASS:
533ef573529Seric 		if (line[0] != '2')
534ef573529Seric 			smtp_client_cancel(proto, FAIL_RESP, line);
535ef573529Seric 		else
536ef573529Seric 			smtp_client_state(proto, STATE_READY);
537ef573529Seric 		break;
538ef573529Seric 
539ef573529Seric 	case STATE_MAIL:
540ef573529Seric 		if (line[0] != '2') {
541ef573529Seric 			smtp_client_mail_status(proto, line);
542ef573529Seric 			smtp_client_state(proto, STATE_RSET);
543ef573529Seric 		}
544ef573529Seric 		else
545ef573529Seric 			smtp_client_state(proto, STATE_RCPT);
546ef573529Seric 		break;
547ef573529Seric 
548ef573529Seric 	case STATE_RCPT:
549ef573529Seric 		rcpt = &proto->mail->rcpt[proto->rcptidx++];
550ef573529Seric 		if (line[0] != '2')
551ef573529Seric 			smtp_client_rcpt_status(proto, rcpt, line);
552ef573529Seric 		else {
553ef573529Seric 			proto->rcptok++;
554ef573529Seric 			smtp_client_state(proto, STATE_RCPT);
555ef573529Seric 		}
556ef573529Seric 		break;
557ef573529Seric 
558ef573529Seric 	case STATE_DATA:
559ef573529Seric 		if (line[0] != '2' && line[0] != '3') {
560ef573529Seric 			smtp_client_mail_status(proto, line);
561ef573529Seric 			smtp_client_state(proto, STATE_RSET);
562ef573529Seric 		}
563ef573529Seric 		else
564ef573529Seric 			smtp_client_state(proto, STATE_BODY);
565ef573529Seric 		break;
566ef573529Seric 
567ef573529Seric 	case STATE_EOM:
568ef573529Seric 		if (proto->params.lmtp) {
569ef573529Seric 			/*
570ef573529Seric 			 * LMTP reports a status of each accepted RCPT.
571ef573529Seric 			 * Report status for the first pending RCPT and read
572ef573529Seric 			 * more lines if another rcpt needs a status.
573ef573529Seric 			 */
574ef573529Seric 			for (i = 0, seen = 0; i < proto->mail->rcptcount; i++) {
575fe463512Skrw 				rcpt = &proto->mail->rcpt[i];
57649b5b10aSeric 				if (rcpt->done)
577ef573529Seric 					continue;
578ef573529Seric 				if (seen) {
579ef573529Seric 					io_set_read(proto->io);
580ef573529Seric 					return;
581ef573529Seric 				}
582ef573529Seric 				smtp_client_rcpt_status(proto,
583ef573529Seric 				    &proto->mail->rcpt[i], line);
584ef573529Seric 				seen = 1;
585ef573529Seric 			}
586ef573529Seric 		}
587ef573529Seric 		smtp_client_mail_status(proto, line);
588ef573529Seric 		smtp_client_state(proto, STATE_READY);
589ef573529Seric 		break;
590ef573529Seric 
591ef573529Seric 	case STATE_RSET:
592ef573529Seric 		if (line[0] != '2')
593ef573529Seric 			smtp_client_cancel(proto, FAIL_RESP, line);
594ef573529Seric 		else
595ef573529Seric 			smtp_client_state(proto, STATE_READY);
596ef573529Seric 		break;
597ef573529Seric 
598ef573529Seric 	case STATE_QUIT:
599ef573529Seric 		smtp_client_free(proto);
600ef573529Seric 		break;
601ef573529Seric 
602ef573529Seric 	default:
603ef573529Seric 		fatalx("%s: bad state %d", __func__, proto->state);
604ef573529Seric 	}
605ef573529Seric }
606ef573529Seric 
607ef573529Seric static void
smtp_client_io(struct io * io,int evt,void * arg)608ef573529Seric smtp_client_io(struct io *io, int evt, void *arg)
609ef573529Seric {
610ef573529Seric 	struct smtp_client *proto = arg;
611ef573529Seric 
612ef573529Seric 	log_trace(TRACE_IO, "%p: %s %s", proto, io_strevent(evt), io_strio(io));
613ef573529Seric 
614ef573529Seric 	switch (evt) {
615ef573529Seric 	case IO_CONNECTED:
616ef573529Seric 		if (proto->params.tls_req == TLS_SMTPS) {
617ef573529Seric 			io_set_write(io);
618cde53503Seric 			smtp_require_tls(proto->tag, proto);
619ef573529Seric 		}
620ef573529Seric 		else
621ef573529Seric 			smtp_client_state(proto, STATE_BANNER);
622ef573529Seric 		break;
623ef573529Seric 
624ef573529Seric 	case IO_TLSREADY:
625ef573529Seric 		proto->flags |= FLAG_TLS;
626eed85469Seric 		if (proto->state == STATE_INIT)
627eed85469Seric 			smtp_client_state(proto, STATE_BANNER);
628eed85469Seric 		else {
629eed85469Seric 			/* Clear extensions before re-issueing an EHLO command. */
630eed85469Seric 			proto->ext = 0;
631eed85469Seric 			smtp_client_state(proto, STATE_EHLO);
632eed85469Seric 		}
633ef573529Seric 		break;
634ef573529Seric 
635ef573529Seric 	case IO_DATAIN:
636ef573529Seric 		while (smtp_client_readline(proto))
637ef573529Seric 			;
638ef573529Seric 		break;
639ef573529Seric 
640ef573529Seric 	case IO_LOWAT:
641ef573529Seric 		if (proto->state == STATE_BODY)
642ef573529Seric 			smtp_client_sendbody(proto);
643ef573529Seric 		else
644ef573529Seric 			io_set_read(io);
645ef573529Seric 		break;
646ef573529Seric 
647ef573529Seric 	case IO_TIMEOUT:
648ef573529Seric 		errno = ETIMEDOUT;
649b968de35Seric 		smtp_client_abort(proto, FAIL_CONN, "Connection timeout");
650ef573529Seric 		break;
651ef573529Seric 
652ef573529Seric 	case IO_ERROR:
653ef573529Seric 		smtp_client_abort(proto, FAIL_CONN, io_error(io));
654ef573529Seric 		break;
655ef573529Seric 
656ef573529Seric 	case IO_DISCONNECTED:
657ef573529Seric 		smtp_client_abort(proto, FAIL_CONN, io_error(io));
658ef573529Seric 		break;
659ef573529Seric 
660ef573529Seric 	default:
661ef573529Seric 		fatalx("%s: bad event %d", __func__, evt);
662ef573529Seric 	}
663ef573529Seric }
664ef573529Seric 
665ef573529Seric /*
666ef573529Seric  * return 1 if a new  line is expected.
667ef573529Seric  */
668ef573529Seric static int
smtp_client_readline(struct smtp_client * proto)669ef573529Seric smtp_client_readline(struct smtp_client *proto)
670ef573529Seric {
671fba2f2d7Seric 	const char *e;
672ef573529Seric 	size_t len;
673ef573529Seric 	char *line, *msg, *p;
674ef573529Seric 	int cont;
675ef573529Seric 
676ef573529Seric 	line = io_getline(proto->io, &len);
677ef573529Seric 	if (line == NULL) {
678ef573529Seric 		if (io_datalen(proto->io) >= proto->params.linemax)
679ef573529Seric 			smtp_client_abort(proto, FAIL_PROTO, "Line too long");
680ef573529Seric 		return 0;
681ef573529Seric 	}
682ef573529Seric 
6830124f38dSeric 	/* Strip trailing '\r' */
6840124f38dSeric 	if (len && line[len - 1] == '\r')
6850124f38dSeric 		line[--len] = '\0';
6860124f38dSeric 
687ef573529Seric 	log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line);
688ef573529Seric 
689ef573529Seric 	/* Validate SMTP  */
690ef573529Seric 	if (len > 3) {
691ef573529Seric 		msg = line + 4;
692ef573529Seric 		cont = (line[3] == '-');
693ef573529Seric 	} else if (len == 3) {
694ef573529Seric 		msg = line + 3;
695ef573529Seric 		cont = 0;
696ef573529Seric 	} else {
697ef573529Seric 		smtp_client_abort(proto, FAIL_PROTO, "Response too short");
698ef573529Seric 		return 0;
699ef573529Seric 	}
700ef573529Seric 
701ef573529Seric 	/* Validate reply code. */
702ef573529Seric 	if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) ||
703ef573529Seric 	    !isdigit((unsigned char)line[2])) {
704ef573529Seric 		smtp_client_abort(proto, FAIL_PROTO, "Invalid reply code");
705ef573529Seric 		return 0;
706ef573529Seric 	}
707ef573529Seric 
708ef573529Seric 	/* Validate reply message. */
709ef573529Seric 	for (p = msg; *p; p++)
710ef573529Seric 		if (!isprint((unsigned char)*p)) {
711ef573529Seric 			smtp_client_abort(proto, FAIL_PROTO,
712ef573529Seric 			    "Non-printable characters in response");
713ef573529Seric 			return 0;
714ef573529Seric 	}
715ef573529Seric 
716ef573529Seric 	/* Read extensions. */
717ef573529Seric 	if (proto->state == STATE_EHLO) {
718ef573529Seric 		if (strcmp(msg, "STARTTLS") == 0)
719ef573529Seric 			proto->ext |= SMTP_EXT_STARTTLS;
720ef573529Seric 		else if (strncmp(msg, "AUTH ", 5) == 0) {
721ef573529Seric 			proto->ext |= SMTP_EXT_AUTH;
722ef573529Seric 			if ((p = strstr(msg, " PLAIN")) &&
723ef573529Seric 			    (*(p+6) == '\0' || *(p+6) == ' '))
724ef573529Seric 				proto->ext |= SMTP_EXT_AUTH_PLAIN;
725ef573529Seric 			if ((p = strstr(msg, " LOGIN")) &&
726ef573529Seric 			    (*(p+6) == '\0' || *(p+6) == ' '))
727ef573529Seric 				proto->ext |= SMTP_EXT_AUTH_LOGIN;
728ef573529Seric 			}
729ef573529Seric 		else if (strcmp(msg, "PIPELINING") == 0)
730ef573529Seric 			proto->ext |= SMTP_EXT_PIPELINING;
731ef573529Seric 		else if (strcmp(msg, "DSN") == 0)
732ef573529Seric 			proto->ext |= SMTP_EXT_DSN;
733fba2f2d7Seric 		else if (strncmp(msg, "SIZE ", 5) == 0) {
734fba2f2d7Seric 			proto->ext_size = strtonum(msg + 5, 0, SIZE_T_MAX, &e);
735fba2f2d7Seric 			if (e == NULL)
736fba2f2d7Seric 				proto->ext |= SMTP_EXT_SIZE;
737fba2f2d7Seric 		}
738ef573529Seric 	}
739ef573529Seric 
740ef573529Seric 	if (smtp_client_replycat(proto, line) == -1) {
741ef573529Seric 		smtp_client_abort(proto, FAIL_INTERNAL, NULL);
742ef573529Seric 		return 0;
743ef573529Seric 	}
744ef573529Seric 
745ef573529Seric 	if (cont)
746ef573529Seric 		return 1;
747ef573529Seric 
748ef573529Seric 	if (io_datalen(proto->io)) {
749ef573529Seric 		/*
750ef573529Seric 		 * There should be no pending data after a response is read,
751ef573529Seric 		 * except for the multiple status lines after a LMTP message.
752ef573529Seric 		 * It can also happen with pipelineing, but we don't do that
753ef573529Seric 		 * for now.
754ef573529Seric 		 */
755ef573529Seric 		if (!(proto->params.lmtp && proto->state == STATE_EOM)) {
756ef573529Seric 			smtp_client_abort(proto, FAIL_PROTO, "Trailing data");
757ef573529Seric 			return 0;
758ef573529Seric 		}
759ef573529Seric 	}
760ef573529Seric 
761ef573529Seric 	io_set_write(proto->io);
762ef573529Seric 	smtp_client_response(proto, proto->reply);
763ef573529Seric 	return 0;
764ef573529Seric }
765ef573529Seric 
766ef573529Seric /*
767ef573529Seric  * Concatenate the given response line.
768ef573529Seric  */
769ef573529Seric static int
smtp_client_replycat(struct smtp_client * proto,const char * line)770ef573529Seric smtp_client_replycat(struct smtp_client *proto, const char *line)
771ef573529Seric {
772ef573529Seric 	size_t len;
773ef573529Seric 	char *tmp;
774ef573529Seric 	int first;
775ef573529Seric 
776ef573529Seric 	if (proto->reply && proto->reply[0]) {
777ef573529Seric 		/*
778ef573529Seric 		 * If the line is the continuation of an multi-line response,
779ef573529Seric 		 * skip the status and ESC parts. First, skip the status, then
780ef573529Seric 		 * skip the separator amd ESC if found.
781ef573529Seric 		 */
782ef573529Seric 		first = 0;
783ef573529Seric 		line += 3;
784ef573529Seric 		if (line[0]) {
785ef573529Seric 			line += 1;
786000eaaf0Smillert 			if (isdigit((unsigned char)line[0]) && line[1] == '.' &&
787000eaaf0Smillert 			    isdigit((unsigned char)line[2]) && line[3] == '.' &&
788000eaaf0Smillert 			    isdigit((unsigned char)line[4]) &&
789000eaaf0Smillert 			    isspace((unsigned char)line[5]))
790ef573529Seric 				line += 5;
791ef573529Seric 		}
792ef573529Seric 	} else
793ef573529Seric 		first = 1;
794ef573529Seric 
795ef573529Seric 	if (proto->reply) {
796ef573529Seric 		len = strlcat(proto->reply, line, proto->replysz);
797ef573529Seric 		if (len < proto->replysz)
798ef573529Seric 			return 0;
799ef573529Seric 	}
800ef573529Seric 	else
801ef573529Seric 		len = strlen(line);
802ef573529Seric 
803ef573529Seric 	if (len > proto->params.ibufmax) {
804ef573529Seric 		errno = EMSGSIZE;
805ef573529Seric 		return -1;
806ef573529Seric 	}
807ef573529Seric 
808ef573529Seric 	/* Allocate by multiples of 2^8 */
809ef573529Seric 	len += (len % 256) ? (256 - (len % 256)) : 0;
810ef573529Seric 
811ef573529Seric 	tmp = realloc(proto->reply, len);
812ef573529Seric 	if (tmp == NULL)
813ef573529Seric 		return -1;
814ef573529Seric 	if (proto->reply == NULL)
815ef573529Seric 		tmp[0] = '\0';
816ef573529Seric 
817ef573529Seric 	proto->reply = tmp;
818ef573529Seric 	proto->replysz = len;
819ef573529Seric 	(void)strlcat(proto->reply, line, proto->replysz);
820ef573529Seric 
821ef573529Seric 	/* Replace the separator with a space for the first line. */
822ef573529Seric 	if (first && proto->reply[3])
823ef573529Seric 		proto->reply[3] = ' ';
824ef573529Seric 
825ef573529Seric 	return 0;
826ef573529Seric }
827ef573529Seric 
828ef573529Seric static void
smtp_client_sendbody(struct smtp_client * proto)829ef573529Seric smtp_client_sendbody(struct smtp_client *proto)
830ef573529Seric {
831ef573529Seric 	ssize_t len;
832ef573529Seric 	size_t sz = 0, total, w;
833ef573529Seric 	char *ln = NULL;
834ef573529Seric 	int n;
835ef573529Seric 
836ef573529Seric 	total = io_queued(proto->io);
837ef573529Seric 	w = 0;
838ef573529Seric 
839ef573529Seric 	while (total < proto->params.obufmax) {
840ef573529Seric 		if ((len = getline(&ln, &sz, proto->mail->fp)) == -1)
841ef573529Seric 			break;
842ef573529Seric 		if (ln[len - 1] == '\n')
843ef573529Seric 			ln[len - 1] = '\0';
844ef573529Seric 		n = io_printf(proto->io, "%s%s\r\n", *ln == '.'?".":"", ln);
845ef573529Seric 		if (n == -1) {
846ef573529Seric 			free(ln);
847ef573529Seric 			smtp_client_abort(proto, FAIL_INTERNAL, NULL);
848ef573529Seric 			return;
849ef573529Seric 		}
850ef573529Seric 		total += n;
851ef573529Seric 		w += n;
852ef573529Seric 	}
853ef573529Seric 	free(ln);
854ef573529Seric 
855ef573529Seric 	if (ferror(proto->mail->fp)) {
856ef573529Seric 		smtp_client_abort(proto, FAIL_INTERNAL, "Cannot read message");
857ef573529Seric 		return;
858ef573529Seric 	}
859ef573529Seric 
860ef573529Seric 	log_trace(TRACE_SMTPCLT, "%p: >>> [...%zd bytes...]", proto, w);
861ef573529Seric 
862ef573529Seric 	if (feof(proto->mail->fp))
863ef573529Seric 		smtp_client_state(proto, STATE_EOM);
864ef573529Seric }
865ef573529Seric 
866ef573529Seric static void
smtp_client_sendcmd(struct smtp_client * proto,char * fmt,...)867ef573529Seric smtp_client_sendcmd(struct smtp_client *proto, char *fmt, ...)
868ef573529Seric {
869ef573529Seric 	va_list ap;
870ef573529Seric 	char *p;
871ef573529Seric 	int len;
872ef573529Seric 
873ef573529Seric 	va_start(ap, fmt);
874ef573529Seric 	len = vasprintf(&p, fmt, ap);
875ef573529Seric 	va_end(ap);
876ef573529Seric 
877ef573529Seric 	if (len == -1) {
878ef573529Seric 		smtp_client_abort(proto, FAIL_INTERNAL, NULL);
879ef573529Seric 		return;
880ef573529Seric 	}
881ef573529Seric 
882ef573529Seric 	log_trace(TRACE_SMTPCLT, "mta: %p: >>> %s", proto, p);
883ef573529Seric 
884ef573529Seric 	len = io_printf(proto->io, "%s\r\n", p);
885ef573529Seric 	free(p);
886ef573529Seric 
887ef573529Seric 	if (len == -1)
888ef573529Seric 		smtp_client_abort(proto, FAIL_INTERNAL, NULL);
889ef573529Seric }
890ef573529Seric 
891ef573529Seric static void
smtp_client_mail_status(struct smtp_client * proto,const char * status)892ef573529Seric smtp_client_mail_status(struct smtp_client *proto, const char *status)
893ef573529Seric {
894ef573529Seric 	int i;
895ef573529Seric 
896ef573529Seric 	for (i = 0; i < proto->mail->rcptcount; i++)
897ef573529Seric 		smtp_client_rcpt_status(proto, &proto->mail->rcpt[i], status);
898ef573529Seric 
899ef573529Seric 	smtp_done(proto->tag, proto, proto->mail);
900ef573529Seric 	proto->mail = NULL;
901ef573529Seric }
902ef573529Seric 
903ef573529Seric static void
smtp_client_mail_abort(struct smtp_client * proto)904ef573529Seric smtp_client_mail_abort(struct smtp_client *proto)
905ef573529Seric {
906ef573529Seric 	smtp_done(proto->tag, proto, proto->mail);
907ef573529Seric 	proto->mail = NULL;
908ef573529Seric }
909ef573529Seric 
910ef573529Seric static void
smtp_client_rcpt_status(struct smtp_client * proto,struct smtp_rcpt * rcpt,const char * line)911ef573529Seric smtp_client_rcpt_status(struct smtp_client *proto, struct smtp_rcpt *rcpt, const char *line)
912ef573529Seric {
913ef573529Seric 	struct smtp_status status;
914ef573529Seric 
915ef573529Seric 	if (rcpt->done)
916ef573529Seric 		return;
917ef573529Seric 
918ef573529Seric 	rcpt->done = 1;
919ef573529Seric 	status.rcpt = rcpt;
920ef573529Seric 	status.cmd = strstate[proto->state];
921ef573529Seric 	status.status = line;
922ef573529Seric 	smtp_status(proto->tag, proto, &status);
923ef573529Seric }
924