vsftpd 2.0.7 - initial checkin.
[tomato.git] / release / src / router / vsftpd / sysdeputil.c
blob3784fef07d942566c8077c6403ed9468f9810500
1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Chris Evans
5 * sysdeputil.c
7 * Highly system dependent utilities - e.g. authentication, capabilities.
8 */
10 #include "sysdeputil.h"
11 #include "str.h"
12 #include "sysutil.h"
13 #include "utility.h"
14 #include "secbuf.h"
15 #include "defs.h"
16 #include "tunables.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
26 #endif
28 /* For INT_MAX */
29 #include <limits.h>
31 /* For fd passing */
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 /* For FreeBSD */
35 #include <sys/param.h>
36 #include <sys/uio.h>
38 /* Configuration.. here are the possibilities */
39 #undef VSF_SYSDEP_HAVE_CAPABILITIES
40 #undef VSF_SYSDEP_HAVE_SETKEEPCAPS
41 #undef VSF_SYSDEP_HAVE_LINUX_SENDFILE
42 #undef VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
43 #undef VSF_SYSDEP_HAVE_HPUX_SENDFILE
44 #undef VSF_SYSDEP_HAVE_AIX_SENDFILE
45 #undef VSF_SYSDEP_HAVE_SETPROCTITLE
46 #undef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
47 #undef VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
48 #undef VSF_SYSDEP_HAVE_MAP_ANON
49 #undef VSF_SYSDEP_NEED_OLD_FD_PASSING
50 #ifdef VSF_BUILD_PAM
51 #define VSF_SYSDEP_HAVE_PAM
52 #endif
53 #define VSF_SYSDEP_HAVE_SHADOW
54 #define VSF_SYSDEP_HAVE_USERSHELL
55 #define VSF_SYSDEP_HAVE_LIBCAP
56 #define VSF_SYSDEP_HAVE_UTMPX
58 #define __USE_GNU
59 #include <utmpx.h>
61 /* BEGIN config */
62 #if defined(__linux__) && !defined(__ia64__) && !defined(__s390__)
63 #define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
64 #include <linux/version.h>
65 #if defined(LINUX_VERSION_CODE) && defined(KERNEL_VERSION)
66 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
67 #define VSF_SYSDEP_HAVE_CAPABILITIES
68 #define VSF_SYSDEP_HAVE_LINUX_SENDFILE
69 #include <sys/prctl.h>
70 #ifdef PR_SET_KEEPCAPS
71 #define VSF_SYSDEP_HAVE_SETKEEPCAPS
72 #endif
73 #endif
74 #endif
75 #endif
77 #if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
78 #define VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
79 #define VSF_SYSDEP_HAVE_SETPROCTITLE
80 #endif
82 #if defined(__NetBSD__)
83 #include <stdlib.h>
84 #define VSF_SYSDEP_HAVE_SETPROCTITLE
85 #include <sys/param.h>
86 #if __NetBSD_Version__ >= 106070000
87 #define WTMPX_FILE _PATH_WTMPX
88 #else
89 #undef VSF_SYSDEP_HAVE_UTMPX
90 #endif
91 #endif
93 #ifdef __hpux
94 #include <sys/socket.h>
95 #ifdef SF_DISCONNECT
96 #define VSF_SYSDEP_HAVE_HPUX_SENDFILE
97 #endif
98 #include <sys/param.h>
99 #include <sys/pstat.h>
100 #ifdef PSTAT_SETCMD
101 #define VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
102 #endif
103 #undef VSF_SYSDEP_HAVE_UTMPX
104 #endif
106 #include <unistd.h>
107 #include <sys/mman.h>
108 #ifdef MAP_ANON
109 #define VSF_SYSDEP_HAVE_MAP_ANON
110 #endif
112 #ifdef __sgi
113 #undef VSF_SYSDEP_HAVE_USERSHELL
114 #undef VSF_SYSDEP_HAVE_LIBCAP
115 #endif
117 #ifdef _AIX
118 #undef VSF_SYSDEP_HAVE_USERSHELL
119 #undef VSF_SYSDEP_HAVE_LIBCAP
120 #undef VSF_SYSDEP_HAVE_UTMPX
121 #undef VSF_SYSDEP_HAVE_PAM
122 #undef VSF_SYSDEP_HAVE_SHADOW
123 #undef VSF_SYSDEP_HAVE_SETPROCTITLE
124 #define VSF_SYSDEP_HAVE_AIX_SENDFILE
125 #define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
126 #define VSF_SYSDEP_HAVE_MAP_ANON
127 #endif
129 #ifdef __osf__
130 #undef VSF_SYSDEP_HAVE_USERSHELL
131 #endif
133 #if (defined(__sgi) || defined(__hpux) || defined(__osf__))
134 #define VSF_SYSDEP_NEED_OLD_FD_PASSING
135 #endif
137 #ifdef __sun
138 #define VSF_SYSDEP_HAVE_SOLARIS_SENDFILE
139 #endif
140 /* END config */
142 /* PAM support - we include our own dummy version if the system lacks this */
143 #include <security/pam_appl.h>
145 /* No PAM? Try getspnam() with a getpwnam() fallback */
146 #ifndef VSF_SYSDEP_HAVE_PAM
147 /* This may hit our own "dummy" include and undef VSF_SYSDEP_HAVE_SHADOW */
148 #include <shadow.h>
149 #include <pwd.h>
150 #include <unistd.h>
151 #include <crypt.h>
152 #endif
154 /* Prefer libcap based capabilities over raw syscall capabilities */
155 #include <sys/capability.h>
157 #if defined(VSF_SYSDEP_HAVE_CAPABILITIES) && !defined(VSF_SYSDEP_HAVE_LIBCAP)
158 #include <linux/unistd.h>
159 #include <linux/capability.h>
160 #include <errno.h>
161 #include <syscall.h>
162 _syscall2(int, capset, cap_user_header_t, header, const cap_user_data_t, data)
163 /* Gross HACK to avoid warnings - linux headers overlap glibc headers */
164 #undef __NFDBITS
165 #undef __FDMASK
166 #endif /* VSF_SYSDEP_HAVE_CAPABILITIES */
168 #if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
169 defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
170 #include <sys/sendfile.h>
171 #elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
172 #include <sys/types.h>
173 #include <sys/socket.h>
174 #elif defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE)
175 #include <sys/socket.h>
176 #else /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
177 #include <unistd.h>
178 #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
180 #ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
181 #include <sys/types.h>
182 #include <unistd.h>
183 #endif
185 #ifdef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
186 extern char** environ;
187 static unsigned int s_proctitle_space = 0;
188 static int s_proctitle_inited = 0;
189 static char* s_p_proctitle = 0;
190 #endif
192 #ifndef VSF_SYSDEP_HAVE_MAP_ANON
193 #include <sys/types.h>
194 #include <sys/stat.h>
195 #include <fcntl.h>
196 static int s_zero_fd = -1;
197 #endif
199 /* File private functions/variables */
200 static int do_sendfile(const int out_fd, const int in_fd,
201 unsigned int num_send, filesize_t start_pos);
202 static void vsf_sysutil_setproctitle_internal(const char* p_text);
203 static struct mystr s_proctitle_prefix_str;
205 /* These two aren't static to avoid OpenBSD build warnings. */
206 void vsf_insert_uwtmp(const struct mystr* p_user_str,
207 const struct mystr* p_host_str);
208 void vsf_remove_uwtmp(void);
210 #ifndef VSF_SYSDEP_HAVE_PAM
212 vsf_sysdep_check_auth(const struct mystr* p_user_str,
213 const struct mystr* p_pass_str,
214 const struct mystr* p_remote_host)
216 const char* p_crypted;
217 const struct passwd* p_pwd = getpwnam(str_getbuf(p_user_str));
218 (void) p_remote_host;
219 if (p_pwd == NULL)
221 return 0;
223 #ifdef VSF_SYSDEP_HAVE_USERSHELL
224 if (tunable_check_shell)
226 const char* p_shell;
227 while ((p_shell = getusershell()) != NULL)
229 if (!vsf_sysutil_strcmp(p_shell, p_pwd->pw_shell))
231 break;
234 endusershell();
235 if (p_shell == NULL)
237 return 0;
240 #endif
241 #ifdef VSF_SYSDEP_HAVE_SHADOW
243 const struct spwd* p_spwd = getspnam(str_getbuf(p_user_str));
244 if (p_spwd != NULL)
246 long curr_time;
247 int days;
248 vsf_sysutil_update_cached_time();
249 curr_time = vsf_sysutil_get_cached_time_sec();
250 days = curr_time / (60 * 60 * 24);
251 if (p_spwd->sp_expire > 0 && p_spwd->sp_expire < days)
253 return 0;
255 if (p_spwd->sp_lstchg > 0 && p_spwd->sp_max > 0 &&
256 p_spwd->sp_lstchg + p_spwd->sp_max < days)
258 return 0;
260 p_crypted = crypt(str_getbuf(p_pass_str), p_spwd->sp_pwdp);
261 if (!vsf_sysutil_strcmp(p_crypted, p_spwd->sp_pwdp))
263 return 1;
267 #endif /* VSF_SYSDEP_HAVE_SHADOW */
268 p_crypted = crypt(str_getbuf(p_pass_str), p_pwd->pw_passwd);
269 if (!vsf_sysutil_strcmp(p_crypted, p_pwd->pw_passwd))
271 return 1;
273 return 0;
276 #else /* VSF_SYSDEP_HAVE_PAM */
278 static pam_handle_t* s_pamh;
279 static struct mystr s_pword_str;
280 static int pam_conv_func(int nmsg, const struct pam_message** p_msg,
281 struct pam_response** p_reply, void* p_addata);
282 static void vsf_auth_shutdown(void);
285 vsf_sysdep_check_auth(const struct mystr* p_user_str,
286 const struct mystr* p_pass_str,
287 const struct mystr* p_remote_host)
289 int retval;
290 struct pam_conv the_conv =
292 &pam_conv_func,
295 if (s_pamh != 0)
297 bug("vsf_sysdep_check_auth");
299 str_copy(&s_pword_str, p_pass_str);
300 retval = pam_start(tunable_pam_service_name,
301 str_getbuf(p_user_str), &the_conv, &s_pamh);
302 if (retval != PAM_SUCCESS)
304 s_pamh = 0;
305 return 0;
307 #ifdef PAM_RHOST
308 retval = pam_set_item(s_pamh, PAM_RHOST, str_getbuf(p_remote_host));
309 if (retval != PAM_SUCCESS)
311 (void) pam_end(s_pamh, 0);
312 s_pamh = 0;
313 return 0;
315 #endif
316 #ifdef PAM_TTY
317 retval = pam_set_item(s_pamh, PAM_TTY, "ftp");
318 if (retval != PAM_SUCCESS)
320 (void) pam_end(s_pamh, 0);
321 s_pamh = 0;
322 return 0;
324 #endif
325 #ifdef PAM_RUSER
326 retval = pam_set_item(s_pamh, PAM_RUSER, str_getbuf(p_user_str));
327 if (retval != PAM_SUCCESS)
329 (void) pam_end(s_pamh, 0);
330 s_pamh = 0;
331 return 0;
333 #endif
334 retval = pam_authenticate(s_pamh, 0);
335 if (retval != PAM_SUCCESS)
337 (void) pam_end(s_pamh, 0);
338 s_pamh = 0;
339 return 0;
341 retval = pam_acct_mgmt(s_pamh, 0);
342 if (retval != PAM_SUCCESS)
344 (void) pam_end(s_pamh, 0);
345 s_pamh = 0;
346 return 0;
348 retval = pam_setcred(s_pamh, PAM_ESTABLISH_CRED);
349 if (retval != PAM_SUCCESS)
351 (void) pam_end(s_pamh, 0);
352 s_pamh = 0;
353 return 0;
355 if (!tunable_session_support)
357 /* You're in already! */
358 (void) pam_end(s_pamh, 0);
359 s_pamh = 0;
360 return 1;
362 /* Must do this BEFORE opening a session for pam_limits to count us */
363 vsf_insert_uwtmp(p_user_str, p_remote_host);
364 retval = pam_open_session(s_pamh, 0);
365 if (retval != PAM_SUCCESS)
367 vsf_remove_uwtmp();
368 (void) pam_setcred(s_pamh, PAM_DELETE_CRED);
369 (void) pam_end(s_pamh, 0);
370 s_pamh = 0;
371 return 0;
373 /* We MUST ensure the PAM session, utmp, wtmp etc. are cleaned up, however
374 * we exit.
376 vsf_sysutil_set_exit_func(vsf_auth_shutdown);
377 /* You're in dude */
378 return 1;
381 static void
382 vsf_auth_shutdown(void)
384 if (s_pamh == 0)
386 bug("vsf_auth_shutdown");
388 (void) pam_close_session(s_pamh, 0);
389 (void) pam_setcred(s_pamh, PAM_DELETE_CRED);
390 (void) pam_end(s_pamh, 0);
391 s_pamh = 0;
392 vsf_remove_uwtmp();
395 static int
396 pam_conv_func(int nmsg, const struct pam_message** p_msg,
397 struct pam_response** p_reply, void* p_addata)
399 int i;
400 struct pam_response* p_resps = 0;
401 (void) p_addata;
402 if (nmsg < 0)
404 bug("dodgy nmsg in pam_conv_func");
406 p_resps = vsf_sysutil_malloc(sizeof(struct pam_response) * nmsg);
407 for (i=0; i<nmsg; i++)
409 switch (p_msg[i]->msg_style)
411 case PAM_PROMPT_ECHO_OFF:
412 p_resps[i].resp_retcode = PAM_SUCCESS;
413 p_resps[i].resp = (char*) str_strdup(&s_pword_str);
414 break;
415 case PAM_TEXT_INFO:
416 case PAM_ERROR_MSG:
417 p_resps[i].resp_retcode = PAM_SUCCESS;
418 p_resps[i].resp = 0;
419 break;
420 case PAM_PROMPT_ECHO_ON:
421 default:
422 vsf_sysutil_free(p_resps);
423 return PAM_CONV_ERR;
424 break;
427 *p_reply = p_resps;
428 return PAM_SUCCESS;
431 #endif /* VSF_SYSDEP_HAVE_PAM */
433 /* Capabilities support (or lack thereof) */
434 void
435 vsf_sysdep_keep_capabilities(void)
437 if (!vsf_sysdep_has_capabilities_as_non_root())
439 bug("asked to keep capabilities, but no support exists");
441 #ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
443 int retval = prctl(PR_SET_KEEPCAPS, 1);
444 if (vsf_sysutil_retval_is_error(retval))
446 die("prctl");
449 #endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
451 #if !defined(VSF_SYSDEP_HAVE_CAPABILITIES) && !defined(VSF_SYSDEP_HAVE_LIBCAP)
454 vsf_sysdep_has_capabilities(void)
456 return 0;
460 vsf_sysdep_has_capabilities_as_non_root(void)
462 return 0;
465 void
466 vsf_sysdep_adopt_capabilities(unsigned int caps)
468 (void) caps;
469 bug("asked to adopt capabilities, but no support exists");
472 #else /* VSF_SYSDEP_HAVE_CAPABILITIES || VSF_SYSDEP_HAVE_LIBCAP */
474 static int do_checkcap(void);
477 vsf_sysdep_has_capabilities_as_non_root(void)
479 static int s_prctl_checked;
480 static int s_runtime_prctl_works;
481 if (!s_prctl_checked)
483 #ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
484 /* Clarity: note embedded call to prctl() syscall */
485 if (!vsf_sysutil_retval_is_error(prctl(PR_SET_KEEPCAPS, 0)))
487 s_runtime_prctl_works = 1;
489 #endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
490 s_prctl_checked = 1;
492 return s_runtime_prctl_works;
496 vsf_sysdep_has_capabilities(void)
498 /* Even though compiled with capabilities, the runtime system may lack them.
499 * Also, RH7.0 kernel headers advertise a 2.4.0 box, but on a 2.2.x kernel!
501 static int s_caps_checked;
502 static int s_runtime_has_caps;
503 if (!s_caps_checked)
505 s_runtime_has_caps = do_checkcap();
506 s_caps_checked = 1;
508 return s_runtime_has_caps;
511 #ifndef VSF_SYSDEP_HAVE_LIBCAP
512 static int
513 do_checkcap(void)
515 /* EFAULT (EINVAL if page 0 mapped) vs. ENOSYS */
516 int retval = capset(0, 0);
517 if (!vsf_sysutil_retval_is_error(retval) ||
518 vsf_sysutil_get_error() != kVSFSysUtilErrNOSYS)
520 return 1;
522 return 0;
525 void
526 vsf_sysdep_adopt_capabilities(unsigned int caps)
528 /* n.b. yes I know I should be using libcap!! */
529 int retval;
530 struct __user_cap_header_struct cap_head;
531 struct __user_cap_data_struct cap_data;
532 __u32 cap_mask = 0;
533 if (!caps)
535 bug("asked to adopt no capabilities");
537 vsf_sysutil_memclr(&cap_head, sizeof(cap_head));
538 vsf_sysutil_memclr(&cap_data, sizeof(cap_data));
539 cap_head.version = _LINUX_CAPABILITY_VERSION;
540 cap_head.pid = 0;
541 if (caps & kCapabilityCAP_CHOWN)
543 cap_mask |= (1 << CAP_CHOWN);
545 if (caps & kCapabilityCAP_NET_BIND_SERVICE)
547 cap_mask |= (1 << CAP_NET_BIND_SERVICE);
549 cap_data.effective = cap_data.permitted = cap_mask;
550 cap_data.inheritable = 0;
551 retval = capset(&cap_head, &cap_data);
552 if (retval != 0)
554 die("capset");
558 #else /* VSF_SYSDEP_HAVE_LIBCAP */
559 static int
560 do_checkcap(void)
562 cap_t current_caps = cap_get_proc();
563 cap_free(current_caps);
564 if (current_caps != NULL)
566 return 1;
568 return 0;
571 void
572 vsf_sysdep_adopt_capabilities(unsigned int caps)
574 int retval;
575 cap_value_t cap_value;
576 cap_t adopt_caps = cap_init();
577 if (caps & kCapabilityCAP_CHOWN)
579 cap_value = CAP_CHOWN;
580 cap_set_flag(adopt_caps, CAP_EFFECTIVE, 1, &cap_value, CAP_SET);
581 cap_set_flag(adopt_caps, CAP_PERMITTED, 1, &cap_value, CAP_SET);
583 if (caps & kCapabilityCAP_NET_BIND_SERVICE)
585 cap_value = CAP_NET_BIND_SERVICE;
586 cap_set_flag(adopt_caps, CAP_EFFECTIVE, 1, &cap_value, CAP_SET);
587 cap_set_flag(adopt_caps, CAP_PERMITTED, 1, &cap_value, CAP_SET);
589 retval = cap_set_proc(adopt_caps);
590 if (retval != 0)
592 die("cap_set_proc");
594 cap_free(adopt_caps);
597 #endif /* !VSF_SYSDEP_HAVE_LIBCAP */
598 #endif /* VSF_SYSDEP_HAVE_CAPABILITIES || VSF_SYSDEP_HAVE_LIBCAP */
601 vsf_sysutil_sendfile(const int out_fd, const int in_fd,
602 filesize_t* p_offset, filesize_t num_send,
603 unsigned int max_chunk)
605 /* Grr - why is off_t signed? */
606 if (*p_offset < 0 || num_send < 0)
608 die("invalid offset or send count in vsf_sysutil_sendfile");
610 if (max_chunk == 0)
612 max_chunk = INT_MAX;
614 while (num_send > 0)
616 int retval;
617 unsigned int send_this_time;
618 if (num_send > max_chunk)
620 send_this_time = max_chunk;
622 else
624 send_this_time = (unsigned int) num_send;
626 /* Keep input file position in line with sendfile() calls */
627 vsf_sysutil_lseek_to(in_fd, *p_offset);
628 retval = do_sendfile(out_fd, in_fd, send_this_time, *p_offset);
629 if (vsf_sysutil_retval_is_error(retval) || retval == 0)
631 return retval;
633 num_send -= retval;
634 *p_offset += retval;
636 return 0;
639 static int do_sendfile(const int out_fd, const int in_fd,
640 unsigned int num_send, filesize_t start_pos)
642 /* Probably should one day be shared with instance in ftpdataio.c */
643 static char* p_recvbuf;
644 unsigned int total_written = 0;
645 int retval;
646 enum EVSFSysUtilError error;
647 (void) start_pos;
648 (void) error;
649 #if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
650 defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \
651 defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE) || \
652 defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) || \
653 defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
654 if (tunable_use_sendfile)
656 static int s_sendfile_checked;
657 static int s_runtime_sendfile_works;
658 if (!s_sendfile_checked || s_runtime_sendfile_works)
662 #ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE
663 retval = sendfile(out_fd, in_fd, NULL, num_send);
664 #elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
666 /* XXX - start_pos will truncate on 32-bit machines - can we
667 * say "start from current pos"?
669 off_t written = 0;
670 retval = sendfile(in_fd, out_fd, start_pos, num_send, NULL,
671 &written, 0);
672 /* Translate to Linux-like retval */
673 if (written > 0)
675 retval = (int) written;
678 #elif defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
680 size_t written = 0;
681 struct sendfilevec the_vec;
682 vsf_sysutil_memclr(&the_vec, sizeof(the_vec));
683 the_vec.sfv_fd = in_fd;
684 the_vec.sfv_off = start_pos;
685 the_vec.sfv_len = num_send;
686 retval = sendfilev(out_fd, &the_vec, 1, &written);
687 /* Translate to Linux-like retval */
688 if (written > 0)
690 retval = (int) written;
693 #elif defined(VSF_SYSDEP_HAVE_AIX_SENDFILE)
695 struct sf_parms sf_iobuf;
696 vsf_sysutil_memclr(&sf_iobuf, sizeof(sf_iobuf));
697 sf_iobuf.header_data = NULL;
698 sf_iobuf.header_length = 0;
699 sf_iobuf.trailer_data = NULL;
700 sf_iobuf.trailer_length = 0;
701 sf_iobuf.file_descriptor = in_fd;
702 sf_iobuf.file_offset = start_pos;
703 sf_iobuf.file_bytes = num_send;
705 retval = send_file((int*)&out_fd, &sf_iobuf, 0);
706 if (retval >= 0)
708 retval = sf_iobuf.bytes_sent;
711 #else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */
713 retval = sendfile(out_fd, in_fd, start_pos, num_send, NULL, 0);
715 #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
716 error = vsf_sysutil_get_error();
717 vsf_sysutil_check_pending_actions(kVSFSysUtilIO, retval, out_fd);
719 while (vsf_sysutil_retval_is_error(retval) &&
720 error == kVSFSysUtilErrINTR);
721 if (!s_sendfile_checked)
723 s_sendfile_checked = 1;
724 if (!vsf_sysutil_retval_is_error(retval) ||
725 error != kVSFSysUtilErrNOSYS)
727 s_runtime_sendfile_works = 1;
730 if (!vsf_sysutil_retval_is_error(retval))
732 return retval;
734 if (s_runtime_sendfile_works && error != kVSFSysUtilErrINVAL &&
735 error != kVSFSysUtilErrOPNOTSUPP)
737 return retval;
739 /* Fall thru to normal implementation. We won't check again. NOTE -
740 * also falls through if sendfile() is OK but it returns EINVAL. For
741 * Linux this means the file was not page cache backed. Original
742 * complaint was trying to serve files from an NTFS filesystem!
746 #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE || VSF_SYSDEP_HAVE_FREEBSD_SENDFILE */
747 if (p_recvbuf == 0)
749 vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE);
751 while (1)
753 unsigned int num_read;
754 unsigned int num_written;
755 unsigned int num_read_this_time = VSFTP_DATA_BUFSIZE;
756 if (num_read_this_time > num_send)
758 num_read_this_time = num_send;
760 retval = vsf_sysutil_read(in_fd, p_recvbuf, num_read_this_time);
761 if (retval < 0)
763 return retval;
765 else if (retval == 0)
767 return -1;
769 num_read = (unsigned int) retval;
770 retval = vsf_sysutil_write_loop(out_fd, p_recvbuf, num_read);
771 if (retval < 0)
773 return retval;
775 num_written = (unsigned int) retval;
776 total_written += num_written;
777 if (num_written != num_read)
779 return num_written;
781 if (num_written > num_send)
783 bug("num_written bigger than num_send in do_sendfile");
785 num_send -= num_written;
786 if (num_send == 0)
788 /* Bingo! */
789 return total_written;
794 void
795 vsf_sysutil_set_proctitle_prefix(const struct mystr* p_str)
797 str_copy(&s_proctitle_prefix_str, p_str);
800 /* This delegation is common to all setproctitle() implementations */
801 void
802 vsf_sysutil_setproctitle_str(const struct mystr* p_str)
804 vsf_sysutil_setproctitle(str_getbuf(p_str));
807 void
808 vsf_sysutil_setproctitle(const char* p_text)
810 struct mystr proctitle_str = INIT_MYSTR;
811 str_copy(&proctitle_str, &s_proctitle_prefix_str);
812 if (!str_isempty(&proctitle_str))
814 str_append_text(&proctitle_str, ": ");
816 str_append_text(&proctitle_str, p_text);
817 vsf_sysutil_setproctitle_internal(str_getbuf(&proctitle_str));
818 str_free(&proctitle_str);
821 #ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
822 void
823 vsf_sysutil_setproctitle_init(int argc, const char* argv[])
825 (void) argc;
826 (void) argv;
829 void
830 vsf_sysutil_setproctitle_internal(const char* p_buf)
832 setproctitle("%s", p_buf);
834 #elif defined(VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE)
835 void
836 vsf_sysutil_setproctitle_init(int argc, const char* argv[])
838 (void) argc;
839 (void) argv;
842 void
843 vsf_sysutil_setproctitle_internal(const char* p_buf)
845 struct mystr proctitle_str = INIT_MYSTR;
846 union pstun p;
847 str_alloc_text(&proctitle_str, "vsftpd: ");
848 str_append_text(&proctitle_str, p_buf);
849 p.pst_command = str_getbuf(&proctitle_str);
850 pstat(PSTAT_SETCMD, p, 0, 0, 0);
851 str_free(&proctitle_str);
853 #elif defined(VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK)
854 void
855 vsf_sysutil_setproctitle_init(int argc, const char* argv[])
857 int i;
858 char** p_env = environ;
859 if (s_proctitle_inited)
861 bug("vsf_sysutil_setproctitle_init called twice");
863 s_proctitle_inited = 1;
864 if (argv[0] == 0)
866 die("no argv[0] in vsf_sysutil_setproctitle_init");
868 for (i=0; i<argc; i++)
870 s_proctitle_space += vsf_sysutil_strlen(argv[i]) + 1;
871 if (i > 0)
873 argv[i] = 0;
876 while (*p_env != 0)
878 s_proctitle_space += vsf_sysutil_strlen(*p_env) + 1;
879 p_env++;
881 /* Oops :-) */
882 environ = 0;
883 s_p_proctitle = (char*) argv[0];
884 vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
887 void
888 vsf_sysutil_setproctitle_internal(const char* p_buf)
890 struct mystr proctitle_str = INIT_MYSTR;
891 unsigned int to_copy;
892 if (!s_proctitle_inited)
894 bug("vsf_sysutil_setproctitle: not initialized");
896 vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
897 if (s_proctitle_space < 32)
899 return;
901 str_alloc_text(&proctitle_str, "vsftpd: ");
902 str_append_text(&proctitle_str, p_buf);
903 to_copy = str_getlen(&proctitle_str);
904 if (to_copy > s_proctitle_space - 1)
906 to_copy = s_proctitle_space - 1;
908 vsf_sysutil_memcpy(s_p_proctitle, str_getbuf(&proctitle_str), to_copy);
909 str_free(&proctitle_str);
910 s_p_proctitle[to_copy] = '\0';
912 #else /* VSF_SYSDEP_HAVE_SETPROCTITLE */
913 void
914 vsf_sysutil_setproctitle_init(int argc, const char* argv[])
916 (void) argc;
917 (void) argv;
920 void
921 vsf_sysutil_setproctitle_internal(const char* p_buf)
923 (void) p_buf;
925 #endif /* VSF_SYSDEP_HAVE_SETPROCTITLE */
927 #ifdef VSF_SYSDEP_HAVE_MAP_ANON
928 void
929 vsf_sysutil_map_anon_pages_init(void)
933 void*
934 vsf_sysutil_map_anon_pages(unsigned int length)
936 char* retval = mmap(0, length, PROT_READ | PROT_WRITE,
937 MAP_PRIVATE | MAP_ANON, -1, 0);
938 if (retval == MAP_FAILED)
940 die("mmap");
942 return retval;
944 #else /* VSF_SYSDEP_HAVE_MAP_ANON */
945 void
946 vsf_sysutil_map_anon_pages_init(void)
948 if (s_zero_fd != -1)
950 bug("vsf_sysutil_map_anon_pages_init called twice");
952 s_zero_fd = open("/dev/zero", O_RDWR);
953 if (s_zero_fd < 0)
955 die("could not open /dev/zero");
959 void*
960 vsf_sysutil_map_anon_pages(unsigned int length)
962 char* retval = mmap(0, length, PROT_READ | PROT_WRITE,
963 MAP_PRIVATE, s_zero_fd, 0);
964 if (retval == MAP_FAILED)
966 die("mmap");
968 return retval;
970 #endif /* VSF_SYSDEP_HAVE_MAP_ANON */
972 #ifndef VSF_SYSDEP_NEED_OLD_FD_PASSING
974 void
975 vsf_sysutil_send_fd(int sock_fd, int send_fd)
977 int retval;
978 struct msghdr msg;
979 struct cmsghdr* p_cmsg;
980 struct iovec vec;
981 char cmsgbuf[CMSG_SPACE(sizeof(send_fd))];
982 int* p_fds;
983 char sendchar = 0;
984 msg.msg_control = cmsgbuf;
985 msg.msg_controllen = sizeof(cmsgbuf);
986 p_cmsg = CMSG_FIRSTHDR(&msg);
987 p_cmsg->cmsg_level = SOL_SOCKET;
988 p_cmsg->cmsg_type = SCM_RIGHTS;
989 p_cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
990 p_fds = (int*)CMSG_DATA(p_cmsg);
991 *p_fds = send_fd;
992 msg.msg_controllen = p_cmsg->cmsg_len;
993 msg.msg_name = NULL;
994 msg.msg_namelen = 0;
995 msg.msg_iov = &vec;
996 msg.msg_iovlen = 1;
997 msg.msg_flags = 0;
998 /* "To pass file descriptors or credentials you need to send/read at
999 * least on byte" (man 7 unix)
1001 vec.iov_base = &sendchar;
1002 vec.iov_len = sizeof(sendchar);
1003 retval = sendmsg(sock_fd, &msg, 0);
1004 if (retval != 1)
1006 die("sendmsg");
1011 vsf_sysutil_recv_fd(const int sock_fd)
1013 int retval;
1014 struct msghdr msg;
1015 char recvchar;
1016 struct iovec vec;
1017 int recv_fd;
1018 char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
1019 struct cmsghdr* p_cmsg;
1020 int* p_fd;
1021 vec.iov_base = &recvchar;
1022 vec.iov_len = sizeof(recvchar);
1023 msg.msg_name = NULL;
1024 msg.msg_namelen = 0;
1025 msg.msg_iov = &vec;
1026 msg.msg_iovlen = 1;
1027 msg.msg_control = cmsgbuf;
1028 msg.msg_controllen = sizeof(cmsgbuf);
1029 msg.msg_flags = 0;
1030 /* In case something goes wrong, set the fd to -1 before the syscall */
1031 p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg));
1032 *p_fd = -1;
1033 retval = recvmsg(sock_fd, &msg, 0);
1034 if (retval != 1)
1036 die("recvmsg");
1038 p_cmsg = CMSG_FIRSTHDR(&msg);
1039 if (p_cmsg == NULL)
1041 die("no passed fd");
1043 /* We used to verify the returned cmsg_level, cmsg_type and cmsg_len here,
1044 * but Linux 2.0 totally uselessly fails to fill these in.
1046 p_fd = (int*)CMSG_DATA(p_cmsg);
1047 recv_fd = *p_fd;
1048 if (recv_fd == -1)
1050 die("no passed fd");
1052 return recv_fd;
1055 #else /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
1057 void
1058 vsf_sysutil_send_fd(int sock_fd, int send_fd)
1060 int retval;
1061 char send_char = 0;
1062 struct msghdr msg;
1063 struct iovec vec;
1064 vec.iov_base = &send_char;
1065 vec.iov_len = 1;
1066 msg.msg_name = NULL;
1067 msg.msg_namelen = 0;
1068 msg.msg_iov = &vec;
1069 msg.msg_iovlen = 1;
1070 msg.msg_accrights = (caddr_t) &send_fd;
1071 msg.msg_accrightslen = sizeof(send_fd);
1072 retval = sendmsg(sock_fd, &msg, 0);
1073 if (retval != 1)
1075 die("sendmsg");
1080 vsf_sysutil_recv_fd(int sock_fd)
1082 int retval;
1083 struct msghdr msg;
1084 struct iovec vec;
1085 char recv_char;
1086 int recv_fd = -1;
1087 vec.iov_base = &recv_char;
1088 vec.iov_len = 1;
1089 msg.msg_name = NULL;
1090 msg.msg_namelen = 0;
1091 msg.msg_iov = &vec;
1092 msg.msg_iovlen = 1;
1093 msg.msg_accrights = (caddr_t) &recv_fd;
1094 msg.msg_accrightslen = sizeof(recv_fd);
1095 retval = recvmsg(sock_fd, &msg, 0);
1096 if (retval != 1)
1098 die("recvmsg");
1100 if (recv_fd == -1)
1102 die("no passed fd");
1104 return recv_fd;
1107 #endif /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
1109 #ifndef VSF_SYSDEP_HAVE_UTMPX
1111 void
1112 vsf_insert_uwtmp(const struct mystr* p_user_str,
1113 const struct mystr* p_host_str)
1115 (void) p_user_str;
1116 (void) p_host_str;
1119 void
1120 vsf_remove_uwtmp(void)
1124 #else /* !VSF_SYSDEP_HAVE_UTMPX */
1126 /* IMHO, the pam_unix module REALLY should be doing this in its SM component */
1127 /* Statics */
1128 static int s_uwtmp_inserted;
1129 static struct utmpx s_utent;
1131 void
1132 vsf_insert_uwtmp(const struct mystr* p_user_str,
1133 const struct mystr* p_host_str)
1135 if (sizeof(s_utent.ut_line) < 16)
1137 return;
1139 if (s_uwtmp_inserted)
1141 bug("vsf_insert_uwtmp");
1144 struct mystr line_str = INIT_MYSTR;
1145 str_alloc_text(&line_str, "vsftpd:");
1146 str_append_ulong(&line_str, vsf_sysutil_getpid());
1147 if (str_getlen(&line_str) >= sizeof(s_utent.ut_line))
1149 str_free(&line_str);
1150 return;
1152 vsf_sysutil_strcpy(s_utent.ut_line, str_getbuf(&line_str),
1153 sizeof(s_utent.ut_line));
1154 str_free(&line_str);
1156 s_uwtmp_inserted = 1;
1157 s_utent.ut_type = USER_PROCESS;
1158 s_utent.ut_pid = vsf_sysutil_getpid();
1159 vsf_sysutil_strcpy(s_utent.ut_user, str_getbuf(p_user_str),
1160 sizeof(s_utent.ut_user));
1161 vsf_sysutil_strcpy(s_utent.ut_host, str_getbuf(p_host_str),
1162 sizeof(s_utent.ut_host));
1163 vsf_sysutil_update_cached_time();
1164 s_utent.ut_tv.tv_sec = vsf_sysutil_get_cached_time_sec();
1165 setutxent();
1166 (void) pututxline(&s_utent);
1167 endutxent();
1168 updwtmpx(WTMPX_FILE, &s_utent);
1171 void
1172 vsf_remove_uwtmp(void)
1174 if (!s_uwtmp_inserted)
1176 return;
1178 s_uwtmp_inserted = 0;
1179 s_utent.ut_type = DEAD_PROCESS;
1180 vsf_sysutil_memclr(s_utent.ut_user, sizeof(s_utent.ut_user));
1181 vsf_sysutil_memclr(s_utent.ut_host, sizeof(s_utent.ut_host));
1182 s_utent.ut_tv.tv_sec = 0;
1183 setutxent();
1184 (void) pututxline(&s_utent);
1185 endutxent();
1186 vsf_sysutil_update_cached_time();
1187 s_utent.ut_tv.tv_sec = vsf_sysutil_get_cached_time_sec();
1188 updwtmpx(WTMPX_FILE, &s_utent);
1191 #endif /* !VSF_SYSDEP_HAVE_UTMPX */