📄 sysdeputil.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#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__) && !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_HACKextern 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_PAMintvsf_sysdep_check_auth(const 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; int days; vsf_sysutil_update_cached_time(); curr_time = vsf_sysutil_get_cached_time_sec(); 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 */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);intvsf_sysdep_check_auth(const struct mystr* p_user_str, const struct mystr* p_pass_str, const struct mystr* p_remote_host){ int retval; 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); 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; } 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 voidvsf_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 intpam_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) */voidvsf_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)intvsf_sysdep_has_capabilities(void){ return 0;}intvsf_sysdep_has_capabilities_as_non_root(void){ return 0;}voidvsf_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);intvsf_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;}intvsf_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_LIBCAPstatic intdo_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;}voidvsf_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 intdo_checkcap(void){ cap_t current_caps = cap_get_proc(); cap_free(current_caps); if (current_caps != NULL) { return 1; } return 0;}voidvsf_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 */intvsf_sysutil_sendfile(const int out_fd, const int in_fd, filesize_t* p_offset, filesize_t num_send, unsigned int max_chunk)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -