mirror of
https://github.com/InfrastructureServices/vsftpd.git
synced 2025-04-19 01:24:02 +03:00
Since the pututxline() bug rhbz#1749439 is now fixed in glibc in Fedora and RHEL-8, we can implement a complete solution for the stale utmp entries issue originally reported as rhbz#1688848. This patch is a followup to commit 896b3694ca062d7. Resolves: rhbz#1688852 Resolves: rhbz#1737433
1376 lines
32 KiB
C
1376 lines
32 KiB
C
/*
|
|
* Part of Very Secure FTPd
|
|
* Licence: GPL v2
|
|
* Author: Chris Evans
|
|
* sysdeputil.c
|
|
*
|
|
* Highly system dependent utilities - e.g. authentication, capabilities.
|
|
*/
|
|
|
|
#include "sysdeputil.h"
|
|
#include "str.h"
|
|
#include "sysutil.h"
|
|
#include "utility.h"
|
|
#include "secbuf.h"
|
|
#include "defs.h"
|
|
#include "tunables.h"
|
|
#include "builddefs.h"
|
|
|
|
/* For Linux, this adds nothing :-) */
|
|
#include "port/porting_junk.h"
|
|
|
|
#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
|
|
#define _FILE_OFFSET_BITS 64
|
|
#define _LARGEFILE_SOURCE 1
|
|
#define _LARGEFILE64_SOURCE 1
|
|
#endif
|
|
|
|
/* For INT_MAX */
|
|
#include <limits.h>
|
|
|
|
/* For fd passing */
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
/* For FreeBSD */
|
|
#include <sys/param.h>
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/prctl.h>
|
|
#include <signal.h>
|
|
|
|
/* Configuration.. here are the possibilities */
|
|
#undef VSF_SYSDEP_HAVE_CAPABILITIES
|
|
#undef VSF_SYSDEP_HAVE_SETKEEPCAPS
|
|
#undef VSF_SYSDEP_HAVE_SETPDEATHSIG
|
|
#undef VSF_SYSDEP_HAVE_LINUX_SENDFILE
|
|
#undef VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
|
|
#undef VSF_SYSDEP_HAVE_HPUX_SENDFILE
|
|
#undef VSF_SYSDEP_HAVE_AIX_SENDFILE
|
|
#undef VSF_SYSDEP_HAVE_SETPROCTITLE
|
|
#undef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
|
#undef VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
|
|
#undef VSF_SYSDEP_HAVE_MAP_ANON
|
|
#undef VSF_SYSDEP_NEED_OLD_FD_PASSING
|
|
#undef VSF_SYSDEP_HAVE_LINUX_CLONE
|
|
#ifdef VSF_BUILD_PAM
|
|
#define VSF_SYSDEP_HAVE_PAM
|
|
#endif
|
|
#define VSF_SYSDEP_HAVE_SHADOW
|
|
#define VSF_SYSDEP_HAVE_USERSHELL
|
|
#define VSF_SYSDEP_HAVE_LIBCAP
|
|
#define VSF_SYSDEP_HAVE_UTMPX
|
|
|
|
#define __USE_GNU
|
|
#include <utmpx.h>
|
|
|
|
/* BEGIN config */
|
|
#if defined(__linux__)
|
|
#include <errno.h>
|
|
#include <syscall.h>
|
|
#define VSF_SYSDEP_HAVE_LINUX_CLONE
|
|
#include <sched.h>
|
|
#ifndef CLONE_NEWPID
|
|
#define CLONE_NEWPID 0x20000000
|
|
#endif
|
|
#ifndef CLONE_NEWIPC
|
|
#define CLONE_NEWIPC 0x08000000
|
|
#endif
|
|
#ifndef CLONE_NEWNET
|
|
#define CLONE_NEWNET 0x40000000
|
|
#endif
|
|
#include <linux/unistd.h>
|
|
#include <errno.h>
|
|
#include <syscall.h>
|
|
#endif
|
|
|
|
#if defined(__linux__) && !defined(__ia64__) && !defined(__s390__)
|
|
#define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
|
#include <linux/version.h>
|
|
#if defined(LINUX_VERSION_CODE) && defined(KERNEL_VERSION)
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
|
|
#define VSF_SYSDEP_HAVE_CAPABILITIES
|
|
#define VSF_SYSDEP_HAVE_LINUX_SENDFILE
|
|
#ifdef PR_SET_KEEPCAPS
|
|
#define VSF_SYSDEP_HAVE_SETKEEPCAPS
|
|
#endif
|
|
#ifdef PR_SET_PDEATHSIG
|
|
#define VSF_SYSDEP_HAVE_SETPDEATHSIG
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
|
|
#define VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
|
|
#define VSF_SYSDEP_HAVE_SETPROCTITLE
|
|
#endif
|
|
|
|
#if defined(__NetBSD__)
|
|
#include <stdlib.h>
|
|
#define VSF_SYSDEP_HAVE_SETPROCTITLE
|
|
#include <sys/param.h>
|
|
#if __NetBSD_Version__ >= 106070000
|
|
#define WTMPX_FILE _PATH_WTMPX
|
|
#else
|
|
#undef VSF_SYSDEP_HAVE_UTMPX
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef __hpux
|
|
#include <sys/socket.h>
|
|
#ifdef SF_DISCONNECT
|
|
#define VSF_SYSDEP_HAVE_HPUX_SENDFILE
|
|
#endif
|
|
#include <sys/param.h>
|
|
#include <sys/pstat.h>
|
|
#ifdef PSTAT_SETCMD
|
|
#define VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
|
|
#endif
|
|
#undef VSF_SYSDEP_HAVE_UTMPX
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#ifdef MAP_ANON
|
|
#define VSF_SYSDEP_HAVE_MAP_ANON
|
|
#endif
|
|
|
|
#ifdef __sgi
|
|
#undef VSF_SYSDEP_HAVE_USERSHELL
|
|
#undef VSF_SYSDEP_HAVE_LIBCAP
|
|
#endif
|
|
|
|
#ifdef _AIX
|
|
#undef VSF_SYSDEP_HAVE_USERSHELL
|
|
#undef VSF_SYSDEP_HAVE_LIBCAP
|
|
#undef VSF_SYSDEP_HAVE_UTMPX
|
|
#undef VSF_SYSDEP_HAVE_PAM
|
|
#undef VSF_SYSDEP_HAVE_SHADOW
|
|
#undef VSF_SYSDEP_HAVE_SETPROCTITLE
|
|
#define VSF_SYSDEP_HAVE_AIX_SENDFILE
|
|
#define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
|
#define VSF_SYSDEP_HAVE_MAP_ANON
|
|
#endif
|
|
|
|
#ifdef __osf__
|
|
#undef VSF_SYSDEP_HAVE_USERSHELL
|
|
#endif
|
|
|
|
#if (defined(__sgi) || defined(__hpux) || defined(__osf__))
|
|
#define VSF_SYSDEP_NEED_OLD_FD_PASSING
|
|
#endif
|
|
|
|
#ifdef __sun
|
|
#define VSF_SYSDEP_HAVE_SOLARIS_SENDFILE
|
|
#endif
|
|
/* END config */
|
|
|
|
/* PAM support - we include our own dummy version if the system lacks this */
|
|
#include <security/pam_appl.h>
|
|
|
|
/* No PAM? Try getspnam() with a getpwnam() fallback */
|
|
#ifndef VSF_SYSDEP_HAVE_PAM
|
|
/* This may hit our own "dummy" include and undef VSF_SYSDEP_HAVE_SHADOW */
|
|
#include <shadow.h>
|
|
#include <pwd.h>
|
|
#include <unistd.h>
|
|
#include <crypt.h>
|
|
#endif
|
|
|
|
/* Prefer libcap based capabilities over raw syscall capabilities */
|
|
#include <sys/capability.h>
|
|
|
|
#if defined(VSF_SYSDEP_HAVE_CAPABILITIES) && !defined(VSF_SYSDEP_HAVE_LIBCAP)
|
|
#include <linux/unistd.h>
|
|
#include <linux/capability.h>
|
|
#include <errno.h>
|
|
#include <syscall.h>
|
|
int capset(cap_user_header_t header, const cap_user_data_t data)
|
|
{
|
|
return syscall(__NR_capset, header, data);
|
|
}
|
|
/* Gross HACK to avoid warnings - linux headers overlap glibc headers */
|
|
#undef __NFDBITS
|
|
#undef __FDMASK
|
|
#endif /* VSF_SYSDEP_HAVE_CAPABILITIES */
|
|
|
|
#if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
|
|
defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
|
|
#include <sys/sendfile.h>
|
|
#elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#elif defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE)
|
|
#include <sys/socket.h>
|
|
#else /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
|
|
#include <unistd.h>
|
|
#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
|
|
|
|
#ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
|
extern char** environ;
|
|
static unsigned int s_proctitle_space = 0;
|
|
static int s_proctitle_inited = 0;
|
|
static char* s_p_proctitle = 0;
|
|
#endif
|
|
|
|
#ifndef VSF_SYSDEP_HAVE_MAP_ANON
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
static int s_zero_fd = -1;
|
|
#endif
|
|
|
|
/* File private functions/variables */
|
|
static int do_sendfile(const int out_fd, const int in_fd,
|
|
unsigned int num_send, filesize_t start_pos);
|
|
static void vsf_sysutil_setproctitle_internal(const char* p_text);
|
|
static struct mystr s_proctitle_prefix_str;
|
|
|
|
/* These two aren't static to avoid OpenBSD build warnings. */
|
|
void vsf_insert_uwtmp(const struct mystr* p_user_str,
|
|
const struct mystr* p_host_str);
|
|
void vsf_remove_uwtmp(void);
|
|
|
|
#ifndef VSF_SYSDEP_HAVE_PAM
|
|
int
|
|
vsf_sysdep_check_auth(struct vsf_session* p_sess,
|
|
struct mystr* p_user_str,
|
|
const struct mystr* p_pass_str,
|
|
const struct mystr* p_remote_host)
|
|
{
|
|
const char* p_crypted;
|
|
const struct passwd* p_pwd = getpwnam(str_getbuf(p_user_str));
|
|
(void) p_remote_host;
|
|
(void) p_sess;
|
|
if (p_pwd == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
#ifdef VSF_SYSDEP_HAVE_USERSHELL
|
|
if (tunable_check_shell)
|
|
{
|
|
const char* p_shell;
|
|
while ((p_shell = getusershell()) != NULL)
|
|
{
|
|
if (!vsf_sysutil_strcmp(p_shell, p_pwd->pw_shell))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
endusershell();
|
|
if (p_shell == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef VSF_SYSDEP_HAVE_SHADOW
|
|
{
|
|
const struct spwd* p_spwd = getspnam(str_getbuf(p_user_str));
|
|
if (p_spwd != NULL)
|
|
{
|
|
long curr_time = vsf_sysutil_get_time_sec();
|
|
int days;
|
|
days = curr_time / (60 * 60 * 24);
|
|
if (p_spwd->sp_expire > 0 && p_spwd->sp_expire < days)
|
|
{
|
|
return 0;
|
|
}
|
|
if (p_spwd->sp_lstchg > 0 && p_spwd->sp_max > 0 &&
|
|
p_spwd->sp_lstchg + p_spwd->sp_max < days)
|
|
{
|
|
return 0;
|
|
}
|
|
p_crypted = crypt(str_getbuf(p_pass_str), p_spwd->sp_pwdp);
|
|
if (!vsf_sysutil_strcmp(p_crypted, p_spwd->sp_pwdp))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
#endif /* VSF_SYSDEP_HAVE_SHADOW */
|
|
p_crypted = crypt(str_getbuf(p_pass_str), p_pwd->pw_passwd);
|
|
if (!vsf_sysutil_strcmp(p_crypted, p_pwd->pw_passwd))
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#else /* VSF_SYSDEP_HAVE_PAM */
|
|
|
|
#if (defined(__sun) || defined(__hpux)) && \
|
|
!defined(LINUX_PAM) && !defined(_OPENPAM)
|
|
/* Sun's PAM doesn't use const here, while Linux-PAM and OpenPAM do */
|
|
#define lo_const
|
|
#else
|
|
#define lo_const const
|
|
#endif
|
|
typedef lo_const void* pam_item_t;
|
|
|
|
static pam_handle_t* s_pamh;
|
|
static struct mystr s_pword_str;
|
|
static int pam_conv_func(int nmsg, const struct pam_message** p_msg,
|
|
struct pam_response** p_reply, void* p_addata);
|
|
static void vsf_auth_shutdown(void);
|
|
|
|
int
|
|
vsf_sysdep_check_auth(struct vsf_session* p_sess,
|
|
struct mystr* p_user_str,
|
|
const struct mystr* p_pass_str,
|
|
const struct mystr* p_remote_host)
|
|
{
|
|
int retval = -1;
|
|
#ifdef PAM_RHOST
|
|
struct mystr hostname = INIT_MYSTR;
|
|
#endif
|
|
pam_item_t item;
|
|
const char* pam_user_name = 0;
|
|
struct pam_conv the_conv =
|
|
{
|
|
&pam_conv_func,
|
|
0
|
|
};
|
|
if (s_pamh != 0)
|
|
{
|
|
bug("vsf_sysdep_check_auth");
|
|
}
|
|
str_copy(&s_pword_str, p_pass_str);
|
|
if (tunable_pam_service_name)
|
|
{
|
|
retval = pam_start(tunable_pam_service_name,
|
|
str_getbuf(p_user_str), &the_conv, &s_pamh);
|
|
}
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
#ifdef PAM_RHOST
|
|
if (tunable_reverse_lookup_enable)
|
|
{
|
|
if (vsf_sysutil_get_hostname(p_sess->p_remote_addr, &hostname) == 0)
|
|
{
|
|
retval = pam_set_item(s_pamh, PAM_RHOST, str_getbuf(&hostname));
|
|
str_free(&hostname);
|
|
}
|
|
else
|
|
{
|
|
retval = pam_set_item(s_pamh, PAM_RHOST, str_getbuf(p_remote_host));
|
|
}
|
|
} else {
|
|
retval = pam_set_item(s_pamh, PAM_RHOST, str_getbuf(p_remote_host));
|
|
}
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef PAM_TTY
|
|
retval = pam_set_item(s_pamh, PAM_TTY, "ftp");
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef PAM_RUSER
|
|
retval = pam_set_item(s_pamh, PAM_RUSER, str_getbuf(p_user_str));
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
#endif
|
|
retval = pam_authenticate(s_pamh, 0);
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
#ifdef PAM_USER
|
|
retval = pam_get_item(s_pamh, PAM_USER, &item);
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
pam_user_name = item;
|
|
str_alloc_text(p_user_str, pam_user_name);
|
|
#endif
|
|
retval = pam_acct_mgmt(s_pamh, 0);
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
retval = pam_setcred(s_pamh, PAM_ESTABLISH_CRED);
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
if (!tunable_session_support)
|
|
{
|
|
/* You're in already! */
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 1;
|
|
}
|
|
/* Must do this BEFORE opening a session for pam_limits to count us */
|
|
vsf_insert_uwtmp(p_user_str, p_remote_host);
|
|
retval = pam_open_session(s_pamh, 0);
|
|
if (retval != PAM_SUCCESS)
|
|
{
|
|
vsf_remove_uwtmp();
|
|
(void) pam_setcred(s_pamh, PAM_DELETE_CRED);
|
|
(void) pam_end(s_pamh, retval);
|
|
s_pamh = 0;
|
|
return 0;
|
|
}
|
|
/* We MUST ensure the PAM session, utmp, wtmp etc. are cleaned up, however
|
|
* we exit.
|
|
*/
|
|
vsf_sysutil_set_exit_func(vsf_auth_shutdown);
|
|
/* You're in dude */
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
vsf_auth_shutdown(void)
|
|
{
|
|
if (s_pamh == 0)
|
|
{
|
|
bug("vsf_auth_shutdown");
|
|
}
|
|
(void) pam_close_session(s_pamh, 0);
|
|
(void) pam_setcred(s_pamh, PAM_DELETE_CRED);
|
|
(void) pam_end(s_pamh, PAM_SUCCESS);
|
|
s_pamh = 0;
|
|
vsf_remove_uwtmp();
|
|
}
|
|
|
|
static int
|
|
pam_conv_func(int nmsg, const struct pam_message** p_msg,
|
|
struct pam_response** p_reply, void* p_addata)
|
|
{
|
|
int i;
|
|
struct pam_response* p_resps = 0;
|
|
(void) p_addata;
|
|
if (nmsg < 0)
|
|
{
|
|
bug("dodgy nmsg in pam_conv_func");
|
|
}
|
|
p_resps = vsf_sysutil_malloc(sizeof(struct pam_response) * nmsg);
|
|
for (i=0; i<nmsg; i++)
|
|
{
|
|
switch (p_msg[i]->msg_style)
|
|
{
|
|
case PAM_PROMPT_ECHO_OFF:
|
|
p_resps[i].resp_retcode = PAM_SUCCESS;
|
|
p_resps[i].resp = (char*) str_strdup(&s_pword_str);
|
|
break;
|
|
case PAM_TEXT_INFO:
|
|
case PAM_ERROR_MSG:
|
|
p_resps[i].resp_retcode = PAM_SUCCESS;
|
|
p_resps[i].resp = 0;
|
|
break;
|
|
case PAM_PROMPT_ECHO_ON:
|
|
default:
|
|
vsf_sysutil_free(p_resps);
|
|
return PAM_CONV_ERR;
|
|
break;
|
|
}
|
|
}
|
|
*p_reply = p_resps;
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
#endif /* VSF_SYSDEP_HAVE_PAM */
|
|
|
|
/* Capabilities support (or lack thereof) */
|
|
void
|
|
vsf_sysdep_keep_capabilities(void)
|
|
{
|
|
if (!vsf_sysdep_has_capabilities_as_non_root())
|
|
{
|
|
bug("asked to keep capabilities, but no support exists");
|
|
}
|
|
#ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
|
|
{
|
|
int retval = prctl(PR_SET_KEEPCAPS, 1);
|
|
if (vsf_sysutil_retval_is_error(retval))
|
|
{
|
|
die("prctl");
|
|
}
|
|
}
|
|
#endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
|
|
}
|
|
#if !defined(VSF_SYSDEP_HAVE_CAPABILITIES) && !defined(VSF_SYSDEP_HAVE_LIBCAP)
|
|
|
|
int
|
|
vsf_sysdep_has_capabilities(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
vsf_sysdep_has_capabilities_as_non_root(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
vsf_sysdep_adopt_capabilities(unsigned int caps)
|
|
{
|
|
(void) caps;
|
|
bug("asked to adopt capabilities, but no support exists");
|
|
}
|
|
|
|
#else /* VSF_SYSDEP_HAVE_CAPABILITIES || VSF_SYSDEP_HAVE_LIBCAP */
|
|
|
|
static int do_checkcap(void);
|
|
|
|
int
|
|
vsf_sysdep_has_capabilities_as_non_root(void)
|
|
{
|
|
static int s_prctl_checked;
|
|
static int s_runtime_prctl_works;
|
|
if (!s_prctl_checked)
|
|
{
|
|
#ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
|
|
/* Clarity: note embedded call to prctl() syscall */
|
|
if (!vsf_sysutil_retval_is_error(prctl(PR_SET_KEEPCAPS, 0)))
|
|
{
|
|
s_runtime_prctl_works = 1;
|
|
}
|
|
#endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
|
|
s_prctl_checked = 1;
|
|
}
|
|
return s_runtime_prctl_works;
|
|
}
|
|
|
|
int
|
|
vsf_sysdep_has_capabilities(void)
|
|
{
|
|
/* Even though compiled with capabilities, the runtime system may lack them.
|
|
* Also, RH7.0 kernel headers advertise a 2.4.0 box, but on a 2.2.x kernel!
|
|
*/
|
|
static int s_caps_checked;
|
|
static int s_runtime_has_caps;
|
|
if (!s_caps_checked)
|
|
{
|
|
s_runtime_has_caps = do_checkcap();
|
|
s_caps_checked = 1;
|
|
}
|
|
return s_runtime_has_caps;
|
|
}
|
|
|
|
#ifndef VSF_SYSDEP_HAVE_LIBCAP
|
|
static int
|
|
do_checkcap(void)
|
|
{
|
|
/* EFAULT (EINVAL if page 0 mapped) vs. ENOSYS */
|
|
int retval = capset(0, 0);
|
|
if (!vsf_sysutil_retval_is_error(retval) ||
|
|
vsf_sysutil_get_error() != kVSFSysUtilErrNOSYS)
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
vsf_sysdep_adopt_capabilities(unsigned int caps)
|
|
{
|
|
/* n.b. yes I know I should be using libcap!! */
|
|
int retval;
|
|
struct __user_cap_header_struct cap_head;
|
|
struct __user_cap_data_struct cap_data;
|
|
__u32 cap_mask = 0;
|
|
if (!caps)
|
|
{
|
|
bug("asked to adopt no capabilities");
|
|
}
|
|
vsf_sysutil_memclr(&cap_head, sizeof(cap_head));
|
|
vsf_sysutil_memclr(&cap_data, sizeof(cap_data));
|
|
cap_head.version = _LINUX_CAPABILITY_VERSION;
|
|
cap_head.pid = 0;
|
|
if (caps & kCapabilityCAP_CHOWN)
|
|
{
|
|
cap_mask |= (1 << CAP_CHOWN);
|
|
}
|
|
if (caps & kCapabilityCAP_NET_BIND_SERVICE)
|
|
{
|
|
cap_mask |= (1 << CAP_NET_BIND_SERVICE);
|
|
}
|
|
cap_data.effective = cap_data.permitted = cap_mask;
|
|
cap_data.inheritable = 0;
|
|
retval = capset(&cap_head, &cap_data);
|
|
if (retval != 0)
|
|
{
|
|
die("capset");
|
|
}
|
|
}
|
|
|
|
#else /* VSF_SYSDEP_HAVE_LIBCAP */
|
|
static int
|
|
do_checkcap(void)
|
|
{
|
|
cap_t current_caps = cap_get_proc();
|
|
cap_free(current_caps);
|
|
if (current_caps != NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
vsf_sysdep_adopt_capabilities(unsigned int caps)
|
|
{
|
|
int retval;
|
|
cap_value_t cap_value;
|
|
cap_t adopt_caps = cap_init();
|
|
if (caps & kCapabilityCAP_CHOWN)
|
|
{
|
|
cap_value = CAP_CHOWN;
|
|
cap_set_flag(adopt_caps, CAP_EFFECTIVE, 1, &cap_value, CAP_SET);
|
|
cap_set_flag(adopt_caps, CAP_PERMITTED, 1, &cap_value, CAP_SET);
|
|
}
|
|
if (caps & kCapabilityCAP_NET_BIND_SERVICE)
|
|
{
|
|
cap_value = CAP_NET_BIND_SERVICE;
|
|
cap_set_flag(adopt_caps, CAP_EFFECTIVE, 1, &cap_value, CAP_SET);
|
|
cap_set_flag(adopt_caps, CAP_PERMITTED, 1, &cap_value, CAP_SET);
|
|
}
|
|
retval = cap_set_proc(adopt_caps);
|
|
if (retval != 0)
|
|
{
|
|
die("cap_set_proc");
|
|
}
|
|
cap_free(adopt_caps);
|
|
}
|
|
|
|
#endif /* !VSF_SYSDEP_HAVE_LIBCAP */
|
|
#endif /* VSF_SYSDEP_HAVE_CAPABILITIES || VSF_SYSDEP_HAVE_LIBCAP */
|
|
|
|
int
|
|
vsf_sysutil_sendfile(const int out_fd, const int in_fd,
|
|
filesize_t* p_offset, filesize_t num_send,
|
|
unsigned int max_chunk)
|
|
{
|
|
/* Grr - why is off_t signed? */
|
|
if (*p_offset < 0 || num_send < 0)
|
|
{
|
|
die("invalid offset or send count in vsf_sysutil_sendfile");
|
|
}
|
|
if (max_chunk == 0)
|
|
{
|
|
max_chunk = INT_MAX;
|
|
}
|
|
while (num_send > 0)
|
|
{
|
|
int retval;
|
|
unsigned int send_this_time;
|
|
if (num_send > max_chunk)
|
|
{
|
|
send_this_time = max_chunk;
|
|
}
|
|
else
|
|
{
|
|
send_this_time = (unsigned int) num_send;
|
|
}
|
|
/* Keep input file position in line with sendfile() calls */
|
|
vsf_sysutil_lseek_to(in_fd, *p_offset);
|
|
retval = do_sendfile(out_fd, in_fd, send_this_time, *p_offset);
|
|
if (vsf_sysutil_retval_is_error(retval) || retval == 0)
|
|
{
|
|
return retval;
|
|
}
|
|
num_send -= retval;
|
|
*p_offset += retval;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int do_sendfile(const int out_fd, const int in_fd,
|
|
unsigned int num_send, filesize_t start_pos)
|
|
{
|
|
/* Probably should one day be shared with instance in ftpdataio.c */
|
|
static char* p_recvbuf;
|
|
unsigned int total_written = 0;
|
|
int retval;
|
|
enum EVSFSysUtilError error;
|
|
(void) start_pos;
|
|
(void) error;
|
|
#if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
|
|
defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \
|
|
defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE) || \
|
|
defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) || \
|
|
defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
|
|
if (tunable_use_sendfile)
|
|
{
|
|
static int s_sendfile_checked;
|
|
static int s_runtime_sendfile_works;
|
|
if (!s_sendfile_checked || s_runtime_sendfile_works)
|
|
{
|
|
do
|
|
{
|
|
#ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE
|
|
retval = sendfile(out_fd, in_fd, NULL, num_send);
|
|
#elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
|
|
{
|
|
/* XXX - start_pos will truncate on 32-bit machines - can we
|
|
* say "start from current pos"?
|
|
*/
|
|
off_t written = 0;
|
|
retval = sendfile(in_fd, out_fd, start_pos, num_send, NULL,
|
|
&written, 0);
|
|
/* Translate to Linux-like retval */
|
|
if (written > 0)
|
|
{
|
|
retval = (int) written;
|
|
}
|
|
}
|
|
#elif defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
|
|
{
|
|
size_t written = 0;
|
|
struct sendfilevec the_vec;
|
|
vsf_sysutil_memclr(&the_vec, sizeof(the_vec));
|
|
the_vec.sfv_fd = in_fd;
|
|
the_vec.sfv_off = start_pos;
|
|
the_vec.sfv_len = num_send;
|
|
retval = sendfilev(out_fd, &the_vec, 1, &written);
|
|
/* Translate to Linux-like retval */
|
|
if (written > 0)
|
|
{
|
|
retval = (int) written;
|
|
}
|
|
}
|
|
#elif defined(VSF_SYSDEP_HAVE_AIX_SENDFILE)
|
|
{
|
|
struct sf_parms sf_iobuf;
|
|
vsf_sysutil_memclr(&sf_iobuf, sizeof(sf_iobuf));
|
|
sf_iobuf.header_data = NULL;
|
|
sf_iobuf.header_length = 0;
|
|
sf_iobuf.trailer_data = NULL;
|
|
sf_iobuf.trailer_length = 0;
|
|
sf_iobuf.file_descriptor = in_fd;
|
|
sf_iobuf.file_offset = start_pos;
|
|
sf_iobuf.file_bytes = num_send;
|
|
|
|
retval = send_file((int*)&out_fd, &sf_iobuf, 0);
|
|
if (retval >= 0)
|
|
{
|
|
retval = sf_iobuf.bytes_sent;
|
|
}
|
|
}
|
|
#else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */
|
|
{
|
|
retval = sendfile(out_fd, in_fd, start_pos, num_send, NULL, 0);
|
|
}
|
|
#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
|
|
error = vsf_sysutil_get_error();
|
|
vsf_sysutil_check_pending_actions(kVSFSysUtilIO, retval, out_fd);
|
|
}
|
|
while (vsf_sysutil_retval_is_error(retval) &&
|
|
error == kVSFSysUtilErrINTR);
|
|
if (!s_sendfile_checked)
|
|
{
|
|
s_sendfile_checked = 1;
|
|
if (!vsf_sysutil_retval_is_error(retval) ||
|
|
error != kVSFSysUtilErrNOSYS)
|
|
{
|
|
s_runtime_sendfile_works = 1;
|
|
}
|
|
}
|
|
if (!vsf_sysutil_retval_is_error(retval))
|
|
{
|
|
return retval;
|
|
}
|
|
if (s_runtime_sendfile_works && error != kVSFSysUtilErrINVAL &&
|
|
error != kVSFSysUtilErrOPNOTSUPP)
|
|
{
|
|
return retval;
|
|
}
|
|
/* Fall thru to normal implementation. We won't check again. NOTE -
|
|
* also falls through if sendfile() is OK but it returns EINVAL. For
|
|
* Linux this means the file was not page cache backed. Original
|
|
* complaint was trying to serve files from an NTFS filesystem!
|
|
*/
|
|
}
|
|
}
|
|
#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE || VSF_SYSDEP_HAVE_FREEBSD_SENDFILE */
|
|
if (p_recvbuf == 0)
|
|
{
|
|
vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE);
|
|
}
|
|
while (1)
|
|
{
|
|
unsigned int num_read;
|
|
unsigned int num_written;
|
|
unsigned int num_read_this_time = VSFTP_DATA_BUFSIZE;
|
|
if (num_read_this_time > num_send)
|
|
{
|
|
num_read_this_time = num_send;
|
|
}
|
|
retval = vsf_sysutil_read(in_fd, p_recvbuf, num_read_this_time);
|
|
if (retval < 0)
|
|
{
|
|
return retval;
|
|
}
|
|
else if (retval == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
num_read = (unsigned int) retval;
|
|
retval = vsf_sysutil_write_loop(out_fd, p_recvbuf, num_read);
|
|
if (retval < 0)
|
|
{
|
|
return retval;
|
|
}
|
|
num_written = (unsigned int) retval;
|
|
total_written += num_written;
|
|
if (num_written != num_read)
|
|
{
|
|
return num_written;
|
|
}
|
|
if (num_written > num_send)
|
|
{
|
|
bug("num_written bigger than num_send in do_sendfile");
|
|
}
|
|
num_send -= num_written;
|
|
if (num_send == 0)
|
|
{
|
|
/* Bingo! */
|
|
return total_written;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
vsf_sysutil_set_proctitle_prefix(const struct mystr* p_str)
|
|
{
|
|
str_copy(&s_proctitle_prefix_str, p_str);
|
|
}
|
|
|
|
/* This delegation is common to all setproctitle() implementations */
|
|
void
|
|
vsf_sysutil_setproctitle_str(const struct mystr* p_str)
|
|
{
|
|
vsf_sysutil_setproctitle(str_getbuf(p_str));
|
|
}
|
|
|
|
void
|
|
vsf_sysutil_setproctitle(const char* p_text)
|
|
{
|
|
struct mystr proctitle_str = INIT_MYSTR;
|
|
str_copy(&proctitle_str, &s_proctitle_prefix_str);
|
|
if (!str_isempty(&proctitle_str))
|
|
{
|
|
str_append_text(&proctitle_str, ": ");
|
|
}
|
|
str_append_text(&proctitle_str, p_text);
|
|
vsf_sysutil_setproctitle_internal(str_getbuf(&proctitle_str));
|
|
str_free(&proctitle_str);
|
|
}
|
|
|
|
#ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
|
|
void
|
|
vsf_sysutil_setproctitle_init(int argc, const char* argv[])
|
|
{
|
|
(void) argc;
|
|
(void) argv;
|
|
}
|
|
|
|
void
|
|
vsf_sysutil_setproctitle_internal(const char* p_buf)
|
|
{
|
|
setproctitle("%s", p_buf);
|
|
}
|
|
#elif defined(VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE)
|
|
void
|
|
vsf_sysutil_setproctitle_init(int argc, const char* argv[])
|
|
{
|
|
(void) argc;
|
|
(void) argv;
|
|
}
|
|
|
|
void
|
|
vsf_sysutil_setproctitle_internal(const char* p_buf)
|
|
{
|
|
struct mystr proctitle_str = INIT_MYSTR;
|
|
union pstun p;
|
|
str_alloc_text(&proctitle_str, "vsftpd: ");
|
|
str_append_text(&proctitle_str, p_buf);
|
|
p.pst_command = str_getbuf(&proctitle_str);
|
|
pstat(PSTAT_SETCMD, p, 0, 0, 0);
|
|
str_free(&proctitle_str);
|
|
}
|
|
#elif defined(VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK)
|
|
void
|
|
vsf_sysutil_setproctitle_init(int argc, const char* argv[])
|
|
{
|
|
int i;
|
|
char** p_env = environ;
|
|
if (s_proctitle_inited)
|
|
{
|
|
bug("vsf_sysutil_setproctitle_init called twice");
|
|
}
|
|
s_proctitle_inited = 1;
|
|
if (argv[0] == 0)
|
|
{
|
|
die("no argv[0] in vsf_sysutil_setproctitle_init");
|
|
}
|
|
for (i=0; i<argc; i++)
|
|
{
|
|
s_proctitle_space += vsf_sysutil_strlen(argv[i]) + 1;
|
|
if (i > 0)
|
|
{
|
|
argv[i] = 0;
|
|
}
|
|
}
|
|
while (*p_env != 0)
|
|
{
|
|
s_proctitle_space += vsf_sysutil_strlen(*p_env) + 1;
|
|
p_env++;
|
|
}
|
|
/* Oops :-) */
|
|
environ = 0;
|
|
s_p_proctitle = (char*) argv[0];
|
|
vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
|
|
}
|
|
|
|
void
|
|
vsf_sysutil_setproctitle_internal(const char* p_buf)
|
|
{
|
|
struct mystr proctitle_str = INIT_MYSTR;
|
|
unsigned int to_copy;
|
|
if (!s_proctitle_inited)
|
|
{
|
|
bug("vsf_sysutil_setproctitle: not initialized");
|
|
}
|
|
vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
|
|
if (s_proctitle_space < 32)
|
|
{
|
|
return;
|
|
}
|
|
str_alloc_text(&proctitle_str, "vsftpd: ");
|
|
str_append_text(&proctitle_str, p_buf);
|
|
to_copy = str_getlen(&proctitle_str);
|
|
if (to_copy > s_proctitle_space - 1)
|
|
{
|
|
to_copy = s_proctitle_space - 1;
|
|
}
|
|
vsf_sysutil_memcpy(s_p_proctitle, str_getbuf(&proctitle_str), to_copy);
|
|
str_free(&proctitle_str);
|
|
s_p_proctitle[to_copy] = '\0';
|
|
}
|
|
#else /* VSF_SYSDEP_HAVE_SETPROCTITLE */
|
|
void
|
|
vsf_sysutil_setproctitle_init(int argc, const char* argv[])
|
|
{
|
|
(void) argc;
|
|
(void) argv;
|
|
}
|
|
|
|
void
|
|
vsf_sysutil_setproctitle_internal(const char* p_buf)
|
|
{
|
|
(void) p_buf;
|
|
}
|
|
#endif /* VSF_SYSDEP_HAVE_SETPROCTITLE */
|
|
|
|
#ifdef VSF_SYSDEP_HAVE_MAP_ANON
|
|
void
|
|
vsf_sysutil_map_anon_pages_init(void)
|
|
{
|
|
}
|
|
|
|
void*
|
|
vsf_sysutil_map_anon_pages(unsigned int length)
|
|
{
|
|
char* retval = mmap(0, length, PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANON, -1, 0);
|
|
if (retval == MAP_FAILED)
|
|
{
|
|
die("mmap");
|
|
}
|
|
return retval;
|
|
}
|
|
#else /* VSF_SYSDEP_HAVE_MAP_ANON */
|
|
void
|
|
vsf_sysutil_map_anon_pages_init(void)
|
|
{
|
|
if (s_zero_fd != -1)
|
|
{
|
|
bug("vsf_sysutil_map_anon_pages_init called twice");
|
|
}
|
|
s_zero_fd = open("/dev/zero", O_RDWR);
|
|
if (s_zero_fd < 0)
|
|
{
|
|
die("could not open /dev/zero");
|
|
}
|
|
}
|
|
|
|
void*
|
|
vsf_sysutil_map_anon_pages(unsigned int length)
|
|
{
|
|
char* retval = mmap(0, length, PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE, s_zero_fd, 0);
|
|
if (retval == MAP_FAILED)
|
|
{
|
|
die("mmap");
|
|
}
|
|
return retval;
|
|
}
|
|
#endif /* VSF_SYSDEP_HAVE_MAP_ANON */
|
|
|
|
#ifndef VSF_SYSDEP_NEED_OLD_FD_PASSING
|
|
|
|
void
|
|
vsf_sysutil_send_fd(int sock_fd, int send_fd)
|
|
{
|
|
int retval;
|
|
struct msghdr msg;
|
|
struct cmsghdr* p_cmsg;
|
|
struct iovec vec;
|
|
char cmsgbuf[CMSG_SPACE(sizeof(send_fd))];
|
|
int* p_fds;
|
|
char sendchar = 0;
|
|
msg.msg_control = cmsgbuf;
|
|
msg.msg_controllen = sizeof(cmsgbuf);
|
|
p_cmsg = CMSG_FIRSTHDR(&msg);
|
|
p_cmsg->cmsg_level = SOL_SOCKET;
|
|
p_cmsg->cmsg_type = SCM_RIGHTS;
|
|
p_cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
|
|
p_fds = (int*)CMSG_DATA(p_cmsg);
|
|
*p_fds = send_fd;
|
|
msg.msg_controllen = p_cmsg->cmsg_len;
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_flags = 0;
|
|
/* "To pass file descriptors or credentials you need to send/read at
|
|
* least on byte" (man 7 unix)
|
|
*/
|
|
vec.iov_base = &sendchar;
|
|
vec.iov_len = sizeof(sendchar);
|
|
retval = sendmsg(sock_fd, &msg, 0);
|
|
if (retval != 1)
|
|
{
|
|
die("sendmsg");
|
|
}
|
|
}
|
|
|
|
int
|
|
vsf_sysutil_recv_fd(const int sock_fd)
|
|
{
|
|
int retval;
|
|
struct msghdr msg;
|
|
char recvchar;
|
|
struct iovec vec;
|
|
int recv_fd;
|
|
char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
|
|
struct cmsghdr* p_cmsg;
|
|
int* p_fd;
|
|
vec.iov_base = &recvchar;
|
|
vec.iov_len = sizeof(recvchar);
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = cmsgbuf;
|
|
msg.msg_controllen = sizeof(cmsgbuf);
|
|
msg.msg_flags = 0;
|
|
/* In case something goes wrong, set the fd to -1 before the syscall */
|
|
p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg));
|
|
*p_fd = -1;
|
|
retval = recvmsg(sock_fd, &msg, 0);
|
|
if (retval != 1)
|
|
{
|
|
die("recvmsg");
|
|
}
|
|
p_cmsg = CMSG_FIRSTHDR(&msg);
|
|
if (p_cmsg == NULL)
|
|
{
|
|
die("no passed fd");
|
|
}
|
|
/* We used to verify the returned cmsg_level, cmsg_type and cmsg_len here,
|
|
* but Linux 2.0 totally uselessly fails to fill these in.
|
|
*/
|
|
p_fd = (int*)CMSG_DATA(p_cmsg);
|
|
recv_fd = *p_fd;
|
|
if (recv_fd == -1)
|
|
{
|
|
die("no passed fd");
|
|
}
|
|
return recv_fd;
|
|
}
|
|
|
|
#else /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
|
|
|
|
void
|
|
vsf_sysutil_send_fd(int sock_fd, int send_fd)
|
|
{
|
|
int retval;
|
|
char send_char = 0;
|
|
struct msghdr msg;
|
|
struct iovec vec;
|
|
vec.iov_base = &send_char;
|
|
vec.iov_len = 1;
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_accrights = (caddr_t) &send_fd;
|
|
msg.msg_accrightslen = sizeof(send_fd);
|
|
retval = sendmsg(sock_fd, &msg, 0);
|
|
if (retval != 1)
|
|
{
|
|
die("sendmsg");
|
|
}
|
|
}
|
|
|
|
int
|
|
vsf_sysutil_recv_fd(int sock_fd)
|
|
{
|
|
int retval;
|
|
struct msghdr msg;
|
|
struct iovec vec;
|
|
char recv_char;
|
|
int recv_fd = -1;
|
|
vec.iov_base = &recv_char;
|
|
vec.iov_len = 1;
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = &vec;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_accrights = (caddr_t) &recv_fd;
|
|
msg.msg_accrightslen = sizeof(recv_fd);
|
|
retval = recvmsg(sock_fd, &msg, 0);
|
|
if (retval != 1)
|
|
{
|
|
die("recvmsg");
|
|
}
|
|
if (recv_fd == -1)
|
|
{
|
|
die("no passed fd");
|
|
}
|
|
return recv_fd;
|
|
}
|
|
|
|
#endif /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
|
|
|
|
#ifndef VSF_SYSDEP_HAVE_UTMPX
|
|
|
|
void
|
|
vsf_insert_uwtmp(const struct mystr* p_user_str,
|
|
const struct mystr* p_host_str)
|
|
{
|
|
(void) p_user_str;
|
|
(void) p_host_str;
|
|
}
|
|
|
|
void
|
|
vsf_remove_uwtmp(void)
|
|
{
|
|
}
|
|
|
|
#else /* !VSF_SYSDEP_HAVE_UTMPX */
|
|
|
|
/* IMHO, the pam_unix module REALLY should be doing this in its SM component */
|
|
/* Statics */
|
|
static int s_uwtmp_inserted;
|
|
static struct utmpx s_utent;
|
|
|
|
void
|
|
vsf_insert_uwtmp(const struct mystr* p_user_str,
|
|
const struct mystr* p_host_str)
|
|
{
|
|
struct utmpx* p_res;
|
|
|
|
if (sizeof(s_utent.ut_line) < 16)
|
|
{
|
|
return;
|
|
}
|
|
if (s_uwtmp_inserted)
|
|
{
|
|
bug("vsf_insert_uwtmp");
|
|
}
|
|
{
|
|
struct mystr line_str = INIT_MYSTR;
|
|
str_alloc_text(&line_str, "vsftpd:");
|
|
str_append_ulong(&line_str, vsf_sysutil_getpid());
|
|
if (str_getlen(&line_str) >= sizeof(s_utent.ut_line))
|
|
{
|
|
str_free(&line_str);
|
|
return;
|
|
}
|
|
vsf_sysutil_strcpy(s_utent.ut_line, str_getbuf(&line_str),
|
|
sizeof(s_utent.ut_line));
|
|
str_free(&line_str);
|
|
}
|
|
s_utent.ut_type = USER_PROCESS;
|
|
s_utent.ut_pid = vsf_sysutil_getpid();
|
|
vsf_sysutil_strcpy(s_utent.ut_user, str_getbuf(p_user_str),
|
|
sizeof(s_utent.ut_user));
|
|
vsf_sysutil_strcpy(s_utent.ut_host, str_getbuf(p_host_str),
|
|
sizeof(s_utent.ut_host));
|
|
s_utent.ut_tv.tv_sec = vsf_sysutil_get_time_sec();
|
|
setutxent();
|
|
do
|
|
{
|
|
p_res = pututxline(&s_utent);
|
|
/* For now we'll ignore errors other than EINTR and EAGAIN */
|
|
} while (p_res == NULL && (errno == EINTR || errno == EAGAIN));
|
|
s_uwtmp_inserted = 1;
|
|
endutxent();
|
|
updwtmpx(WTMPX_FILE, &s_utent);
|
|
}
|
|
|
|
void
|
|
vsf_remove_uwtmp(void)
|
|
{
|
|
struct utmpx* p_res;
|
|
|
|
if (!s_uwtmp_inserted)
|
|
{
|
|
return;
|
|
}
|
|
s_utent.ut_type = DEAD_PROCESS;
|
|
vsf_sysutil_memclr(s_utent.ut_user, sizeof(s_utent.ut_user));
|
|
vsf_sysutil_memclr(s_utent.ut_host, sizeof(s_utent.ut_host));
|
|
s_utent.ut_tv.tv_sec = 0;
|
|
setutxent();
|
|
do
|
|
{
|
|
p_res = pututxline(&s_utent);
|
|
/* For now we'll ignore errors other than EINTR and EAGAIN */
|
|
} while (p_res == NULL && (errno == EINTR || errno == EAGAIN));
|
|
endutxent();
|
|
s_uwtmp_inserted = 0;
|
|
s_utent.ut_tv.tv_sec = vsf_sysutil_get_time_sec();
|
|
updwtmpx(WTMPX_FILE, &s_utent);
|
|
}
|
|
|
|
#endif /* !VSF_SYSDEP_HAVE_UTMPX */
|
|
|
|
void
|
|
vsf_set_die_if_parent_dies()
|
|
{
|
|
#ifdef VSF_SYSDEP_HAVE_SETPDEATHSIG
|
|
if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) != 0)
|
|
{
|
|
die("prctl");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
vsf_set_term_if_parent_dies()
|
|
{
|
|
#ifdef VSF_SYSDEP_HAVE_SETPDEATHSIG
|
|
if (prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) != 0)
|
|
{
|
|
die("prctl");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int
|
|
vsf_sysutil_fork_isolate_all_failok()
|
|
{
|
|
#ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
|
|
static int cloneflags_work = 1;
|
|
if (cloneflags_work)
|
|
{
|
|
int ret = syscall(__NR_clone,
|
|
CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWNET | SIGCHLD,
|
|
NULL);
|
|
if (ret != -1 || (errno != EINVAL && errno != EPERM))
|
|
{
|
|
if (ret == 0)
|
|
{
|
|
vsf_sysutil_post_fork();
|
|
}
|
|
return ret;
|
|
}
|
|
cloneflags_work = 0;
|
|
}
|
|
#endif
|
|
return vsf_sysutil_fork_isolate_failok();
|
|
}
|
|
|
|
int
|
|
vsf_sysutil_fork_isolate_failok()
|
|
{
|
|
#ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
|
|
static int cloneflags_work = 1;
|
|
if (cloneflags_work)
|
|
{
|
|
int ret = clone(NULL, NULL, CLONE_NEWPID | CLONE_NEWIPC | SIGCHLD, NULL);
|
|
if (ret != -1 || (errno != EINVAL && errno != EPERM))
|
|
{
|
|
if (ret == 0)
|
|
{
|
|
vsf_sysutil_post_fork();
|
|
}
|
|
return ret;
|
|
}
|
|
cloneflags_work = 0;
|
|
}
|
|
#endif
|
|
return vsf_sysutil_fork_failok();
|
|
}
|
|
|
|
int
|
|
vsf_sysutil_fork_newnet()
|
|
{
|
|
#ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
|
|
static int cloneflags_work = 1;
|
|
if (cloneflags_work)
|
|
{
|
|
int ret = clone(NULL, NULL, CLONE_NEWNET | SIGCHLD, NULL);
|
|
if (ret != -1 || (errno != EINVAL && errno != EPERM))
|
|
{
|
|
if (ret == 0)
|
|
{
|
|
vsf_sysutil_post_fork();
|
|
}
|
|
return ret;
|
|
}
|
|
cloneflags_work = 0;
|
|
}
|
|
#endif
|
|
return vsf_sysutil_fork();
|
|
}
|
|
|
|
int
|
|
vsf_sysutil_getpid_nocache(void)
|
|
{
|
|
#ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
|
|
/* Need to defeat the glibc pid caching because we need to hit a raw
|
|
* sys_clone() above.
|
|
*/
|
|
return syscall(__NR_getpid);
|
|
#else
|
|
return getpid();
|
|
#endif
|
|
}
|