* smtp.c -- nmh SMTP interface
*
* $Id$
+ *
+ * This code is Copyright (c) 2002, by the authors of nmh. See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
*/
#include <h/mh.h>
#include "smtp.h"
-#include <zotnet/mts/mts.h>
+#include <h/mts.h>
#include <signal.h>
#include <h/signals.h>
#ifdef MPOP
#include <errno.h>
#endif
+#ifdef CYRUS_SASL
+#include <sasl/sasl.h>
+#include <sasl/saslutil.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#endif /* CYRUS_SASL */
+
/*
* This module implements an interface to SendMail very similar
* to the MMDF mm_(3) routines. The sm_() routines herein talk
/*
* these codes must all be different!
*/
-#define SM_OPEN 90 /* Changed from 30 in case of nameserver flakiness */
+#define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
#define SM_HELO 20
#define SM_RSET 15
-#define SM_MAIL 40
-#define SM_RCPT 120
-#define SM_DATA 20
-#define SM_TEXT 150
-#define SM_DOT 180
+#define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
+#define SM_RCPT 302 /* see above */
+#define SM_DATA 120 /* see above */
+#define SM_TEXT 180 /* see above */
+#define SM_DOT 600 /* see above */
#define SM_QUIT 30
#define SM_CLOS 10
+#define SM_AUTH 45
static int sm_addrs = 0;
static int sm_alarmed = 0;
static char sm_tmpfil[BUFSIZ];
#endif /* MPOP */
+#ifdef CYRUS_SASL
+/*
+ * Some globals needed by SASL
+ */
+
+static sasl_conn_t *conn = NULL; /* SASL connection state */
+static int sasl_complete = 0; /* Has authentication succeded? */
+static sasl_ssf_t sasl_ssf; /* Our security strength factor */
+static char *sasl_pw_context[2]; /* Context to pass into sm_get_pass */
+static int maxoutbuf; /* Maximum crypto output buffer */
+static int sm_get_user(void *, int, const char **, unsigned *);
+static int sm_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
+
+static sasl_callback_t callbacks[] = {
+ { SASL_CB_USER, sm_get_user, NULL },
+#define SM_SASL_N_CB_USER 0
+ { SASL_CB_PASS, sm_get_pass, NULL },
+#define SM_SASL_N_CB_PASS 1
+ { SASL_CB_LIST_END, NULL, NULL },
+};
+#endif /* CYRUS_SASL */
+
static char *sm_noreply = "No reply text given";
static char *sm_moreply = "; ";
/*
* static prototypes
*/
-static int smtp_init (char *, char *, int, int, int, int, int);
+static int smtp_init (char *, char *, int, int, int, int, int, int,
+ char *, char *);
static int sendmail_init (char *, char *, int, int, int, int, int);
static int rclient (char *, char *, char *);
char **smail_copyip (char **, char **, int);
#endif
-/* from zotnet/mts/client.c */
+#ifdef CYRUS_SASL
+/*
+ * Function prototypes needed for SASL
+ */
+
+static int sm_auth_sasl(char *, char *, char *);
+#endif /* CYRUS_SASL */
+
+/* from mts/generic/client.c */
int client (char *, char *, char *, int, char *, int);
int
sm_init (char *client, char *server, int watch, int verbose,
- int debug, int onex, int queued)
+ int debug, int onex, int queued, int sasl, char *saslmech,
+ char *user)
{
if (sm_mts == MTS_SMTP)
return smtp_init (client, server, watch, verbose,
- debug, onex, queued);
+ debug, onex, queued, sasl, saslmech, user);
else
return sendmail_init (client, server, watch, verbose,
debug, onex, queued);
static int
smtp_init (char *client, char *server, int watch, int verbose,
- int debug, int onex, int queued)
+ int debug, int onex, int queued, int sasl, char *saslmech,
+ char *user)
{
+#ifdef CYRUS_SASL
+ char *server_mechs;
+#endif /* CYRUS_SASL */
int result, sd1, sd2;
if (watch)
}
}
+#ifdef CYRUS_SASL
+ /*
+ * If the user asked for SASL, then check to see if the SMTP server
+ * supports it. Otherwise, error out (because the SMTP server
+ * might have been spoofed; we don't want to just silently not
+ * do authentication
+ */
+
+ if (sasl) {
+ if (! (server_mechs = EHLOset("AUTH"))) {
+ sm_end(NOTOK);
+ return sm_ierror("SMTP server does not support SASL");
+ }
+
+ if (saslmech && stringdex(saslmech, server_mechs) == -1) {
+ sm_end(NOTOK);
+ return sm_ierror("Requested SASL mech \"%s\" is not in the "
+ "list of supported mechanisms:\n%s",
+ saslmech, server_mechs);
+ }
+
+ if (sm_auth_sasl(user, saslmech ? saslmech : server_mechs,
+ server) != RP_OK) {
+ sm_end(NOTOK);
+ return NOTOK;
+ }
+ }
+#endif /* CYRUS_SASL */
+
send_options: ;
if (watch && EHLOset ("XVRB"))
smtalk (SM_HELO, "VERB on");
return NOTOK;
}
-#ifdef CYRUS_SASL
-#include <sasl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <errno.h>
-#endif /* CYRUS_SASL */
-
int
sm_winit (int mode, char *from)
{
alarm (0);
}
- if (sm_mts == MTS_SMTP)
+ if (sm_mts == MTS_SMTP) {
status = 0;
- else {
+#ifdef CYRUS_SASL
+ if (conn)
+ sasl_dispose(&conn);
+#endif /* CYRUS_SASL */
+ } else {
status = pidwait (sm_child, OK);
sm_child = NOTOK;
}
free (cp);
{
-#ifdef HAVE_ST_BLKSIZE
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
struct stat st;
if (fstat (fileno (sm_wfp), &st) == NOTOK || (cc = st.st_blksize) < BUFSIZ)
static int
sm_auth_sasl(char *user, char *mechlist, char *host)
{
- int result, status, outlen;
- unsigned int buflen;
+ int result, status;
+ unsigned int buflen, outlen;
char *buf, outbuf[BUFSIZ];
const char *chosen_mech;
sasl_security_properties_t secprops;
- sasl_external_properties_t extprops;
sasl_ssf_t *ssf;
int *outbufmax;
return NOTOK;
}
- result = sasl_client_new("smtp", host, NULL, SASL_SECURITY_LAYER, &conn);
+ result = sasl_client_new("smtp", host, NULL, NULL, NULL, 0, &conn);
if (result != SASL_OK) {
sm_ierror("SASL client initialization failed: %s",
memset(&secprops, 0, sizeof(secprops));
secprops.maxbufsize = BUFSIZ;
secprops.max_ssf = 0; /* XXX change this when we do encryption */
- memset(&extprops, 0, sizeof(extprops));
result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
return NOTOK;
}
- result = sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops);
-
- if (result != SASL_OK) {
- sm_ierror("SASL external property initialization failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
/*
* Start the actual protocol. Feed the mech list into the library
* and get out a possible initial challenge
*/
- result = sasl_client_start(conn, mechlist, NULL, NULL, &buf, &buflen,
- &chosen_mech);
+ result = sasl_client_start(conn, mechlist, NULL, (const char **) &buf,
+ &buflen, (const char **) &chosen_mech);
if (result != SASL_OK && result != SASL_CONTINUE) {
sm_ierror("SASL client start failed: %s",
if (buflen) {
status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
- free(buf);
if (status != SASL_OK) {
sm_ierror("SASL base64 encode failed: %s",
sasl_errstring(status, NULL, NULL));
outlen = 0;
} else {
result = sasl_decode64(sm_reply.text, sm_reply.length,
- outbuf, &outlen);
+ outbuf, sizeof(outbuf), &outlen);
if (result != SASL_OK) {
smtalk(SM_AUTH, "*");
}
}
- result = sasl_client_step(conn, outbuf, outlen, NULL, &buf, &buflen);
+ result = sasl_client_step(conn, outbuf, outlen, NULL,
+ (const char **) &buf, &buflen);
if (result != SASL_OK && result != SASL_CONTINUE) {
smtalk(SM_AUTH, "*");
}
status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
- free(buf);
if (status != SASL_OK) {
smtalk(SM_AUTH, "*");
return RP_BHST;
/*
- * Depending on the mechanism, we need to do a FINAL call to
- * sasl_client_step(). Do that now.
- */
-
- result = sasl_client_step(conn, NULL, 0, NULL, &buf, &buflen);
-
- if (result != SASL_OK) {
- sm_ierror("SASL final client negotiation failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- /*
* We _should_ have completed the authentication successfully.
* Get a few properties from the authentication exchange.
*/
- result = sasl_getprop(conn, SASL_MAXOUTBUF, (void **) &outbufmax);
+ result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &outbufmax);
if (result != SASL_OK) {
sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
maxoutbuf = *outbufmax;
- result = sasl_getprop(conn, SASL_SSF, (void **) &ssf);
+ result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
sasl_ssf = *ssf;
}
(*psecret)->len = len;
- strcpy((*psecret)->data, pass);
+ strcpy((char *) (*psecret)->data, pass);
/* free(pass); */
return SASL_OK;