2 * Part of Very Secure FTPd
7 * Highly system dependent utilities - e.g. authentication, capabilities.
10 #include "sysdeputil.h"
17 #include "builddefs.h"
19 /* For Linux, this adds nothing :-) */
20 #include "port/porting_junk.h"
22 #if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
23 #define _FILE_OFFSET_BITS 64
24 #define _LARGEFILE_SOURCE 1
25 #define _LARGEFILE64_SOURCE 1
32 #include <sys/types.h>
33 #include <sys/socket.h>
35 #include <sys/param.h>
38 #include <sys/prctl.h>
41 /* Configuration.. here are the possibilities */
42 #undef VSF_SYSDEP_HAVE_CAPABILITIES
43 #undef VSF_SYSDEP_HAVE_SETKEEPCAPS
44 #undef VSF_SYSDEP_HAVE_SETPDEATHSIG
45 #undef VSF_SYSDEP_HAVE_LINUX_SENDFILE
46 #undef VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
47 #undef VSF_SYSDEP_HAVE_HPUX_SENDFILE
48 #undef VSF_SYSDEP_HAVE_AIX_SENDFILE
49 #undef VSF_SYSDEP_HAVE_SETPROCTITLE
50 #undef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
51 #undef VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
52 #undef VSF_SYSDEP_HAVE_MAP_ANON
53 #undef VSF_SYSDEP_NEED_OLD_FD_PASSING
54 #undef VSF_SYSDEP_HAVE_LINUX_CLONE
56 #define VSF_SYSDEP_HAVE_PAM
58 #define VSF_SYSDEP_HAVE_SHADOW
59 #define VSF_SYSDEP_HAVE_USERSHELL
60 #define VSF_SYSDEP_HAVE_LIBCAP
61 #define VSF_SYSDEP_HAVE_UTMPX
67 #if defined(__linux__)
70 #define VSF_SYSDEP_HAVE_LINUX_CLONE
73 #define CLONE_NEWPID 0x20000000
76 #define CLONE_NEWIPC 0x08000000
79 #define CLONE_NEWNET 0x40000000
81 #include <linux/unistd.h>
86 #if defined(__linux__) && !defined(__ia64__) && !defined(__s390__)
87 #define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
88 #include <linux/version.h>
89 #if defined(LINUX_VERSION_CODE) && defined(KERNEL_VERSION)
90 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
91 #define VSF_SYSDEP_HAVE_LINUX_SENDFILE
92 #ifdef PR_SET_KEEPCAPS
93 #define VSF_SYSDEP_HAVE_SETKEEPCAPS
95 #ifdef PR_SET_PDEATHSIG
96 #define VSF_SYSDEP_HAVE_SETPDEATHSIG
102 #if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
103 #define VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
104 #define VSF_SYSDEP_HAVE_SETPROCTITLE
107 #if defined(__NetBSD__)
109 #define VSF_SYSDEP_HAVE_SETPROCTITLE
110 #include <sys/param.h>
111 #if __NetBSD_Version__ >= 106070000
112 #define WTMPX_FILE _PATH_WTMPX
114 #undef VSF_SYSDEP_HAVE_UTMPX
119 #include <sys/socket.h>
121 #define VSF_SYSDEP_HAVE_HPUX_SENDFILE
123 #include <sys/param.h>
124 #include <sys/pstat.h>
126 #define VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
128 #undef VSF_SYSDEP_HAVE_UTMPX
132 #include <sys/mman.h>
134 #define VSF_SYSDEP_HAVE_MAP_ANON
138 #undef VSF_SYSDEP_HAVE_USERSHELL
139 #undef VSF_SYSDEP_HAVE_LIBCAP
143 #undef VSF_SYSDEP_HAVE_USERSHELL
144 #undef VSF_SYSDEP_HAVE_LIBCAP
145 #undef VSF_SYSDEP_HAVE_UTMPX
146 #undef VSF_SYSDEP_HAVE_PAM
147 #undef VSF_SYSDEP_HAVE_SHADOW
148 #undef VSF_SYSDEP_HAVE_SETPROCTITLE
149 #define VSF_SYSDEP_HAVE_AIX_SENDFILE
150 #define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
151 #define VSF_SYSDEP_HAVE_MAP_ANON
155 #undef VSF_SYSDEP_HAVE_USERSHELL
158 #if (defined(__sgi) || defined(__hpux) || defined(__osf__))
159 #define VSF_SYSDEP_NEED_OLD_FD_PASSING
163 #define VSF_SYSDEP_HAVE_SOLARIS_SENDFILE
167 /* PAM support - we include our own dummy version if the system lacks this */
168 #include <security/pam_appl.h>
170 /* No PAM? Try getspnam() with a getpwnam() fallback */
171 #ifndef VSF_SYSDEP_HAVE_PAM
172 /* This may hit our own "dummy" include and undef VSF_SYSDEP_HAVE_SHADOW */
179 /* Prefer libcap based capabilities over raw syscall capabilities */
180 #include <sys/capability.h>
182 #if defined(VSF_SYSDEP_HAVE_CAPABILITIES) && !defined(VSF_SYSDEP_HAVE_LIBCAP)
183 #include <linux/unistd.h>
184 #include <linux/capability.h>
187 int capset(cap_user_header_t header
, const cap_user_data_t data
)
189 return syscall(__NR_capset
, header
, data
);
191 /* Gross HACK to avoid warnings - linux headers overlap glibc headers */
194 #endif /* VSF_SYSDEP_HAVE_CAPABILITIES */
196 #if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
197 defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
198 #include <sys/sendfile.h>
199 #elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
200 #include <sys/types.h>
201 #include <sys/socket.h>
202 #elif defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE)
203 #include <sys/socket.h>
204 #else /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
206 #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
208 #ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
209 #include <sys/types.h>
213 #ifdef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
214 extern char** environ
;
215 static unsigned int s_proctitle_space
= 0;
216 static int s_proctitle_inited
= 0;
217 static char* s_p_proctitle
= 0;
220 #ifndef VSF_SYSDEP_HAVE_MAP_ANON
221 #include <sys/types.h>
222 #include <sys/stat.h>
224 static int s_zero_fd
= -1;
227 /* File private functions/variables */
228 static int do_sendfile(const int out_fd
, const int in_fd
,
229 unsigned int num_send
, filesize_t start_pos
);
230 static void vsf_sysutil_setproctitle_internal(const char* p_text
);
231 static struct mystr s_proctitle_prefix_str
;
233 /* These two aren't static to avoid OpenBSD build warnings. */
234 void vsf_insert_uwtmp(const struct mystr
* p_user_str
,
235 const struct mystr
* p_host_str
);
236 void vsf_remove_uwtmp(void);
238 #ifndef VSF_SYSDEP_HAVE_PAM
240 vsf_sysdep_check_auth(struct mystr
* p_user_str
,
241 const struct mystr
* p_pass_str
,
242 const struct mystr
* p_remote_host
)
244 const char* p_crypted
;
245 const struct passwd
* p_pwd
= (struct passwd
*) vsf_sysutil_getpwnam(str_getbuf(p_user_str
));
246 (void) p_remote_host
;
251 #ifdef VSF_SYSDEP_HAVE_USERSHELL
252 if (tunable_check_shell
)
255 while ((p_shell
= getusershell()) != NULL
)
257 if (!vsf_sysutil_strcmp(p_shell
, p_pwd
->pw_shell
))
269 #ifdef VSF_SYSDEP_HAVE_SHADOW
271 const struct spwd
* p_spwd
= getspnam(str_getbuf(p_user_str
));
274 long curr_time
= vsf_sysutil_get_time_sec();
276 days
= curr_time
/ (60 * 60 * 24);
277 if (p_spwd
->sp_expire
> 0 && p_spwd
->sp_expire
< days
)
281 if (p_spwd
->sp_lstchg
> 0 && p_spwd
->sp_max
> 0 &&
282 p_spwd
->sp_lstchg
+ p_spwd
->sp_max
< days
)
286 p_crypted
= crypt(str_getbuf(p_pass_str
), p_spwd
->sp_pwdp
);
287 if (!vsf_sysutil_strcmp(p_crypted
, p_spwd
->sp_pwdp
))
293 #endif /* VSF_SYSDEP_HAVE_SHADOW */
294 p_crypted
= crypt(str_getbuf(p_pass_str
), p_pwd
->pw_passwd
);
295 if (!vsf_sysutil_strcmp(p_crypted
, p_pwd
->pw_passwd
))
302 #else /* VSF_SYSDEP_HAVE_PAM */
304 #if (defined(__sun) || defined(__hpux)) && \
305 !defined(LINUX_PAM) && !defined(_OPENPAM)
306 /* Sun's PAM doesn't use const here, while Linux-PAM and OpenPAM do */
309 #define lo_const const
311 typedef lo_const
void* pam_item_t
;
313 static pam_handle_t
* s_pamh
;
314 static struct mystr s_pword_str
;
315 static int pam_conv_func(int nmsg
, const struct pam_message
** p_msg
,
316 struct pam_response
** p_reply
, void* p_addata
);
317 static void vsf_auth_shutdown(void);
320 vsf_sysdep_check_auth(struct mystr
* p_user_str
,
321 const struct mystr
* p_pass_str
,
322 const struct mystr
* p_remote_host
)
326 const char* pam_user_name
= 0;
327 struct pam_conv the_conv
=
334 bug("vsf_sysdep_check_auth");
336 str_copy(&s_pword_str
, p_pass_str
);
337 retval
= pam_start(tunable_pam_service_name
,
338 str_getbuf(p_user_str
), &the_conv
, &s_pamh
);
339 if (retval
!= PAM_SUCCESS
)
345 retval
= pam_set_item(s_pamh
, PAM_RHOST
, str_getbuf(p_remote_host
));
346 if (retval
!= PAM_SUCCESS
)
348 (void) pam_end(s_pamh
, retval
);
354 retval
= pam_set_item(s_pamh
, PAM_TTY
, "ftp");
355 if (retval
!= PAM_SUCCESS
)
357 (void) pam_end(s_pamh
, retval
);
363 retval
= pam_set_item(s_pamh
, PAM_RUSER
, str_getbuf(p_user_str
));
364 if (retval
!= PAM_SUCCESS
)
366 (void) pam_end(s_pamh
, retval
);
371 retval
= pam_authenticate(s_pamh
, 0);
372 if (retval
!= PAM_SUCCESS
)
374 (void) pam_end(s_pamh
, retval
);
379 retval
= pam_get_item(s_pamh
, PAM_USER
, &item
);
380 if (retval
!= PAM_SUCCESS
)
382 (void) pam_end(s_pamh
, retval
);
386 pam_user_name
= item
;
387 str_alloc_text(p_user_str
, pam_user_name
);
389 retval
= pam_acct_mgmt(s_pamh
, 0);
390 if (retval
!= PAM_SUCCESS
)
392 (void) pam_end(s_pamh
, retval
);
396 retval
= pam_setcred(s_pamh
, PAM_ESTABLISH_CRED
);
397 if (retval
!= PAM_SUCCESS
)
399 (void) pam_end(s_pamh
, retval
);
403 if (!tunable_session_support
)
405 /* You're in already! */
406 (void) pam_end(s_pamh
, retval
);
410 /* Must do this BEFORE opening a session for pam_limits to count us */
411 vsf_insert_uwtmp(p_user_str
, p_remote_host
);
412 retval
= pam_open_session(s_pamh
, 0);
413 if (retval
!= PAM_SUCCESS
)
416 (void) pam_setcred(s_pamh
, PAM_DELETE_CRED
);
417 (void) pam_end(s_pamh
, retval
);
421 /* We MUST ensure the PAM session, utmp, wtmp etc. are cleaned up, however
424 vsf_sysutil_set_exit_func(vsf_auth_shutdown
);
430 vsf_auth_shutdown(void)
434 bug("vsf_auth_shutdown");
436 (void) pam_close_session(s_pamh
, 0);
437 (void) pam_setcred(s_pamh
, PAM_DELETE_CRED
);
438 (void) pam_end(s_pamh
, PAM_SUCCESS
);
444 pam_conv_func(int nmsg
, const struct pam_message
** p_msg
,
445 struct pam_response
** p_reply
, void* p_addata
)
448 struct pam_response
* p_resps
= 0;
452 bug("dodgy nmsg in pam_conv_func");
454 p_resps
= vsf_sysutil_malloc(sizeof(struct pam_response
) * nmsg
);
455 for (i
=0; i
<nmsg
; i
++)
457 switch (p_msg
[i
]->msg_style
)
459 case PAM_PROMPT_ECHO_OFF
:
460 p_resps
[i
].resp_retcode
= PAM_SUCCESS
;
461 p_resps
[i
].resp
= (char*) str_strdup(&s_pword_str
);
465 p_resps
[i
].resp_retcode
= PAM_SUCCESS
;
468 case PAM_PROMPT_ECHO_ON
:
470 vsf_sysutil_free(p_resps
);
479 #endif /* VSF_SYSDEP_HAVE_PAM */
481 /* Capabilities support (or lack thereof) */
483 vsf_sysdep_keep_capabilities(void)
485 if (!vsf_sysdep_has_capabilities_as_non_root())
487 bug("asked to keep capabilities, but no support exists");
489 #ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
491 int retval
= prctl(PR_SET_KEEPCAPS
, 1);
492 if (vsf_sysutil_retval_is_error(retval
))
497 #endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
499 #if !defined(VSF_SYSDEP_HAVE_CAPABILITIES) && !defined(VSF_SYSDEP_HAVE_LIBCAP)
502 vsf_sysdep_has_capabilities(void)
508 vsf_sysdep_has_capabilities_as_non_root(void)
514 vsf_sysdep_adopt_capabilities(unsigned int caps
)
517 bug("asked to adopt capabilities, but no support exists");
520 #else /* VSF_SYSDEP_HAVE_CAPABILITIES || VSF_SYSDEP_HAVE_LIBCAP */
522 static int do_checkcap(void);
525 vsf_sysdep_has_capabilities_as_non_root(void)
527 static int s_prctl_checked
;
528 static int s_runtime_prctl_works
;
529 if (!s_prctl_checked
)
531 #ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
532 /* Clarity: note embedded call to prctl() syscall */
533 if (!vsf_sysutil_retval_is_error(prctl(PR_SET_KEEPCAPS
, 0)))
535 s_runtime_prctl_works
= 1;
537 #endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
540 return s_runtime_prctl_works
;
544 vsf_sysdep_has_capabilities(void)
546 /* Even though compiled with capabilities, the runtime system may lack them.
547 * Also, RH7.0 kernel headers advertise a 2.4.0 box, but on a 2.2.x kernel!
549 static int s_caps_checked
;
550 static int s_runtime_has_caps
;
553 s_runtime_has_caps
= do_checkcap();
556 return s_runtime_has_caps
;
559 #ifndef VSF_SYSDEP_HAVE_LIBCAP
563 /* EFAULT (EINVAL if page 0 mapped) vs. ENOSYS */
564 int retval
= capset(0, 0);
565 if (!vsf_sysutil_retval_is_error(retval
) ||
566 vsf_sysutil_get_error() != kVSFSysUtilErrNOSYS
)
574 vsf_sysdep_adopt_capabilities(unsigned int caps
)
576 /* n.b. yes I know I should be using libcap!! */
578 struct __user_cap_header_struct cap_head
;
579 struct __user_cap_data_struct cap_data
;
583 bug("asked to adopt no capabilities");
585 vsf_sysutil_memclr(&cap_head
, sizeof(cap_head
));
586 vsf_sysutil_memclr(&cap_data
, sizeof(cap_data
));
587 cap_head
.version
= _LINUX_CAPABILITY_VERSION
;
589 if (caps
& kCapabilityCAP_CHOWN
)
591 cap_mask
|= (1 << CAP_CHOWN
);
593 if (caps
& kCapabilityCAP_NET_BIND_SERVICE
)
595 cap_mask
|= (1 << CAP_NET_BIND_SERVICE
);
597 cap_data
.effective
= cap_data
.permitted
= cap_mask
;
598 cap_data
.inheritable
= 0;
599 retval
= capset(&cap_head
, &cap_data
);
606 #else /* VSF_SYSDEP_HAVE_LIBCAP */
610 cap_t current_caps
= cap_get_proc();
611 cap_free(current_caps
);
612 if (current_caps
!= NULL
)
620 vsf_sysdep_adopt_capabilities(unsigned int caps
)
623 cap_value_t cap_value
;
624 cap_t adopt_caps
= cap_init();
625 if (caps
& kCapabilityCAP_CHOWN
)
627 cap_value
= CAP_CHOWN
;
628 cap_set_flag(adopt_caps
, CAP_EFFECTIVE
, 1, &cap_value
, CAP_SET
);
629 cap_set_flag(adopt_caps
, CAP_PERMITTED
, 1, &cap_value
, CAP_SET
);
631 if (caps
& kCapabilityCAP_NET_BIND_SERVICE
)
633 cap_value
= CAP_NET_BIND_SERVICE
;
634 cap_set_flag(adopt_caps
, CAP_EFFECTIVE
, 1, &cap_value
, CAP_SET
);
635 cap_set_flag(adopt_caps
, CAP_PERMITTED
, 1, &cap_value
, CAP_SET
);
637 retval
= cap_set_proc(adopt_caps
);
642 cap_free(adopt_caps
);
645 #endif /* !VSF_SYSDEP_HAVE_LIBCAP */
646 #endif /* VSF_SYSDEP_HAVE_CAPABILITIES || VSF_SYSDEP_HAVE_LIBCAP */
649 vsf_sysutil_sendfile(const int out_fd
, const int in_fd
,
650 filesize_t
* p_offset
, filesize_t num_send
,
651 unsigned int max_chunk
)
653 /* Grr - why is off_t signed? */
654 if (*p_offset
< 0 || num_send
< 0)
656 die("invalid offset or send count in vsf_sysutil_sendfile");
665 unsigned int send_this_time
;
666 if (num_send
> max_chunk
)
668 send_this_time
= max_chunk
;
672 send_this_time
= (unsigned int) num_send
;
674 /* Keep input file position in line with sendfile() calls */
675 vsf_sysutil_lseek_to(in_fd
, *p_offset
);
676 retval
= do_sendfile(out_fd
, in_fd
, send_this_time
, *p_offset
);
677 if (vsf_sysutil_retval_is_error(retval
) || retval
== 0)
687 static int do_sendfile(const int out_fd
, const int in_fd
,
688 unsigned int num_send
, filesize_t start_pos
)
690 /* Probably should one day be shared with instance in ftpdataio.c */
691 static char* p_recvbuf
;
692 unsigned int total_written
= 0;
694 enum EVSFSysUtilError error
;
697 #if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
698 defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \
699 defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE) || \
700 defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) || \
701 defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
702 if (tunable_use_sendfile
)
704 static int s_sendfile_checked
;
705 static int s_runtime_sendfile_works
;
706 if (!s_sendfile_checked
|| s_runtime_sendfile_works
)
710 #ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE
711 retval
= sendfile(out_fd
, in_fd
, NULL
, num_send
);
712 #elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
714 /* XXX - start_pos will truncate on 32-bit machines - can we
715 * say "start from current pos"?
718 retval
= sendfile(in_fd
, out_fd
, start_pos
, num_send
, NULL
,
720 /* Translate to Linux-like retval */
723 retval
= (int) written
;
726 #elif defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
729 struct sendfilevec the_vec
;
730 vsf_sysutil_memclr(&the_vec
, sizeof(the_vec
));
731 the_vec
.sfv_fd
= in_fd
;
732 the_vec
.sfv_off
= start_pos
;
733 the_vec
.sfv_len
= num_send
;
734 retval
= sendfilev(out_fd
, &the_vec
, 1, &written
);
735 /* Translate to Linux-like retval */
738 retval
= (int) written
;
741 #elif defined(VSF_SYSDEP_HAVE_AIX_SENDFILE)
743 struct sf_parms sf_iobuf
;
744 vsf_sysutil_memclr(&sf_iobuf
, sizeof(sf_iobuf
));
745 sf_iobuf
.header_data
= NULL
;
746 sf_iobuf
.header_length
= 0;
747 sf_iobuf
.trailer_data
= NULL
;
748 sf_iobuf
.trailer_length
= 0;
749 sf_iobuf
.file_descriptor
= in_fd
;
750 sf_iobuf
.file_offset
= start_pos
;
751 sf_iobuf
.file_bytes
= num_send
;
753 retval
= send_file((int*)&out_fd
, &sf_iobuf
, 0);
756 retval
= sf_iobuf
.bytes_sent
;
759 #else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */
761 retval
= sendfile(out_fd
, in_fd
, start_pos
, num_send
, NULL
, 0);
763 #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
764 error
= vsf_sysutil_get_error();
765 vsf_sysutil_check_pending_actions(kVSFSysUtilIO
, retval
, out_fd
);
767 while (vsf_sysutil_retval_is_error(retval
) &&
768 error
== kVSFSysUtilErrINTR
);
769 if (!s_sendfile_checked
)
771 s_sendfile_checked
= 1;
772 if (!vsf_sysutil_retval_is_error(retval
) ||
773 error
!= kVSFSysUtilErrNOSYS
)
775 s_runtime_sendfile_works
= 1;
778 if (!vsf_sysutil_retval_is_error(retval
))
782 if (s_runtime_sendfile_works
&& error
!= kVSFSysUtilErrINVAL
&&
783 error
!= kVSFSysUtilErrOPNOTSUPP
)
787 /* Fall thru to normal implementation. We won't check again. NOTE -
788 * also falls through if sendfile() is OK but it returns EINVAL. For
789 * Linux this means the file was not page cache backed. Original
790 * complaint was trying to serve files from an NTFS filesystem!
794 #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE || VSF_SYSDEP_HAVE_FREEBSD_SENDFILE */
797 vsf_secbuf_alloc(&p_recvbuf
, VSFTP_DATA_BUFSIZE
);
801 unsigned int num_read
;
802 unsigned int num_written
;
803 unsigned int num_read_this_time
= VSFTP_DATA_BUFSIZE
;
804 if (num_read_this_time
> num_send
)
806 num_read_this_time
= num_send
;
808 retval
= vsf_sysutil_read(in_fd
, p_recvbuf
, num_read_this_time
);
813 else if (retval
== 0)
817 num_read
= (unsigned int) retval
;
818 retval
= vsf_sysutil_write_loop(out_fd
, p_recvbuf
, num_read
);
823 num_written
= (unsigned int) retval
;
824 total_written
+= num_written
;
825 if (num_written
!= num_read
)
829 if (num_written
> num_send
)
831 bug("num_written bigger than num_send in do_sendfile");
833 num_send
-= num_written
;
837 return total_written
;
843 vsf_sysutil_set_proctitle_prefix(const struct mystr
* p_str
)
845 str_copy(&s_proctitle_prefix_str
, p_str
);
848 /* This delegation is common to all setproctitle() implementations */
850 vsf_sysutil_setproctitle_str(const struct mystr
* p_str
)
852 vsf_sysutil_setproctitle(str_getbuf(p_str
));
856 vsf_sysutil_setproctitle(const char* p_text
)
858 struct mystr proctitle_str
= INIT_MYSTR
;
859 str_copy(&proctitle_str
, &s_proctitle_prefix_str
);
860 if (!str_isempty(&proctitle_str
))
862 str_append_text(&proctitle_str
, ": ");
864 str_append_text(&proctitle_str
, p_text
);
865 vsf_sysutil_setproctitle_internal(str_getbuf(&proctitle_str
));
866 str_free(&proctitle_str
);
869 #ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
871 vsf_sysutil_setproctitle_init(int argc
, const char* argv
[])
878 vsf_sysutil_setproctitle_internal(const char* p_buf
)
880 setproctitle("%s", p_buf
);
882 #elif defined(VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE)
884 vsf_sysutil_setproctitle_init(int argc
, const char* argv
[])
891 vsf_sysutil_setproctitle_internal(const char* p_buf
)
893 struct mystr proctitle_str
= INIT_MYSTR
;
895 str_alloc_text(&proctitle_str
, "vsftpd: ");
896 str_append_text(&proctitle_str
, p_buf
);
897 p
.pst_command
= str_getbuf(&proctitle_str
);
898 pstat(PSTAT_SETCMD
, p
, 0, 0, 0);
899 str_free(&proctitle_str
);
901 #elif defined(VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK)
903 vsf_sysutil_setproctitle_init(int argc
, const char* argv
[])
906 char** p_env
= environ
;
907 if (s_proctitle_inited
)
909 bug("vsf_sysutil_setproctitle_init called twice");
911 s_proctitle_inited
= 1;
914 die("no argv[0] in vsf_sysutil_setproctitle_init");
916 for (i
=0; i
<argc
; i
++)
918 s_proctitle_space
+= vsf_sysutil_strlen(argv
[i
]) + 1;
926 s_proctitle_space
+= vsf_sysutil_strlen(*p_env
) + 1;
931 s_p_proctitle
= (char*) argv
[0];
932 vsf_sysutil_memclr(s_p_proctitle
, s_proctitle_space
);
936 vsf_sysutil_setproctitle_internal(const char* p_buf
)
938 struct mystr proctitle_str
= INIT_MYSTR
;
939 unsigned int to_copy
;
940 if (!s_proctitle_inited
)
942 bug("vsf_sysutil_setproctitle: not initialized");
944 vsf_sysutil_memclr(s_p_proctitle
, s_proctitle_space
);
945 if (s_proctitle_space
< 32)
949 str_alloc_text(&proctitle_str
, "vsftpd: ");
950 str_append_text(&proctitle_str
, p_buf
);
951 to_copy
= str_getlen(&proctitle_str
);
952 if (to_copy
> s_proctitle_space
- 1)
954 to_copy
= s_proctitle_space
- 1;
956 vsf_sysutil_memcpy(s_p_proctitle
, str_getbuf(&proctitle_str
), to_copy
);
957 str_free(&proctitle_str
);
958 s_p_proctitle
[to_copy
] = '\0';
960 #else /* VSF_SYSDEP_HAVE_SETPROCTITLE */
962 vsf_sysutil_setproctitle_init(int argc
, const char* argv
[])
969 vsf_sysutil_setproctitle_internal(const char* p_buf
)
973 #endif /* VSF_SYSDEP_HAVE_SETPROCTITLE */
975 #ifdef VSF_SYSDEP_HAVE_MAP_ANON
977 vsf_sysutil_map_anon_pages_init(void)
982 vsf_sysutil_map_anon_pages(unsigned int length
)
984 char* retval
= mmap(0, length
, PROT_READ
| PROT_WRITE
,
985 MAP_PRIVATE
| MAP_ANON
, -1, 0);
986 if (retval
== MAP_FAILED
)
992 #else /* VSF_SYSDEP_HAVE_MAP_ANON */
994 vsf_sysutil_map_anon_pages_init(void)
998 bug("vsf_sysutil_map_anon_pages_init called twice");
1000 s_zero_fd
= open("/dev/zero", O_RDWR
);
1003 die("could not open /dev/zero");
1008 vsf_sysutil_map_anon_pages(unsigned int length
)
1010 char* retval
= mmap(0, length
, PROT_READ
| PROT_WRITE
,
1011 MAP_PRIVATE
, s_zero_fd
, 0);
1012 if (retval
== MAP_FAILED
)
1018 #endif /* VSF_SYSDEP_HAVE_MAP_ANON */
1020 #ifndef VSF_SYSDEP_NEED_OLD_FD_PASSING
1023 vsf_sysutil_send_fd(int sock_fd
, int send_fd
)
1027 struct cmsghdr
* p_cmsg
;
1029 char cmsgbuf
[CMSG_SPACE(sizeof(send_fd
))];
1032 msg
.msg_control
= cmsgbuf
;
1033 msg
.msg_controllen
= sizeof(cmsgbuf
);
1034 p_cmsg
= CMSG_FIRSTHDR(&msg
);
1035 p_cmsg
->cmsg_level
= SOL_SOCKET
;
1036 p_cmsg
->cmsg_type
= SCM_RIGHTS
;
1037 p_cmsg
->cmsg_len
= CMSG_LEN(sizeof(send_fd
));
1038 p_fds
= (int*)CMSG_DATA(p_cmsg
);
1040 msg
.msg_controllen
= p_cmsg
->cmsg_len
;
1041 msg
.msg_name
= NULL
;
1042 msg
.msg_namelen
= 0;
1046 /* "To pass file descriptors or credentials you need to send/read at
1047 * least on byte" (man 7 unix)
1049 vec
.iov_base
= &sendchar
;
1050 vec
.iov_len
= sizeof(sendchar
);
1051 retval
= sendmsg(sock_fd
, &msg
, 0);
1059 vsf_sysutil_recv_fd(const int sock_fd
)
1066 char cmsgbuf
[CMSG_SPACE(sizeof(recv_fd
))];
1067 struct cmsghdr
* p_cmsg
;
1069 vec
.iov_base
= &recvchar
;
1070 vec
.iov_len
= sizeof(recvchar
);
1071 msg
.msg_name
= NULL
;
1072 msg
.msg_namelen
= 0;
1075 msg
.msg_control
= cmsgbuf
;
1076 msg
.msg_controllen
= sizeof(cmsgbuf
);
1078 /* In case something goes wrong, set the fd to -1 before the syscall */
1079 p_fd
= (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg
));
1081 retval
= recvmsg(sock_fd
, &msg
, 0);
1086 p_cmsg
= CMSG_FIRSTHDR(&msg
);
1089 die("no passed fd");
1091 /* We used to verify the returned cmsg_level, cmsg_type and cmsg_len here,
1092 * but Linux 2.0 totally uselessly fails to fill these in.
1094 p_fd
= (int*)CMSG_DATA(p_cmsg
);
1098 die("no passed fd");
1103 #else /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
1106 vsf_sysutil_send_fd(int sock_fd
, int send_fd
)
1112 vec
.iov_base
= &send_char
;
1114 msg
.msg_name
= NULL
;
1115 msg
.msg_namelen
= 0;
1118 msg
.msg_accrights
= (caddr_t
) &send_fd
;
1119 msg
.msg_accrightslen
= sizeof(send_fd
);
1120 retval
= sendmsg(sock_fd
, &msg
, 0);
1128 vsf_sysutil_recv_fd(int sock_fd
)
1135 vec
.iov_base
= &recv_char
;
1137 msg
.msg_name
= NULL
;
1138 msg
.msg_namelen
= 0;
1141 msg
.msg_accrights
= (caddr_t
) &recv_fd
;
1142 msg
.msg_accrightslen
= sizeof(recv_fd
);
1143 retval
= recvmsg(sock_fd
, &msg
, 0);
1150 die("no passed fd");
1155 #endif /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
1157 #ifndef VSF_SYSDEP_HAVE_UTMPX
1160 vsf_insert_uwtmp(const struct mystr
* p_user_str
,
1161 const struct mystr
* p_host_str
)
1168 vsf_remove_uwtmp(void)
1172 #else /* !VSF_SYSDEP_HAVE_UTMPX */
1174 /* IMHO, the pam_unix module REALLY should be doing this in its SM component */
1176 static int s_uwtmp_inserted
;
1177 static struct utmpx s_utent
;
1180 vsf_insert_uwtmp(const struct mystr
* p_user_str
,
1181 const struct mystr
* p_host_str
)
1183 if (sizeof(s_utent
.ut_line
) < 16)
1187 if (s_uwtmp_inserted
)
1189 bug("vsf_insert_uwtmp");
1192 struct mystr line_str
= INIT_MYSTR
;
1193 str_alloc_text(&line_str
, "vsftpd:");
1194 str_append_ulong(&line_str
, vsf_sysutil_getpid());
1195 if (str_getlen(&line_str
) >= sizeof(s_utent
.ut_line
))
1197 str_free(&line_str
);
1200 vsf_sysutil_strcpy(s_utent
.ut_line
, str_getbuf(&line_str
),
1201 sizeof(s_utent
.ut_line
));
1202 str_free(&line_str
);
1204 s_uwtmp_inserted
= 1;
1205 s_utent
.ut_type
= USER_PROCESS
;
1206 s_utent
.ut_pid
= vsf_sysutil_getpid();
1207 vsf_sysutil_strcpy(s_utent
.ut_user
, str_getbuf(p_user_str
),
1208 sizeof(s_utent
.ut_user
));
1209 vsf_sysutil_strcpy(s_utent
.ut_host
, str_getbuf(p_host_str
),
1210 sizeof(s_utent
.ut_host
));
1211 s_utent
.ut_tv
.tv_sec
= vsf_sysutil_get_time_sec();
1213 (void) pututxline(&s_utent
);
1215 updwtmpx(WTMPX_FILE
, &s_utent
);
1219 vsf_remove_uwtmp(void)
1221 if (!s_uwtmp_inserted
)
1225 s_uwtmp_inserted
= 0;
1226 s_utent
.ut_type
= DEAD_PROCESS
;
1227 vsf_sysutil_memclr(s_utent
.ut_user
, sizeof(s_utent
.ut_user
));
1228 vsf_sysutil_memclr(s_utent
.ut_host
, sizeof(s_utent
.ut_host
));
1229 s_utent
.ut_tv
.tv_sec
= 0;
1231 (void) pututxline(&s_utent
);
1233 s_utent
.ut_tv
.tv_sec
= vsf_sysutil_get_time_sec();
1234 updwtmpx(WTMPX_FILE
, &s_utent
);
1237 #endif /* !VSF_SYSDEP_HAVE_UTMPX */
1240 vsf_set_die_if_parent_dies()
1242 #ifdef VSF_SYSDEP_HAVE_SETPDEATHSIG
1243 if (prctl(PR_SET_PDEATHSIG
, SIGKILL
, 0, 0, 0) != 0)
1251 vsf_set_term_if_parent_dies()
1253 #ifdef VSF_SYSDEP_HAVE_SETPDEATHSIG
1254 if (prctl(PR_SET_PDEATHSIG
, SIGTERM
, 0, 0, 0) != 0)
1262 vsf_sysutil_fork_isolate_all_failok()
1264 #ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
1265 static int cloneflags_work
= 1;
1266 if (cloneflags_work
)
1268 int ret
= syscall(__NR_clone
,
1269 CLONE_NEWPID
| CLONE_NEWIPC
| CLONE_NEWNET
| SIGCHLD
,
1271 if (ret
!= -1 || (errno
!= EINVAL
&& errno
!= EPERM
))
1275 vsf_sysutil_post_fork();
1279 cloneflags_work
= 0;
1282 return vsf_sysutil_fork_isolate_failok();
1286 vsf_sysutil_fork_isolate_failok()
1288 #ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
1289 static int cloneflags_work
= 1;
1290 if (cloneflags_work
)
1292 int ret
= syscall(__NR_clone
, CLONE_NEWPID
| CLONE_NEWIPC
| SIGCHLD
, NULL
);
1293 if (ret
!= -1 || (errno
!= EINVAL
&& errno
!= EPERM
))
1297 vsf_sysutil_post_fork();
1301 cloneflags_work
= 0;
1304 return vsf_sysutil_fork_failok();
1308 vsf_sysutil_fork_newnet()
1310 #ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
1311 static int cloneflags_work
= 1;
1312 if (cloneflags_work
)
1314 int ret
= syscall(__NR_clone
, CLONE_NEWNET
| SIGCHLD
, NULL
);
1315 if (ret
!= -1 || (errno
!= EINVAL
&& errno
!= EPERM
))
1319 vsf_sysutil_post_fork();
1323 cloneflags_work
= 0;
1326 return vsf_sysutil_fork();
1330 vsf_sysutil_getpid_nocache(void)
1332 #ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
1333 /* Need to defeat the glibc pid caching because we need to hit a raw
1334 * sys_clone() above.
1336 return syscall(__NR_getpid
);