1 Star 0 Fork 0

晓敬/vsftp

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
sysdeputil.c 31.35 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
/*
* 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 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;
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 mystr* p_user_str,
const struct mystr* p_pass_str,
const struct mystr* p_remote_host)
{
int retval = -1;
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
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)
{
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_uwtmp_inserted = 1;
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();
(void) pututxline(&s_utent);
endutxent();
updwtmpx(WTMPX_FILE, &s_utent);
}
void
vsf_remove_uwtmp(void)
{
if (!s_uwtmp_inserted)
{
return;
}
s_uwtmp_inserted = 0;
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();
(void) pututxline(&s_utent);
endutxent();
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 = syscall(__NR_clone, 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 = syscall(__NR_clone, 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
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/jinger7281/vsftp.git
git@gitee.com:jinger7281/vsftp.git
jinger7281
vsftp
vsftp
master

搜索帮助