/*****************************************************************************\ * 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; }