1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/*
18 * Part of Very Secure FTPd
19 * Licence: GPL v2
20 * Author: Chris Evans
21 * sysdeputil.c
22 *
23 * Highly system dependent utilities - e.g. authentication, capabilities.
24 */
25
26#include "sysdeputil.h"
27#include "str.h"
28#include "sysutil.h"
29#include "utility.h"
30#include "secbuf.h"
31#include "defs.h"
32#include "tunables.h"
33#include "builddefs.h"
34
35/* For Linux, this adds nothing :-) */
36#include "port/porting_junk.h"
37
38#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
39  #define _FILE_OFFSET_BITS 64
40  #define _LARGEFILE_SOURCE 1
41  #define _LARGEFILE64_SOURCE 1
42#endif
43
44/* For INT_MAX */
45#include <limits.h>
46
47/* For fd passing */
48#include <sys/types.h>
49#include <sys/socket.h>
50/* For FreeBSD */
51#include <sys/param.h>
52#include <sys/uio.h>
53
54/* Configuration.. here are the possibilities */
55#undef VSF_SYSDEP_HAVE_CAPABILITIES
56#undef VSF_SYSDEP_HAVE_SETKEEPCAPS
57#undef VSF_SYSDEP_HAVE_LINUX_SENDFILE
58#undef VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
59#undef VSF_SYSDEP_HAVE_HPUX_SENDFILE
60#undef VSF_SYSDEP_HAVE_AIX_SENDFILE
61#undef VSF_SYSDEP_HAVE_SETPROCTITLE
62#undef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
63#undef VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
64#undef VSF_SYSDEP_HAVE_MAP_ANON
65#undef VSF_SYSDEP_NEED_OLD_FD_PASSING
66#ifdef VSF_BUILD_PAM
67  #define VSF_SYSDEP_HAVE_PAM
68#endif
69//#define VSF_SYSDEP_HAVE_SHADOW	// Jiahao
70//#define VSF_SYSDEP_HAVE_USERSHELL	// Jiahao
71//#define VSF_SYSDEP_HAVE_LIBCAP	// Jiahao
72//#define VSF_SYSDEP_HAVE_UTMPX		// Jiahao
73
74#define __USE_GNU
75//#include <utmpx.h>			// Jiahao
76
77/* BEGIN config */
78#if defined(__linux__) && !defined(__ia64__) && !defined(__s390__)
79  #define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
80  #include <linux/version.h>
81  #if defined(LINUX_VERSION_CODE) && defined(KERNEL_VERSION)
82    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
83      #define VSF_SYSDEP_HAVE_CAPABILITIES
84      #define VSF_SYSDEP_HAVE_LINUX_SENDFILE
85      #include <sys/prctl.h>
86      #ifdef PR_SET_KEEPCAPS
87        #define VSF_SYSDEP_HAVE_SETKEEPCAPS
88      #endif
89    #endif
90  #endif
91#endif
92
93#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
94  #define VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
95  #define VSF_SYSDEP_HAVE_SETPROCTITLE
96#endif
97
98#if defined(__NetBSD__)
99  #include <stdlib.h>
100  #define VSF_SYSDEP_HAVE_SETPROCTITLE
101  #include <sys/param.h>
102  #if __NetBSD_Version__ >= 106070000
103    #define WTMPX_FILE _PATH_WTMPX
104  #else
105    #undef VSF_SYSDEP_HAVE_UTMPX
106  #endif
107#endif
108
109#ifdef __hpux
110  #include <sys/socket.h>
111  #ifdef SF_DISCONNECT
112    #define VSF_SYSDEP_HAVE_HPUX_SENDFILE
113  #endif
114  #include <sys/param.h>
115  #include <sys/pstat.h>
116  #ifdef PSTAT_SETCMD
117    #define VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
118  #endif
119  #undef VSF_SYSDEP_HAVE_UTMPX
120#endif
121
122#include <unistd.h>
123#include <sys/mman.h>
124#ifdef MAP_ANON
125  #define VSF_SYSDEP_HAVE_MAP_ANON
126#endif
127
128#ifdef __sgi
129  #undef VSF_SYSDEP_HAVE_USERSHELL
130  #undef VSF_SYSDEP_HAVE_LIBCAP
131#endif
132
133#ifdef _AIX
134  #undef VSF_SYSDEP_HAVE_USERSHELL
135  #undef VSF_SYSDEP_HAVE_LIBCAP
136  #undef VSF_SYSDEP_HAVE_UTMPX
137  #undef VSF_SYSDEP_HAVE_PAM
138  #undef VSF_SYSDEP_HAVE_SHADOW
139  #undef VSF_SYSDEP_HAVE_SETPROCTITLE
140  #define VSF_SYSDEP_HAVE_AIX_SENDFILE
141  #define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
142  #define VSF_SYSDEP_HAVE_MAP_ANON
143#endif
144
145#ifdef __osf__
146  #undef VSF_SYSDEP_HAVE_USERSHELL
147#endif
148
149#if (defined(__sgi) || defined(__hpux) || defined(__osf__))
150  #define VSF_SYSDEP_NEED_OLD_FD_PASSING
151#endif
152
153#ifdef __sun
154  #define VSF_SYSDEP_HAVE_SOLARIS_SENDFILE
155#endif
156/* END config */
157
158/* PAM support - we include our own dummy version if the system lacks this */
159#include <security/pam_appl.h>
160
161/* No PAM? Try getspnam() with a getpwnam() fallback */
162#ifndef VSF_SYSDEP_HAVE_PAM
163/* This may hit our own "dummy" include and undef VSF_SYSDEP_HAVE_SHADOW */
164//#include <shadow.h>	// Jiahao
165#include <pwd.h>
166#include <unistd.h>
167//#include <crypt.h>	// Jiahao
168#endif
169
170/* Prefer libcap based capabilities over raw syscall capabilities */
171#include <sys/capability.h>
172
173#if defined(VSF_SYSDEP_HAVE_CAPABILITIES) && !defined(VSF_SYSDEP_HAVE_LIBCAP)
174#include <linux/unistd.h>
175#include <linux/capability.h>
176#include <errno.h>
177#include <syscall.h>
178//_syscall2(int, capset, cap_user_header_t, header, const cap_user_data_t, data)
179/* Gross HACK to avoid warnings - linux headers overlap glibc headers */
180#undef __NFDBITS
181#undef __FDMASK
182#endif /* VSF_SYSDEP_HAVE_CAPABILITIES */
183
184#if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
185    defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
186#include <sys/sendfile.h>
187#elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
188#include <sys/types.h>
189#include <sys/socket.h>
190#elif defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE)
191#include <sys/socket.h>
192#else /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
193#include <unistd.h>
194#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
195
196#ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
197#include <sys/types.h>
198#include <unistd.h>
199#endif
200
201#ifdef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
202extern char** environ;
203static unsigned int s_proctitle_space = 0;
204static int s_proctitle_inited = 0;
205static char* s_p_proctitle = 0;
206#endif
207
208#ifndef VSF_SYSDEP_HAVE_MAP_ANON
209#include <sys/types.h>
210#include <sys/stat.h>
211#include <fcntl.h>
212static int s_zero_fd = -1;
213#endif
214
215/* File private functions/variables */
216static int do_sendfile(const int out_fd, const int in_fd,
217                       unsigned int num_send, filesize_t start_pos);
218static void vsf_sysutil_setproctitle_internal(const char* p_text);
219static struct mystr s_proctitle_prefix_str;
220/*
221static void vsf_insert_uwtmp(const struct mystr* p_user_str,
222                             const struct mystr* p_host_str);
223static void vsf_remove_uwtmp(void);
224*/
225#ifndef VSF_SYSDEP_HAVE_PAM
226int	// Jiahao
227vsf_sysdep_check_auth(const struct mystr* p_user_str,
228                      const struct mystr* p_pass_str,
229                      const struct mystr* p_remote_host)
230{
231//  const char* p_crypted;
232//  const struct passwd* p_pwd = getpwnam(str_getbuf(p_user_str));
233  const struct passwd* p_pwd = (struct passwd*)vsf_sysutil_getpwnam(str_getbuf(p_user_str));
234  (void) p_remote_host;
235  if (p_pwd == NULL)
236  {
237    return 0;
238  }
239//  #ifdef VSF_SYSDEP_HAVE_USERSHELL
240/*
241  if (tunable_check_shell)
242  {
243    const char* p_shell;
244    while ((p_shell = getusershell()) != NULL)
245    {
246      if (!vsf_sysutil_strcmp(p_shell, p_pwd->pw_shell))
247      {
248        break;
249      }
250    }
251    endusershell();
252    if (p_shell == NULL)
253    {
254      return 0;
255    }
256  }
257*/
258//  #endif
259//  #ifdef VSF_SYSDEP_HAVE_SHADOW
260/*
261  {
262    const struct spwd* p_spwd = getspnam(str_getbuf(p_user_str));
263    if (p_spwd != NULL)
264    {
265      long curr_time;
266      int days;
267      vsf_sysutil_update_cached_time();
268      curr_time = vsf_sysutil_get_cached_time_sec();
269      days = curr_time / (60 * 60 * 24);
270      if (p_spwd->sp_expire > 0 && p_spwd->sp_expire < days)
271      {
272        return 0;
273      }
274      if (p_spwd->sp_lstchg > 0 && p_spwd->sp_max > 0 &&
275          p_spwd->sp_lstchg + p_spwd->sp_max < days)
276      {
277        return 0;
278      }
279      p_crypted = crypt(str_getbuf(p_pass_str), p_spwd->sp_pwdp);
280      if (!vsf_sysutil_strcmp(p_crypted, p_spwd->sp_pwdp))
281      {
282        return 1;
283      }
284    }
285  }
286*/
287//  #endif /* VSF_SYSDEP_HAVE_SHADOW */
288//  p_crypted = crypt(str_getbuf(p_pass_str), p_pwd->pw_passwd);
289//  if (!vsf_sysutil_strcmp(p_crypted, p_pwd->pw_passwd))
290  if (!vsf_sysutil_strcmp(str_getbuf(p_pass_str), p_pwd->pw_passwd))
291  {
292    return 1;
293  }
294  return 0;
295}
296
297#else /* VSF_SYSDEP_HAVE_PAM */
298
299static pam_handle_t* s_pamh;
300static struct mystr s_pword_str;
301static int pam_conv_func(int nmsg, const struct pam_message** p_msg,
302                         struct pam_response** p_reply, void* p_addata);
303static void vsf_auth_shutdown(void);
304
305int
306vsf_sysdep_check_auth(const struct mystr* p_user_str,
307                      const struct mystr* p_pass_str,
308                      const struct mystr* p_remote_host)
309{
310  int retval;
311  struct pam_conv the_conv =
312  {
313    &pam_conv_func,
314    0
315  };
316  if (s_pamh != 0)
317  {
318    bug("vsf_sysdep_check_auth");
319  }
320  str_copy(&s_pword_str, p_pass_str);
321  retval = pam_start(tunable_pam_service_name,
322                     str_getbuf(p_user_str), &the_conv, &s_pamh);
323  if (retval != PAM_SUCCESS)
324  {
325    s_pamh = 0;
326    return 0;
327  }
328#ifdef PAM_RHOST
329  retval = pam_set_item(s_pamh, PAM_RHOST, str_getbuf(p_remote_host));
330  if (retval != PAM_SUCCESS)
331  {
332    (void) pam_end(s_pamh, 0);
333    s_pamh = 0;
334    return 0;
335  }
336#endif
337  retval = pam_authenticate(s_pamh, 0);
338  if (retval != PAM_SUCCESS)
339  {
340    (void) pam_end(s_pamh, 0);
341    s_pamh = 0;
342    return 0;
343  }
344  retval = pam_acct_mgmt(s_pamh, 0);
345  if (retval != PAM_SUCCESS)
346  {
347    (void) pam_end(s_pamh, 0);
348    s_pamh = 0;
349    return 0;
350  }
351  retval = pam_setcred(s_pamh, PAM_ESTABLISH_CRED);
352  if (retval != PAM_SUCCESS)
353  {
354    (void) pam_end(s_pamh, 0);
355    s_pamh = 0;
356    return 0;
357  }
358  if (!tunable_session_support)
359  {
360    /* You're in already! */
361    (void) pam_end(s_pamh, 0);
362    s_pamh = 0;
363    return 1;
364  }
365  /* Must do this BEFORE opening a session for pam_limits to count us */
366  vsf_insert_uwtmp(p_user_str, p_remote_host);
367  retval = pam_open_session(s_pamh, 0);
368  if (retval != PAM_SUCCESS)
369  {
370    vsf_remove_uwtmp();
371    (void) pam_setcred(s_pamh, PAM_DELETE_CRED);
372    (void) pam_end(s_pamh, 0);
373    s_pamh = 0;
374    return 0;
375  }
376  /* We MUST ensure the PAM session, utmp, wtmp etc. are cleaned up, however
377   * we exit.
378   */
379  vsf_sysutil_set_exit_func(vsf_auth_shutdown);
380  /* You're in dude */
381  return 1;
382}
383
384static void
385vsf_auth_shutdown(void)
386{
387  if (s_pamh == 0)
388  {
389    bug("vsf_auth_shutdown");
390  }
391  (void) pam_close_session(s_pamh, 0);
392  (void) pam_setcred(s_pamh, PAM_DELETE_CRED);
393  (void) pam_end(s_pamh, 0);
394  s_pamh = 0;
395  vsf_remove_uwtmp();
396}
397
398static int
399pam_conv_func(int nmsg, const struct pam_message** p_msg,
400              struct pam_response** p_reply, void* p_addata)
401{
402  int i;
403  struct pam_response* p_resps = 0;
404  (void) p_addata;
405  if (nmsg < 0)
406  {
407    bug("dodgy nmsg in pam_conv_func");
408  }
409  p_resps = vsf_sysutil_malloc(sizeof(struct pam_response) * nmsg);
410  for (i=0; i<nmsg; i++)
411  {
412    switch (p_msg[i]->msg_style)
413    {
414      case PAM_PROMPT_ECHO_OFF:
415        p_resps[i].resp_retcode = PAM_SUCCESS;
416        p_resps[i].resp = (char*) str_strdup(&s_pword_str);
417        break;
418      case PAM_TEXT_INFO:
419      case PAM_ERROR_MSG:
420        p_resps[i].resp_retcode = PAM_SUCCESS;
421        p_resps[i].resp = 0;
422        break;
423      case PAM_PROMPT_ECHO_ON:
424      default:
425        vsf_sysutil_free(p_resps);
426        return PAM_CONV_ERR;
427        break;
428    }
429  }
430  *p_reply = p_resps;
431  return PAM_SUCCESS;
432}
433
434#endif /* VSF_SYSDEP_HAVE_PAM */
435
436/* Capabilities support (or lack thereof) */
437void
438vsf_sysdep_keep_capabilities(void)
439{
440  if (!vsf_sysdep_has_capabilities_as_non_root())
441  {
442    bug("asked to keep capabilities, but no support exists");
443  }
444#ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
445  {
446    int retval = prctl(PR_SET_KEEPCAPS, 1);
447    if (vsf_sysutil_retval_is_error(retval))
448    {
449      die("prctl");
450    }
451  }
452#endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
453}
454#if !defined(VSF_SYSDEP_HAVE_CAPABILITIES) && !defined(VSF_SYSDEP_HAVE_LIBCAP)
455
456int
457vsf_sysdep_has_capabilities(void)
458{
459  return 0;
460}
461
462int
463vsf_sysdep_has_capabilities_as_non_root(void)
464{
465  return 0;
466}
467
468void
469vsf_sysdep_adopt_capabilities(unsigned int caps)
470{
471  (void) caps;
472  bug("asked to adopt capabilities, but no support exists");
473}
474
475#else /* VSF_SYSDEP_HAVE_CAPABILITIES || VSF_SYSDEP_HAVE_LIBCAP */
476
477static int do_checkcap(void);
478
479int
480vsf_sysdep_has_capabilities_as_non_root(void)
481{
482  static int s_prctl_checked;
483  static int s_runtime_prctl_works;
484  if (!s_prctl_checked)
485  {
486  #ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
487    /* Clarity: note embedded call to prctl() syscall */
488    if (!vsf_sysutil_retval_is_error(prctl(PR_SET_KEEPCAPS, 0)))
489    {
490      s_runtime_prctl_works = 1;
491    }
492  #endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
493    s_prctl_checked = 1;
494  }
495  return s_runtime_prctl_works;
496}
497
498int
499vsf_sysdep_has_capabilities(void)
500{
501  /* Even though compiled with capabilities, the runtime system may lack them.
502   * Also, RH7.0 kernel headers advertise a 2.4.0 box, but on a 2.2.x kernel!
503   */
504  static int s_caps_checked;
505  static int s_runtime_has_caps;
506  if (!s_caps_checked)
507  {
508    s_runtime_has_caps = do_checkcap();
509    s_caps_checked = 1;
510  }
511  return s_runtime_has_caps;
512}
513
514  #ifndef VSF_SYSDEP_HAVE_LIBCAP
515static int
516do_checkcap(void)
517{
518  /* EFAULT (EINVAL if page 0 mapped) vs. ENOSYS */
519  int retval = capset(0, 0);
520  if (!vsf_sysutil_retval_is_error(retval) ||
521      vsf_sysutil_get_error() != kVSFSysUtilErrNOSYS)
522  {
523    return 1;
524  }
525  return 0;
526}
527
528void
529vsf_sysdep_adopt_capabilities(unsigned int caps)
530{
531  /* n.b. yes I know I should be using libcap!! */
532  int retval;
533  struct __user_cap_header_struct cap_head;
534  struct __user_cap_data_struct cap_data;
535  __u32 cap_mask = 0;
536  if (!caps)
537  {
538    bug("asked to adopt no capabilities");
539  }
540  vsf_sysutil_memclr(&cap_head, sizeof(cap_head));
541  vsf_sysutil_memclr(&cap_data, sizeof(cap_data));
542  cap_head.version = _LINUX_CAPABILITY_VERSION;
543  cap_head.pid = 0;
544  if (caps & kCapabilityCAP_CHOWN)
545  {
546    cap_mask |= (1 << CAP_CHOWN);
547  }
548  if (caps & kCapabilityCAP_NET_BIND_SERVICE)
549  {
550    cap_mask |= (1 << CAP_NET_BIND_SERVICE);
551  }
552  cap_data.effective = cap_data.permitted = cap_mask;
553  cap_data.inheritable = 0;
554  retval = capset(&cap_head, &cap_data);
555  if (retval != 0)
556  {
557    die("capset");
558  }
559}
560
561  #else /* VSF_SYSDEP_HAVE_LIBCAP */
562static int
563do_checkcap(void)
564{
565  cap_t current_caps = cap_get_proc();
566  cap_free(current_caps);
567  if (current_caps != NULL)
568  {
569    return 1;
570  }
571  return 0;
572}
573
574void
575vsf_sysdep_adopt_capabilities(unsigned int caps)
576{
577  int retval;
578  cap_value_t cap_value;
579  cap_t adopt_caps = cap_init();
580  if (caps & kCapabilityCAP_CHOWN)
581  {
582    cap_value = CAP_CHOWN;
583    cap_set_flag(adopt_caps, CAP_EFFECTIVE, 1, &cap_value, CAP_SET);
584    cap_set_flag(adopt_caps, CAP_PERMITTED, 1, &cap_value, CAP_SET);
585  }
586  if (caps & kCapabilityCAP_NET_BIND_SERVICE)
587  {
588    cap_value = CAP_NET_BIND_SERVICE;
589    cap_set_flag(adopt_caps, CAP_EFFECTIVE, 1, &cap_value, CAP_SET);
590    cap_set_flag(adopt_caps, CAP_PERMITTED, 1, &cap_value, CAP_SET);
591  }
592  retval = cap_set_proc(adopt_caps);
593  if (retval != 0)
594  {
595    die("cap_set_proc");
596  }
597  cap_free(adopt_caps);
598}
599
600  #endif /* !VSF_SYSDEP_HAVE_LIBCAP */
601#endif /* VSF_SYSDEP_HAVE_CAPABILITIES || VSF_SYSDEP_HAVE_LIBCAP */
602
603int
604vsf_sysutil_sendfile(const int out_fd, const int in_fd,
605                     filesize_t* p_offset, filesize_t num_send,
606                     unsigned int max_chunk)
607{
608  /* Grr - why is off_t signed? */
609  if (*p_offset < 0 || num_send < 0)
610  {
611    die("invalid offset or send count in vsf_sysutil_sendfile");
612  }
613  if (max_chunk == 0)
614  {
615    max_chunk = INT_MAX;
616  }
617  while (num_send > 0)
618  {
619    int retval;
620    unsigned int send_this_time;
621    if (num_send > max_chunk)
622    {
623      send_this_time = max_chunk;
624    }
625    else
626    {
627      send_this_time = (unsigned int) num_send;
628    }
629    /* Keep input file position in line with sendfile() calls */
630    vsf_sysutil_lseek_to(in_fd, *p_offset);
631    retval = do_sendfile(out_fd, in_fd, send_this_time, *p_offset);
632    if (vsf_sysutil_retval_is_error(retval) || retval == 0)
633    {
634      return retval;
635    }
636    num_send -= retval;
637    *p_offset += retval;
638  }
639  return 0;
640}
641
642static int do_sendfile(const int out_fd, const int in_fd,
643                       unsigned int num_send, filesize_t start_pos)
644{
645  /* Probably should one day be shared with instance in ftpdataio.c */
646  static char* p_recvbuf;
647  unsigned int total_written = 0;
648  int retval;
649  enum EVSFSysUtilError error;
650  (void) start_pos;
651#if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
652    defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \
653    defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE) || \
654    defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) || \
655    defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
656  if (tunable_use_sendfile)
657  {
658    static int s_sendfile_checked;
659    static int s_runtime_sendfile_works;
660    if (!s_sendfile_checked || s_runtime_sendfile_works)
661    {
662      do
663      {
664  #ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE
665        retval = sendfile(out_fd, in_fd, NULL, num_send);
666  #elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
667        {
668          /* XXX - start_pos will truncate on 32-bit machines - can we
669           * say "start from current pos"?
670           */
671          off_t written = 0;
672          retval = sendfile(in_fd, out_fd, start_pos, num_send, NULL,
673                            &written, 0);
674          /* Translate to Linux-like retval */
675          if (written > 0)
676          {
677            retval = (int) written;
678          }
679        }
680  #elif defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
681        {
682          size_t written = 0;
683          struct sendfilevec the_vec;
684          vsf_sysutil_memclr(&the_vec, sizeof(the_vec));
685          the_vec.sfv_fd = in_fd;
686          the_vec.sfv_off = start_pos;
687          the_vec.sfv_len = num_send;
688          retval = sendfilev(out_fd, &the_vec, 1, &written);
689          /* Translate to Linux-like retval */
690          if (written > 0)
691          {
692            retval = (int) written;
693          }
694        }
695  #elif defined(VSF_SYSDEP_HAVE_AIX_SENDFILE)
696        {
697          struct sf_parms sf_iobuf;
698          vsf_sysutil_memclr(&sf_iobuf, sizeof(sf_iobuf));
699          sf_iobuf.header_data = NULL;
700          sf_iobuf.header_length = 0;
701          sf_iobuf.trailer_data = NULL;
702          sf_iobuf.trailer_length = 0;
703          sf_iobuf.file_descriptor = in_fd;
704          sf_iobuf.file_offset = start_pos;
705          sf_iobuf.file_bytes = num_send;
706
707          retval = send_file((int*)&out_fd, &sf_iobuf, 0);
708          if (retval >= 0)
709          {
710            retval = sf_iobuf.bytes_sent;
711          }
712        }
713  #else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */
714        {
715          retval = sendfile(out_fd, in_fd, start_pos, num_send, NULL, 0);
716        }
717  #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
718        error = vsf_sysutil_get_error();
719        vsf_sysutil_check_pending_actions(kVSFSysUtilIO, retval, out_fd);
720      }
721      while (vsf_sysutil_retval_is_error(retval) &&
722             error == kVSFSysUtilErrINTR);
723      if (!s_sendfile_checked)
724      {
725        s_sendfile_checked = 1;
726        if (!vsf_sysutil_retval_is_error(retval) ||
727            error != kVSFSysUtilErrNOSYS)
728        {
729          s_runtime_sendfile_works = 1;
730        }
731      }
732      if (!vsf_sysutil_retval_is_error(retval))
733      {
734        return retval;
735      }
736      if (s_runtime_sendfile_works && error != kVSFSysUtilErrINVAL &&
737          error != kVSFSysUtilErrOPNOTSUPP)
738      {
739        return retval;
740      }
741      /* Fall thru to normal implementation. We won't check again. NOTE -
742       * also falls through if sendfile() is OK but it returns EINVAL. For
743       * Linux this means the file was not page cache backed. Original
744       * complaint was trying to serve files from an NTFS filesystem!
745       */
746    }
747  }
748#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE || VSF_SYSDEP_HAVE_FREEBSD_SENDFILE */
749  if (p_recvbuf == 0)
750  {
751    vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE);
752  }
753  while (1)
754  {
755    unsigned int num_read;
756    unsigned int num_written;
757    unsigned int num_read_this_time = VSFTP_DATA_BUFSIZE;
758    if (num_read_this_time > num_send)
759    {
760      num_read_this_time = num_send;
761    }
762    retval = vsf_sysutil_read(in_fd, p_recvbuf, num_read_this_time);
763    if (retval < 0)
764    {
765      return retval;
766    }
767    else if (retval == 0)
768    {
769      return -1;
770    }
771    num_read = (unsigned int) retval;
772    retval = vsf_sysutil_write_loop(out_fd, p_recvbuf, num_read);
773    if (retval < 0)
774    {
775      return retval;
776    }
777    num_written = (unsigned int) retval;
778    total_written += num_written;
779    if (num_written != num_read)
780    {
781      return num_written;
782    }
783    if (num_written > num_send)
784    {
785      bug("num_written bigger than num_send in do_sendfile");
786    }
787    num_send -= num_written;
788    if (num_send == 0)
789    {
790      /* Bingo! */
791      return total_written;
792    }
793  }
794}
795
796void
797vsf_sysutil_set_proctitle_prefix(const struct mystr* p_str)
798{
799  str_copy(&s_proctitle_prefix_str, p_str);
800}
801
802/* This delegation is common to all setproctitle() implementations */
803void
804vsf_sysutil_setproctitle_str(const struct mystr* p_str)
805{
806  vsf_sysutil_setproctitle(str_getbuf(p_str));
807}
808
809void
810vsf_sysutil_setproctitle(const char* p_text)
811{
812  struct mystr proctitle_str = INIT_MYSTR;
813  str_copy(&proctitle_str, &s_proctitle_prefix_str);
814  if (!str_isempty(&proctitle_str))
815  {
816    str_append_text(&proctitle_str, ": ");
817  }
818  str_append_text(&proctitle_str, p_text);
819  vsf_sysutil_setproctitle_internal(str_getbuf(&proctitle_str));
820  str_free(&proctitle_str);
821}
822
823#ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
824void
825vsf_sysutil_setproctitle_init(int argc, const char* argv[])
826{
827  (void) argc;
828  (void) argv;
829}
830
831void
832vsf_sysutil_setproctitle_internal(const char* p_buf)
833{
834  setproctitle("%s", p_buf);
835}
836#elif defined(VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE)
837void
838vsf_sysutil_setproctitle_init(int argc, const char* argv[])
839{
840  (void) argc;
841  (void) argv;
842}
843
844void
845vsf_sysutil_setproctitle_internal(const char* p_buf)
846{
847  struct mystr proctitle_str = INIT_MYSTR;
848  union pstun p;
849  str_alloc_text(&proctitle_str, "vsftpd: ");
850  str_append_text(&proctitle_str, p_buf);
851  p.pst_command = str_getbuf(&proctitle_str);
852  pstat(PSTAT_SETCMD, p, 0, 0, 0);
853  str_free(&proctitle_str);
854}
855#elif defined(VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK)
856void
857vsf_sysutil_setproctitle_init(int argc, const char* argv[])
858{
859  int i;
860  char** p_env = environ;
861  if (s_proctitle_inited)
862  {
863    bug("vsf_sysutil_setproctitle_init called twice");
864  }
865  s_proctitle_inited = 1;
866  if (argv[0] == 0)
867  {
868    die("no argv[0] in vsf_sysutil_setproctitle_init");
869  }
870  for (i=0; i<argc; i++)
871  {
872    s_proctitle_space += vsf_sysutil_strlen(argv[i]) + 1;
873    if (i > 0)
874    {
875      argv[i] = 0;
876    }
877  }
878  while (*p_env != 0)
879  {
880    s_proctitle_space += vsf_sysutil_strlen(*p_env) + 1;
881    p_env++;
882  }
883  /* Oops :-) */
884  environ = 0;
885  s_p_proctitle = (char*) argv[0];
886  vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
887}
888
889void
890vsf_sysutil_setproctitle_internal(const char* p_buf)
891{
892  struct mystr proctitle_str = INIT_MYSTR;
893  unsigned int to_copy;
894  if (!s_proctitle_inited)
895  {
896    bug("vsf_sysutil_setproctitle: not initialized");
897  }
898  vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
899  if (s_proctitle_space < 32)
900  {
901    return;
902  }
903  str_alloc_text(&proctitle_str, "vsftpd: ");
904  str_append_text(&proctitle_str, p_buf);
905  to_copy = str_getlen(&proctitle_str);
906  if (to_copy > s_proctitle_space - 1)
907  {
908    to_copy = s_proctitle_space - 1;
909  }
910  vsf_sysutil_memcpy(s_p_proctitle, str_getbuf(&proctitle_str), to_copy);
911  str_free(&proctitle_str);
912  s_p_proctitle[to_copy] = '\0';
913}
914#else /* VSF_SYSDEP_HAVE_SETPROCTITLE */
915void
916vsf_sysutil_setproctitle_init(int argc, const char* argv[])
917{
918  (void) argc;
919  (void) argv;
920}
921
922void
923vsf_sysutil_setproctitle_internal(const char* p_buf)
924{
925  (void) p_buf;
926}
927#endif /* VSF_SYSDEP_HAVE_SETPROCTITLE */
928
929#ifdef VSF_SYSDEP_HAVE_MAP_ANON
930void
931vsf_sysutil_map_anon_pages_init(void)
932{
933}
934
935void*
936vsf_sysutil_map_anon_pages(unsigned int length)
937{
938  char* retval = mmap(0, length, PROT_READ | PROT_WRITE,
939                      MAP_PRIVATE | MAP_ANON, -1, 0);
940  if (retval == MAP_FAILED)
941  {
942    die("mmap");
943  }
944  return retval;
945}
946#else /* VSF_SYSDEP_HAVE_MAP_ANON */
947void
948vsf_sysutil_map_anon_pages_init(void)
949{
950  if (s_zero_fd != -1)
951  {
952    bug("vsf_sysutil_map_anon_pages_init called twice");
953  }
954  s_zero_fd = open("/dev/zero", O_RDWR);
955  if (s_zero_fd < 0)
956  {
957    die("could not open /dev/zero");
958  }
959}
960
961void*
962vsf_sysutil_map_anon_pages(unsigned int length)
963{
964  char* retval = mmap(0, length, PROT_READ | PROT_WRITE,
965                      MAP_PRIVATE, s_zero_fd, 0);
966  if (retval == MAP_FAILED)
967  {
968    die("mmap");
969  }
970  return retval;
971}
972#endif /* VSF_SYSDEP_HAVE_MAP_ANON */
973
974#ifndef VSF_SYSDEP_NEED_OLD_FD_PASSING
975
976void
977vsf_sysutil_send_fd(int sock_fd, int send_fd)
978{
979  int retval;
980  struct msghdr msg;
981  struct cmsghdr* p_cmsg;
982  struct iovec vec;
983  char cmsgbuf[CMSG_SPACE(sizeof(send_fd))];
984  int* p_fds;
985  char sendchar = 0;
986  msg.msg_control = cmsgbuf;
987  msg.msg_controllen = sizeof(cmsgbuf);
988  p_cmsg = CMSG_FIRSTHDR(&msg);
989  p_cmsg->cmsg_level = SOL_SOCKET;
990  p_cmsg->cmsg_type = SCM_RIGHTS;
991  p_cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
992  p_fds = (int*)CMSG_DATA(p_cmsg);
993  *p_fds = send_fd;
994  msg.msg_controllen = p_cmsg->cmsg_len;
995  msg.msg_name = NULL;
996  msg.msg_namelen = 0;
997  msg.msg_iov = &vec;
998  msg.msg_iovlen = 1;
999  msg.msg_flags = 0;
1000  /* "To pass file descriptors or credentials you need to send/read at
1001   * least on byte" (man 7 unix)
1002   */
1003  vec.iov_base = &sendchar;
1004  vec.iov_len = sizeof(sendchar);
1005  retval = sendmsg(sock_fd, &msg, 0);
1006  if (retval != 1)
1007  {
1008    die("sendmsg");
1009  }
1010}
1011
1012int
1013vsf_sysutil_recv_fd(const int sock_fd)
1014{
1015  int retval;
1016  struct msghdr msg;
1017  char recvchar;
1018  struct iovec vec;
1019  int recv_fd;
1020  char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
1021  struct cmsghdr* p_cmsg;
1022  int* p_fd;
1023  vec.iov_base = &recvchar;
1024  vec.iov_len = sizeof(recvchar);
1025  msg.msg_name = NULL;
1026  msg.msg_namelen = 0;
1027  msg.msg_iov = &vec;
1028  msg.msg_iovlen = 1;
1029  msg.msg_control = cmsgbuf;
1030  msg.msg_controllen = sizeof(cmsgbuf);
1031  msg.msg_flags = 0;
1032  /* In case something goes wrong, set the fd to -1 before the syscall */
1033  p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg));
1034  *p_fd = -1;
1035  retval = recvmsg(sock_fd, &msg, 0);
1036  if (retval != 1)
1037  {
1038    die("recvmsg");
1039  }
1040  p_cmsg = CMSG_FIRSTHDR(&msg);
1041  if (p_cmsg == NULL)
1042  {
1043    die("no passed fd");
1044  }
1045  /* We used to verify the returned cmsg_level, cmsg_type and cmsg_len here,
1046   * but Linux 2.0 totally uselessly fails to fill these in.
1047   */
1048  p_fd = (int*)CMSG_DATA(p_cmsg);
1049  recv_fd = *p_fd;
1050  if (recv_fd == -1)
1051  {
1052    die("no passed fd");
1053  }
1054  return recv_fd;
1055}
1056
1057#else /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
1058
1059void
1060vsf_sysutil_send_fd(int sock_fd, int send_fd)
1061{
1062  int retval;
1063  char send_char = 0;
1064  struct msghdr msg;
1065  struct iovec vec;
1066  vec.iov_base = &send_char;
1067  vec.iov_len = 1;
1068  msg.msg_name = NULL;
1069  msg.msg_namelen = 0;
1070  msg.msg_iov = &vec;
1071  msg.msg_iovlen = 1;
1072  msg.msg_accrights = (caddr_t) &send_fd;
1073  msg.msg_accrightslen = sizeof(send_fd);
1074  retval = sendmsg(sock_fd, &msg, 0);
1075  if (retval != 1)
1076  {
1077    die("sendmsg");
1078  }
1079}
1080
1081int
1082vsf_sysutil_recv_fd(int sock_fd)
1083{
1084  int retval;
1085  struct msghdr msg;
1086  struct iovec vec;
1087  char recv_char;
1088  int recv_fd = -1;
1089  vec.iov_base = &recv_char;
1090  vec.iov_len = 1;
1091  msg.msg_name = NULL;
1092  msg.msg_namelen = 0;
1093  msg.msg_iov = &vec;
1094  msg.msg_iovlen = 1;
1095  msg.msg_accrights = (caddr_t) &recv_fd;
1096  msg.msg_accrightslen = sizeof(recv_fd);
1097  retval = recvmsg(sock_fd, &msg, 0);
1098  if (retval != 1)
1099  {
1100    die("recvmsg");
1101  }
1102  if (recv_fd == -1)
1103  {
1104    die("no passed fd");
1105  }
1106  return recv_fd;
1107}
1108
1109#endif /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
1110
1111#ifndef VSF_SYSDEP_HAVE_UTMPX
1112/*
1113static void
1114vsf_insert_uwtmp(const struct mystr* p_user_str,
1115                 const struct mystr* p_host_str)
1116{
1117  (void) p_user_str;
1118  (void) p_host_str;
1119}
1120
1121static void
1122vsf_remove_uwtmp(void)
1123{
1124}
1125*/
1126#else /* !VSF_SYSDEP_HAVE_UTMPX */
1127
1128/* IMHO, the pam_unix module REALLY should be doing this in its SM component */
1129/* Statics */
1130
1131static int s_uwtmp_inserted;
1132static struct utmpx s_utent;
1133
1134static void
1135vsf_insert_uwtmp(const struct mystr* p_user_str,
1136                 const struct mystr* p_host_str)
1137{
1138  if (sizeof(s_utent.ut_line) < 16)
1139  {
1140    return;
1141  }
1142  if (s_uwtmp_inserted)
1143  {
1144    bug("vsf_insert_uwtmp");
1145  }
1146  {
1147    struct mystr line_str = INIT_MYSTR;
1148    str_alloc_text(&line_str, "vsftpd:");
1149    str_append_ulong(&line_str, vsf_sysutil_getpid());
1150    if (str_getlen(&line_str) >= sizeof(s_utent.ut_line))
1151    {
1152      str_free(&line_str);
1153      return;
1154    }
1155    vsf_sysutil_strcpy(s_utent.ut_line, str_getbuf(&line_str),
1156                       sizeof(s_utent.ut_line));
1157    str_free(&line_str);
1158  }
1159  s_uwtmp_inserted = 1;
1160  s_utent.ut_type = USER_PROCESS;
1161  s_utent.ut_pid = vsf_sysutil_getpid();
1162  vsf_sysutil_strcpy(s_utent.ut_user, str_getbuf(p_user_str),
1163                     sizeof(s_utent.ut_user));
1164  vsf_sysutil_strcpy(s_utent.ut_host, str_getbuf(p_host_str),
1165                     sizeof(s_utent.ut_host));
1166  vsf_sysutil_update_cached_time();
1167  s_utent.ut_tv.tv_sec = vsf_sysutil_get_cached_time_sec();
1168  setutxent();
1169  (void) pututxline(&s_utent);
1170  endutxent();
1171  updwtmpx(WTMPX_FILE, &s_utent);
1172}
1173
1174static void
1175vsf_remove_uwtmp(void)
1176{
1177  if (!s_uwtmp_inserted)
1178  {
1179    return;
1180  }
1181  s_uwtmp_inserted = 0;
1182  s_utent.ut_type = DEAD_PROCESS;
1183  vsf_sysutil_memclr(s_utent.ut_user, sizeof(s_utent.ut_user));
1184  vsf_sysutil_memclr(s_utent.ut_host, sizeof(s_utent.ut_host));
1185  s_utent.ut_tv.tv_sec = 0;
1186  setutxent();
1187  (void) pututxline(&s_utent);
1188  endutxent();
1189  vsf_sysutil_update_cached_time();
1190  s_utent.ut_tv.tv_sec = vsf_sysutil_get_cached_time_sec();
1191  updwtmpx(WTMPX_FILE, &s_utent);
1192}
1193#endif /* !VSF_SYSDEP_HAVE_UTMPX */
1194
1195