1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2009-2020 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, The ProFTPD Project team and other respective
20 * copyright holders give permission to link this program with OpenSSL, and
21 * distribute the resulting executable, without including the source code for
22 * OpenSSL in the source distribution.
23 */
24
25 #include "conf.h"
26
27 /* From src/main.c */
28 extern unsigned char is_master;
29
sess_cleanup(int flags)30 static void sess_cleanup(int flags) {
31
32 /* Clear the scoreboard entry. */
33 if (ServerType == SERVER_STANDALONE) {
34
35 /* For standalone daemons, we only clear the scoreboard slot if we are
36 * an exiting child process.
37 */
38
39 if (!is_master) {
40 if (pr_scoreboard_entry_del(TRUE) < 0 &&
41 errno != EINVAL &&
42 errno != ENOENT) {
43 pr_log_debug(DEBUG1, "error deleting scoreboard entry: %s",
44 strerror(errno));
45 }
46 }
47
48 } else if (ServerType == SERVER_INETD) {
49 /* For inetd-spawned daemons, we always clear the scoreboard slot. */
50 if (pr_scoreboard_entry_del(TRUE) < 0 &&
51 errno != EINVAL &&
52 errno != ENOENT) {
53 pr_log_debug(DEBUG1, "error deleting scoreboard entry: %s",
54 strerror(errno));
55 }
56 }
57
58 /* If session.user is set, we have a valid login. */
59 if (session.user != NULL &&
60 session.wtmp_log) {
61 const char *tty_name;
62
63 tty_name = pr_session_get_ttyname(session.pool);
64 log_wtmp(tty_name, "", pr_netaddr_get_sess_remote_name(),
65 pr_netaddr_get_sess_remote_addr());
66 }
67
68 /* These are necessary in order that cleanups associated with these pools
69 * (and their subpools) are properly run.
70 */
71 if (session.d) {
72 pr_inet_close(session.pool, session.d);
73 session.d = NULL;
74 }
75
76 if (session.c) {
77 pr_inet_close(session.pool, session.c);
78 session.c = NULL;
79 }
80
81 /* Run all the exit handlers */
82 pr_event_generate("core.exit", NULL);
83
84 if (!is_master ||
85 (ServerType == SERVER_INETD &&
86 !(flags & PR_SESS_END_FL_SYNTAX_CHECK))) {
87 pr_log_pri(PR_LOG_INFO, "%s session closed.",
88 pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT));
89 }
90
91 log_closesyslog();
92 }
93
pr_session_disconnect(module * m,int reason_code,const char * details)94 void pr_session_disconnect(module *m, int reason_code,
95 const char *details) {
96 int flags = 0;
97
98 session.disconnect_reason = reason_code;
99 session.disconnect_module = m;
100
101 if (details != NULL &&
102 session.notes != NULL) {
103 /* Stash any extra details in the session.notes table */
104 if (pr_table_add_dup(session.notes, "core.disconnect-details",
105 (char *) details, 0) < 0) {
106 int xerrno = errno;
107
108 if (xerrno != EEXIST) {
109 pr_log_debug(DEBUG5, "error stashing 'core.disconnect-details' in "
110 "session.notes: %s", strerror(xerrno));
111 }
112 }
113 }
114
115 if (reason_code == PR_SESS_DISCONNECT_SEGFAULT) {
116 flags |= PR_SESS_END_FL_ERROR;
117 }
118
119 pr_session_end(flags);
120 }
121
pr_session_end(int flags)122 void pr_session_end(int flags) {
123 int exitcode = 0;
124
125 sess_cleanup(flags);
126
127 if (flags & PR_SESS_END_FL_NOEXIT) {
128 return;
129 }
130
131 if (flags & PR_SESS_END_FL_ERROR) {
132 exitcode = 1;
133 }
134
135 #ifdef PR_USE_DEVEL
136 destroy_pool(session.pool);
137
138 if (is_master) {
139 main_server = NULL;
140 free_pools();
141 pr_proctitle_free();
142 }
143 #endif /* PR_USE_DEVEL */
144
145 #ifdef PR_DEVEL_PROFILE
146 /* Populating the gmon.out gprof file requires that the process exit
147 * via exit(3) or by returning from main(). Using _exit(2) doesn't allow
148 * the process the time to write its profile data out.
149 */
150 exit(exitcode);
151 #else
152 _exit(exitcode);
153 #endif /* PR_DEVEL_PROFILE */
154 }
155
pr_session_get_disconnect_reason(const char ** details)156 const char *pr_session_get_disconnect_reason(const char **details) {
157 const char *reason_str = NULL;
158
159 switch (session.disconnect_reason) {
160 case PR_SESS_DISCONNECT_UNSPECIFIED:
161 reason_str = "Unknown/unspecified";
162 break;
163
164 case PR_SESS_DISCONNECT_CLIENT_QUIT:
165 reason_str = "Quit";
166 break;
167
168 case PR_SESS_DISCONNECT_CLIENT_EOF:
169 reason_str = "Read EOF from client";
170 break;
171
172 case PR_SESS_DISCONNECT_SESSION_INIT_FAILED:
173 reason_str = "Session initialized failed";
174 break;
175
176 case PR_SESS_DISCONNECT_SIGNAL:
177 reason_str = "Terminated by signal";
178 break;
179
180 case PR_SESS_DISCONNECT_NOMEM:
181 reason_str = "Low memory";
182 break;
183
184 case PR_SESS_DISCONNECT_SERVER_SHUTDOWN:
185 reason_str = "Server shutting down";
186 break;
187
188 case PR_SESS_DISCONNECT_TIMEOUT:
189 reason_str = "Timeout exceeded";
190 break;
191
192 case PR_SESS_DISCONNECT_BANNED:
193 reason_str = "Banned";
194 break;
195
196 case PR_SESS_DISCONNECT_CONFIG_ACL:
197 reason_str = "Configured policy";
198 break;
199
200 case PR_SESS_DISCONNECT_MODULE_ACL:
201 reason_str = "Module-specific policy";
202 break;
203
204 case PR_SESS_DISCONNECT_BAD_CONFIG:
205 reason_str = "Server misconfiguration";
206 break;
207
208 case PR_SESS_DISCONNECT_BY_APPLICATION:
209 reason_str = "Application error";
210 break;
211 }
212
213 if (details != NULL) {
214 *details = pr_table_get(session.notes, "core.disconnect-details", NULL);
215 }
216
217 return reason_str;
218 }
219
pr_session_get_protocol(int flags)220 const char *pr_session_get_protocol(int flags) {
221 const char *sess_proto;
222
223 sess_proto = pr_table_get(session.notes, "protocol", NULL);
224 if (sess_proto == NULL) {
225 sess_proto = "ftp";
226 }
227
228 if (!(flags & PR_SESS_PROTO_FL_LOGOUT)) {
229 /* Return the protocol as is. */
230 return sess_proto;
231 }
232
233 /* Otherwise, we need to return either "FTP" or "SSH2", for consistency. */
234 if (strncmp(sess_proto, "ftp", 4) == 0 ||
235 strncmp(sess_proto, "ftps", 5) == 0) {
236 return "FTP";
237
238 } else if (strncmp(sess_proto, "ssh2", 5) == 0 ||
239 strncmp(sess_proto, "sftp", 5) == 0 ||
240 strncmp(sess_proto, "scp", 4) == 0 ||
241 strncmp(sess_proto, "publickey", 10) == 0) {
242 return "SSH2";
243 }
244
245 /* Should never reach here, but just in case... */
246 return "unknown";
247 }
248
pr_session_send_banner(server_rec * s,int flags)249 void pr_session_send_banner(server_rec *s, int flags) {
250 config_rec *c = NULL;
251 char *display = NULL;
252 const char *serveraddress = NULL;
253 config_rec *masq = NULL;
254
255 display = get_param_ptr(s->conf, "DisplayConnect", FALSE);
256 if (display != NULL) {
257 if (pr_display_file(display, NULL, R_220, flags) < 0) {
258 pr_log_debug(DEBUG6, "unable to display DisplayConnect file '%s': %s",
259 display, strerror(errno));
260 }
261 }
262
263 serveraddress = pr_netaddr_get_ipstr(session.c->local_addr);
264
265 masq = find_config(s->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
266 if (masq != NULL) {
267 const pr_netaddr_t *masq_addr = NULL;
268
269 if (masq->argv[0] != NULL) {
270 masq_addr = masq->argv[0];
271
272 } else {
273 const char *name;
274
275 /* Here we do a delayed lookup, to see if the configured name
276 * can be resolved yet (e.g. the network is now up); see Bug#4104.
277 */
278
279 name = masq->argv[1];
280
281 pr_log_debug(DEBUG10,
282 "performing delayed resolution of MasqueradeAddress '%s'", name);
283 masq_addr = pr_netaddr_get_addr(session.pool, name, NULL);
284 if (masq_addr != NULL) {
285 /* Stash the resolved pr_netaddr_t in the config_rec, so that other
286 * code paths will find it (within this session process).
287 */
288 masq->argv[0] = (void *) masq_addr;
289
290 } else {
291 pr_log_debug(DEBUG5, "unable to resolve '%s'", name);
292 }
293 }
294
295 if (masq_addr != NULL) {
296 serveraddress = pr_netaddr_get_ipstr(masq_addr);
297 }
298 }
299
300 c = find_config(s->conf, CONF_PARAM, "ServerIdent", FALSE);
301 if (c == NULL ||
302 *((unsigned char *) c->argv[0]) == TRUE) {
303 unsigned char *defer_welcome;
304
305 defer_welcome = get_param_ptr(s->conf, "DeferWelcome", FALSE);
306
307 if (c &&
308 c->argc > 1) {
309 const char *server_ident;
310
311 server_ident = c->argv[1];
312
313 if (strstr(server_ident, "%L") != NULL) {
314 server_ident = sreplace(session.pool, server_ident, "%L",
315 serveraddress, NULL);
316 }
317
318 if (strstr(server_ident, "%V") != NULL) {
319 server_ident = sreplace(session.pool, server_ident, "%V",
320 main_server->ServerFQDN, NULL);
321 }
322
323 if (strstr(server_ident, "%v") != NULL) {
324 server_ident = sreplace(session.pool, server_ident, "%v",
325 main_server->ServerName, NULL);
326 }
327
328 if (strstr(server_ident, "%{version}") != NULL) {
329 server_ident = sreplace(session.pool, server_ident, "%{version}",
330 PROFTPD_VERSION_TEXT, NULL);
331 }
332
333 if (flags & PR_DISPLAY_FL_SEND_NOW) {
334 pr_response_send(R_220, "%s", server_ident);
335
336 } else {
337 pr_response_add(R_220, "%s", server_ident);
338 }
339
340 } else if (defer_welcome &&
341 *defer_welcome == TRUE) {
342
343 if (flags & PR_DISPLAY_FL_SEND_NOW) {
344 pr_response_send(R_220, _("ProFTPD Server ready."));
345
346 } else {
347 pr_response_add(R_220, _("ProFTPD Server ready."));
348 }
349
350 } else {
351 if (flags & PR_DISPLAY_FL_SEND_NOW) {
352 pr_response_send(R_220, _("ProFTPD Server (%s) [%s]"), s->ServerName,
353 serveraddress);
354
355 } else {
356 pr_response_add(R_220, _("ProFTPD Server (%s) [%s]"), s->ServerName,
357 serveraddress);
358 }
359 }
360
361 } else {
362 if (flags & PR_DISPLAY_FL_SEND_NOW) {
363 pr_response_send(R_220, _("%s FTP server ready"), serveraddress);
364
365 } else {
366 pr_response_add(R_220, _("%s FTP server ready"), serveraddress);
367 }
368 }
369 }
370
pr_session_set_idle(void)371 int pr_session_set_idle(void) {
372 const char *user = NULL;
373
374 pr_scoreboard_entry_update(session.pid,
375 PR_SCORE_BEGIN_IDLE, time(NULL),
376 PR_SCORE_CMD, "%s", "idle", NULL, NULL);
377
378 pr_scoreboard_entry_update(session.pid,
379 PR_SCORE_CMD_ARG, "%s", "", NULL, NULL);
380
381 if (session.user) {
382 user = session.user;
383
384 } else {
385 user = "(authenticating)";
386 }
387
388 pr_proctitle_set("%s - %s: IDLE", user, session.proc_prefix);
389 return 0;
390 }
391
pr_session_set_protocol(const char * sess_proto)392 int pr_session_set_protocol(const char *sess_proto) {
393 int count, res = 0, xerrno = 0;
394
395 if (sess_proto == NULL) {
396 errno = EINVAL;
397 return -1;
398 }
399
400 count = pr_table_exists(session.notes, "protocol");
401 if (count > 0) {
402 res = pr_table_set(session.notes, pstrdup(session.pool, "protocol"),
403 pstrdup(session.pool, sess_proto), 0);
404 xerrno = errno;
405
406 } else {
407 res = pr_table_add(session.notes, pstrdup(session.pool, "protocol"),
408 pstrdup(session.pool, sess_proto), 0);
409 xerrno = errno;
410 }
411
412 /* Update the scoreboard entry for this session with the protocol. */
413 pr_scoreboard_entry_update(session.pid, PR_SCORE_PROTOCOL, sess_proto, NULL);
414
415 errno = xerrno;
416 return res;
417 }
418
419 static const char *sess_ttyname = NULL;
420
pr_session_get_ttyname(pool * p)421 const char *pr_session_get_ttyname(pool *p) {
422 char ttybuf[32];
423 const char *sess_proto, *tty_proto = NULL;
424
425 if (p == NULL) {
426 errno = EINVAL;
427 return NULL;
428 }
429
430 if (sess_ttyname) {
431 /* Return the cached name. */
432 return pstrdup(p, sess_ttyname);
433 }
434
435 sess_proto = pr_table_get(session.notes, "protocol", NULL);
436 if (sess_proto) {
437 if (strncmp(sess_proto, "ftp", 4) == 0 ||
438 strncmp(sess_proto, "ftps", 5) == 0) {
439 #if (defined(BSD) && (BSD >= 199103))
440 tty_proto = "ftp";
441 #else
442 tty_proto = "ftpd";
443 #endif
444
445 } else if (strncmp(sess_proto, "ssh2", 5) == 0 ||
446 strncmp(sess_proto, "sftp", 5) == 0 ||
447 strncmp(sess_proto, "scp", 4) == 0 ||
448 strncmp(sess_proto, "publickey", 10) == 0) {
449
450 /* Just use the plain "ssh" string for the tty name for these cases. */
451 tty_proto = "ssh";
452
453 /* Cache the originally constructed tty name for any later retrievals. */
454 sess_ttyname = pstrdup(session.pool, tty_proto);
455 return pstrdup(p, sess_ttyname);
456 }
457 }
458
459 if (tty_proto == NULL) {
460 #if (defined(BSD) && (BSD >= 199103))
461 tty_proto = "ftp";
462 #else
463 tty_proto = "ftpd";
464 #endif
465 }
466
467 memset(ttybuf, '\0', sizeof(ttybuf));
468 #if (defined(BSD) && (BSD >= 199103))
469 pr_snprintf(ttybuf, sizeof(ttybuf), "%s%ld", tty_proto,
470 (long) (session.pid ? session.pid : getpid()));
471 #else
472 pr_snprintf(ttybuf, sizeof(ttybuf), "%s%d", tty_proto,
473 (int) (session.pid ? session.pid : getpid()));
474 #endif
475
476 /* Cache the originally constructed tty name for any later retrievals. */
477 sess_ttyname = pstrdup(session.pool, ttybuf);
478
479 return pstrdup(p, sess_ttyname);
480 }
481