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