/* $Id: auth.C,v 1.9 2005/08/17 02:34:29 dm Exp $ */ /* * * Copyright (C) 2005 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "asmtpd.h" #ifndef SASL void smtpd::cmd_auth (str cmd, str arg) { respond ("502 command not implemented\r\n"); } str smtpd::helo_auth () { static str empty (""); return empty; } #else /* SASL */ #include "serial.h" static sasl_callback_t saslcbs[] = { { SASL_CB_LIST_END, NULL, NULL } }; inline str sasl_encode (const char *buf, size_t len) { if (len == 0) return "="; return armor64 (buf, len); } inline str sasl_decode (const str &buf) { if (buf == "=") return ""; return (dearmor64 (buf)); } static bool authinit () { static int authinitialized; if (authinitialized) return authinitialized > 0; if (sasl_server_init (saslcbs, progname.cstr ())) { warn ("sasl_server_init failed\n"); authinitialized = -1; return false; } authinitialized = 1; return true; } str smtpd::helo_auth () { int res; if (!opt->sasl || !authinit ()) return ""; if (!sasl) { saslstr.clear (); saslstr.push_back (opt->hostname); saslstr.push_back (strbuf ("%s;%d", inet_ntoa (myipaddr), mytcpport)); saslstr.push_back (strbuf ("%s;%d", inet_ntoa (ipaddr), tcpport)); res = sasl_server_new ("smtp", saslstr[0].cstr (), NULL, // realm saslstr[1].cstr (), saslstr[2].cstr (), saslcbs, 0, &sasl); if (res) return ""; sasl_security_properties_t prop; bzero (&prop, sizeof (prop)); prop.max_ssf = SASL_SEC_MAXIMUM; prop.security_flags |= SASL_SEC_NOANONYMOUS; if (!encrypted) prop.security_flags |= SASL_SEC_NOPLAINTEXT; res = sasl_setprop (sasl, SASL_SEC_PROPS, &prop); if (res) { sasl_dispose (&sasl); sasl = NULL; return ""; } } const char *mech = NULL; unsigned mechlen = 0; int mechnum = 0; res = sasl_listmech (sasl, NULL, "250-AUTH ", " ", "\r\n", &mech, &mechlen, &mechnum); return res ? str ("") : str (mech, mechlen); } void smtpd::cmd_auth (str cmd, str arg) { static rxx cmdarg ("^(\\S+)(\\s*|\\s+(\\S.*))$"); if (!sasl) { respond ("502 command not implemented\r\n"); return; } else if (!arg || !cmdarg.match (arg)) { respond ("501 syntax error\r\n"); return; } else if (auth_user) { respond ("503 already authenticated\r\n"); return; } const char *out = NULL; unsigned outlen = 0; str opt = cmdarg[3]; if (opt) opt = sasl_decode (opt); int res = sasl_server_start (sasl, cmdarg[1], opt, opt ? opt.len () : 0, &out, &outlen); cmd_auth_2 (res, out, outlen); } void smtpd::cmd_auth_2 (int res, const char *out, unsigned outlen) { switch (res) { case SASL_OK: { const void *val = NULL; sasl_getprop (sasl, SASL_USERNAME, &val); if (val) { quota_user = auth_user = static_cast (val); if (opt->sasl >= 2 && trust < TRUST_AUTH) trust = TRUST_AUTH; } sasl_dispose (&sasl); sasl = NULL; respond ("235 OK\r\n"); break; } case SASL_CONTINUE: aio << "334 " << sasl_encode (out, outlen) << "\r\n"; cmdwait = true; aio->readline (wrap (this, &smtpd::cmd_auth_3)); break; default: { const void *val = NULL; sasl_getprop (sasl, SASL_USERNAME, &val); if (!val) val = "unknown user"; warn << "SASL authentication FAILED for " << static_cast (val) << " from " << name << "\n"; sasl_dispose (&sasl); sasl = NULL; respond ("535 authentication failed\r\n"); break; } } } void smtpd::cmd_auth_3 (str line, int err) { cmdwait = false; if (!line) { getcmd (line, err); return; } line = sasl_decode (line); if (!line) { sasl_dispose (&sasl); sasl = NULL; respond ("501 base64 decoding error\r\n"); return; } const char *out = NULL; unsigned outlen = 0; int res = sasl_server_step (sasl, line, line.len (), &out, &outlen); cmd_auth_2 (res, out, outlen); } #endif /* SASL */