1 /* <!-- copyright */
2 /*
3  * aria2 - The high speed download utility
4  *
5  * Copyright (C) 2015 Tatsuhiro Tsujikawa
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  * In addition, as a special exception, the copyright holders give
22  * permission to link the code of portions of this program with the
23  * OpenSSL library under certain conditions as described in each
24  * individual source file, and distribute linked combinations
25  * including the two.
26  * You must obey the GNU General Public License in all respects
27  * for all of the code used other than OpenSSL.  If you modify
28  * file(s) with this exception, you may extend this exception to your
29  * version of the file(s), but you are not obligated to do so.  If you
30  * do not wish to do so, delete this exception statement from your
31  * version.  If you delete this exception statement from all source
32  * files in the program, then also delete it here.
33  */
34 /* copyright --> */
35 #include "SSHSession.h"
36 
37 #include <cassert>
38 
39 #include "MessageDigest.h"
40 
41 namespace aria2 {
42 
SSHSession()43 SSHSession::SSHSession()
44     : ssh2_(nullptr), sftp_(nullptr), sftph_(nullptr), fd_(-1)
45 {
46 }
47 
~SSHSession()48 SSHSession::~SSHSession() { closeConnection(); }
49 
closeConnection()50 int SSHSession::closeConnection()
51 {
52   if (sftph_) {
53     // TODO this could return LIBSSH2_ERROR_EAGAIN
54     libssh2_sftp_close(sftph_);
55     sftph_ = nullptr;
56   }
57   if (sftp_) {
58     // TODO this could return LIBSSH2_ERROR_EAGAIN
59     libssh2_sftp_shutdown(sftp_);
60     sftp_ = nullptr;
61   }
62   if (ssh2_) {
63     // TODO this could return LIBSSH2_ERROR_EAGAIN
64     libssh2_session_disconnect(ssh2_, "bye");
65     libssh2_session_free(ssh2_);
66     ssh2_ = nullptr;
67   }
68   return SSH_ERR_OK;
69 }
70 
gracefulShutdown()71 int SSHSession::gracefulShutdown()
72 {
73   if (sftph_) {
74     auto rv = libssh2_sftp_close(sftph_);
75     if (rv == LIBSSH2_ERROR_EAGAIN) {
76       return SSH_ERR_WOULDBLOCK;
77     }
78     if (rv != 0) {
79       return SSH_ERR_ERROR;
80     }
81     sftph_ = nullptr;
82   }
83   if (sftp_) {
84     auto rv = libssh2_sftp_shutdown(sftp_);
85     if (rv == LIBSSH2_ERROR_EAGAIN) {
86       return SSH_ERR_WOULDBLOCK;
87     }
88     if (rv != 0) {
89       return SSH_ERR_ERROR;
90     }
91     sftp_ = nullptr;
92   }
93   if (ssh2_) {
94     auto rv = libssh2_session_disconnect(ssh2_, "bye");
95     if (rv == LIBSSH2_ERROR_EAGAIN) {
96       return SSH_ERR_WOULDBLOCK;
97     }
98     if (rv != 0) {
99       return SSH_ERR_ERROR;
100     }
101     libssh2_session_free(ssh2_);
102     ssh2_ = nullptr;
103   }
104   return SSH_ERR_OK;
105 }
106 
sftpClose()107 int SSHSession::sftpClose()
108 {
109   if (!sftph_) {
110     return SSH_ERR_OK;
111   }
112 
113   auto rv = libssh2_sftp_close(sftph_);
114   if (rv == LIBSSH2_ERROR_EAGAIN) {
115     return SSH_ERR_WOULDBLOCK;
116   }
117   if (rv != 0) {
118     return SSH_ERR_ERROR;
119   }
120   sftph_ = nullptr;
121   return SSH_ERR_OK;
122 }
123 
init(sock_t sockfd)124 int SSHSession::init(sock_t sockfd)
125 {
126   ssh2_ = libssh2_session_init();
127   if (!ssh2_) {
128     return SSH_ERR_ERROR;
129   }
130   libssh2_session_set_blocking(ssh2_, 0);
131   fd_ = sockfd;
132   return SSH_ERR_OK;
133 }
134 
checkDirection()135 int SSHSession::checkDirection()
136 {
137   auto dir = libssh2_session_block_directions(ssh2_);
138   if (dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
139     return SSH_WANT_WRITE;
140   }
141   return SSH_WANT_READ;
142 }
143 
writeData(const void * data,size_t len)144 ssize_t SSHSession::writeData(const void* data, size_t len)
145 {
146   // net implemented yet
147   assert(0);
148 }
149 
readData(void * data,size_t len)150 ssize_t SSHSession::readData(void* data, size_t len)
151 {
152   auto nread = libssh2_sftp_read(sftph_, static_cast<char*>(data), len);
153   if (nread == LIBSSH2_ERROR_EAGAIN) {
154     return SSH_ERR_WOULDBLOCK;
155   }
156   if (nread < 0) {
157     return SSH_ERR_ERROR;
158   }
159   return nread;
160 }
161 
handshake()162 int SSHSession::handshake()
163 {
164   auto rv = libssh2_session_handshake(ssh2_, fd_);
165   if (rv == LIBSSH2_ERROR_EAGAIN) {
166     return SSH_ERR_WOULDBLOCK;
167   }
168   if (rv != 0) {
169     return SSH_ERR_ERROR;
170   }
171   return SSH_ERR_OK;
172 }
173 
hostkeyMessageDigest(const std::string & hashType)174 std::string SSHSession::hostkeyMessageDigest(const std::string& hashType)
175 {
176   int h;
177   if (hashType == "sha-1") {
178     h = LIBSSH2_HOSTKEY_HASH_SHA1;
179   }
180   else if (hashType == "md5") {
181     h = LIBSSH2_HOSTKEY_HASH_MD5;
182   }
183   else {
184     return "";
185   }
186   auto fingerprint = libssh2_hostkey_hash(ssh2_, h);
187   if (!fingerprint) {
188     return "";
189   }
190   return std::string(fingerprint, MessageDigest::getDigestLength(hashType));
191 }
192 
authPassword(const std::string & user,const std::string & password)193 int SSHSession::authPassword(const std::string& user,
194                              const std::string& password)
195 {
196   auto rv = libssh2_userauth_password(ssh2_, user.c_str(), password.c_str());
197   if (rv == LIBSSH2_ERROR_EAGAIN) {
198     return SSH_ERR_WOULDBLOCK;
199   }
200   if (rv != 0) {
201     return SSH_ERR_ERROR;
202   }
203   return SSH_ERR_OK;
204 }
205 
sftpOpen(const std::string & path)206 int SSHSession::sftpOpen(const std::string& path)
207 {
208   if (!sftp_) {
209     sftp_ = libssh2_sftp_init(ssh2_);
210     if (!sftp_) {
211       if (libssh2_session_last_errno(ssh2_) == LIBSSH2_ERROR_EAGAIN) {
212         return SSH_ERR_WOULDBLOCK;
213       }
214       return SSH_ERR_ERROR;
215     }
216   }
217   if (!sftph_) {
218     sftph_ = libssh2_sftp_open(sftp_, path.c_str(), LIBSSH2_FXF_READ, 0);
219     if (!sftph_) {
220       if (libssh2_session_last_errno(ssh2_) == LIBSSH2_ERROR_EAGAIN) {
221         return SSH_ERR_WOULDBLOCK;
222       }
223       return SSH_ERR_ERROR;
224     }
225   }
226   return SSH_ERR_OK;
227 }
228 
sftpStat(int64_t & totalLength,time_t & mtime)229 int SSHSession::sftpStat(int64_t& totalLength, time_t& mtime)
230 {
231   LIBSSH2_SFTP_ATTRIBUTES attrs;
232   auto rv = libssh2_sftp_fstat_ex(sftph_, &attrs, 0);
233   if (rv == LIBSSH2_ERROR_EAGAIN) {
234     return SSH_ERR_WOULDBLOCK;
235   }
236   if (rv != 0) {
237     return SSH_ERR_ERROR;
238   }
239   totalLength = attrs.filesize;
240   mtime = attrs.mtime;
241   return SSH_ERR_OK;
242 }
243 
sftpSeek(int64_t pos)244 void SSHSession::sftpSeek(int64_t pos) { libssh2_sftp_seek64(sftph_, pos); }
245 
getLastErrorString()246 std::string SSHSession::getLastErrorString()
247 {
248   if (!ssh2_) {
249     return "SSH session has not been initialized yet";
250   }
251   char* errmsg;
252   libssh2_session_last_error(ssh2_, &errmsg, nullptr, 0);
253   return errmsg;
254 }
255 
256 } // namespace aria2
257