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("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, "ftpz: ");
- 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("sysutil_setproctitle_init called twice");
- }
- s_proctitle_inited = 1;
- if (argv[0] == 0)
- {
- die("no argv[0] in 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("sysutil_setproctitle: not initialized");
- }
- vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
- if (s_proctitle_space < 32)
- {
- return;
- }
- str_alloc_text(&proctitle_str, "ftpz: ");
- 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("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("insert_uwtmp");
- }
- {
- struct mystr line_str = INIT_MYSTR;
- str_alloc_text(&line_str, "ftpz:");
- 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
- }
|