/*****************************************************************************\
* Copyright (c) 2002 Pelle Johansson. *
* All rights reserved. *
* *
* This file is part of the moftpd package. Use and distribution of *
* this software is governed by the terms in the file LICENCE, which *
* should have come with this package. *
\*****************************************************************************/
/* $moftpd: com_login.c 1245 2004-12-09 12:01:59Z morth $ */
#include "system.h"
#include "commands.h"
#include "connection.h"
#include "main.h"
#include "utf8fs/memory.h"
#include "defaults.h"
#include "accounter.h"
#include "events.h"
#ifdef HAVE_LIBPAM
static const char *tmp_passwd;
static int tmp_passwd_reqed;
int conv_fun (int num_msg, PAM_CONST struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr)
{
//connection_t *conn = appdata_ptr;
int i;
struct pam_response *res;
res = malloc (sizeof (*res) * num_msg);
if (!res)
return PAM_CONV_ERR;
for (i = 0; i < num_msg; i++)
{
switch (msg[i]->msg_style)
{
case PAM_PROMPT_ECHO_OFF:
if (tmp_passwd)
res[i].resp = strdup (tmp_passwd);
else
{
tmp_passwd_reqed = 1;
free (res);
return PAM_CONV_ERR;
}
break;
default:
free (res);
return PAM_CONV_ERR;
}
res[i].resp_retcode = PAM_SUCCESS;
}
*resp = res;
return PAM_SUCCESS;
}
#endif
int ext_login_read (int fd, void *user, int urgent)
{
char buf[4097], *bp, *nbp;
int l;
connection_t *conn = user;
if (conn->sock == -1)
return 0;
l = read (fd, buf, sizeof (buf) - 1);
if (l < 0)
{
if (errno == EINTR || errno == EAGAIN)
return 0;
reply (conn, "530 %s.", strerror (errno));
unauthenticated (conn);
return 0;
}
if (!l)
{
remove_read_fd (fd);
close (conn->extRFd);
conn->extRFd = -1;
close (conn->extWFd);
conn->extWFd = -1;
return 0;
}
for (bp = buf; (nbp = strchr (bp, '\n')); bp = nbp)
{
*nbp++ = 0;
if (!strncmp (bp, "230 ", 4))
{
if (authenticated (conn))
{
reply (conn, "530 Failed to authenticate: %s (root dir inaccessible?).", strerror (errno));
break;
}
}
else if (!strncmp (bp, "331 ", 4))
conn->expect = "PASS";
else if (!strncmp (bp, "332 ", 4))
conn->expect = "ACCT";
else if (!strncmp (bp, "421 ", 4))
{
reply (conn, "%s", bp);
disconnected (conn);
break;
}
else if (!strncmp (bp, "530 ", 4))
unauthenticated (conn);
reply (conn, "%s", bp);
}
return 0;
}
/* Login commands */
int command_user(connection_t *conn, const char *arg, int expected)
{
#ifdef HAVE_LIBPAM
int err;
PAM_CONST void *parg;
#endif
if (conn->authed && conn->user && !strcmp (arg, conn->user->name))
{
reply (conn, "230 Already logged in as %s.", conn->user->name);
return 0;
}
if (
#ifdef USE_TLS
(conn->tlsControl && conn->server->loginTLS && conn->server->tlsNoLogout) ||
#endif
super_privs (1))
{
reply(conn, "503 Can't relogin.");
return 0;
}
unauthenticated(conn);
if (!conn->inLookUp)
{
conn->inLookUp = 1;
#ifdef USE_SQL
conn->server->sqlRefs++;
#endif
}
conn->user = find_user (conn, arg,
#ifdef USE_TLS
conn->tlsControl? 2 :
#endif
0, conn);
#ifdef HAVE_LIBPAM
pam_set_item (conn->pamh, PAM_USER, arg);
#endif
if (conn->user && conn->user->external_login)
{
int ipipe[2], opipe[2], i;
const char *args[3], **ap = args;
const char *envs[] = { NULL };
const char *prog = conn->user->external_login;
if (pipe (ipipe))
{
reply (conn, "530 %s.", strerror (errno));
return 0;
}
if (pipe (opipe))
{
reply (conn, "530 %s.", strerror (errno));
close (ipipe[0]);
close (ipipe[1]);
return 0;
}
*ap++ = prog;
*ap++ = arg;
*ap = NULL;
switch (vfork ())
{
case -1:
// Error
reply (conn, "530 %s.", strerror (errno));
close (ipipe[0]);
close (ipipe[1]);
close (opipe[0]);
close (opipe[1]);
return 0;
case 0:
// Child
if (dup2 (ipipe[1], 1) == -1)
_exit (1);
if (dup2 (opipe[0], 0) == -1)
_exit (1);
for (i = 2; i < getdtablesize (); i++)
close (i);
execve (prog, (char**)args, (char**)envs);
_exit (1);
default:
close (ipipe[1]);
close (opipe[0]);
break;
}
fcntl (ipipe[0], F_SETFL, O_NONBLOCK);
add_read_fd (ipipe[0], ext_login_read, conn);
conn->extRFd = ipipe[0];
conn->extWFd = opipe[1];
return 0;
}
#ifdef HAVE_LIBPAM
tmp_passwd_reqed = 0;
err = pam_authenticate (conn->pamh, PAM_SILENT);
if (pam_get_item (conn->pamh, PAM_USER, &parg) == PAM_SUCCESS)
{
arg = parg;
pfree (conn->user, conn);
conn->user = find_user (conn, arg,
#ifdef USE_TLS
conn->tlsControl? 2 :
#endif
0, conn);
}
if (err != PAM_SUCCESS || !conn->user)
{
if (!tmp_passwd_reqed || !conn->user || conn->user->passwordNeeded)
{
if (conn->server->passIfInvalid || (conn->user && (tmp_passwd_reqed || conn->user->anonymous || conn->user->password)))
{
if (conn->user && conn->user->anonymous)
reply_msg (conn, "331 %s", conn->server->anonPassMsg? conn->server->anonPassMsg
: defAnonPassMsg);
else
reply_msg (conn, "331 %s", conn->server->passRequestMsg?
conn->server->passRequestMsg : defPassRequestMsg);
conn->expect = "PASS";
}
else
{
if (err == PAM_MAXTRIES)
{
syslog (LOG_NOTICE, "%d: Unknown user %s.", conn->id, arg);
syslog (LOG_AUTHPRIV | LOG_NOTICE, "%d: Unknown user %s.",
conn->id, arg);
reply_msg (conn, "421 %s", conn->server->userInvalidMsg?
conn->server->userInvalidMsg : defUserInvalidMsg);
disconnected (conn);
return 1;
}
if (err == PAM_AUTH_ERR)
{
syslog (LOG_NOTICE, "%d: Unknown user %s.", conn->id, arg);
syslog (LOG_AUTHPRIV | LOG_NOTICE, "%d: Unknown user %s.",
conn->id, arg);
reply_msg (conn, "530 %s", conn->server->userInvalidMsg?
conn->server->userInvalidMsg : defUserInvalidMsg);
if (conn->server->sleepOnFail)
{
time (&conn->sleepUntil);
conn->sleepUntil += conn->server->sleepOnFail;
}
}
else
reply (conn, "530 %s.", pam_strerror (conn->pamh, err));
unauthenticated (conn);
}
return 0;
}
}
err = pam_acct_mgmt (conn->pamh, PAM_SILENT);
if (err != PAM_SUCCESS)
{
reply (conn, "530 %s.", pam_strerror (conn->pamh, err));
unauthenticated (conn);
return 0;
}
accounter (conn->accSock, "LOGIN %s - - %d\n", conn->user->name, conn->user->maxLogins);
conn->accWait = acLogin;
#else /*HAVE_LIBPAM*/
if(conn->user && !conn->user->passwordNeeded)
{
accounter (conn->accWait, "LOGIN %s - - %d\n", conn->user->name, conn->user->maxLogins);
conn->accWait = acLogin;
}
else if(!conn->user && !conn->server->passIfInvalid)
{
syslog (LOG_NOTICE, "%d: Unknown user %s.", conn->id, arg);
syslog (LOG_AUTHPRIV | LOG_NOTICE, "%d: Unknown user %s.",
conn->id, arg);
reply_msg (conn, "530 %s", conn->server->userInvalidMsg?
conn->server->userInvalidMsg : defUserInvalidMsg);
unauthenticated (conn);
if (conn->server->sleepOnFail)
{
time (&conn->sleepUntil);
conn->sleepUntil += conn->server->sleepOnFail;
}
}
else
{
if (!conn->user)
{
syslog (LOG_NOTICE, "%d: Unknwon user %s.", conn->id, arg);
syslog (LOG_AUTHPRIV | LOG_NOTICE, "%d: Unknown user %s.",
conn->id, arg);
}
if(conn->user && conn->user->anonymous)
reply_msg (conn, "331 %s", conn->server->anonPassMsg? conn->server->anonPassMsg
: defAnonPassMsg);
else
reply_msg (conn, "331 %s", conn->server->passRequestMsg?
conn->server->passRequestMsg : defPassRequestMsg);
conn->expect = "PASS";
}
#endif /*!HAVE_LIBPAM*/
return 0;
}
int command_pass(connection_t *conn, const char *arg, int expected)
{
const char *pass;
#ifdef HAVE_LIBPAM
const char *user;
PAM_CONST void *puser;
#endif
if (!expected)
{
reply(conn, "503 PASS not expected here.");
return 0;
}
if (conn->user && conn->user->external_login)
{
char *warg = talloc (strlen (arg) + 2);
strcpy (warg, arg);
strcat (warg, "\n");
if (write (conn->extWFd, warg, strlen (warg)) < 0)
{
reply (conn, "530 %s.", strerror (errno));
unauthenticated (conn);
}
return 0;
}
pass = user_pass (conn->user);
if (conn->user && conn->user->anonymous)
{
const char *ap;
if (!strlen (arg))
arg = "-";
else
{
for (ap = arg; *ap; ap++)
{
if (isspace (*ap & 0xFF))
{
unauthenticated (conn);
reply (conn, "530 Invalid email.");
return 0;
}
}
}
}
#ifdef HAVE_LIBPAM
else if (!pass)
{
int err;
tmp_passwd = arg;
err = pam_authenticate (conn->pamh, PAM_SILENT);
tmp_passwd = NULL;
if (err != PAM_SUCCESS)
{
if (err == PAM_MAXTRIES)
{
if (conn->user)
{
syslog (LOG_NOTICE, "%d: User %s failed login.", conn->id,
conn->user->name);
syslog (LOG_AUTHPRIV | LOG_NOTICE, "%d: User %s failed login.",
conn->id, conn->user->name);
}
reply_msg (conn, "421 %s", conn->server->loginFailedMsg?
conn->server->loginFailedMsg : defLoginFailedMsg);
disconnected (conn);
return 1;
}
if (err == PAM_AUTH_ERR)
{
if (conn->user)
{
syslog (LOG_NOTICE, "%d: User %s failed login.", conn->id,
conn->user->name);
syslog (LOG_AUTHPRIV | LOG_NOTICE, "%d: User %s failed login.",
conn->id, conn->user->name);
}
reply_msg (conn, "530 %s", conn->server->loginFailedMsg?
conn->server->loginFailedMsg : defLoginFailedMsg);
if (conn->server->sleepOnFail)
{
time (&conn->sleepUntil);
conn->sleepUntil += conn->server->sleepOnFail;
}
}
else
reply (conn, "530 %s.", pam_strerror (conn->pamh, err));
unauthenticated (conn);
return 0;
}
err = pam_acct_mgmt (conn->pamh, PAM_SILENT);
if (err != PAM_SUCCESS)
{
reply (conn, "530 %s.", pam_strerror (conn->pamh, err));
unauthenticated (conn);
return 0;
}
}
else if (strcmp (crypt (arg, pass), pass))
#else /*HAVE_LIBPAM*/
else if (!pass || strcmp (crypt (arg, pass), pass))
#endif
{
if (conn->user)
{
syslog (LOG_NOTICE, "%d: User %s failed login.", conn->id,
conn->user->name);
syslog (LOG_AUTHPRIV | LOG_NOTICE, "%d: User %s failed login.",
conn->id, conn->user->name);
}
if (conn->server->maxLoginAttempts && ++conn->failedLogins >=
conn->server->maxLoginAttempts)
{
reply_msg (conn, "421 %s", conn->server->loginFailedMsg?
conn->server->loginFailedMsg : defLoginFailedMsg);
disconnected (conn);
return 1;
}
unauthenticated (conn);
reply_msg (conn, "530 %s", conn->server->loginFailedMsg?
conn->server->loginFailedMsg : defLoginFailedMsg);
if (conn->server->sleepOnFail)
{
time (&conn->sleepUntil);
conn->sleepUntil += conn->server->sleepOnFail;
}
return 0;
}
#ifdef HAVE_LIBPAM
if (pam_get_item (conn->pamh, PAM_USER, &puser) == PAM_SUCCESS)
{
user = puser;
pfree (conn->user, conn);
conn->user = find_user (conn, user,
#ifdef USE_TLS
conn->tlsControl? 2 :
#endif
0, conn);
}
else
user = "";
if (!conn->user)
{
syslog (LOG_NOTICE, "%d: Unknown user %s.", conn->id, user);
syslog (LOG_AUTHPRIV | LOG_NOTICE, "%d: Unknown user %s.",
conn->id, user);
reply_msg (conn, "530 %s", conn->server->loginFailedMsg?
conn->server->loginFailedMsg : defLoginFailedMsg);
if (conn->server->sleepOnFail)
{
time (&conn->sleepUntil);
conn->sleepUntil += conn->server->sleepOnFail;
}
return 0;
}
#endif
if (conn->user->anonymous)
{
conn->email = pstring (arg, conn);
accounter (conn->accSock, "LOGIN %s - %s %d\n", conn->user->name, conn->email, conn->user->maxLogins);
}
else
accounter (conn->accSock, "LOGIN %s - - %d\n", conn->user->name, conn->user->maxLogins);
conn->accWait = acLogin;
return 0;
}
int command_acct(connection_t *conn, const char *arg, int expected)
{
const char *ap;
if (!expected)
{
reply(conn, "503 ACCT not expected here.");
return 0;
}
for (ap = arg; *ap; ap++)
{
if (isspace (*ap & 0xFF))
{
unauthenticated (conn);
reply (conn, "530 Invalid account.");
return 0;
}
}
conn->account = pstring (arg, conn);
if (conn->user && conn->user->external_login)
{
char *warg = talloc (strlen (arg) + 2);
strcpy (warg, arg);
strcat (warg, "\n");
if (write (conn->extWFd, warg, strlen (warg)) < 0)
{
reply (conn, "530 %s.", strerror (errno));
unauthenticated (conn);
}
return 0;
}
if (conn->user)
{
accounter (conn->accSock, "LOGIN %s %s - %d\n", conn->user->name, conn->account, conn->user->maxLogins);
conn->accWait = acLogin;
}
else
{
reply (conn, "530 Not logged in.");
unauthenticated (conn);
}
return 0;
}
/* Logout commands */
int command_rein(connection_t *conn, const char *arg, int expected)
{
if (super_privs (1))
{
reply(conn, "503 Can't relogin.");
return 0;
}
if (conn->user)
syslog (LOG_INFO, "%d: User %s logged out", conn->id, conn->user->name);
unauthenticated(conn);
pfree (conn->currLang, conn);
conn->currLang = NULL;
if (conn->langFd >= 0)
close_shared (conn->langFd);
conn->langFd = -1;
conn->protLevel = 0;
conn->mlstTags = tfType | tfUnique | tfModify | tfPerm | tfSize;
reply (conn, "220 You have been logged out.");
#ifdef USE_TLS
if (conn->tlsControl)
{
tls_stop (conn->tlsControl);
conn->tlsState = 2;
}
#endif
accounter (conn->accSock, "LOGOUT\n");
return 0;
}
int command_quit(connection_t *conn, const char *arg, int expected)
{
if (conn->working)
reply_spont(conn, "221 Good bye.", 1);
else
{
reply(conn, "221 Good bye.");
disconnected(conn);
}
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1