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