1 /*
2  * ProFTPD - mod_sftp disconnect msgs
3  * Copyright (c) 2008-2017 TJ Saunders
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, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  */
24 
25 #include "mod_sftp.h"
26 #include "ssh2.h"
27 #include "msg.h"
28 #include "packet.h"
29 #include "disconnect.h"
30 
31 extern module sftp_module;
32 
33 struct disconnect_reason {
34   uint32_t code;
35   const char *explain;
36   const char *lang;
37 };
38 
39 static struct disconnect_reason explanations[] = {
40   { SFTP_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT, "Host not allowed to connect", NULL },
41   { SFTP_SSH2_DISCONNECT_PROTOCOL_ERROR, "Protocol error", NULL },
42   { SFTP_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED, "Key exchange failed", NULL },
43   { SFTP_SSH2_DISCONNECT_MAC_ERROR, "MAC error", NULL },
44   { SFTP_SSH2_DISCONNECT_COMPRESSION_ERROR, "Compression error", NULL },
45   { SFTP_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE, "Requested service not available", NULL },
46   { SFTP_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, "Protocol version not supported", NULL },
47   { SFTP_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, "Host key not verifiable", NULL },
48   { SFTP_SSH2_DISCONNECT_CONNECTION_LOST, "Connection lost", NULL },
49   { SFTP_SSH2_DISCONNECT_BY_APPLICATION, "Application disconnected", NULL },
50   { SFTP_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS, "Too many connections", NULL },
51   { SFTP_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER, "Authentication cancelled by user", NULL },
52   { SFTP_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, "No other authentication mechanisms available", NULL },
53   { SFTP_SSH2_DISCONNECT_ILLEGAL_USER_NAME, "Illegal user name", NULL },
54   { 0, NULL, NULL }
55 };
56 
57 static const char *trace_channel = "ssh2";
58 
sftp_disconnect_get_str(uint32_t reason_code)59 const char *sftp_disconnect_get_str(uint32_t reason_code) {
60   register unsigned int i;
61 
62   for (i = 0; explanations[i].explain; i++) {
63     if (explanations[i].code == reason_code) {
64       return explanations[i].explain;
65     }
66   }
67 
68   errno = ENOENT;
69   return NULL;
70 }
71 
sftp_disconnect_send(uint32_t reason,const char * explain,const char * file,int lineno,const char * func)72 void sftp_disconnect_send(uint32_t reason, const char *explain,
73     const char *file, int lineno, const char *func) {
74   struct ssh2_packet *pkt;
75   const pr_netaddr_t *remote_addr;
76   const char *lang = "en-US";
77   unsigned char *buf, *ptr;
78   uint32_t buflen, bufsz;
79   int sockfd;
80 
81   /* Send the client a DISCONNECT mesg. */
82   pkt = sftp_ssh2_packet_create(sftp_pool);
83 
84   remote_addr = pr_netaddr_get_sess_remote_addr();
85 
86   buflen = bufsz = 1024;
87   ptr = buf = palloc(pkt->pool, bufsz);
88 
89   if (explain == NULL) {
90     register unsigned int i;
91 
92     for (i = 0; explanations[i].explain; i++) {
93       if (explanations[i].code == reason) {
94         explain = explanations[i].explain;
95         lang = explanations[i].lang;
96         if (lang == NULL) {
97           lang = "en-US";
98         }
99         break;
100       }
101     }
102 
103     if (explain == NULL) {
104       explain = "Unknown reason";
105     }
106 
107   } else {
108     lang = "en-US";
109   }
110 
111   if (strlen(func) > 0) {
112     pr_trace_msg(trace_channel, 9, "disconnecting (%s) [at %s:%d:%s()]",
113       explain, file, lineno, func);
114 
115   } else {
116     pr_trace_msg(trace_channel, 9, "disconnecting (%s) [at %s:%d]", explain,
117       file, lineno);
118   }
119 
120   sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_MSG_DISCONNECT);
121   sftp_msg_write_int(&buf, &buflen, reason);
122   sftp_msg_write_string(&buf, &buflen, explain);
123   sftp_msg_write_string(&buf, &buflen, lang);
124 
125   pkt->payload = ptr;
126   pkt->payload_len = (bufsz - buflen);
127 
128   (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
129     "disconnecting %s (%s)", pr_netaddr_get_ipstr(remote_addr), explain);
130 
131   /* If we are called very early in the connection lifetime, then the
132    * sftp_conn variable may not have been set yet, thus the conditional here.
133    */
134   if (sftp_conn != NULL) {
135     sockfd = sftp_conn->wfd;
136 
137   } else {
138     sockfd = session.c->wfd;
139   }
140 
141   /* Explicitly set a short poll timeout of 5 secs. */
142   sftp_ssh2_packet_set_poll_timeout(5);
143 
144   if (sftp_ssh2_packet_write(sockfd, pkt) < 0) {
145     int xerrno = errno;
146 
147     pr_trace_msg(trace_channel, 12,
148       "error writing DISCONNECT message: %s", strerror(xerrno));
149   }
150 
151   destroy_pool(pkt->pool);
152 }
153 
sftp_disconnect_conn(uint32_t reason,const char * explain,const char * file,int lineno,const char * func)154 void sftp_disconnect_conn(uint32_t reason, const char *explain,
155     const char *file, int lineno, const char *func) {
156   sftp_disconnect_send(reason, explain, file, lineno, func);
157 
158 #ifdef PR_DEVEL_COREDUMP
159   pr_session_end(PR_SESS_END_FL_NOEXIT);
160   abort();
161 
162 #else
163   pr_session_disconnect(&sftp_module, PR_SESS_DISCONNECT_BY_APPLICATION, NULL);
164 #endif /* PR_DEVEL_COREDUMP */
165 }
166