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