1 /*
2  * Part of Very Secure FTPd
3  * Licence: GPL v2
4  * Author: Chris Evans
5  * postprivparent.c
6  *
7  * This file contains all privileged parent services offered after logging
8  * in. This includes e.g. chown() of uploaded files, issuing of port 20
9  * sockets.
10  */
11 
12 #include "postprivparent.h"
13 #include "session.h"
14 #include "privops.h"
15 #include "privsock.h"
16 #include "utility.h"
17 #include "tunables.h"
18 #include "defs.h"
19 #include "sysutil.h"
20 #include "str.h"
21 #include "secutil.h"
22 #include "sysstr.h"
23 #include "sysdeputil.h"
24 #include "seccompsandbox.h"
25 
26 static void minimize_privilege(struct vsf_session* p_sess);
27 static void process_post_login_req(struct vsf_session* p_sess);
28 static void cmd_process_chown(struct vsf_session* p_sess);
29 static void cmd_process_get_data_sock(struct vsf_session* p_sess);
30 static void cmd_process_pasv_cleanup(struct vsf_session* p_sess);
31 static void cmd_process_pasv_active(struct vsf_session* p_sess);
32 static void cmd_process_pasv_listen(struct vsf_session* p_sess);
33 static void cmd_process_pasv_accept(struct vsf_session* p_sess);
34 
35 void
vsf_priv_parent_postlogin(struct vsf_session * p_sess)36 vsf_priv_parent_postlogin(struct vsf_session* p_sess)
37 {
38   minimize_privilege(p_sess);
39   /* We're still here... */
40   while (1)
41   {
42     process_post_login_req(p_sess);
43   }
44 }
45 
46 static void
process_post_login_req(struct vsf_session * p_sess)47 process_post_login_req(struct vsf_session* p_sess)
48 {
49   char cmd;
50   /* Blocks */
51   cmd = priv_sock_get_cmd(p_sess->parent_fd);
52   if (tunable_chown_uploads && cmd == PRIV_SOCK_CHOWN)
53   {
54     cmd_process_chown(p_sess);
55   }
56   else if (cmd == PRIV_SOCK_GET_DATA_SOCK)
57   {
58     cmd_process_get_data_sock(p_sess);
59   }
60   else if (cmd == PRIV_SOCK_PASV_CLEANUP)
61   {
62     cmd_process_pasv_cleanup(p_sess);
63   }
64   else if (cmd == PRIV_SOCK_PASV_ACTIVE)
65   {
66     cmd_process_pasv_active(p_sess);
67   }
68   else if (cmd == PRIV_SOCK_PASV_LISTEN)
69   {
70     cmd_process_pasv_listen(p_sess);
71   }
72   else if (cmd == PRIV_SOCK_PASV_ACCEPT)
73   {
74     cmd_process_pasv_accept(p_sess);
75   }
76   else
77   {
78     die("bad request in process_post_login_req");
79   }
80 }
81 
82 static void
minimize_privilege(struct vsf_session * p_sess)83 minimize_privilege(struct vsf_session* p_sess)
84 {
85   /* So, we logged in and forked a totally unprivileged child. Our job
86    * now is to minimize the privilege we need in order to act as a helper
87    * to the child.
88    */
89   if (!p_sess->is_anonymous && tunable_session_support)
90   {
91     /* Need to hang around to update logs, utmp, wtmp etc. on logout.
92      * Need to keep privs to do this. */
93     return;
94   }
95   {
96     unsigned int caps = 0;
97     struct mystr user_str = INIT_MYSTR;
98     struct mystr dir_str = INIT_MYSTR;
99     if (tunable_nopriv_user)
100     {
101       str_alloc_text(&user_str, tunable_nopriv_user);
102     }
103     if (tunable_secure_chroot_dir)
104     {
105       str_alloc_text(&dir_str, tunable_secure_chroot_dir);
106     }
107     if (tunable_chown_uploads)
108     {
109       caps |= kCapabilityCAP_CHOWN;
110     }
111     if (tunable_connect_from_port_20)
112     {
113       caps |= kCapabilityCAP_NET_BIND_SERVICE;
114     }
115     vsf_secutil_change_credentials(&user_str, &dir_str, 0, caps,
116                                    VSF_SECUTIL_OPTION_CHROOT);
117     str_free(&user_str);
118     str_free(&dir_str);
119   }
120   seccomp_sandbox_init();
121   seccomp_sandbox_setup_postlogin_broker();
122   seccomp_sandbox_lockdown();
123 }
124 
125 static void
cmd_process_chown(struct vsf_session * p_sess)126 cmd_process_chown(struct vsf_session* p_sess)
127 {
128   int the_fd = priv_sock_recv_fd(p_sess->parent_fd);
129   vsf_privop_do_file_chown(p_sess, the_fd);
130   vsf_sysutil_close(the_fd);
131   priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
132 }
133 
134 static void
cmd_process_get_data_sock(struct vsf_session * p_sess)135 cmd_process_get_data_sock(struct vsf_session* p_sess)
136 {
137   unsigned short port = (unsigned short) priv_sock_get_int(p_sess->parent_fd);
138   int sock_fd = vsf_privop_get_ftp_port_sock(p_sess, port, 0);
139   if (sock_fd == -1)
140   {
141     priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_BAD);
142     return;
143   }
144   priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
145   priv_sock_send_fd(p_sess->parent_fd, sock_fd);
146   vsf_sysutil_close(sock_fd);
147 }
148 
149 static void
cmd_process_pasv_cleanup(struct vsf_session * p_sess)150 cmd_process_pasv_cleanup(struct vsf_session* p_sess)
151 {
152   vsf_privop_pasv_cleanup(p_sess);
153   priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
154 }
155 
156 static void
cmd_process_pasv_active(struct vsf_session * p_sess)157 cmd_process_pasv_active(struct vsf_session* p_sess)
158 {
159   int active = vsf_privop_pasv_active(p_sess);
160   priv_sock_send_int(p_sess->parent_fd, active);
161 }
162 
163 static void
cmd_process_pasv_listen(struct vsf_session * p_sess)164 cmd_process_pasv_listen(struct vsf_session* p_sess)
165 {
166   unsigned short port = vsf_privop_pasv_listen(p_sess);
167   priv_sock_send_int(p_sess->parent_fd, port);
168 }
169 
170 static void
cmd_process_pasv_accept(struct vsf_session * p_sess)171 cmd_process_pasv_accept(struct vsf_session* p_sess)
172 {
173   int fd = vsf_privop_accept_pasv(p_sess);
174   if (fd < 0)
175   {
176     priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_BAD);
177     priv_sock_send_int(p_sess->parent_fd, fd);
178     return;
179   }
180   priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
181   priv_sock_send_fd(p_sess->parent_fd, fd);
182   vsf_sysutil_close(fd);
183 }
184 
185